-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Field-backed properties: definite assignment #75116
Conversation
a1ecdc9
to
45ab7f9
Compare
src/Compilers/CSharp/Portable/Symbols/Source/SourcePropertySymbolBase.cs
Show resolved
Hide resolved
propertyAccess.ReceiverOpt, | ||
propertyAccess.InitialBindingReceiverIsSubjectToCloning, | ||
propertyAccess.PropertySymbol, | ||
useAsLvalue.Value ? AccessorKind.Set : AccessorKind.Get, // PROTOTYPE: What about compound assignment, increment? |
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 extracting and reusing logic from 'GetIndexerAccessorKind' in order to determine this. #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.
Per offline discussion I am wondering if a somewhat smaller change would be adequate to address the scenario. It looks like the existing behavior with auto properties in struct constructors is:
- property get: auto-default all unassigned fields, even if getter is auto-implemented.
- property set: don't auto-default anything, if the set accessor is auto-implemented or absent.
So, shouldn't a relatively small change to logic in definite assignment be adequate here (check if setter specifically is auto-implemented rather than whole property)? There would be no need to adjust handling of property reads, compound assignments or increment/decrement.
Calling For instance, with the following, struct S1
{
public int F1;
public int P1 { get; }
public S1(int x, out int y)
{
y = P1;
F1 = x;
}
} |
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.
Sorry, I still need more time to finish review, but had a few more comments.
@@ -1073,6 +1074,7 @@ static IEnumerable<Symbol> getAllMembersToBeDefaulted(Symbol requiredMember) | |||
} | |||
} | |||
|
|||
// PROTOTYPE: Is checking IsAutoProperty sufficient or should we check the appropriate accessors are missing or auto-implemented? |
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 will try and resolve this comment in my upcoming PR. I think we should just check for a BackingField
and return it here.
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.
Moved comment to test plan #57012.
@@ -1964,6 +1966,8 @@ protected override bool TryGetReceiverAndMember(BoundExpression expr, out BoundE | |||
var propAccess = (BoundPropertyAccess)expr; | |||
var propSymbol = propAccess.PropertySymbol; | |||
member = propSymbol; | |||
// PROTOTYPE: Why don't we call Binder.AccessingAutoPropertyFromConstructor |
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 will also try to resolve this comment. I think I know why--nullable prefers to rewrite field initializers to be assigning to the property, for example, and then when a field is visited for constructor checks, we work back to the property and use the property's slot. Perhaps definite assignment works the opposite way, deciding when a property access is "morally" an access of the backing field, and treating it as the latter instead.
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.
Moved comment to test plan #57012.
{ | ||
if (_found is { }) | ||
{ | ||
return node; |
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.
Is this return value used? #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.
No, the return value is not used, and in fact most of the visit methods return null
. I'll change this to return 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.
LGTM, nothing blocking.
{ | ||
public int F1; | ||
public int P1 { get; } | ||
public C1(int i) { P1 += i; } |
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.
It seems like this and the other scenarios in this test would be more illustrative if F1
were explicitly assigned after the P1 += i;
statement. That would show whether the property access is causing auto-default of the whole instance.
Same for the ++
, --
, ??=
tests below. #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.
Thanks, that makes sense.
@@ -2194,7 +2194,7 @@ | |||
<!-- Whether receiver will need to be cloned during emit (only valid before lowering) --> | |||
<Field Name="InitialBindingReceiverIsSubjectToCloning" Type="ThreeState"/> | |||
<Field Name="PropertySymbol" Type="PropertySymbol"/> | |||
<!-- The property accessor referenced if the property access should use the backing field directly instead. --> | |||
<!-- The property accessor kind, set for properties with synthesized backing field only. --> | |||
<Field Name="UseBackingField" Type="AccessorKind" /> |
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'm not 100% happy with this field name, but I don't have any suggestions for a better one. It feels like this name is saying "this property access should be replaced with a use of the backing field". #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.
Renamed to BoundPropertyAccess.AutoPropertyAccessorKind
.
@@ -2195,7 +2195,7 @@ | |||
<Field Name="InitialBindingReceiverIsSubjectToCloning" Type="ThreeState"/> | |||
<Field Name="PropertySymbol" Type="PropertySymbol"/> | |||
<!-- The property accessor kind, set for properties with synthesized backing field only. --> |
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.
is it also the case that this will only have a non-Unknown value when auto accessors are being used? #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.
The value will be set to something other than AccessorKind.Unknown
for classic auto properties and for properties that use field
. Properties that use field
may include an auto-implemented accessor.
See proposal.
Similar to changes started in earlier
features/semi-auto-props
branch in #58832, #60601.