Skip to content
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

Extended property patterns: IOperation and Symbols #53082

Merged
69 changes: 33 additions & 36 deletions src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -688,11 +688,11 @@ private BoundPattern BindRecursivePattern(
inputValEscape = GetValEscape(declType, inputValEscape);

MethodSymbol? deconstructMethod = null;
ImmutableArray<BoundSubpattern> deconstructionSubpatterns = default;
ImmutableArray<BoundPositionalSubpattern> deconstructionSubpatterns = default;
if (node.PositionalPatternClause != null)
{
PositionalPatternClauseSyntax positionalClause = node.PositionalPatternClause;
var patternsBuilder = ArrayBuilder<BoundSubpattern>.GetInstance(positionalClause.Subpatterns.Count);
var patternsBuilder = ArrayBuilder<BoundPositionalSubpattern>.GetInstance(positionalClause.Subpatterns.Count);
if (IsZeroElementTupleType(declType))
{
// Work around https://github.com/dotnet/roslyn/issues/20648: The compiler's internal APIs such as `declType.IsTupleType`
Expand Down Expand Up @@ -737,7 +737,7 @@ private BoundPattern BindRecursivePattern(
deconstructionSubpatterns = patternsBuilder.ToImmutableAndFree();
}

ImmutableArray<BoundSubpattern> properties = default;
ImmutableArray<BoundPropertySubpattern> properties = default;
if (node.PropertyPatternClause != null)
{
properties = BindPropertyPatternClause(node.PropertyPatternClause, declType, inputValEscape, permitDesignations, diagnostics, ref hasErrors);
Expand Down Expand Up @@ -765,7 +765,7 @@ deconstructMethod is null &&
bool permitDesignations,
BoundExpression deconstruct,
ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders,
ArrayBuilder<BoundSubpattern> patterns,
ArrayBuilder<BoundPositionalSubpattern> patterns,
ref bool hasErrors,
BindingDiagnosticBag diagnostics)
{
Expand Down Expand Up @@ -797,7 +797,7 @@ deconstructMethod is null &&
}
}

var boundSubpattern = new BoundSubpattern(
var boundSubpattern = new BoundPositionalSubpattern(
subPattern,
parameter,
BindPattern(subPattern.Pattern, elementType, GetValEscape(elementType, inputValEscape), permitDesignations, isError, diagnostics)
Expand All @@ -810,7 +810,7 @@ deconstructMethod is null &&

private void BindITupleSubpatterns(
PositionalPatternClauseSyntax node,
ArrayBuilder<BoundSubpattern> patterns,
ArrayBuilder<BoundPositionalSubpattern> patterns,
bool permitDesignations,
BindingDiagnosticBag diagnostics)
{
Expand All @@ -826,7 +826,7 @@ private void BindITupleSubpatterns(
diagnostics.Add(ErrorCode.ERR_ArgumentNameInITuplePattern, subpatternSyntax.NameColon.Location);
}

var boundSubpattern = new BoundSubpattern(
var boundSubpattern = new BoundPositionalSubpattern(
subpatternSyntax,
null,
BindPattern(subpatternSyntax.Pattern, objectType, valEscape, permitDesignations, hasErrors: false, diagnostics));
Expand All @@ -836,7 +836,7 @@ private void BindITupleSubpatterns(

private void BindITupleSubpatterns(
ParenthesizedVariableDesignationSyntax node,
ArrayBuilder<BoundSubpattern> patterns,
ArrayBuilder<BoundPositionalSubpattern> patterns,
bool permitDesignations,
BindingDiagnosticBag diagnostics)
{
Expand All @@ -846,7 +846,7 @@ private void BindITupleSubpatterns(
foreach (var variable in node.Variables)
{
BoundPattern pattern = BindVarDesignation(variable, objectType, valEscape, permitDesignations, hasErrors: false, diagnostics);
var boundSubpattern = new BoundSubpattern(
var boundSubpattern = new BoundPositionalSubpattern(
variable,
null,
pattern);
Expand All @@ -861,7 +861,7 @@ private void BindValueTupleSubpatterns(
uint inputValEscape,
bool permitDesignations,
ref bool hasErrors,
ArrayBuilder<BoundSubpattern> patterns,
ArrayBuilder<BoundPositionalSubpattern> patterns,
BindingDiagnosticBag diagnostics)
{
if (elementTypesWithAnnotations.Length != node.Subpatterns.Count && !hasErrors)
Expand All @@ -883,7 +883,7 @@ private void BindValueTupleSubpatterns(
foundField = CheckIsTupleElement(subpatternSyntax.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
}

BoundSubpattern boundSubpattern = new BoundSubpattern(
BoundPositionalSubpattern boundSubpattern = new BoundPositionalSubpattern(
subpatternSyntax,
foundField,
BindPattern(subpatternSyntax.Pattern, elementType, GetValEscape(elementType, inputValEscape), permitDesignations, isError, diagnostics));
Expand Down Expand Up @@ -1028,7 +1028,6 @@ private BoundPattern BindVarPattern(
inputType = CreateErrorType();
}

TypeSymbol declType = inputType;
Symbol foundSymbol = BindTypeOrAliasOrKeyword(node.VarKeyword, node, diagnostics, out bool isVar).Symbol;
if (!isVar)
{
Expand Down Expand Up @@ -1072,7 +1071,7 @@ private BoundPattern BindVarDesignation(
case SyntaxKind.ParenthesizedVariableDesignation:
{
var tupleDesignation = (ParenthesizedVariableDesignationSyntax)node;
var subPatterns = ArrayBuilder<BoundSubpattern>.GetInstance(tupleDesignation.Variables.Count);
var subPatterns = ArrayBuilder<BoundPositionalSubpattern>.GetInstance(tupleDesignation.Variables.Count);
MethodSymbol? deconstructMethod = null;
var strippedInputType = inputType.StrippedType();

Expand Down Expand Up @@ -1121,7 +1120,7 @@ private BoundPattern BindVarDesignation(
bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
BoundPattern pattern = BindVarDesignation(variable, elementType, GetValEscape(elementType, inputValEscape), permitDesignations, isError, diagnostics);
subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
subPatterns.Add(new BoundPositionalSubpattern(variable, symbol: null, pattern));
}
}

Expand All @@ -1144,7 +1143,7 @@ void addSubpatternsForTuple(ImmutableArray<TypeWithAnnotations> elementTypes)
bool isError = i >= elementTypes.Length;
TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i].Type;
BoundPattern pattern = BindVarDesignation(variable, elementType, GetValEscape(elementType, inputValEscape), permitDesignations, isError, diagnostics);
subPatterns.Add(new BoundSubpattern(variable, symbol: null, pattern));
subPatterns.Add(new BoundPositionalSubpattern(variable, symbol: null, pattern));
}
}
}
Expand All @@ -1155,72 +1154,71 @@ void addSubpatternsForTuple(ImmutableArray<TypeWithAnnotations> elementTypes)
}
}

private ImmutableArray<BoundSubpattern> BindPropertyPatternClause(
private ImmutableArray<BoundPropertySubpattern> BindPropertyPatternClause(
PropertyPatternClauseSyntax node,
TypeSymbol inputType,
uint inputValEscape,
bool permitDesignations,
BindingDiagnosticBag diagnostics,
ref bool hasErrors)
{
var builder = ArrayBuilder<BoundSubpattern>.GetInstance(node.Subpatterns.Count);
var builder = ArrayBuilder<BoundPropertySubpattern>.GetInstance(node.Subpatterns.Count);
foreach (SubpatternSyntax p in node.Subpatterns)
{
ExpressionSyntax? expr = p.ExpressionColon?.Expression;
PatternSyntax pattern = p.Pattern;
ImmutableArray<Symbol> members;
BoundPropertySubpatternMember? member;
TypeSymbol memberType;
if (expr == null)
{
if (!hasErrors)
diagnostics.Add(ErrorCode.ERR_PropertyPatternNameMissing, pattern.Location, pattern);

memberType = CreateErrorType();
members = ImmutableArray<Symbol>.Empty;
member = null;
hasErrors = true;
}
else
{
var memberBuilder = ArrayBuilder<Symbol>.GetInstance();
LookupMembersForPropertyPattern(inputType, expr, memberBuilder, diagnostics, ref hasErrors, out memberType);
members = memberBuilder.ToImmutableAndFree();
member = LookupMembersForPropertyPattern(inputType, expr, diagnostics, ref hasErrors);
memberType = member.Type;
}

BoundPattern boundPattern = BindPattern(pattern, memberType, GetValEscape(memberType, inputValEscape), permitDesignations, hasErrors, diagnostics);
builder.Add(new BoundSubpattern(p, members, boundPattern));
builder.Add(new BoundPropertySubpattern(p, member, boundPattern));
}

return builder.ToImmutableAndFree();
}

private void LookupMembersForPropertyPattern(
TypeSymbol inputType, ExpressionSyntax expr, ArrayBuilder<Symbol> builder, BindingDiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType)
private BoundPropertySubpatternMember LookupMembersForPropertyPattern(
TypeSymbol inputType, ExpressionSyntax expr, BindingDiagnosticBag diagnostics, ref bool hasErrors)
{
Symbol? symbol;
BoundPropertySubpatternMember? receiver = null;
Symbol? symbol = null;
switch (expr)
{
case IdentifierNameSyntax name:
symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);
break;
case MemberAccessExpressionSyntax { Name: IdentifierNameSyntax name } memberAccess when memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression):
LookupMembersForPropertyPattern(inputType, memberAccess.Expression, builder, diagnostics, ref hasErrors, out memberType);
symbol = BindPropertyPatternMember(memberType.StrippedType(), name, ref hasErrors, diagnostics);
receiver = LookupMembersForPropertyPattern(inputType, memberAccess.Expression, diagnostics, ref hasErrors);
symbol = BindPropertyPatternMember(receiver.Type.StrippedType(), name, ref hasErrors, diagnostics);
break;
default:
Error(diagnostics, ErrorCode.ERR_InvalidNameInSubpattern, expr);
symbol = null;
hasErrors = true;
break;
}

memberType = symbol switch
TypeSymbol memberType = symbol switch
{
FieldSymbol field => field.Type,
PropertySymbol property => property.Type,
_ => CreateErrorType()
};

builder.AddIfNotNull(symbol);
return new BoundPropertySubpatternMember(expr, receiver, symbol, type: memberType, hasErrors);
}

private Symbol? BindPropertyPatternMember(
Expand Down Expand Up @@ -1280,14 +1278,13 @@ private void LookupMembersForPropertyPattern(
Error(diagnostics, ErrorCode.ERR_PropertyLacksGet, memberName, name);
break;
}
hasErrors = true;
}

hasErrors = true;
return boundMember.ExpressionSymbol;
break;
}

if (hasErrors || !CheckValueKind(node: memberName.Parent, expr: boundMember, valueKind: BindValueKind.RValue,
checkingReceiver: false, diagnostics: diagnostics))
if (!hasErrors && !CheckValueKind(node: memberName.Parent, expr: boundMember, valueKind: BindValueKind.RValue,
checkingReceiver: false, diagnostics: diagnostics))
{
hasErrors = true;
}
Expand Down
66 changes: 30 additions & 36 deletions src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis.CSharp.Symbols;
Expand Down Expand Up @@ -531,41 +532,11 @@ private Tests MakeTestsAndBindingsForRecursivePattern(
{
BoundPattern pattern = subpattern.Pattern;
BoundDagTemp currentInput = input;
if (subpattern.Symbols.IsEmpty)
if (!tryMakeSubpatternMemberTests(subpattern.Member, ref currentInput))
{
addErrorTest();
goto done;
Debug.Assert(recursive.HasAnyErrors);
tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true)));
}

for (int index = 0, count = subpattern.Symbols.Length; ;)
{
Symbol symbol = subpattern.Symbols[index];
BoundDagEvaluation evaluation;
switch (symbol)
{
case PropertySymbol property:
evaluation = new BoundDagPropertyEvaluation(pattern.Syntax, property, OriginalInput(currentInput, property));
break;
case FieldSymbol field:
evaluation = new BoundDagFieldEvaluation(pattern.Syntax, field, OriginalInput(currentInput, field));
break;
default:
addErrorTest();
goto done;
}

tests.Add(new Tests.One(evaluation));
TypeSymbol type = symbol.GetTypeOrReturnType().Type;
currentInput = new BoundDagTemp(pattern.Syntax, type, evaluation);

if (++index == count)
break;

// If this is not the last member, add null test, unwrap nullables, and continue.
currentInput = MakeConvertToType(currentInput, pattern.Syntax, type.StrippedType(), isExplicitTest: false, tests);
}

done:
tests.Add(MakeTestsAndBindings(currentInput, pattern, bindings));
}
}
Expand All @@ -578,10 +549,33 @@ private Tests MakeTestsAndBindingsForRecursivePattern(

return Tests.AndSequence.Create(tests);

void addErrorTest()
bool tryMakeSubpatternMemberTests([NotNullWhen(true)] BoundPropertySubpatternMember? member, ref BoundDagTemp input)
{
Debug.Assert(recursive.HasAnyErrors);
tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true)));
if (member is null)
return false;

if (tryMakeSubpatternMemberTests(member.Receiver, ref input))
{
// If this is not the last member, add null test, unwrap nullables, and continue.
input = MakeConvertToType(input, member.Syntax, member.Receiver.Type.StrippedType(), isExplicitTest: false, tests);
}

BoundDagEvaluation evaluation;
switch (member.Symbol)
{
case PropertySymbol property:
evaluation = new BoundDagPropertyEvaluation(member.Syntax, property, OriginalInput(input, property));
break;
case FieldSymbol field:
evaluation = new BoundDagFieldEvaluation(member.Syntax, field, OriginalInput(input, field));
break;
default:
return false;
}

tests.Add(new Tests.One(evaluation));
input = new BoundDagTemp(member.Syntax, member.Type, evaluation);
return true;
}
}

Expand Down
26 changes: 18 additions & 8 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2067,8 +2067,8 @@
<Node Name="BoundRecursivePattern" Base="BoundPattern">
<Field Name="DeclaredType" Type="BoundTypeExpression?"/>
<Field Name="DeconstructMethod" Type="MethodSymbol?"/>
<Field Name="Deconstruction" Type="ImmutableArray&lt;BoundSubpattern&gt;" Null="allow"/>
<Field Name="Properties" Type="ImmutableArray&lt;BoundSubpattern&gt;" Null="allow"/>
<Field Name="Deconstruction" Type="ImmutableArray&lt;BoundPositionalSubpattern&gt;" Null="allow"/>
<Field Name="Properties" Type="ImmutableArray&lt;BoundPropertySubpattern&gt;" Null="allow"/>
<!-- Variable is a local symbol, or in the case of top-level code in scripts and interactive,
a field that is a member of the script class. Variable is null if `_` is used or if the identifier is omitted. -->
<Field Name="Variable" Type="Symbol?"/>
Expand All @@ -2086,14 +2086,24 @@
<Node Name="BoundITuplePattern" Base="BoundPattern">
<Field Name="GetLengthMethod" Type="MethodSymbol" Null="disallow"/>
<Field Name="GetItemMethod" Type="MethodSymbol" Null="disallow"/>
<Field Name="Subpatterns" Type="ImmutableArray&lt;BoundSubpattern&gt;" Null="disallow"/>
<Field Name="Subpatterns" Type="ImmutableArray&lt;BoundPositionalSubpattern&gt;" Null="disallow"/>
</Node>

<!-- A subpattern, either in a positional or property part of a recursive pattern. -->
<Node Name="BoundSubpattern" Base="BoundNode">
<!-- The tuple element or parameter in a positional pattern, or the property or field in a property pattern. -->
<Field Name="Symbols" Type="ImmutableArray&lt;Symbol&gt;"/>
<Field Name="Pattern" Type="BoundPattern" Null="disallow"/>
<AbstractNode Name="BoundSubpattern" Base="BoundNode">
<Field Name="Pattern" Type="BoundPattern"/>
</AbstractNode>
<Node Name="BoundPositionalSubpattern" Base="BoundSubpattern">
<!-- The tuple element or parameter in a positional pattern. -->
<Field Name="Symbol" Type="Symbol?"/>
</Node>
<Node Name="BoundPropertySubpattern" Base="BoundSubpattern">
<!-- The property or field access in a property pattern. -->
<Field Name="Member" Type="BoundPropertySubpatternMember?"/>
</Node>
<Node Name="BoundPropertySubpatternMember" Base="BoundNode">
<Field Name="Receiver" Type="BoundPropertySubpatternMember?"/>
<Field Name="Symbol" Type="Symbol?"/>
<Field Name="Type" Type="TypeSymbol"/>
</Node>

<Node Name="BoundTypePattern" Base="BoundPattern">
Expand Down
Loading