Skip to content

Conversation

@AlekseyTs
Copy link
Contributor

Fixes #45739.

…of binding possible type-or-value receiver (so-called “Color Color” scenario)

Fixes dotnet#45739.
@AlekseyTs AlekseyTs requested a review from a team as a code owner November 1, 2025 16:04

[WorkItem("https://github.com/dotnet/roslyn/issues/45739")]
[Fact]
public void ConstFieldEnum_02()
Copy link
Contributor

@Rekkonnect Rekkonnect Nov 1, 2025

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

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 3, 2025

@dotnet/roslyn-compiler Please review #Closed

5 similar comments
@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 4, 2025

@dotnet/roslyn-compiler Please review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 5, 2025

@dotnet/roslyn-compiler Please review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 6, 2025

@dotnet/roslyn-compiler Please review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 7, 2025

@dotnet/roslyn-compiler Please review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 10, 2025

@dotnet/roslyn-compiler Please review #Closed

@RikkiGibson
Copy link
Member

RikkiGibson commented Nov 10, 2025

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));
Copy link
Member

@RikkiGibson RikkiGibson Nov 10, 2025

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

Copy link
Contributor Author

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.

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 11, 2025

@dotnet/roslyn-compiler Please review #Closed

@AlekseyTs AlekseyTs requested review from a team and RikkiGibson November 11, 2025 18:36
@AlekseyTs AlekseyTs requested a review from a team November 11, 2025 19:42
@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 11, 2025

@dotnet/roslyn-compiler For a second review #Closed

3 similar comments
@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 12, 2025

@dotnet/roslyn-compiler For a second review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 12, 2025

@dotnet/roslyn-compiler For a second review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 13, 2025

@dotnet/roslyn-compiler For a second review #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 14, 2025

@dotnet/roslyn-compiler For a second review on a PR opened for review on Nov 1st. #Closed

2 similar comments
@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 17, 2025

@dotnet/roslyn-compiler For a second review on a PR opened for review on Nov 1st. #Closed

@AlekseyTs
Copy link
Contributor Author

AlekseyTs commented Nov 18, 2025

@dotnet/roslyn-compiler For a second review on a PR opened for review on Nov 1st. #Closed

@jcouv jcouv self-assigned this Nov 19, 2025
{
public override ConstantValue? ConstantValueOpt => Data?.ConstantValue;

public override Symbol? ExpressionSymbol => this.BinaryOperatorMethod;
Copy link
Member

@jcouv jcouv Nov 19, 2025

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

Copy link
Contributor Author

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)
Copy link
Member

@jcouv jcouv Nov 19, 2025

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

Copy link
Contributor Author

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

Copy link
Member

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.

Copy link
Contributor Author

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));
Copy link
Member

@jcouv jcouv Nov 19, 2025

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

Copy link
Contributor Author

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),
Copy link
Member

@jcouv jcouv Nov 19, 2025

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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the resolution?

Copy link
Contributor Author

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.
Copy link
Member

@jcouv jcouv Nov 19, 2025

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

Copy link
Contributor Author

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.
Copy link
Member

@jcouv jcouv Nov 19, 2025

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);
Copy link
Member

@jcouv jcouv Nov 19, 2025

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does BindLeftIdentifierOfPotentialColorColorMemberAccess need to worry about nint and _ like BindIdentifier does?

Could you please elaborate? What code in BindIdentifier do you refer to?

Copy link
Contributor Author

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?

Copy link
Member

@jcouv jcouv left a 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)

@AlekseyTs AlekseyTs requested a review from jcouv November 20, 2025 08:07
Copy link
Member

@jcouv jcouv left a 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)

Copy link
Member

@jcouv jcouv left a 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)

@AlekseyTs AlekseyTs enabled auto-merge (squash) November 20, 2025 17:10
@AlekseyTs AlekseyTs merged commit ac2327f into dotnet:main Nov 20, 2025
28 of 29 checks passed
@dotnet-policy-service dotnet-policy-service bot added this to the Next milestone Nov 20, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

code fix of IDE0002 produces invalid code when a constant has same name as its type

4 participants