Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for ref-readonly in the .NET interop source generators #97720

Merged
merged 1 commit into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/// <summary>
Expand Down Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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}")
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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<AttributeSyntax> rehydratedAttributes = new();
Expand Down Expand Up @@ -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))),
Expand All @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,9 @@ public IEnumerable<ParameterSyntax> 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));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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
Expand Down Expand Up @@ -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"),
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ public static IEnumerable<object[]> CodeSnippetsToCompile()

// Parameter modifiers
yield return new[] { ID(), CodeSnippets.SingleParameterWithModifier("int", "scoped ref") };
yield return new[] { ID(), CodeSnippets.SingleParameterWithModifier("int", "ref readonly") };
}

public static IEnumerable<object[]> CustomCollections()
Expand Down
Loading