-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Fix receiver capturing for various extension scenarios #79472
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
Fix receiver capturing for various extension scenarios #79472
Conversation
This reverts commit c2412e0b5ecd6b14f4ba9d7a66a52e05f511128f.
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/azp run |
|
Azure Pipelines successfully started running 2 pipeline(s). |
| } | ||
|
|
||
| receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind); | ||
| receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, (refKind is RefKind.RefReadOnlyParameter or RefKind.In) ? RefKindExtensions.StrictIn : 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.
(refKind is RefKind.RefReadOnlyParameter or RefKind.In) ? RefKindExtensions.StrictIn : refKind
I didn't follow why we need this change. When I revert it, it doesn't seem to affect any extensions test.
If the change is desirable, would we also want it in TransformPropertyOrEventReceiver when computing the refKind for the temp for the receiver? #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.
When I revert it, it doesn't seem to affect any extensions test.
Added InterpolationTests.StructReceiver_Lvalue_08 which crashes compiler without the change.
Adding similar change to TransformPropertyOrEventReceiver for consistency and future proofing
|
|
||
| if (IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) | ||
| { | ||
| // The receiever has location, but extension indexer takes receiver by value. |
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.
Typo:
| // The receiever has location, but extension indexer takes receiver by value. | |
| // The receiver has location, but extension indexer takes receiver by value. | |
| ``` #Closed |
| IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, property) && | ||
| (arguments.Length != 0 || !IsSafeForReordering(rewrittenRight, RefKind.None))) | ||
| { | ||
| // The receiever has location, but extension property/indexer takes receiver by value. |
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.
Typo:
| // The receiever has location, but extension property/indexer takes receiver by value. | |
| // The receiver has location, but extension property/indexer takes receiver by value. | |
| ``` #Closed |
| } | ||
| } | ||
|
|
||
| private bool IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(BoundExpression rewrittenReceiver, PropertySymbol property) |
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.
IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads
Consider renaming to IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReads as only extension properties qualify and to avoid multiplying concepts ("home" versus "location"). #Closed
| ArrayBuilder<BoundExpression>? storesOpt = null; | ||
|
|
||
| if (IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) | ||
| { |
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.
Consider leaving follow-up comment to cover this logic when implementing extension indexers Never mind, I see you added skipped tests #Closed
nit: consider adding tests for disambiguation invocation syntax ( Refers to: src/Compilers/CSharp/Test/Emit3/Semantics/ExtensionTests2.cs:5570 in 814baf6. [](commit_id = 814baf6, deletion_comment = False) |
jcouv
left a comment
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 Thanks (commit 7) with some nits to consider
|
@jjonescz, @dotnet/roslyn-compiler For a second review |
| ArrayBuilder<BoundExpression>? storesOpt = null; | ||
|
|
||
| if (rewrittenReceiver is not null && | ||
| assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullCoalescingAssignment or AssignmentKind.Deconstruction or AssignmentKind.IncrementDecrement) && |
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 we check that assignmentKind is AssignmentKind.SimpleAssignment instead? #Resolved
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.
I am checking for assignments that I know don't need special treatment. Doing an opposite check is not robust in the long run, if new kind is added.
| (arguments.Length != 0 || !IsSafeForReordering(rewrittenRight, RefKind.None))) | ||
| { | ||
| // The receiever has location, but extension property/indexer takes receiver by value. | ||
| // This means that we need to ensure that the the receiver value is read after |
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.
| // This means that we need to ensure that the the receiver value is read after | |
| // This means that we need to ensure that the receiver value is read after | |
| ``` #Resolved |
| // The receiever has location, but extension property/indexer takes receiver by value. | ||
| // This means that we need to ensure that the the receiver value is read after | ||
| // any side-effecting arguments and right hand side are evaluated, so that the | ||
| // the setter receives the last value of the receiver, not the value before 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.
| // the setter receives the last value of the receiver, not the value before the | |
| // setter receives the last value of the receiver, not the value before the | |
| ``` #Closed |
| // stores in storesToTemps and make the actual argument a reference to the temp. | ||
| arguments = ExtractSideEffectsFromArguments(arguments, property, expanded, argsToParamsOpt, ref argumentRefKindsOpt, storesOpt, argTempsBuilder); | ||
|
|
||
| if (!IsSafeForReordering(rewrittenRight, RefKind.None)) |
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.
Consider caching and reusing the result of IsSafeForReordering(rewrittenRight, RefKind.None) from above. I guess it might not be always computed, but perhaps having a bool? and recomputing it only if null would still be worth it. #Resolved
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.
Consider caching and reusing the result of
I prefer to not complicate code trying to optimize something that is not yet proven to be a problem
|
|
||
| if (rewrittenReceiver is not null && | ||
| assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullCoalescingAssignment or AssignmentKind.Deconstruction or AssignmentKind.IncrementDecrement) && | ||
| IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, property) && |
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.
Do you now get some trophy for the longest method name in the codebase? :D #Resolved
| /// the sideeffects of capturing are the sifeeffects of the sequence and its result is the captured value. | ||
| /// | ||
| /// All temps introduced by this function for capturing purposes (including the temp capturing the receiver) are appended | ||
| /// to <paramref name="tempsOpt"/>, which is allocated, if 'null' on input. |
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.
| /// to <paramref name="tempsOpt"/>, which is allocated, if 'null' on input. | |
| /// to <paramref name="tempsOpt"/>, which is allocated if 'null' on input. | |
| ``` #Resolved |
| // The receiever has location, but extension indexer takes receiver by value. | ||
| // This means that we need to ensure that the the receiver value is read after | ||
| // any side-effecting arguments are evaluated, so that the | ||
| // the setter receives the last value of the receiver, not the value before 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.
| // the setter receives the last value of the receiver, not the value before the | |
| // setter receives the last value of the receiver, not the value before the | |
| ``` #Closed |
| if (IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, indexer)) | ||
| { | ||
| // The receiever has location, but extension indexer takes receiver by value. | ||
| // This means that we need to ensure that the the receiver value is read after |
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.
| // This means that we need to ensure that the the receiver value is read after | |
| // This means that we need to ensure that the receiver value is read after | |
| ``` #Resolved |
| [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/78137")] | ||
| [CombinatorialData] | ||
| public void InterpolationHandler_ReceiverParameter_ByIn_WithConstantReceiver(bool useMetadataRef) | ||
| public void InterpolationHandler_ReceiverParameter_Generic_ByRef(bool useMetadataRef) |
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.
Consider referencing #79379 in the modified/added tests #Resolved
| // any side-effecting arguments and right hand side are evaluated, so that the | ||
| // the setter receives the last value of the receiver, not the value before the | ||
| // arguments/rhs were evaluated. Receiver sideeffects should be evaluated at | ||
| // the very beginning, of course. |
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.
What happens when the receiver does not have a home; is that always an error scenario? #Resolved
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.
What happens when the receiver does not have a home; is that always an error scenario?
Not necessary. If the receiver doesn't have home, no one can change its value after we read it. Therefore, we don't need to worry about capturing it by reference and dereferencing it right before invoking the accessor. If we try to do so, the value will be captured in a temp anyway.
jcouv
left a comment
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 Thanks (commit 8)
Fixes #79379
Fixes #79436