-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Avoid premature full binding of a receiver as a value in the process of binding possible type-or-value receiver (so-called “Color Color” scenario) #80978
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
Conversation
…of binding possible type-or-value receiver (so-called “Color Color” scenario) Fixes dotnet#45739.
|
|
||
| [WorkItem("https://github.com/dotnet/roslyn/issues/45739")] | ||
| [Fact] | ||
| public void ConstFieldEnum_02() |
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.
We should also test that
public const Color Color = Color.Red | Color.Red;is not considered circular #Resolved
|
@dotnet/roslyn-compiler Please review #Closed |
5 similar comments
|
@dotnet/roslyn-compiler Please review #Closed |
|
@dotnet/roslyn-compiler Please review #Closed |
|
@dotnet/roslyn-compiler Please review #Closed |
|
@dotnet/roslyn-compiler Please review #Closed |
|
@dotnet/roslyn-compiler Please review #Closed |
|
Taking a look shortly. #Closed |
| throw ExceptionUtilities.UnexpectedValue(valueText); | ||
| } | ||
| var local = new ObjectAddressLocalSymbol(_containingMethod, name, this.Compilation.GetSpecialType(SpecialType.System_Object), address); | ||
| var local = new ObjectAddressLocalSymbol(_containingMethod, name, this.Compilation.GetSpecialType(SpecialType.System_Object)); |
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 didn't follow why this change is being made, could you please explain? #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 didn't follow why this change is being made, could you please explain?
Added asserts exposed a bug in comparison of ObjectAddressLocalSymbols. We are not reusing them and were never considered them equal, i.e. representing the same entity. I went ahead and implemented equality for ObjectAddressLocalSymbol. In the process, I incorporated the code above into constructor, that makes it clear what do we need to compare.
|
@dotnet/roslyn-compiler Please review #Closed |
|
@dotnet/roslyn-compiler For a second review #Closed |
3 similar comments
|
@dotnet/roslyn-compiler For a second review #Closed |
|
@dotnet/roslyn-compiler For a second review #Closed |
|
@dotnet/roslyn-compiler For a second review #Closed |
|
@dotnet/roslyn-compiler For a second review on a PR opened for review on Nov 1st. #Closed |
2 similar comments
|
@dotnet/roslyn-compiler For a second review on a PR opened for review on Nov 1st. #Closed |
|
@dotnet/roslyn-compiler For a second review on a PR opened for review on Nov 1st. #Closed |
| { | ||
| public override ConstantValue? ConstantValueOpt => Data?.ConstantValue; | ||
|
|
||
| public override Symbol? ExpressionSymbol => this.BinaryOperatorMethod; |
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.
Regarding comment on BoundTypeOrValueData type (line 669 to 672)
ValueExpression and TypeExpression were removed so the comment above should not reference those members.
Also, the justification provided for this type was to hide some bound nodes from the regular HasErrors bound node handling. Is this still the case?
Could this information be inlined into BoundTypeOrValueExpression? Or should the comment be updated? #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.
Removed the BoundTypeOrValueData type
| { | ||
| Debug.Assert(symbol.IsExtensionBlockMember()); | ||
| Debug.Assert(symbol.GetIsNewExtensionMember()); | ||
| if (SourceNamedTypeSymbol.ReduceExtensionMember(binder.Compilation, symbol, receiverType, wasExtensionFullyInferred: out _) is { } compatibleSubstitutedMember) |
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.
Regarding comment on case BoundKind.TypeOrValueExpression: in GetSemanticSymbols (line 3424)
Comment above seems out-of-date (remove?) #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.
Comment above seems out-of-date (remove?)
Adjusted
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.
nit: There's still a problem with the comment. It says:
// If we're seeing a node of this kind, then we failed to resolve the member access
// as either a type or a property/field/event/local/parameter. In such cases,
// the second interpretation applies.
But then we assert: Debug.Assert(boundNode is not BoundTypeOrValueExpression);
I'd just remove the 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.
The comment is correct. The assert is there to help us to make sure that we do not leave BoundTypeOrValueExpression in the tree. If it is there, we can recover, but we prefer not getting in a situation like that.
| return CheckValue(typeOrValue.Data.ValueExpression, BindValueKind.RValue, diagnostics); | ||
| var boundValue = typeOrValue.Data.Binder.BindIdentifier(identifier, invoked: false, indexed: false, diagnostics: diagnostics); | ||
|
|
||
| Debug.Assert(typeOrValue.Type.Equals(boundValue.Type, TypeCompareKind.ConsiderEverything)); |
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.
Debug.Assert(typeOrValue.Type.Equals(boundValue.Type, TypeCompareKind.ConsiderEverything)); (line 1973)
Could the nullability of the value differ? For example Color? Color = ...; the Color value would have type Color? #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.
Could the nullability of the value differ?
Bound tree doesn't preserve top level nullability of types and we are binding simple identifiers. Perhaps nested nullability can somehow sneak in. Since this is an assert, I am not worried about this and I am comfortable going with a possibly too strong condition. We can relax it once the assert starts causing problems.
| resultKind, | ||
| symbols, | ||
| childNodes.SelectAsArray((e, self) => self.BindToTypeForErrorRecovery(e), this), | ||
| childNodes.SelectAsArray((e, self) => self.AdjustBadExpressionChild(self.BindToTypeForErrorRecovery(e)), this), |
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.
self.AdjustBadExpressionChild(self.BindToTypeForErrorRecovery(e)) (line 195)
nit: Would it be simpler for BindToTypeForErrorRecovery to handle calling AdjustBadExpressionChild, even if it involves a tiny bit of extra work in scenarios were a type-or-value couldn't not be encountered? #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 was the resolution?
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 was the resolution?
Since this was a "nit" I didn't spend much time thinking about that and didn't feel like this comment needs a specific response. I took it under advisement. I am comfortable with the current approach.
I felt that suggested simplification won't be appropriate because the node has type, and therefore, should not be changed by a helper the sole purpose of which is to figure out the type, even when that operation is requested as part of an error recovery.
| } | ||
| } | ||
|
|
||
| // The type calculation here should be kept in sync with logic in BindLeftIdentifierOfPotentialColorColorMemberAccess. |
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 type calculation here should be kept in sync with logic in BindLeftIdentifierOfPotentialColorColorMemberAccess. (line 2148)
It's not obvious what type calculation this comment is referring to
#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.
It's not obvious what type calculation this comment is referring to
Clarified
| } | ||
|
|
||
| case SymbolKind.RangeVariable: | ||
| // The type calculation here should be kept in sync with logic in BindLeftIdentifierOfPotentialColorColorMemberAccess. |
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.
Comment refers to case SymbolKind.Field: a few lines above (line 2171)
Should a similar comment be left in the field case?
BindLeftIdentifierOfPotentialColorColorMemberAccess has a case for fields when computing type
Never mind. I see it's in BindFieldAccess #Closed
|
|
||
| var lookupResult = LookupResult.GetInstance(); | ||
| var useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; | ||
| this.LookupIdentifier(lookupResult, left, invoked: false, ref useSiteInfo); |
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.
Not directly related to this PR: Does BindLeftIdentifierOfPotentialColorColorMemberAccess need to worry about nint and _ like BindIdentifier does?
nint nint = 0; and _ _ = null; class _ { } appear legal. #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.
Does
BindLeftIdentifierOfPotentialColorColorMemberAccessneed to worry aboutnintand_likeBindIdentifierdoes?
Could you please elaborate? What code in BindIdentifier do you refer to?
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 looks like this is legal
_ _ = null;
_.ReferenceEquals(null, null);
class _ { }
This seems correct because - in this case is identifier and the name of the type.
The following is illegal because the name of the type is not "nint", it is "IntPtr"
nint nint = 0;
nint.ReferenceEquals(null, null);
However, this works:
using System;
nint IntPtr = 0;
IntPtr.ReferenceEquals(null, null);
Does this answer the question?
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.
Done with review pass (commit 3)
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.
Done with review pass (commit 5)
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 5)
Fixes #45739.