-
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
Changes from 7 commits
b079ba0
cf4369f
df39446
47f2aa8
f478367
eacecf1
814baf6
3951497
f6cbd13
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||
|---|---|---|---|---|---|---|---|---|
|
|
@@ -81,15 +81,24 @@ private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bo | |||||||
| break; | ||||||||
| } | ||||||||
|
|
||||||||
| return MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, used); | ||||||||
| return MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, used, AssignmentKind.SimpleAssignment); | ||||||||
| } | ||||||||
|
|
||||||||
| private enum AssignmentKind | ||||||||
| { | ||||||||
| SimpleAssignment, | ||||||||
| CompoundAssignment, | ||||||||
| IncrementDecrement, | ||||||||
| Deconstruction, | ||||||||
| NullCoalescingAssignment, | ||||||||
| } | ||||||||
|
|
||||||||
| /// <summary> | ||||||||
| /// Generates a lowered form of the assignment operator for the given left and right sub-expressions. | ||||||||
| /// Left and right sub-expressions must be in lowered form. | ||||||||
| /// </summary> | ||||||||
| private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, | ||||||||
| bool used, bool isChecked, bool isCompoundAssignment) | ||||||||
| bool used, bool isChecked, AssignmentKind assignmentKind) | ||||||||
| { | ||||||||
| switch (rewrittenLeft.Kind) | ||||||||
| { | ||||||||
|
|
@@ -102,15 +111,15 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio | |||||||
| indexerAccess.ArgumentNamesOpt, | ||||||||
| indexerAccess.ArgumentRefKindsOpt, | ||||||||
| rewrittenRight, | ||||||||
| isCompoundAssignment, isChecked); | ||||||||
| isCompoundAssignment: assignmentKind == AssignmentKind.CompoundAssignment, isChecked); | ||||||||
|
|
||||||||
| case BoundKind.DynamicMemberAccess: | ||||||||
| var memberAccess = (BoundDynamicMemberAccess)rewrittenLeft; | ||||||||
| return _dynamicFactory.MakeDynamicSetMember( | ||||||||
| memberAccess.Receiver, | ||||||||
| memberAccess.Name, | ||||||||
| rewrittenRight, | ||||||||
| isCompoundAssignment, | ||||||||
| isCompoundAssignment: assignmentKind == AssignmentKind.CompoundAssignment, | ||||||||
| isChecked).ToExpression(); | ||||||||
|
|
||||||||
| case BoundKind.EventAccess: | ||||||||
|
|
@@ -132,7 +141,7 @@ private BoundExpression MakeAssignmentOperator(SyntaxNode syntax, BoundExpressio | |||||||
| throw ExceptionUtilities.Unreachable(); | ||||||||
|
|
||||||||
| default: | ||||||||
| return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, used: used); | ||||||||
| return MakeStaticAssignmentOperator(syntax, rewrittenLeft, rewrittenRight, isRef: false, used: used, assignmentKind); | ||||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
|
|
@@ -168,7 +177,8 @@ private BoundExpression MakeStaticAssignmentOperator( | |||||||
| BoundExpression rewrittenLeft, | ||||||||
| BoundExpression rewrittenRight, | ||||||||
| bool isRef, | ||||||||
| bool used) | ||||||||
| bool used, | ||||||||
| AssignmentKind assignmentKind) | ||||||||
| { | ||||||||
| switch (rewrittenLeft.Kind) | ||||||||
| { | ||||||||
|
|
@@ -192,7 +202,8 @@ private BoundExpression MakeStaticAssignmentOperator( | |||||||
| false, | ||||||||
| default(ImmutableArray<int>), | ||||||||
| rewrittenRight, | ||||||||
| used); | ||||||||
| used, | ||||||||
| assignmentKind); | ||||||||
| } | ||||||||
|
|
||||||||
| case BoundKind.IndexerAccess: | ||||||||
|
|
@@ -212,7 +223,8 @@ private BoundExpression MakeStaticAssignmentOperator( | |||||||
| indexerAccess.Expanded, | ||||||||
| indexerAccess.ArgsToParamsOpt, | ||||||||
| rewrittenRight, | ||||||||
| used); | ||||||||
| used, | ||||||||
| assignmentKind); | ||||||||
| } | ||||||||
|
|
||||||||
| case BoundKind.Local: | ||||||||
|
|
@@ -248,7 +260,8 @@ private BoundExpression MakeStaticAssignmentOperator( | |||||||
| sequence.Value, | ||||||||
| rewrittenRight, | ||||||||
| isRef, | ||||||||
| used), | ||||||||
| used, | ||||||||
| assignmentKind), | ||||||||
| sequence.Type); | ||||||||
| } | ||||||||
| goto default; | ||||||||
|
|
@@ -264,6 +277,17 @@ private BoundExpression MakeStaticAssignmentOperator( | |||||||
| } | ||||||||
| } | ||||||||
|
|
||||||||
| private bool IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(BoundExpression rewrittenReceiver, PropertySymbol property) | ||||||||
| { | ||||||||
| return CanChangeValueBetweenReads(rewrittenReceiver, localsMayBeAssignedOrCaptured: true, structThisCanChangeValueBetweenReads: true) && | ||||||||
| IsNewExtensionMemberWithByValPossiblyStructReceiver(property) && | ||||||||
| CodeGen.CodeGenerator.HasHome(rewrittenReceiver, | ||||||||
| CodeGen.CodeGenerator.AddressKind.ReadOnlyStrict, | ||||||||
| _factory.CurrentFunction, | ||||||||
| peVerifyCompatEnabled: false, | ||||||||
| stackLocalsOpt: null); | ||||||||
| } | ||||||||
|
|
||||||||
| private BoundExpression MakePropertyAssignment( | ||||||||
| SyntaxNode syntax, | ||||||||
| BoundExpression? rewrittenReceiver, | ||||||||
|
|
@@ -273,7 +297,8 @@ private BoundExpression MakePropertyAssignment( | |||||||
| bool expanded, | ||||||||
| ImmutableArray<int> argsToParamsOpt, | ||||||||
| BoundExpression rewrittenRight, | ||||||||
| bool used) | ||||||||
| bool used, | ||||||||
| AssignmentKind assignmentKind) | ||||||||
| { | ||||||||
| // Rewrite property assignment into call to setter. | ||||||||
| var setMethod = property.GetOwnOrInheritedSetMethod(); | ||||||||
|
|
@@ -293,25 +318,73 @@ private BoundExpression MakePropertyAssignment( | |||||||
| } | ||||||||
|
|
||||||||
| ArrayBuilder<LocalSymbol>? argTempsBuilder = null; | ||||||||
|
|
||||||||
| bool needSpecialExtensionPropertyReceiverReadOrder = false; | ||||||||
| ArrayBuilder<BoundExpression>? storesOpt = null; | ||||||||
|
|
||||||||
| if (rewrittenReceiver is not null && | ||||||||
| assignmentKind is not (AssignmentKind.CompoundAssignment or AssignmentKind.NullCoalescingAssignment or AssignmentKind.Deconstruction or AssignmentKind.IncrementDecrement) && | ||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we check that
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||||||||
| IsPropertyWithByValPossiblyStructReceiverWhichHasLocationAndCanChangeValueBetweenReads(rewrittenReceiver, property) && | ||||||||
|
||||||||
| (arguments.Length != 0 || !IsSafeForReordering(rewrittenRight, RefKind.None))) | ||||||||
| { | ||||||||
| // The receiever has location, but extension property/indexer takes receiver by value. | ||||||||
|
||||||||
| // 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 |
Outdated
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 |
Outdated
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 |
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.
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
Uh oh!
There was an error while loading. Please reload this page.
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 renaming to
IsExtensionPropertyWithByValPossiblyStructReceiverWhichHasHomeAndCanChangeValueBetweenReadsas only extension properties qualify and to avoid multiplying concepts ("home" versus "location"). #Closed