Skip to content

Commit 3ae4026

Browse files
committed
Add a prototype implementation for GetTypeInfo(PatternSyntax)
This is a first draft of #26494, and requires more testing.
1 parent 1d14dbb commit 3ae4026

9 files changed

Lines changed: 240 additions & 41 deletions

File tree

src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,7 @@ private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node,
517517
TypeSyntax typeSyntax = node.Type;
518518
TypeSymbol declType = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
519519

520-
var patterns = ArrayBuilder<BoundPattern>.GetInstance(node.SubPatterns.Count);
520+
var patterns = ArrayBuilder<BoundSubpattern>.GetInstance(node.SubPatterns.Count);
521521
MethodSymbol deconstructMethod = null;
522522
if (declType.IsTupleType)
523523
{
@@ -534,14 +534,19 @@ private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node,
534534
var subPattern = node.SubPatterns[i];
535535
bool isError = i >= elementTypes.Length;
536536
TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i];
537+
FieldSymbol foundField = null;
537538
if (subPattern.NameColon != null)
538539
{
539540
string name = subPattern.NameColon.Name.Identifier.ValueText;
540-
FieldSymbol foundField = CheckIsTupleElement(subPattern.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
541+
foundField = CheckIsTupleElement(subPattern.NameColon.Name, (NamedTypeSymbol)declType, name, i, diagnostics);
541542
// PROTOTYPE(patterns2): Should the tuple field binding for the name be stored somewhere in the node?
542543

543544
}
544-
BoundPattern boundSubpattern = BindPattern(subPattern.Pattern, elementType, isError, diagnostics);
545+
BoundSubpattern boundSubpattern = new BoundSubpattern(
546+
subPattern,
547+
foundField,
548+
BindPattern(subPattern.Pattern, elementType, isError, diagnostics)
549+
);
545550
patterns.Add(boundSubpattern);
546551
}
547552
}
@@ -565,26 +570,32 @@ private BoundPattern BindDeconstructionPattern(DeconstructionPatternSyntax node,
565570
var subPattern = node.SubPatterns[i];
566571
bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
567572
TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
573+
ParameterSymbol parameter = null;
568574
if (subPattern.NameColon != null && !isError)
569575
{
570576
// Check that the given name is the same as the corresponding parameter of the method.
571577
string name = subPattern.NameColon.Name.Identifier.ValueText;
572578
int parameterIndex = i + skippedExtensionParameters;
573-
string parameterName = deconstructMethod.Parameters[parameterIndex].Name;
579+
parameter = deconstructMethod.Parameters[parameterIndex];
580+
string parameterName = parameter.Name;
574581
if (name != parameterName)
575582
{
576583
diagnostics.Add(ErrorCode.ERR_DeconstructParameterNameMismatch, subPattern.NameColon.Name.Location, name, parameterName);
577584
}
578585
// PROTOTYPE(patterns2): Should the parameter binding for the name be stored somewhere in the node?
579586
}
580-
BoundPattern boundSubpattern = BindPattern(subPattern.Pattern, elementType, isError, diagnostics);
587+
BoundSubpattern boundSubpattern = new BoundSubpattern(
588+
subPattern,
589+
parameter,
590+
BindPattern(subPattern.Pattern, elementType, isError, diagnostics)
591+
);
581592
patterns.Add(boundSubpattern);
582593
}
583594

584595
// PROTOTYPE(patterns2): If no Deconstruct method is found, try casting to `ITuple`.
585596
}
586597

587-
ImmutableArray<(Symbol property, BoundPattern pattern)> propertiesOpt = default;
598+
ImmutableArray<BoundSubpattern> propertiesOpt = default;
588599
if (node.PropertySubpattern != null)
589600
{
590601
propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors);
@@ -658,7 +669,7 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
658669
case SyntaxKind.ParenthesizedVariableDesignation:
659670
{
660671
var tupleDesignation = (ParenthesizedVariableDesignationSyntax)designation;
661-
var patterns = ArrayBuilder<BoundPattern>.GetInstance(tupleDesignation.Variables.Count);
672+
var subPatterns = ArrayBuilder<BoundSubpattern>.GetInstance(tupleDesignation.Variables.Count);
662673
MethodSymbol deconstructMethod = null;
663674
if (inputType.IsTupleType)
664675
{
@@ -676,7 +687,7 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
676687
bool isError = i >= elementTypes.Length;
677688
TypeSymbol elementType = isError ? CreateErrorType() : elementTypes[i];
678689
BoundPattern boundSubpattern = BindVarDesignation(node, tupleDesignation.Variables[i], elementType, isError, diagnostics);
679-
patterns.Add(boundSubpattern);
690+
subPatterns.Add(new BoundSubpattern(node, null, boundSubpattern));
680691
}
681692
}
682693
else
@@ -692,16 +703,16 @@ private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignati
692703
{
693704
bool isError = outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
694705
TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
695-
BoundPattern boundSubpattern = BindVarDesignation(node, tupleDesignation.Variables[i], elementType, isError, diagnostics);
696-
patterns.Add(boundSubpattern);
706+
BoundPattern pattern = BindVarDesignation(node, tupleDesignation.Variables[i], elementType, isError, diagnostics);
707+
subPatterns.Add(new BoundSubpattern(node, null, pattern));
697708
}
698709

699710
// PROTOTYPE(patterns2): If no Deconstruct method is found, try casting to `ITuple`.
700711
}
701712

702713
return new BoundRecursivePattern(
703714
syntax: node, declaredType: null, inputType: inputType, deconstructMethodOpt: deconstructMethod,
704-
deconstruction: patterns.ToImmutableAndFree(), propertiesOpt: default, variable: null, variableAccess: null, hasErrors: hasErrors);
715+
deconstruction: subPatterns.ToImmutableAndFree(), propertiesOpt: default, variable: null, variableAccess: null, hasErrors: hasErrors);
705716
}
706717
default:
707718
{
@@ -714,20 +725,20 @@ private BoundPattern BindPropertyPattern(PropertyPatternSyntax node, TypeSymbol
714725
{
715726
TypeSyntax typeSyntax = node.Type;
716727
TypeSymbol declType = BindRecursivePatternType(typeSyntax, inputType, diagnostics, ref hasErrors, out BoundTypeExpression boundDeclType);
717-
ImmutableArray<(Symbol property, BoundPattern pattern)> propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors);
728+
ImmutableArray<BoundSubpattern> propertiesOpt = BindPropertySubpattern(node.PropertySubpattern, declType, diagnostics, ref hasErrors);
718729
BindPatternDesignation(node, node.Designation, declType, typeSyntax, diagnostics, ref hasErrors, out Symbol variableSymbol, out BoundExpression variableAccess);
719730
return new BoundRecursivePattern(
720731
syntax: node, declaredType: boundDeclType, inputType: inputType, deconstructMethodOpt: null,
721732
deconstruction: default, propertiesOpt: propertiesOpt, variable: variableSymbol, variableAccess: variableAccess, hasErrors: hasErrors);
722733
}
723734

724-
ImmutableArray<(Symbol property, BoundPattern pattern)> BindPropertySubpattern(
735+
ImmutableArray<BoundSubpattern> BindPropertySubpattern(
725736
PropertySubpatternSyntax node,
726737
TypeSymbol inputType,
727738
DiagnosticBag diagnostics,
728739
ref bool hasErrors)
729740
{
730-
var builder = ArrayBuilder<(Symbol property, BoundPattern pattern)>.GetInstance(node.SubPatterns.Count);
741+
var builder = ArrayBuilder<BoundSubpattern>.GetInstance(node.SubPatterns.Count);
731742
foreach (SubpatternElementSyntax p in node.SubPatterns)
732743
{
733744
IdentifierNameSyntax name = p.NameColon?.Name;
@@ -750,7 +761,7 @@ private BoundPattern BindPropertyPattern(PropertyPatternSyntax node, TypeSymbol
750761
}
751762

752763
BoundPattern boundPattern = BindPattern(pattern, memberType, hasErrors, diagnostics);
753-
builder.Add((member, boundPattern));
764+
builder.Add(new BoundSubpattern(pattern, member, boundPattern));
754765
}
755766

756767
return builder.ToImmutableAndFree();

src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ private void MakeTestsAndBindings(
400400
ArrayBuilder<BoundDagTest> tests,
401401
ArrayBuilder<BoundPatternBinding> bindings)
402402
{
403-
Debug.Assert(input.Type.IsErrorType() || input.Type == recursive.InputType);
403+
Debug.Assert(input.Type.IsErrorType() || recursive.InputType.IsErrorType() || input.Type == recursive.InputType);
404404
if (recursive.DeclaredType != null)
405405
{
406406
input = MakeConvertToType(input, recursive.Syntax, recursive.DeclaredType.Type, tests);
@@ -422,7 +422,7 @@ private void MakeTestsAndBindings(
422422
int count = Math.Min(method.ParameterCount - extensionExtra, recursive.Deconstruction.Length);
423423
for (int i = 0; i < count; i++)
424424
{
425-
BoundPattern pattern = recursive.Deconstruction[i];
425+
BoundPattern pattern = recursive.Deconstruction[i].Pattern;
426426
SyntaxNode syntax = pattern.Syntax;
427427
var output = new BoundDagTemp(syntax, method.Parameters[i + extensionExtra].Type, evaluation, i);
428428
MakeTestsAndBindings(output, pattern, tests, bindings);
@@ -435,7 +435,7 @@ private void MakeTestsAndBindings(
435435
int count = Math.Min(elementTypes.Length, recursive.Deconstruction.Length);
436436
for (int i = 0; i < count; i++)
437437
{
438-
BoundPattern pattern = recursive.Deconstruction[i];
438+
BoundPattern pattern = recursive.Deconstruction[i].Pattern;
439439
SyntaxNode syntax = pattern.Syntax;
440440
FieldSymbol field = elements[i];
441441
var evaluation = new BoundDagFieldEvaluation(syntax, field, input); // fetch the ItemN field
@@ -459,16 +459,17 @@ private void MakeTestsAndBindings(
459459
// we have a "property" form
460460
for (int i = 0; i < recursive.PropertiesOpt.Length; i++)
461461
{
462-
(Symbol symbol, BoundPattern pattern) prop = recursive.PropertiesOpt[i];
463-
Symbol symbol = prop.symbol;
462+
var subPattern = recursive.PropertiesOpt[i];
463+
Symbol symbol = subPattern.Symbol;
464+
BoundPattern pattern = subPattern.Pattern;
464465
BoundDagEvaluation evaluation;
465466
switch (symbol)
466467
{
467468
case PropertySymbol property:
468-
evaluation = new BoundDagPropertyEvaluation(prop.pattern.Syntax, property, input);
469+
evaluation = new BoundDagPropertyEvaluation(pattern.Syntax, property, input);
469470
break;
470471
case FieldSymbol field:
471-
evaluation = new BoundDagFieldEvaluation(prop.pattern.Syntax, field, input);
472+
evaluation = new BoundDagFieldEvaluation(pattern.Syntax, field, input);
472473
break;
473474
default:
474475
Debug.Assert(recursive.HasAnyErrors);
@@ -477,8 +478,8 @@ private void MakeTestsAndBindings(
477478
}
478479

479480
tests.Add(evaluation);
480-
var output = new BoundDagTemp(prop.pattern.Syntax, prop.symbol.GetTypeOrReturnType(), evaluation, index: 0);
481-
MakeTestsAndBindings(output, prop.pattern, tests, bindings);
481+
var output = new BoundDagTemp(pattern.Syntax, symbol.GetTypeOrReturnType(), evaluation, index: 0);
482+
MakeTestsAndBindings(output, pattern, tests, bindings);
482483
}
483484
}
484485

src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,8 +1758,8 @@
17581758
<Node Name="BoundRecursivePattern" Base="BoundPattern">
17591759
<Field Name="DeclaredType" Type="BoundTypeExpression" Null="allow"/>
17601760
<Field Name="DeconstructMethodOpt" Type="MethodSymbol" Null="allow"/>
1761-
<Field Name="Deconstruction" Type="ImmutableArray&lt;BoundPattern&gt;" Null="allow"/>
1762-
<Field Name="PropertiesOpt" Type="ImmutableArray&lt;(Symbol symbol, BoundPattern pattern)&gt;" Null="allow"/>
1761+
<Field Name="Deconstruction" Type="ImmutableArray&lt;BoundSubpattern&gt;" Null="allow"/>
1762+
<Field Name="PropertiesOpt" Type="ImmutableArray&lt;BoundSubpattern&gt;" Null="allow"/>
17631763
<!-- Variable is a local symbol, or in the case of top-level code in scripts and interactive,
17641764
a field that is a member of the script class. Variable is null if `_` is used or if the identifier is omitted. -->
17651765
<Field Name="Variable" Type="Symbol" Null="allow"/>
@@ -1772,6 +1772,11 @@
17721772
removed. -->
17731773
<Field Name="VariableAccess" Type="BoundExpression" Null="allow"/>
17741774
</Node>
1775+
1776+
<Node Name="BoundSubpattern" Base="BoundNode">
1777+
<Field Name="Symbol" Type="Symbol" Null="allow"/>
1778+
<Field Name="Pattern" Type="BoundPattern" Null="disallow"/>
1779+
</Node>
17751780

17761781
<Node Name="BoundDiscardExpression" Base="BoundExpression">
17771782
<!-- A discarded value, when a designator uses `_`. When the type is given as `var, its Type
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
2+
3+
using Microsoft.CodeAnalysis.CSharp.Symbols;
4+
5+
namespace Microsoft.CodeAnalysis.CSharp
6+
{
7+
internal partial class BoundPattern
8+
{
9+
/// <summary>
10+
/// The type to which we attempt to convert the input in order to match this pattern.
11+
/// </summary>
12+
public virtual TypeSymbol ConvertedType => InputType;
13+
}
14+
15+
internal partial class BoundConstantPattern
16+
{
17+
public override TypeSymbol ConvertedType => Value.Type;
18+
}
19+
20+
internal partial class BoundDeclarationPattern
21+
{
22+
public override TypeSymbol ConvertedType => DeclaredType?.Type ?? InputType;
23+
}
24+
25+
internal partial class BoundRecursivePattern
26+
{
27+
public override TypeSymbol ConvertedType => DeclaredType?.Type ?? InputType;
28+
}
29+
}

src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,12 @@ public SymbolInfo GetSpeculativeSymbolInfo(int position, CrefSyntax cref, Symbol
832832

833833
public abstract TypeInfo GetTypeInfo(SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken));
834834

835+
public TypeInfo GetTypeInfo(PatternSyntax pattern, CancellationToken cancellationToken = default(CancellationToken))
836+
{
837+
CheckSyntaxNode(pattern);
838+
return GetTypeInfoWorker(pattern, cancellationToken);
839+
}
840+
835841
/// <summary>
836842
/// Gets type information about an expression.
837843
/// </summary>
@@ -1866,6 +1872,14 @@ internal CSharpTypeInfo GetTypeInfoForNode(
18661872
BoundNode highestBoundNode,
18671873
BoundNode boundNodeForSyntacticParent)
18681874
{
1875+
if (lowestBoundNode is BoundPattern pat)
1876+
{
1877+
HashSet<DiagnosticInfo> useSiteDiagnostics = null;
1878+
return new CSharpTypeInfo(
1879+
pat.InputType, pat.ConvertedType,
1880+
this.GetEnclosingBinder(pat.Syntax.Position).Conversions.ClassifyBuiltInConversion(pat.InputType, pat.ConvertedType, ref useSiteDiagnostics));
1881+
}
1882+
18691883
var boundExpr = lowestBoundNode as BoundExpression;
18701884
var highestBoundExpr = highestBoundNode as BoundExpression;
18711885

@@ -4564,6 +4578,8 @@ private TypeInfo GetTypeInfoFromNode(SyntaxNode node, CancellationToken cancella
45644578
return this.GetTypeInfo(attribute, cancellationToken);
45654579
case SelectOrGroupClauseSyntax selectOrGroupClause:
45664580
return this.GetTypeInfo(selectOrGroupClause, cancellationToken);
4581+
case PatternSyntax pattern:
4582+
return this.GetTypeInfo(pattern, cancellationToken);
45674583
}
45684584

45694585
return CSharpTypeInfo.None;

src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1916,7 +1916,8 @@ internal protected virtual CSharpSyntaxNode GetBindableSyntaxNode(CSharpSyntaxNo
19161916
!(node is JoinIntoClauseSyntax) &&
19171917
!(node is QueryContinuationSyntax) &&
19181918
!(node is ConstructorInitializerSyntax) &&
1919-
!(node is ArrowExpressionClauseSyntax))
1919+
!(node is ArrowExpressionClauseSyntax) &&
1920+
!(node is PatternSyntax))
19201921
{
19211922
return GetBindableSyntaxNode(parent);
19221923
}

src/Compilers/CSharp/Portable/FlowAnalysis/DataFlowPass.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1546,14 +1546,14 @@ private void AssignPatternVariables(BoundPattern pattern)
15461546
{
15471547
foreach (var subpat in pat.Deconstruction)
15481548
{
1549-
AssignPatternVariables(subpat);
1549+
AssignPatternVariables(subpat.Pattern);
15501550
}
15511551
}
15521552
if (!pat.PropertiesOpt.IsDefaultOrEmpty)
15531553
{
1554-
foreach (var (_, subpat) in pat.PropertiesOpt)
1554+
foreach (BoundSubpattern sub in pat.PropertiesOpt)
15551555
{
1556-
AssignPatternVariables(subpat);
1556+
AssignPatternVariables(sub.Pattern);
15571557
}
15581558
}
15591559
Assign(pat, null, false, false);

0 commit comments

Comments
 (0)