-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Reuse previous lambda body binding if possible #43366
Conversation
@@ -3195,6 +3193,15 @@ public BoundBlock BindLambdaExpressionAsBlock(ExpressionSyntax body, DiagnosticB | |||
return bodyBinder.CreateBlockFromExpression(body, bodyBinder.GetDeclaredLocalsForScope(body), refKind, expression, expressionSyntax, diagnostics); | |||
} | |||
|
|||
public BoundBlock BindLambdaExpressionAsBlockContinued(ExpressionSyntax body, BoundExpression expression, DiagnosticBag diagnostics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BindLambdaExpressionAsBlockContinued [](start = 26, length = 36)
"Continued" doesn't really convey anything specific. Continued from where? Etc. Consider renaming with a more specific name, it feels like simply using CreateBlockFromExpression
would work. #Closed
types.Add((returnStatement.ExpressionOpt, type)); | ||
} | ||
} | ||
|
||
HashSet<DiagnosticInfo> useSiteDiagnostics = null; | ||
var bestType = CalculateReturnType(compilation, conversions, delegateType, types, isAsync, node, ref useSiteDiagnostics); | ||
return new InferredLambdaReturnType(types.Count, hasReturnWithoutArgument, refKind, bestType, useSiteDiagnostics.AsImmutableOrEmpty()); | ||
types.Free(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
types.Free(); [](start = 12, length = 13)
Were we leaking this builder before? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -927,7 +933,7 @@ BoundLambda rebind(BoundLambda lambda) | |||
returnType = DelegateReturnTypeWithAnnotations(invokeMethod, out refKind); | |||
if (!returnType.HasType || returnType.Type.ContainsTypeParameter()) | |||
{ | |||
var t = (inferredReturnType.HadExpressionlessReturn || inferredReturnType.NumExpressions == 0) | |||
var t = !inferredReturnType.HasReturnValue |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!inferredReturnType.HasReturnValue [](start = 32, length = 34)
It feels like this refactoring is not semantically equivalent. It looks like before we could have HadExpressionlessReturn == true
and inferredReturnType.NumExpressions > 0
at the same time. The orignal condition would be true
. The new condition for the same scenario would be false
. #Closed
if (statements.Length == 1 && | ||
statements[0] is BoundReturnStatement { RefKind: Microsoft.CodeAnalysis.RefKind.None, ExpressionOpt: BoundExpression expr }) | ||
{ | ||
return expr; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return expr; [](start = 20, length = 12)
It is not clear why it is reusable? The name doesn't imply any restrictions on the input. I can imagine a situation when some conversions are have already been applied based on some context, therefore making the expression un-reusable. If the method is not responsible for enforcing reusability, then it should not be part of the name. This is just a helper that extracts return expression from a block with a single ref-none return. #Closed
return null; | ||
} | ||
|
||
protected override BoundBlock CreateBlockFromExpression(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, BoundExpression expression, DiagnosticBag diagnostics) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LambdaSymbol lambdaSymbol [](start = 64, length = 25)
It doesn't look like this parameter aver used, please remove. #Closed
var cacheKey = ReturnInferenceCacheKey.Create(delegateType, IsAsync); | ||
|
||
// When binding for real (not for return inference), there is still | ||
// a good chance that we could reuse a body of a lambda previous bound for |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a good chance that we could reuse a body of a lambda previous bound for [](start = 15, length = 71)
It doesn't look like we are reusing the body, we are reusing some portion of it. Consider reflecting this in the comment and also explaining why it is Ok to reuse that portion. #Closed
{ | ||
var statements = body.Statements; | ||
if (statements.Length == 1 && | ||
statements[0] is BoundReturnStatement { RefKind: Microsoft.CodeAnalysis.RefKind.None, ExpressionOpt: BoundExpression expr }) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RefKind: Microsoft.CodeAnalysis.RefKind.None [](start = 60, length = 44)
It is not obvious why this condition is significant, consider adding a clarifying comment. #Closed
// return type inference. | ||
if (_returnInferenceCache.TryGetValue(cacheKey, out BoundLambda returnInferenceLambda) && | ||
GetReusableLambdaExpressionBody(returnInferenceLambda.Body) is BoundExpression expression && | ||
(lambdaSymbol = returnInferenceLambda.Symbol).RefKind == refKind && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(lambdaSymbol = returnInferenceLambda.Symbol).RefKind == refKind [](start = 16, length = 64)
Should we also check that both are None as GetReusableLambdaExpressionBody
checks below? #Closed
|
||
[ConditionalFactAttribute(typeof(IsRelease))] | ||
[WorkItem(1083969, "https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1083969")] | ||
public void NestedLambdas_02() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NestedLambdas_02 [](start = 20, length = 16)
What exactly are we testing here? Were we unable to compile the code before? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're testing performance. On my machine, this test completes in a couple of seconds with this change and 15+ mins without.
In reply to: 410978595 [](ancestors = 410978595)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We're testing performance. On my machine, this test completes in a couple of seconds with this change and 15+ mins without.
Consider adding a comment to make it easy to analyze failures.
In reply to: 411004957 [](ancestors = 411004957,410978595)
Done with review pass (iteration 1) #Closed |
RefKind refKind, | ||
TypeWithAnnotations typeWithAnnotations, | ||
ImmutableArray<DiagnosticInfo> useSiteDiagnostics) | ||
{ | ||
NumExpressions = numExpressions; | ||
HadExpressionlessReturn = hadExpressionlessReturn; | ||
HasReturnValue = hasReturnValue; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HasReturnValue = hasReturnValue; [](start = 12, length = 32)
It looks like this refactoring is not related to primary goal of this PR, please exclude it from the PR. #Closed
@@ -467,6 +469,17 @@ internal UnboundLambdaState WithCaching(bool includeCache) | |||
public abstract RefKind RefKind(int index); | |||
protected abstract BoundBlock BindLambdaBody(LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics); | |||
|
|||
/// <summary> | |||
/// Return the bound expression if the lambda is has an expression body and can be reused easily. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is has [](start = 54, length = 6)
Typo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM (iteration 2)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Allow re-use of expression-bodied lambda bodies bound during return type inference. (Re-use was disabled in #37052.)