diff --git a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs index b319aac03586e8..dd04872d97b4a6 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/ComInterfaceGenerator/ComInterfaceGenerator.cs @@ -298,7 +298,6 @@ private static IncrementalMethodStubGenerationContext CalculateStubInformation(M var managedSignatureAsNativeOut = returnSwappedSignatureElements[i] with { RefKind = RefKind.Out, - RefKindSyntax = SyntaxKind.OutKeyword, ManagedIndex = TypePositionInfo.ReturnIndex, NativeIndex = symbol.Parameters.Length }; diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs index f17a2b013bbd09..90aea0de280c38 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/BreakingChangeDetector.cs @@ -23,7 +23,7 @@ public ResolvedGenerator Create(TypePositionInfo info, StubCodeContext context) } // Breaking change: [MarshalAs(UnmanagedType.Struct)] in object in unmanaged-to-managed scenarios will not respect VT_BYREF. - if (info is { RefKind: RefKind.In, MarshallingAttributeInfo: NativeMarshallingAttributeInfo(ManagedTypeInfo(_, TypeNames.ComVariantMarshaller), _) } + if (info is { RefKind: RefKind.In or RefKind.RefReadOnlyParameter, MarshallingAttributeInfo: NativeMarshallingAttributeInfo(ManagedTypeInfo(_, TypeNames.ComVariantMarshaller), _) } && context.Direction == MarshalDirection.UnmanagedToManaged) { gen = ResolvedGenerator.ResolvedWithDiagnostics( diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs index b9b007cbbfb33a..6da4b4c69fa51c 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallerHelpers.cs @@ -88,7 +88,7 @@ public static string GetLastIndexMarshalledIdentifier(TypePositionInfo info, Stu internal static bool CanUseCallerAllocatedBuffer(TypePositionInfo info, StubCodeContext context) { - return context.SingleFrameSpansNativeContext && (!info.IsByRef || info.RefKind == RefKind.In); + return context.SingleFrameSpansNativeContext && (!info.IsByRef || info.RefKind == RefKind.In || info.RefKind == RefKind.RefReadOnlyParameter); } /// @@ -291,6 +291,7 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo switch (info.RefKind) { case RefKind.In: + case RefKind.RefReadOnlyParameter: return MarshalDirection.ManagedToUnmanaged; case RefKind.Ref: return MarshalDirection.Bidirectional; @@ -312,6 +313,7 @@ public static MarshalDirection GetMarshalDirection(TypePositionInfo info, StubCo switch (info.RefKind) { case RefKind.In: + case RefKind.RefReadOnlyParameter: return MarshalDirection.UnmanagedToManaged; case RefKind.Ref: return MarshalDirection.Bidirectional; @@ -380,5 +382,56 @@ public static void ValidateCountInfoAvailableAtCall(MarshalDirection stubDirecti // If the parameter is multidimensional and a higher indirection level parameter is ByValue [Out], then we should warn. } } + + public static SyntaxTokenList GetManagedParameterModifiers(TypePositionInfo typeInfo) + { + SyntaxTokenList tokens = TokenList(); + + // "out" parameters are implicitly scoped, so we can't put the "scoped" keyword on them. + // All other cases of explicit parameters are only scoped when the "scoped" keyword is present. + // When the "scoped" keyword is present, it must be present on all declarations. + if (typeInfo.ScopedKind != ScopedKind.None && typeInfo.RefKind != RefKind.Out) + { + tokens = tokens.Add(Token(SyntaxKind.ScopedKeyword)); + } + + if (typeInfo.IsByRef) + { + switch (typeInfo.RefKind) + { + case RefKind.In: + tokens = tokens.Add(Token(SyntaxKind.InKeyword)); + break; + case RefKind.Ref: + tokens = tokens.Add(Token(SyntaxKind.RefKeyword)); + break; + + case RefKind.Out: + tokens = tokens.Add(Token(SyntaxKind.OutKeyword)); + break; + case RefKind.RefReadOnlyParameter: + tokens = tokens.Add(Token(SyntaxKind.RefKeyword)); + tokens = tokens.Add(Token(SyntaxKind.ReadOnlyKeyword)); + break; + default: + throw new NotImplementedException($"Support for some RefKind: {typeInfo.RefKind}"); + } + } + + return tokens; + } + + public static SyntaxToken GetManagedArgumentRefKindKeyword(TypePositionInfo typeInfo) + { + return typeInfo.RefKind switch + { + RefKind.None => default, + RefKind.In => Token(SyntaxKind.InKeyword), + RefKind.Ref => Token(SyntaxKind.RefKeyword), + RefKind.Out => Token(SyntaxKind.OutKeyword), + RefKind.RefReadOnlyParameter => Token(SyntaxKind.RefKeyword), + _ => throw new NotImplementedException($"Support for some RefKind: {typeInfo.RefKind}") + }; + } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs index 4c5454e7794090..9b9ad2589b1024 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/Marshalling/MarshallingGeneratorExtensions.cs @@ -105,7 +105,7 @@ public static ParameterSyntax AsParameter(this IMarshallingGenerator generator, private static ParameterSyntax GenerateForwardingParameter(TypePositionInfo info, string identifier) { ParameterSyntax param = Parameter(Identifier(identifier)) - .WithModifiers(TokenList(Token(info.RefKindSyntax))) + .WithModifiers(MarshallerHelpers.GetManagedParameterModifiers(info)) .WithType(info.ManagedType.Syntax); List rehydratedAttributes = new(); @@ -143,7 +143,7 @@ public static ArgumentSyntax AsArgument(this IMarshallingGenerator generator, Ty return generator.GetValueBoundaryBehavior(info, context) switch { ValueBoundaryBehavior.ManagedIdentifier when !info.IsByRef => Argument(IdentifierName(managedIdentifier)), - ValueBoundaryBehavior.ManagedIdentifier when info.IsByRef => Argument(IdentifierName(managedIdentifier)).WithRefKindKeyword(Token(info.RefKindSyntax)), + ValueBoundaryBehavior.ManagedIdentifier when info.IsByRef => Argument(IdentifierName(managedIdentifier)).WithRefKindKeyword(MarshallerHelpers.GetManagedArgumentRefKindKeyword(info)), ValueBoundaryBehavior.NativeIdentifier => Argument(IdentifierName(nativeIdentifier)), ValueBoundaryBehavior.AddressOfNativeIdentifier => Argument(PrefixUnaryExpression(SyntaxKind.AddressOfExpression, IdentifierName(nativeIdentifier))), ValueBoundaryBehavior.CastNativeIdentifier => Argument(CastExpression(generator.AsParameter(info, context).Type, IdentifierName(nativeIdentifier))), @@ -156,7 +156,7 @@ public static ArgumentSyntax AsManagedArgument(this IMarshallingGenerator genera var (managedIdentifier, _) = context.GetIdentifiers(info); if (info.IsByRef) { - return Argument(IdentifierName(managedIdentifier)).WithRefKindKeyword(Token(info.RefKindSyntax)); + return Argument(IdentifierName(managedIdentifier)).WithRefKindKeyword(MarshallerHelpers.GetManagedArgumentRefKindKeyword(info)); } return Argument(IdentifierName(managedIdentifier)); } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SignatureContext.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SignatureContext.cs index 59a5ee733cab69..f36857d379e632 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SignatureContext.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/SignatureContext.cs @@ -42,24 +42,9 @@ public IEnumerable StubParameters if (typeInfo.ManagedIndex != TypePositionInfo.UnsetIndex && typeInfo.ManagedIndex != TypePositionInfo.ReturnIndex) { - SyntaxTokenList tokens = TokenList(); - - // "out" parameters are implicitly scoped, so we can't put the "scoped" keyword on them. - // All other cases of explicit parameters are only scoped when the "scoped" keyword is present. - // When the "scoped" keyword is present, it must be present on all declarations. - if (typeInfo.ScopedKind != ScopedKind.None && typeInfo.RefKind != RefKind.Out) - { - tokens = tokens.Add(Token(SyntaxKind.ScopedKeyword)); - } - - if (typeInfo.IsByRef) - { - tokens = tokens.Add(Token(typeInfo.RefKindSyntax)); - } - yield return Parameter(Identifier(typeInfo.InstanceIdentifier)) .WithType(typeInfo.ManagedType.Syntax) - .WithModifiers(tokens); + .WithModifiers(MarshallerHelpers.GetManagedParameterModifiers(typeInfo)); } } } diff --git a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs index 76697b0e6dd6a1..49ebf92f600ec1 100644 --- a/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs +++ b/src/libraries/System.Runtime.InteropServices/gen/Microsoft.Interop.SourceGeneration/TypePositionInfo.cs @@ -62,7 +62,6 @@ public static int IncrementIndex(int index) public string InstanceIdentifier { get; init; } = string.Empty; public RefKind RefKind { get; init; } = RefKind.None; - public SyntaxKind RefKindSyntax { get; init; } = SyntaxKind.None; public bool IsByRef => RefKind != RefKind.None; @@ -87,7 +86,6 @@ public static TypePositionInfo CreateForParameter(IParameterSymbol paramSymbol, { InstanceIdentifier = ParseToken(paramSymbol.Name).IsReservedKeyword() ? $"@{paramSymbol.Name}" : paramSymbol.Name, RefKind = paramSymbol.RefKind, - RefKindSyntax = RefKindToSyntax(paramSymbol.RefKind), ByValueContentsMarshalKind = byValueContentsMarshalKind, ByValueMarshalAttributeLocations = (inLocation, outLocation), ScopedKind = paramSymbol.ScopedKind @@ -132,17 +130,5 @@ private static (ByValueContentsMarshalKind, Location? inAttribute, Location? out return (marshalKind, inAttributeLocation, outAttributeLocation); } - - private static SyntaxKind RefKindToSyntax(RefKind refKind) - { - return refKind switch - { - RefKind.In => SyntaxKind.InKeyword, - RefKind.Ref => SyntaxKind.RefKeyword, - RefKind.Out => SyntaxKind.OutKeyword, - RefKind.None => SyntaxKind.None, - _ => throw new NotImplementedException("Support for some RefKind"), - }; - } } } diff --git a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs index 03f678773a895a..b78fbc8552f2a4 100644 --- a/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs +++ b/src/libraries/System.Runtime.InteropServices/tests/LibraryImportGenerator.UnitTests/Compiles.cs @@ -257,6 +257,7 @@ public static IEnumerable CodeSnippetsToCompile() // Parameter modifiers yield return new[] { ID(), CodeSnippets.SingleParameterWithModifier("int", "scoped ref") }; + yield return new[] { ID(), CodeSnippets.SingleParameterWithModifier("int", "ref readonly") }; } public static IEnumerable CustomCollections()