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

Synthesize delegate types for lambda expressions and method groups #55203

Merged
merged 6 commits into from
Jul 30, 2021

Conversation

cston
Copy link
Member

@cston cston commented Jul 28, 2021

Synthesize delegate types for lambda expressions and method groups with inferred type that cannot be represented with System.Func<> or System.Action<> types.

Uses the existing compiler infrastructure for delegates created at dynamic call-sites.

The PR does not include support for parameter or return types that cannot be used as generic type arguments: pointer types, ref struct types, etc. (see #55217). Those cases will be addressed separately.

Proposal: lambda-improvements.md
Test plan: #52192

@cston cston marked this pull request as ready for review July 29, 2021 09:26
@cston cston requested a review from a team as a code owner July 29, 2021 09:26
@333fred 333fred self-assigned this Jul 29, 2021
if (returnsVoid && returnRefKind != RefKind.None)
{
// Invalid return type.
return null;
Copy link
Member

@333fred 333fred Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to consider diagnostics for the cases we're returning null? It would be better to have specific error messages for those cases, I think. #Closed

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A specific error shouldn't be necessary. There are two cases where we return null: the case here of a ref void return type which will have been reported as a declaration error; and the case below where a parameter or return type cannot be used as a type argument. The second case should be resolved in #55217.


internal int Capacity => _bits.Capacity / 2;

internal IEnumerable<ulong> Words() => _bits.Words();
Copy link
Member

@jcouv jcouv Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm surprised dynamic tests weren't affected by the change from BitVector to RefKindVector. MakeDynamicCallSiteDelegateName uses those "words" as part of the name for the synthesized delegate type.
Never mind, they were #Closed

static bool isValidTypeArgument(TypeSymbol? type)
{
return type is { } &&
!type.IsPointerOrFunctionPointer() &&
Copy link
Member

@jcouv jcouv Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Consider including a test with an array of pointer types (that's an example to illustrate the difference between IsPointerOrFunctionPointer and IsUnsafe) #Closed

var delegateType = ((INamedTypeSymbol)local.Type);
Assert.Equal(Accessibility.Internal, delegateType.DeclaredAccessibility);
Assert.Equal(expectedInvokeMethod, delegateType.DelegateInvokeMethod.ToTestDisplayString());
}
Copy link
Member

@jcouv jcouv Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's include a test with more types: dynamic, tuples with names, nint or reference types with nullability. #Resolved

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To add to the list, arrays of function pointer types where the parameters/return type of the function pointer have refkinds.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also think we should have public API verification, ie what are we returning from GetSymbolInfo or GetTypeInfo for these synthesized symbols.

}
static void M2<T>(T t) where T : struct
{
var d = (ref T t) => t;
Copy link
Member

@jcouv jcouv Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider varying parameter name here #Closed

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done with review pass(iteration 5)

}

int parameterCount = delegateSignature.Length - (returnsVoid ? 0 : 1);
Debug.Assert(_factory.CompilationState.ModuleBuilderOpt is { });
int generation = _factory.CompilationState.ModuleBuilderOpt.CurrentGenerationOrdinal;
var synthesizedType = _factory.Compilation.AnonymousTypeManager.SynthesizeDelegate(parameterCount, byRefs, returnsVoid, generation);
return synthesizedType.Construct(delegateSignature);

// The distinction between by-ref kinds is dropped.
Copy link
Member

@333fred 333fred Jul 29, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? #Resolved

Copy link
Member Author

@cston cston Jul 30, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure, perhaps to allow greater sharing of delegate types. But I believe this preserves the original behavior.

}
}

public bool Equals(RefKindVector other)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use record structs yet?

@333fred
Copy link
Member

333fred commented Jul 29, 2021

Done review pass (commit 5)

@cston
Copy link
Member Author

cston commented Jul 30, 2021

@jcouv, @333fred, all feedback has been addressed, thanks.

@jcouv jcouv self-assigned this Jul 30, 2021
Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks (iteration 6)

Copy link
Member

@333fred 333fred left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM (commit 6)

@jcouv
Copy link
Member

jcouv commented Jul 30, 2021

Approved to merge

@cston cston merged commit bbf1e87 into dotnet:release/dev17.0 Jul 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants