-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Avoid sharing NullableWalker state between method and nested functions #49765
Conversation
fd3831c
to
c96cc14
Compare
@cston Could you provide more details about the issue? Is this just a matter of "lifetime" for the slots, or did we add new slots for the same things every time we analyzed the same local function on a new pass? #Closed |
@@ -2623,6 +2623,18 @@ private void VisitStatementsWithLocalFunctions(BoundBlock block) | |||
|
|||
var oldState = this.State; | |||
this.State = state; | |||
var oldVariableSlot = _variableSlot; | |||
_variableSlot = PooledDictionary<VariableIdentifier, int>.GetInstance(); |
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.
_variableSlot [](start = 12, length = 13)
Should we also capture and reset variableBySlot
and nextVariableSlot
? #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.
@AlekseyTs, I've updated the description to give details, thanks. |
_variableTypes.Free(); | ||
_variableTypes = oldVariableTypes; | ||
_variableSlot.Free(); | ||
_variableSlot = oldVariableSlot; |
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.
_variableSlot [](start = 12, length = 13)
Is it possible that, while analyzing a local function, we allocate a slot for something that is not scoped to the body of the function and, therefore, the slot should be shared with containing method? #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.
The containing method state is reset after the nested function is visited so any slots added in the method can be ignored.
In reply to: 535419178 [](ancestors = 535419178)
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.
The containing method state is reset after the nested function is visited so any slots added in the method can be ignored.
That may not be true. _localFuncVarUsages
may have been updated and that tracks state as well.
In reply to: 535547721 [](ancestors = 535547721,535419178)
Done with review pass (commit 1) #Closed |
// with variables in the outer function that were first used in the nested function, | ||
// such as a field access on a captured local, but the state associated with | ||
// any such entries are dropped, so the slots can be dropped as well.) | ||
// We don't drop slots and types if there are local function usages but the |
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.
Should this read "...if there are local function usages, because the _localFuncVarUsages dictionary..."? #Resolved
{ | ||
_variableTypes.Add(pair.Key, pair.Value); | ||
} | ||
variableBySlot = new VariableIdentifier[oldVariableBySlot.Length]; |
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.
variableBySlot = new VariableIdentifier[oldVariableBySlot.Length]; [](start = 16, length = 66)
Consider if we can simply zero out elements in the array starting with oldNextVariableSlot after the analysis.
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 (commit 5), modulo the CI failure.
|
||
public override BoundNode? Visit(BoundNode? node) | ||
{ | ||
if (node is null) |
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.
if (node is null) [](start = 16, length = 17)
Consider checking _hasComplexity
and returning early when it is already true. #Closed
{ | ||
if (node is BoundMethodBodyBase methodBody) | ||
{ | ||
node = methodBody.BlockBody ?? methodBody.ExpressionBody; |
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.
node = methodBody.BlockBody ?? methodBody.ExpressionBody; [](start = 20, length = 57)
It feels like we should check both, we are visiting both if present in AbstractFlowPass.VisitMethodBodies.
For BoundConstructorMethodBody we also visit initializer. #Closed
@@ -418,6 +424,68 @@ protected override void Free() | |||
} | |||
} | |||
|
|||
internal sealed class IsSimpleMethodVisitor : BoundTreeWalkerWithStackGuard |
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.
BoundTreeWalkerWithStackGuard [](start = 54, length = 29)
Consider inheriting from BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator to avoid stack overflow on deeply nested binary operators.
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 (commit 9)
…functions (dotnet#49765)" This reverts commit dedcd58.
…functions (dotnet#49765)" This reverts commit dedcd58.
When analyzing a method in
NullableWalker
, avoid sharing_variableTypes
and_variableSlots
between that method and any lambdas or local functions. Currently, the parameters and locals for any nested functions are added to the_variableTypes
and_variableSlots
tables in the containing method, adding to the state tracked for containing method and any subsequent nested functions. If there are many nested functions, this can significantly increase the size of the tables that are tracked, particularly for the semantic model.Fixes #49745