Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 76 additions & 51 deletions src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,31 +34,9 @@ partial class Binder
}

internal static PropertySymbol? GetUnionTypeValueProperty(NamedTypeSymbol inputUnionType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
PropertySymbol? valueProperty = GetUnionTypeValuePropertyOriginalDefinition(inputUnionType, ref useSiteInfo);
if (valueProperty is null)
{
return null;
}

return valueProperty.AsMember(inputUnionType);
}

private static PropertySymbol? GetUnionTypeValuePropertyOriginalDefinition(NamedTypeSymbol inputUnionType, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(inputUnionType.IsUnionType);

// https://github.com/dotnet/roslyn/issues/82636: Not dealing with inheritance for now.
// The inherited property might not be a definition, therefore the name of the function should probably change
PropertySymbol? valueProperty = null;
foreach (var m in inputUnionType.OriginalDefinition.GetMembers(WellKnownMemberNames.ValuePropertyName))
{
if (m is PropertySymbol prop && hasUnionValueSignature(prop))
{
valueProperty = prop;
break;
}
}
PropertySymbol? valueProperty = TryGetOwnOrInheritedUnionMember<PropertySymbol>(inputUnionType, WellKnownMemberNames.ValuePropertyName, isSuitableProperty, ref useSiteInfo);

if (valueProperty is not null)
{
Expand All @@ -67,11 +45,23 @@ partial class Binder
}
else
{
useSiteInfo.AddDiagnosticInfo(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, inputUnionType, WellKnownMemberNames.ValuePropertyName)); // https://github.com/dotnet/roslyn/issues/82636: Cover this code path
useSiteInfo.AddDiagnosticInfo(new CSDiagnosticInfo(ErrorCode.ERR_MissingPredefinedMember, inputUnionType.OriginalDefinition, WellKnownMemberNames.ValuePropertyName));
}

return valueProperty;

static bool isSuitableProperty(Symbol m, [NotNullWhen(true)] out PropertySymbol? valueProperty)
{
if (m is PropertySymbol prop && hasUnionValueSignature(prop))
{
valueProperty = prop;
return true;
}

valueProperty = null;
return false;
}

static bool hasUnionValueSignature(PropertySymbol property)
{
// https://github.com/dotnet/roslyn/issues/82636: Cover individual conditions with tests
Expand All @@ -88,6 +78,52 @@ static bool hasUnionValueSignature(PropertySymbol property)
}
}

private delegate bool IsSuitableUnionMember<T>(Symbol m, [NotNullWhen(true)] out T? member) where T : Symbol;

private static T? TryGetOwnOrInheritedUnionMember<T>(NamedTypeSymbol inputUnionType, string memberName, IsSuitableUnionMember<T> isSuitableUnionMember, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
where T : Symbol
{
T? member;
NamedTypeSymbol declaringType = inputUnionType.OriginalDefinition;
if (getMemberDeclaredInType(declaringType, memberName, isSuitableUnionMember, out member))
{
member = (T)member.SymbolAsMember(inputUnionType);
}
else
{
for (NamedTypeSymbol baseType1 = declaringType.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo), baseType2 = inputUnionType.BaseTypeNoUseSiteDiagnostics;
Copy link
Member

Choose a reason for hiding this comment

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

Is there any need to defend against cycles like class MyUnion1 : MyUnion2; class MyUnion2 : MyUnion1 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any need to defend against cycles like class MyUnion1 : MyUnion2; class MyUnion2 : MyUnion1 here?

I do not think so. I do not expect us to get here while bases are being resolved.

baseType1 is not null;
baseType1 = baseType1.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo), baseType2 = baseType2.BaseTypeNoUseSiteDiagnostics)
{
if (getMemberDeclaredInType(baseType1, memberName, isSuitableUnionMember, out member))
{
if (!inputUnionType.IsDefinition)
Copy link
Member

Choose a reason for hiding this comment

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

Checking my understanding: is this just an optimization to avoid creating an unnecessary constructed member symbol?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Checking my understanding: is this just an optimization to avoid creating an unnecessary constructed member symbol?

Yes, If we are dealing with a type definition, no additional substitution is needed for members from it's definition base.

{
member = (T)member.OriginalDefinition.SymbolAsMember(baseType2);
}

break;
}
}
}

return member;

static bool getMemberDeclaredInType(NamedTypeSymbol declaringType, string memberName, IsSuitableUnionMember<T> isSuitableUnionMember, [NotNullWhen(true)] out T? member)
{
foreach (var m in declaringType.GetMembers(memberName))
{
if (isSuitableUnionMember(m, out member))
{
return true;
}
}

member = null;
return false;
}
}

internal static PropertySymbol? GetUnionTypeValuePropertyNoUseSiteDiagnostics(NamedTypeSymbol inputUnionType)
{
var useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
Expand All @@ -96,34 +132,35 @@ static bool hasUnionValueSignature(PropertySymbol property)

internal static bool IsUnionTypeValueProperty(NamedTypeSymbol unionType, Symbol symbol)
{
// https://github.com/dotnet/roslyn/issues/82636: Deal with inheritance?
var useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
return Binder.GetUnionTypeValuePropertyOriginalDefinition(unionType, ref useSiteInfo) == (object)symbol.OriginalDefinition;
return Symbol.Equals(Binder.GetUnionTypeValuePropertyNoUseSiteDiagnostics(unionType), symbol, TypeCompareKind.AllIgnoreOptions);
}

private static PropertySymbol? GetUnionTypeHasValuePropertyOriginalDefinition(NamedTypeSymbol inputUnionType)
internal static PropertySymbol? GetUnionTypeHasValueProperty(NamedTypeSymbol inputUnionType)
{
Debug.Assert(inputUnionType.IsUnionType);

PropertySymbol? valueProperty = null;
var useSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
Copy link
Member

@jjonescz jjonescz Mar 16, 2026

Choose a reason for hiding this comment

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

Why are we discarding use site info here? #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.

Why are we discarding use site info here?

It is not an error to not find matching HasValue property, therefore we don't want to report any errors or warnings.

PropertySymbol? valueProperty = TryGetOwnOrInheritedUnionMember<PropertySymbol>(inputUnionType, WellKnownMemberNames.HasValuePropertyName, isSuitableProperty, ref useSiteInfo);
Copy link
Member

@jjonescz jjonescz Mar 16, 2026

Choose a reason for hiding this comment

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

valueProperty

nit: "hasValueProperty"? #Resolved


// https://github.com/dotnet/roslyn/issues/82636: Not dealing with inheritance for now.
foreach (var m in inputUnionType.OriginalDefinition.GetMembers(WellKnownMemberNames.HasValuePropertyName))
if (valueProperty?.GetUseSiteInfo().DiagnosticInfo?.DefaultSeverity == DiagnosticSeverity.Error)
{
return null; // https://github.com/dotnet/roslyn/issues/82636: Cover this code path
}

return valueProperty;

static bool isSuitableProperty(Symbol m, [NotNullWhen(true)] out PropertySymbol? valueProperty)
{
if (m is PropertySymbol prop && hasHasValueSignature(prop))
{
valueProperty = prop;
break;
return true;
}
}

if (valueProperty is null || valueProperty.GetUseSiteInfo().DiagnosticInfo?.DefaultSeverity == DiagnosticSeverity.Error)
{
return null; // https://github.com/dotnet/roslyn/issues/82636: Cover this code path
valueProperty = null;
return false;
}

return valueProperty;

static bool hasHasValueSignature(PropertySymbol property)
{
// https://github.com/dotnet/roslyn/issues/82636: Cover individual conditions with tests
Expand All @@ -140,17 +177,6 @@ static bool hasHasValueSignature(PropertySymbol property)
}
}

internal static PropertySymbol? GetUnionTypeHasValueProperty(NamedTypeSymbol inputUnionType)
{
PropertySymbol? valueProperty = GetUnionTypeHasValuePropertyOriginalDefinition(inputUnionType);
if (valueProperty is null)
{
return null;
}

return valueProperty.AsMember(inputUnionType);
}

internal static MethodSymbol? GetUnionTypeTryGetValueMethod(NamedTypeSymbol inputUnionType, TypeSymbol type)
{
Debug.Assert(inputUnionType.IsUnionType);
Expand Down Expand Up @@ -196,8 +222,7 @@ static bool isTryGetValueSignature(MethodSymbol method)

internal static bool IsUnionTypeHasValueProperty(NamedTypeSymbol unionType, PropertySymbol property)
{
// https://github.com/dotnet/roslyn/issues/82636: Deal with inheritance?
return (object)property.OriginalDefinition == Binder.GetUnionTypeHasValuePropertyOriginalDefinition(unionType);
return Symbol.Equals(Binder.GetUnionTypeHasValueProperty(unionType), property, TypeCompareKind.AllIgnoreOptions);
}

private BoundExpression BindIsPatternExpression(IsPatternExpressionSyntax node, BindingDiagnosticBag diagnostics)
Expand Down
6 changes: 3 additions & 3 deletions src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ private BoundDagTemp MakeUnionValue(BoundDagTemp input, BoundPropertySubpatternM
{
Debug.Assert(unionValue.Symbol is PropertySymbol);
var property = (PropertySymbol)unionValue.Symbol;
valueEvaluation = new BoundDagPropertyEvaluation(unionValue.Syntax, property, isLengthOrCount: false, OriginalInput(input, property));
valueEvaluation = new BoundDagPropertyEvaluation(unionValue.Syntax, property, isLengthOrCount: false, input);
var result = valueEvaluation.MakeResultTemp();

Debug.Assert(IsUnionValue(result, out _));
Expand Down Expand Up @@ -751,7 +751,7 @@ private bool TryMakeTestsForUnionHasValue(SyntaxNode syntax, TestInputOutputInfo
{
if (_forLowering)
{
BoundDagEvaluation hasValueEvaluation = new BoundDagPropertyEvaluation(unionValue.Syntax, hasValue, isLengthOrCount: false, OriginalInput(inputInfo.DagTemp, hasValue));
BoundDagEvaluation hasValueEvaluation = new BoundDagPropertyEvaluation(unionValue.Syntax, hasValue, isLengthOrCount: false, inputInfo.DagTemp);
var temp = hasValueEvaluation.MakeResultTemp();
Debug.Assert(IsUnionHasValue(temp, out _));

Expand Down Expand Up @@ -794,7 +794,7 @@ private TestInputOutputInfo MakeConvertToType(
{
if (_forLowering)
{
var deconstructEvaluation = new BoundDagDeconstructEvaluation(syntax, tryGetValue, OriginalInput(inputInfo.DagTemp, tryGetValue));
var deconstructEvaluation = new BoundDagDeconstructEvaluation(syntax, tryGetValue, OriginalInput(inputInfo.DagTemp, tryGetValue)); // PROTOTYPE: Inherited TryGetValue and OriginalInput?
tests.Add(new Tests.One(deconstructEvaluation));

var boolResult = deconstructEvaluation.MakeReturnValueTemp();
Expand Down
Loading
Loading