Refactor VisitLambda to not create a child walker#45527
Refactor VisitLambda to not create a child walker#45527RikkiGibson merged 8 commits intodotnet:masterfrom
Conversation
c0dc4ba to
0d4d671
Compare
0d4d671 to
9f032bd
Compare
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Show resolved
Hide resolved
| } | ||
|
|
||
| LeaveParameters(localFuncSymbol.Parameters, localFunc.Syntax, location); | ||
| LeaveParameters(lambdaOrFunctionSymbol.Parameters, lambdaOrFunction.Syntax, location); |
There was a problem hiding this comment.
Why do we condition EnterParameters but not LeaveParameters?
There was a problem hiding this comment.
Good question! I found that the IsCompilerGenerated check went by unremarked when it was introduced: https://github.com/dotnet/roslyn/pull/40422/files#diff-e3717bc6a837e218d79277c0e45f213aR1620
One thing I did notice is that LeaveParameters does absolutely nothing in NullableWalker, it only has an effect in DefiniteAssignment.
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Outdated
Show resolved
Hide resolved
| private void EnterParameters() | ||
| { | ||
| if (!(_symbol is MethodSymbol methodSymbol)) | ||
| if (!(CurrentSymbol is MethodSymbol methodSymbol)) |
There was a problem hiding this comment.
CurrentSymbol [](start = 18, length = 13)
There are other references to _symbol that are not updated. Are those other references correct?
There was a problem hiding this comment.
I made a pass over existing usages of _symbol and changed the ones that seemed wrong.
| _variableSlot.ToImmutableDictionary(), | ||
| ImmutableArray.Create(variableBySlot, start: 0, length: nextVariableSlot), | ||
| _variableTypes.ToImmutableDictionary(), | ||
| _variableTypes.ToImmutableDictionary(_variableTypes.Comparer, TypeWithAnnotations.EqualsComparer.ConsiderEverythingComparer), |
There was a problem hiding this comment.
_variableTypes.Comparer, TypeWithAnnotations.EqualsComparer.ConsiderEverythingComparer [](start = 53, length = 86)
Is this change needed? (The key comparer is the default, and the value comparer doesn't appear to be used.)
There was a problem hiding this comment.
Yes, without this change, the bootstrap build crashes because it attempts to call TypeWithAnnotations.Equals on the dictionary values.
There was a problem hiding this comment.
Where are we calling Equals on the dictionary values?
In reply to: 451162769 [](ancestors = 451162769)
There was a problem hiding this comment.
ToImmutableDictionary() may call Equals on dictionary values internally when constructing the immutable dictionary. This is something we might want to examine in greater detail. It's not obvious to me why sharing the _variableTypes between a lambda and its containing method would make this behavior start to surface in the bootstrap build.
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Semantic/Semantics/NullableReferenceTypesTests.cs
Show resolved
Hide resolved
| // (15,17): warning CS8602: Dereference of a possibly null reference. | ||
| // _ = x.ToString(); // 1 | ||
| Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x").WithLocation(15, 17)); | ||
| } |
There was a problem hiding this comment.
} [](start = 8, length = 1)
Please add a similar test where the local function is passed as a delegate.
| private void EnforceDoesNotReturn(SyntaxNode? syntaxOpt) | ||
| { | ||
| if (_symbol is MethodSymbol method && | ||
| // DoesNotReturn is only supported in member methods |
There was a problem hiding this comment.
DoesNotReturn [](start = 15, length = 13)
Can [DoesNotReturn] be applied to a local function?
There was a problem hiding this comment.
We don't complain if it is applied, but we do not change analysis when it is present.
I actually expected us to mark regions unreachable in NullableWalker when calling a local function that does not return, and thus give no warnings in those regions--definite assignment already works such that the following scenario gives no errors. Unfortunately this does not seem to be the case. It's an inconsistency we might want to follow up on. Once we start doing this, it should not be necessary to use [DoesNotReturn] or [DoesNotReturnIf] on local functions.
string s;
local1();
s.ToString();
void local1() => throw null!;string? s = null;
local1();
s.ToString(); // this warns, but shouldn't
void local1() => throw null!;There was a problem hiding this comment.
I will try and ensure that DoesNotReturn does not impact analysis of local functions for the moment, and file a follow-up issue to confirm that this is expected.
|
|
||
| [Theory] | ||
| [InlineData("")] | ||
| [InlineData("static ")] |
There was a problem hiding this comment.
Does [DoesNotReturn] analysis depend on whether the local function is static?
There was a problem hiding this comment.
No, I pushed this by accident, I believe this function should be deleted.
There was a problem hiding this comment.
Simplified the test down to a [Fact] to simplify, since 'static' differences are not expected to impact the test.
| [DoesNotReturn] | ||
| " + modifiers + @"void boom() | ||
| { | ||
| } |
There was a problem hiding this comment.
} [](start = 8, length = 1)
Should report WRN_ShouldNotReturn. Not blocking this PR.
| using System.Diagnostics.CodeAnalysis; | ||
| class D | ||
| { | ||
| void Main(object? o) |
There was a problem hiding this comment.
object? o [](start = 14, length = 9)
Not used.
It seems like Refers to: src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs:1218 in c5b1eec. [](commit_id = c5b1eec, deletion_comment = False) |
|
| } | ||
|
|
||
| public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement localFunc) | ||
| public override BoundNode? VisitLocalFunctionStatement(BoundLocalFunctionStatement node) |
There was a problem hiding this comment.
Alright, so I think speculative analysis in local functions was already broken in current master: we're never calling snapshotBuilderOpt?.EnterNewWalker() on the local function symbol, so state saving and restore will be broken.
There was a problem hiding this comment.
This should reproduce in that variables declared in the local function don't have correct state when restoring for semantic analysis (if previous statements in the local function checked them for null, for example.
In reply to: 451908958 [](ancestors = 451908958)
There was a problem hiding this comment.
Opened #45825 to track this. I think that in order to not introduce this bug for lambdas in this PR you're going to have to fix this one as well.
|
It appears that tests already verify that we respect |
| SetUpdatedSymbol(node, node.Symbol, delegateTypeOpt); | ||
| } | ||
|
|
||
| Analyze( |
There was a problem hiding this comment.
By no longer calling this analyze overload, we're not entering a new scope for the lambda, so speculation will be broken. We need to restore the calls to snapshotManager?.EnterNewWalker with the new symbol. And exiting the walker after lambda analysis is done.
|
Done review pass (commit 7). I didn't look at tests. |
333fred
left a comment
There was a problem hiding this comment.
Explicitly blocking since there are 2 approvals, want to make sure this doesn't accidentally go in.
…)" This reverts commit ffb3ca7.
Fixes #44411
Fixes #45795
I had hoped to outright remove the
_useDelegateInvokeParameterTypesfield and ctor parameter, as well as thedelegateInvokeMethodOptctor parameter, but I cannot prove to my satisfaction that they will never make a difference in the one scenario where we create a NullableWalker specifically from a lambda, which isUnboundLambda.GetInferredReturnType. There are no tests which fail when thedelegateInvokeMethodOptis forced tonullin this context.I also found that after this change, we were throwing on ImmutableDictionary creation in the bootstrap build, because dictionary values of type
TypeWithAnnotationswere being compared for equality. This is solved by manually passing a value equality comparer when creating the dictionary.