Skip to content

Conversation

@jjonescz
Copy link
Member

@jjonescz jjonescz commented Apr 8, 2024

Fixes #72873.

@ghost ghost added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Apr 8, 2024
@jjonescz jjonescz marked this pull request as ready for review April 9, 2024 06:43
@jjonescz jjonescz requested a review from a team as a code owner April 9, 2024 06:43
@dotnet dotnet deleted a comment from azure-pipelines bot Apr 9, 2024
@AlekseyTs
Copy link
Contributor

@jjonescz Could you please provide more details about the change. What is happening and why. Why you think the change is the right change to make?

@AlekseyTs
Copy link
Contributor

It doesn't look like the scenario in the issue is an error scenario, If that is the case, it is not clear to me why things aren't visited

@jjonescz
Copy link
Member Author

There's a comment in the code that tried explaining this:

// Nested binary operators on the left side are not visited by BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator.

To elaborate - BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator doesn't visit nested BoundBinaryOperators, it only visits the operands. This is done for a good reason - to avoid stack overflows. Hence I think BoundBinaryOperators not being visited is the expected behavior and the "debug verifier" should be adjusted to not assert when BoundBinaryOperators are not visited.

GetValEscape(binary.Right, scopeOfTheContainingExpression));
#if DEBUG
// Nested binary operators on the left side are not visited by BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator.
var assertNestedVisited = binary.Left is not BoundBinaryOperator;
Copy link
Contributor

Choose a reason for hiding this comment

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

var assertNestedVisited = binary.Left is not BoundBinaryOperator;

It is probably better to mark the node as visited right here, instead of dealing with an extra debug only parameter.

Copy link
Member Author

@jjonescz jjonescz Apr 15, 2024

Choose a reason for hiding this comment

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

I'm not sure how exactly would we do that - for expression like A+B+C+D calling Visit at this point on the left-hand side would recursively visit A+B+C, then we would get to this point again for A+B+C and call Visit on A+B - so we would visit the same nodes multiple times which we probably don't want (apart from another assert firing, it seems inefficient).

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure how exactly would we do that

The assert checks that the node is in some set. Perhaps we can add an entry in the into the set right here.

Copy link
Member Author

Choose a reason for hiding this comment

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

You're right, that looks like a good solution, thanks!

),
GetValEscape(binary.Right, scopeOfTheContainingExpression
#if DEBUG
, assertNestedVisited
Copy link
Contributor

Choose a reason for hiding this comment

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

assertNestedVisited

This looks suspicious. Why is it appropriate to pass this flag for the right hand side?

Copy link
Member Author

Choose a reason for hiding this comment

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

It's not necessary to pass it for the right-hand side, thanks.

Copy link
Contributor

Choose a reason for hiding this comment

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

It's not necessary to pass it for the right-hand side

Not necessary or wrong?

Copy link
Member Author

Choose a reason for hiding this comment

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

Wrong in the sense that we would not check that the right-hand side was visited.

return Math.Max(GetValEscape(binary.Left, scopeOfTheContainingExpression),
GetValEscape(binary.Right, scopeOfTheContainingExpression));
#if DEBUG
// Nested binary operators on the left side are not visited by BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nested binary operators on the left side are not visited by BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator.

It is not obvious to me that not visiting a user-defined binary operator on the left is the right thing to do for the purpose of this visitor. The fact that the base class skips it completely doesn't mean that doing the same is appropriate for every derived visitor.

Copy link
Contributor

Choose a reason for hiding this comment

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

Note, BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator also does similar trick for calls. Should they be treated specially too?

Copy link
Member Author

Choose a reason for hiding this comment

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

Note, BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator also does similar trick for calls. Should they be treated specially too?

That does not seem needed, as the ref safety analysis doesn't seem to (even indirectly) call AssertVisited on the receiver like it does for binary operator (where escape of the left-hand side is checked which in turn calls AssertVisited on it)

Copy link
Member Author

Choose a reason for hiding this comment

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

However, it is true we need special handling in other methods than just GetValEscape, for example CheckValEscape

Copy link
Member Author

Choose a reason for hiding this comment

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

It is not obvious to me that not visiting a user-defined binary operator on the left is the right thing to do for the purpose of this visitor. The fact that the base class skips it completely doesn't mean that doing the same is appropriate for every derived visitor.

RefSafetyAnalysis does not currently override VisitBinaryOperator, so assuming that's correct, the optimization seems valid, i.e., it should be transparent to RefSafetyAnalysis that only the operands are visited, not the operators themselves.

Note that the handling of binary operators (and also the special handling of user-defined binary operators) doesn't happen in VisitBinaryOperator, it happens in GetValEscape/CheckValEscape, so it's unrelated to the walker's visiting behavior.

Copy link
Member Author

Choose a reason for hiding this comment

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

Note, BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator also does similar trick for calls. Should they be treated specially too?

That does not seem needed, as the ref safety analysis doesn't seem to (even indirectly) call AssertVisited on the receiver like it does for binary operator (where escape of the left-hand side is checked which in turn calls AssertVisited on it)

Actually, AssertVisited is called on receivers. But there is not such problem for a different reason - the BoundTreeWalkerWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator handling of nested calls doesn't skip visiting any nodes.

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 3)

@jjonescz jjonescz marked this pull request as draft April 18, 2024 10:17
@jjonescz jjonescz closed this Apr 23, 2024
@jjonescz jjonescz deleted the 72873-RefSafetyAssert branch April 23, 2024 08:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Debug Assertion fails for RefSafetyAnalysis

2 participants