Fix result and slot of conditional receiver#33997
Conversation
| // so we reset before leaving the function. | ||
| _lastConditionalAccessSlot = -1; | ||
|
|
||
| _lastConditionalAccessSlot = previousConditionalAccessSlot; |
There was a problem hiding this comment.
Did you consider a finally block here? Whenever we have resets like this I worry about the risk of a later change inserting a return in the middle. I may be over paranoid here. #Pending
There was a problem hiding this comment.
I don't think we need a try/finally here because any exception will terminate the analysis, so the state we leave it in won't matter.
We have very limited exception handling in flow analysis (only one finally to free the pooled object when hitting stackguard). #Pending
There was a problem hiding this comment.
The concern is with returning early rather than exceptions. I think I'd prefer to have this reset in a finally block.
In reply to: 264336845 [](ancestors = 264336845)
There was a problem hiding this comment.
Sorry, I'd misunderstood. Will fix #Pending
|
|
||
| var receiver = node.Receiver; | ||
| var receiverType = VisitRvalueWithState(receiver); | ||
| _currentConditionalReceiverVisitResult = _visitResult; |
There was a problem hiding this comment.
Consider a Debug.Assert(_currentConditionalReceiverVisitResult == default predicate to this method. #Resolved
There was a problem hiding this comment.
I think that wouldn't be the case in x?.y?.f when we get the y. I'll double-check. #Resolved
|
@dotnet/roslyn-compiler for a second review. Thanks #Resolved |
| var rvalueType = _currentConditionalReceiverVisitResult.RValueType.Type; | ||
| if (rvalueType?.IsNullableType() == true) | ||
| { | ||
| rvalueType = rvalueType.GetNullableUnderlyingTypeWithAnnotations().TypeSymbol; |
There was a problem hiding this comment.
GetNullableUnderlyingTypeWithAnnotations().TypeSymbol [](start = 40, length = 53)
GetNullableUnderlyingType() #Pending
| var x = Create(s1); | ||
| var y = Create(s2); | ||
| x?.Field /*1*/ | ||
| .Extension(y?.Field.ToString()); |
There was a problem hiding this comment.
Does this test that the conditional access receiver field is reset? Is there overlap between the two receivers?
There was a problem hiding this comment.
I don't think any scenario can currently hit the "reset". I've included the "reset" to be safe for future changes.
The reason we can't hit it now is that to have a nested conditional, you must have some invocation or indexer with arguments, but then we're no longer dealing with fields (ie. slots).
In reply to: 264353430 [](ancestors = 264353430)
| public C? f; | ||
| int? Test1(C? c) => c?.f.M(c.f.ToString()); // nested use of `c.f` is safe | ||
| int? Test2(C? c) => c.f.M(c.f.ToString()); | ||
| int M(string s) => s.Length; |
There was a problem hiding this comment.
int M(string s) => s.Length; [](start = 4, length = 28)
Can this be removed?
There was a problem hiding this comment.
I've simplified M a bit, but otherwise left it. I need it to test access on c.f nested in expression with c?.f.
| void Test2(C? c) => c.Nested?.M(c.Nested.ToString()); | ||
| void Test3(C c) => c?.Nested?.M(c.Nested.ToString()); | ||
|
|
||
| void M(string s) => throw null!; |
There was a problem hiding this comment.
void M(string s) => throw null!; [](start = 4, length = 32)
Can this be removed?
There was a problem hiding this comment.
As the
Dump()for a conditional access (excerpted below) shows, the call or member access uses a placeholder ("conditional receiver") in place of the actual receiver.There are two parts of this PR:
To do that, we store some information (the result and the slot) about the real receiver when we visit a conditional access.
?.access #33289 (fails to apply inferred type arguments' nullability after?.access)