Skip to content

Commit

Permalink
Extended property patterns: IOperation and Symbols (#53082)
Browse files Browse the repository at this point in the history
  • Loading branch information
alrz authored May 10, 2021
1 parent 64f69fb commit 2a82d69
Show file tree
Hide file tree
Showing 13 changed files with 905 additions and 210 deletions.
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 first 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

0 comments on commit 2a82d69

Please sign in to comment.