Skip to content

Infer delegate type for method groups and anonymous functions#52448

Merged
cston merged 18 commits intodotnet:features/lambdasfrom
cston:delegate-type
Apr 24, 2021
Merged

Infer delegate type for method groups and anonymous functions#52448
cston merged 18 commits intodotnet:features/lambdasfrom
cston:delegate-type

Conversation

@cston
Copy link
Contributor

@cston cston commented Apr 6, 2021

Infer delegate type for method groups and anonymous functions.

The inferred delegate type is used to allow implicit conversion of method groups and lambdas to System.Delegate.

static void Main(string[] args)
{
    System.Delegate d;
    d = Main;              // System.Action<System.String[]>
    d = () => args.Length; // System.Func<System.Int32>
}

The inferred delegate type is ignored in other scenarios currently, so method groups and lambdas cannot be used as var initializers, nor implicitly converted to object with this change.

Proposal: lambda-improvements.md#natural-delegate-type
Test plan: #52192

@ghost ghost added the Area-Compilers label Apr 6, 2021
@cston cston force-pushed the delegate-type branch 3 times, most recently from 3338080 to 87f22bc Compare April 7, 2021 23:29
@cston cston marked this pull request as ready for review April 8, 2021 02:49
@cston cston requested a review from a team as a code owner April 8, 2021 02:49
@cston
Copy link
Contributor Author

cston commented Apr 8, 2021

cc @halter73

// (9,13): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// if (a.E! != null) // 2
Diagnostic(ErrorCode.ERR_BadBinaryOps, "a.E! != null").WithArguments("!=", "method group", "<null>").WithLocation(9, 13),
Diagnostic(ErrorCode.ERR_FeatureInPreview, "a.E").WithArguments("inferred delegate type").WithLocation(9, 13),
Copy link
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

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

Please test with LangVer=preview (I assume the delegate is considered not-null, so warnings on lines 2 and 9 go away) #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

DelegateTypeTests contains several specific tests for language version. For other tests, I chose to either use /langversion:9 or /langversion:preview but not both since both seemed unnecessary. Let me know if you'd prefer the other tests to use /langversion:preview.

}

[Fact]
[ConditionalFact(typeof(IsRelease))]
Copy link
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

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

What motivated the change? #Closed

Copy link
Contributor Author

@cston cston Apr 13, 2021

Choose a reason for hiding this comment

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

What motivated the change?

There is additional validation of lambdas in Binder.CreateAnonymousFunctionConversion(), and similar validation of method groups in Binder.CreateMethodGroupConversion(), that made these tests unnecessarily expensive in DEBUG. #Closed

Copy link
Member

Choose a reason for hiding this comment

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

Consider leaving a comment explaining why we skip (it's just slow)


In reply to: 612782049 [](ancestors = 612782049)

{
object o = () => { };
System.ICloneable c = () => { };
System.Delegate d = () => { };
Copy link
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

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

Should we also test MulticastDelegate per LDM discussion yesterday? #Closed

Copy link
Member

Choose a reason for hiding this comment

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

PROTOTYPE for object and ICloneable (those should work too, right?)


In reply to: 612747777 [](ancestors = 612747777)

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

Added failing test for System.MulticastDelegate and added items to the test plan for each (rather than adding PROTOTYPE comments). #Closed

var comp = CreateCompilation(source);
comp.VerifyDiagnostics(
// (6,30): error CS0019: Operator '==' cannot be applied to operands of type '<null>' and 'lambda expression'
// (6,65): error CS8652: The feature 'inferred delegate type' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
Copy link
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

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

Could you also verify diagnostics with LangVer=preview? (also consider doing the same in TestTypelessTupleAndTupleOfDefaults above, and TestWithTypelessTuple below) #Closed

yield return getData("static object F(int _1, object _2, int _3, object _4, int _5, object _6, int _7, object _8, int _9, object _10, int _11, object _12, int _13, object _14, int _15, object _16, int _17) => null;", "F", null, null);
yield return getData("T F<T>() => default;", "(new Program()).F<int>", "System.Func<System.Int32>", "Func`1");

static object?[] getData(string methodDeclaration, string methodGroupExpression, string? expectedType, string? expectedName) =>
Copy link
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

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

It looks like methodGroupExpression is always "F". Consider removing that parameter and hard-coding in tests #Closed

Copy link
Contributor Author

@cston cston Apr 13, 2021

Choose a reason for hiding this comment

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

The method declaration and method group are related, and at the moment, the test and the test data are mostly independent. #Closed

var delegateType = type.GetDelegateType();
var delegateType = (type.SpecialType == SpecialType.System_Delegate) ?
// PROTOTYPE: We're resolving the method group multiple times in the code path for a single conversion.
_binder.GetMethodGroupDelegateType(methodGroup, diagnostics).Item1 :
Copy link
Member

@jcouv jcouv Apr 13, 2021

Choose a reason for hiding this comment

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

Consider using tuple names.
Do we have a caller of GetMethodGroupDelegateType that consumes Item2? #Closed

Copy link
Contributor Author

@cston cston Apr 13, 2021

Choose a reason for hiding this comment

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

Do we have a caller of GetMethodGroupDelegateType that consumes Item2?

Good catch, that value is unused. #Closed

// C# preview features.
case MessageID.IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction:
case MessageID.IDS_FeatureLambdaAttributes:
case MessageID.IDS_FeatureInferredDelegateType:
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

// semantic check #Closed

foreach (var scope in new ExtensionMethodScopes(this))
{
var methodGroup = MethodGroup.GetInstance();
try
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Do we need the try/finally? For comparison, BindExtensionMethod does without. #Closed

d = t.F1<int>;
d = t.F1<T>;
d = t.F2<T, object>;
d = t.F2<object, T>;
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Do we have a test similar to this (instance or extension method with type parameters converted to Delegate) but where type arguments are not provided on the method group? I assume then the method group will just be empty, is that right? #Closed

Copy link
Member

Choose a reason for hiding this comment

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

What was the resolution?


In reply to: 612862173 [](ancestors = 612862173)

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

What was the resolution?

I've updated this test to include method groups without type arguments. It'll be in the next commit. #Closed

return null;
}
var method = node.Methods.SingleOrDefault();
if (node.SearchExtensionMethods)
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Should we even look at extension methods if we've found exactly one instance method already?

It looks like this was discussed in LDM (I must have zoned out). From the spec:

Requiring a method group to contain a single method means that adding an overload (including an extension method overload) for a method that previously had no overloads is a breaking change if the original method was used as a method group with inferred type. #Closed

void F()
{
Delegate d1 = F1;
Delegate d2 = this.F2;
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

It looks like base has special rules for binding method groups. Consider adding a test:
See BindInstanceMemberAccess (line 6290)
var searchExtensionMethodsIfNecessary = !leftIsBaseReference; #Closed

Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Consider also testing with type as LHS: Delegate d3 = Program3.StaticMethod and Delegate d4 = Program4<int>.StaticMethod
Also test with a dynamic receiver (error)


In reply to: 612866844 [](ancestors = 612866844)

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

Added ExtensionMethods_05() for base with an extension method; added tests for static methods with explicit type to GetMethodGroupData(); added DynamicReceiver() for dynamic test. #Closed

{
// Must be a bona fide delegate type, not an expression tree type.
if (!destination.IsDelegateType())
if (!(destination.IsDelegateType() || destination.SpecialType == SpecialType.System_Delegate))
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Should we also allow destination to be object, or ICloneable, or MulticastDelegate here? #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

Should we also allow destination to be object, or ICloneable, or MulticastDelegate here?

Perhaps. Let's address later when handling those cases. #Closed

constantValueOpt: ConstantValue.NotAvailable,
type: destination)
{ WasCompilerGenerated = source.WasCompilerGenerated };
if (destination.SpecialType == SpecialType.System_Delegate || destination.IsNonGenericExpressionType())
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

destination.SpecialType == SpecialType.System_Delegate

Same here (object, ICloneable, etc) and below in createAnonymousFunctionConversion, CreateMethodGroupConversion, MethodGroupConversionHasErrors and other places in this PR. Consider extracting this check to a helper method, so that we don't forget to fix some places when adding support for those other types #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

Let's address later when addressing implicit conversions to those base and interface types. #Closed

var conversion = (resolution.IsEmpty || resolution.HasAnyErrors) ?
Conversion.NoConversion :
ToConversion(resolution.OverloadResolutionResult, resolution.MethodGroup, ((NamedTypeSymbol)destination).DelegateInvokeMethod.ParameterCount);
ToConversion(resolution.OverloadResolutionResult, resolution.MethodGroup, (destination.SpecialType == SpecialType.System_Delegate ? methodSymbol : ((NamedTypeSymbol)destination).DelegateInvokeMethod).ParameterCount);
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

destination.SpecialType == SpecialType.System_Delegate ? methodSymbol : ((NamedTypeSymbol)destination).DelegateInvokeMethod

Could we unconditionally use methodSymbol here? #Closed


return expr;

BoundConversion createAnonymousFunctionConversion(SyntaxNode syntax, UnboundLambda unboundLambda, Conversion conversion, bool isCast, ConversionGroup? conversionGroup, TypeSymbol destination, BindingDiagnosticBag diagnostics)
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

static? #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

The local function uses this.Compilation. #Closed

return true;
}
if (type.Arity == 1 &&
type.MangleName)
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

nit: this is existing logic, but isn't it redundant with the Arity check? #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

this is existing logic, but isn't it redundant with the Arity check?

This check will ensure we don't match to a generic type named Expression without the trailing `1, although that scenario is unlikely. Since this is an existing check, I'll leave it as is. #Closed

else
{
lambdaSymbol = CreateLambdaSymbol(Binder.ContainingMemberOrLambda, returnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, refKind);
lambdaSymbol = CreateLambdaSymbol(Binder.ContainingMemberOrLambda!, returnType, cacheKey.ParameterTypes, cacheKey.ParameterRefKinds, refKind);
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Binder.ContainingMemberOrLambda!

nit: should be an assertion. Also in BindWithParameterAndReturnType below #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

Added an assertion to UnboundLambda..ctor(). #Closed

return true;
}

private BoundLambda ReallyBindNaturalType(TypeSymbol expressionType)
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

nit: consider calling the parameter "delegateType" to match caller and other similar method #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

In this case, the type is not a (strongly-typed) delegate type though. It is either System.Delegate or System.Linq.Expressions.Expression. I realize the naming is inconsistent since callers refer to this as "delegateType" but I was reluctant to change the callers. The current names were a compromise although not ideal. #Closed

}");
}
}
}
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

Some test ideas:

  1. the spec calls out behavior when the lambda or method group involves a modreq or modopt, but I couldn't find tests for that. ("modopt() or modreq() in the method group signature are ignored in the corresponding delegate type.")
  2. make Action, Func, or Delegate missing
  3. have we covered this section of the spec: "if [...] any of the parameter types or return are not valid type arguments"?
  4. I'm not sure how to track this: we should remember to test delegate type inference once we support explicit return types. (I don't expect any problem though)
  5. You already have tests for Type and Converted types of the lambda and method group. We should do the same but also verifying inferred nullability.
  6. var and invocation sections of the spec are intentionally out of scope for this PR, is that correct?
  7. Do we have some tests that verify the emitted delegate type (as opposed to just the semantic model)? #Closed

Copy link
Contributor Author

@cston cston Apr 19, 2021

Choose a reason for hiding this comment

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

Thanks for the suggestions:

  1. Added tests for modopt and modreq.
  2. Added tests for missing System.Action and System.Func; added missing System.Delegate to the test plan.
  3. Added test for parameter types and return type that are not valid type arguments.
  4. No change.
  5. Added nullability to test plan.
  6. Correct, var and directly invoking a lambda expression are out of scope. Included in test plan.
  7. A number of tests report the delegate type from the compiled executable. #Closed

comp.VerifyDiagnostics(
// (6,20): error CS0030: Cannot convert type 'method' to 'Delegate'
// object o = (System.Delegate)F;
Diagnostic(ErrorCode.ERR_NoExplicitConv, "(System.Delegate)F").WithArguments("method", "System.Delegate").WithLocation(6, 20));
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

nit: indent #Closed

}";
var comp = CreateCompilation(source, parseOptions: TestOptions.RegularPreview);
comp.VerifyDiagnostics();
}
Copy link
Member

@jcouv jcouv Apr 14, 2021

Choose a reason for hiding this comment

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

If we don't have tests for that already, can we check the natural type (on semantic model) in extension scenario? #Closed

Copy link
Contributor Author

@cston cston Apr 14, 2021

Choose a reason for hiding this comment

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

Tested type from the semantic model and at runtime. (Note, the semantic model currently does not expose the natural type. I've added an item to the test plan to handle that.) #Closed

else
{
CompileAndVerify(comp, expectedOutput: expectedType);
}
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

Consider asserting TypeInfo here as well. #Resolved

Copy link
Member

Choose a reason for hiding this comment

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

Same here.


In reply to: 618753395 [](ancestors = 618753395)

else
{
comp.VerifyDiagnostics();
}
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

Consider asserting TypeInfo here as well. #Resolved

Copy link
Member

Choose a reason for hiding this comment

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

Same here.


In reply to: 618754496 [](ancestors = 618754496)

if (m.IsStatic) continue;
break;
}
if (!updateCandidate(ref method, m))
Copy link
Member

@jcouv jcouv Apr 22, 2021

Choose a reason for hiding this comment

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

nit: the name and return value of this local function make this odd. Consider "if (!isCandidateUnique(ref candidate, m))`? #Closed

{{
void M()
{{
System.Delegate d = {methodGroupExpression};
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

methodGroupExpression

Consider using GetSymbolInfo to validate what extension method this is binding to. #Resolved

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added runtime validation.

GetSymbolInfo() does not return the resolved extension method currently. I've added a PROTOTYPE comment for now.

{{
void M()
{{
System.Delegate d = {methodGroupExpression};
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

methodGroupExpression

Same here: consider asserting what method this is actually binding to. #Resolved

comp.VerifyDiagnostics(
// (14,15): error CS8915: The delegate type could not be inferred.
// d = p.F2;
Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "F2").WithLocation(14, 15));
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

This isn't making sense to me: why can't this be inferred to Action? #Closed

Copy link
Contributor Author

@cston cston Apr 22, 2021

Choose a reason for hiding this comment

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

why can't this be inferred to Action?

Because there are two overloads with distinct signatures: Program.F2(int x) and E.F2(this object x). #Closed

Copy link
Member

Choose a reason for hiding this comment

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

Ah, ok, thanks. I missed that one.


In reply to: 618776762 [](ancestors = 618776762)

if (!m.IsStatic) continue;
break;
case { WasCompilerGenerated: false }:
if (m.IsStatic) continue;
Copy link
Member

@jcouv jcouv Apr 22, 2021

Choose a reason for hiding this comment

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

I need help understanding this (why distinguish on whether the receiver is compiler-generated or not). Let's chat later this afternoon. #Closed

Copy link
Contributor Author

@cston cston Apr 22, 2021

Choose a reason for hiding this comment

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

Changed to use explicit case BoundThisReference { ... }: for clarity based on our conversation. #Closed

}
}";

var expectedOutput = @"M(Action<string> a)";
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

This should end up being M<T>(T t) in a follow up, right? #Closed

Copy link
Contributor Author

@cston cston Apr 22, 2021

Choose a reason for hiding this comment

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

Yes, this test and the following test will change. #Closed

}

[Fact]
public void NullableAnalysis_01()
Copy link
Member

@333fred 333fred Apr 22, 2021

Choose a reason for hiding this comment

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

I'm not sure what these are actually testing? #Closed

Copy link
Contributor Author

@cston cston Apr 23, 2021

Choose a reason for hiding this comment

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

Added comments. #Closed

@333fred
Copy link
Member

333fred commented Apr 22, 2021

Done review pass (commit 15)

{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret }
}";
var refA = CompileIL(sourceA, prependDefaultHeader: false, autoInherit: false);
Copy link
Member

@jcouv jcouv Apr 23, 2021

Choose a reason for hiding this comment

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

nit: Is it necessary to use IL to test those types are missing? (also in test below). I'd have though MakeMemberMissing or a C# implementation could do.
It would be good to also test Expression e = () => {}; with missing Expression1` #Closed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The IL is simple enough for these missing types so that seemed easiest.

Added a test for Expression<TDelegate>.


var comp = CreateEmptyCompilation(sourceB, new[] { refA }, parseOptions: TestOptions.RegularPreview);
comp.VerifyDiagnostics(
// (9,13): error CS0648: 'Action<T>' is a type not supported by the language
Copy link
Member

@jcouv jcouv Apr 23, 2021

Choose a reason for hiding this comment

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

Seems like we're getting a duplicate error. Is that expected? #Closed

Copy link
Contributor Author

@cston cston Apr 23, 2021

Choose a reason for hiding this comment

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

Yes, we're currently calling GetMethodGroupDelegateType() twice in the code path that generates the conversion. There are PROTOTYPE comments to avoid the duplicate calls. #Closed

Diagnostic(ErrorCode.ERR_FeatureInPreview, "() => { }").WithArguments("inferred delegate type").WithLocation(8, 13));

CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput:
@"C.M
Copy link
Member

@jcouv jcouv Apr 23, 2021

Choose a reason for hiding this comment

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

It would be good to comment on behavior change. Should document the break in compiler doc. #Closed

Copy link
Contributor Author

@cston cston Apr 23, 2021

Choose a reason for hiding this comment

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

The behavior has not changed yet. I will update the breaking change document at the same time. #Closed

Copy link
Member

Choose a reason for hiding this comment

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

Copy&pasting this test into sharplab I get `E.M" in output.


In reply to: 618955062 [](ancestors = 618955062)

Copy link
Contributor Author

@cston cston Apr 23, 2021

Choose a reason for hiding this comment

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

You're right, thanks. I've added a comment and updated the breaking change document. #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 16)

@cston
Copy link
Contributor Author

cston commented Apr 23, 2021

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

@jcouv
Copy link
Member

jcouv commented Apr 23, 2021

        // Breaking change from C#9 which binds to E.M.

Is this the only overload resolution test illustrates a breaking change?


In reply to: 825981242


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs:1832 in ec93572. [](commit_id = ec93572, deletion_comment = False)

return LambdaConversionResult.ExpressionTreeFromAnonymousMethod;
}

if (delegateType is null)
Copy link
Member

@jcouv jcouv Apr 23, 2021

Choose a reason for hiding this comment

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

nit: Consider if (type.Arity == 0) ... if that is more directly what is intended, and assert that delegateType is not null otherwise. #ByDesign

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll leave as is, since delegateType here means the type argument to Expression<TDelegate> which is null for non-generic Expression.

@cston
Copy link
Contributor Author

cston commented Apr 23, 2021

        // Breaking change from C#9 which binds to E.M.

Sure


In reply to: 825982476


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs:1832 in ec93572. [](commit_id = ec93572, deletion_comment = False)

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 17) with a question and a nit to consider.

@jcouv
Copy link
Member

jcouv commented Apr 23, 2021

        // Breaking change from C#9 which binds to E.M.

Sure


In reply to: 825982476 [](ancestors = 825982476)


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs:1832 in ec93572. [](commit_id = ec93572, deletion_comment = False)

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 17). A couple of test followup comments left, but they should be quick to address.

@jcouv
Copy link
Member

jcouv commented Apr 24, 2021

        // https://github.com/dotnet/roslyn/issues/52870: GetSymbolInfo() should return resolved method from method group.

It looks like you added tests for GetSymbolInfo on method groups, but not lambdas. Could you show what's the current behavior and file an issue if needed?


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs:766 in b147208. [](commit_id = b147208, deletion_comment = False)

@cston cston merged commit db80551 into dotnet:features/lambdas Apr 24, 2021
@cston cston deleted the delegate-type branch April 24, 2021 04:26
@davidfowl
Copy link
Member

🔥🔥🔥

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.

5 participants