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

Implement extended property patterns #52139

Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
75bf6e7
WIP
alrz Mar 25, 2021
9f51bbb
WIP
alrz Mar 25, 2021
1e67ee2
WIP
alrz Mar 25, 2021
b4019e1
WIP
alrz Mar 25, 2021
d61c3c8
WIP
alrz Mar 25, 2021
5593114
WIP
alrz Mar 25, 2021
b384f78
WIP
alrz Mar 25, 2021
808ce1f
WIP
alrz Mar 25, 2021
675a57d
WIP
alrz Mar 25, 2021
a4cebf9
WIP
alrz Mar 25, 2021
bc7efe6
WIP
alrz Apr 7, 2021
c620d01
Update resources
alrz Apr 7, 2021
daea3f1
WIP
alrz Apr 7, 2021
411a6b5
Merge remote-tracking branch 'origin/features/extended-property-patte…
alrz Apr 7, 2021
4bc2dac
Update resources
alrz Apr 7, 2021
be4e20a
Address feedback on syntax tree
alrz Apr 25, 2021
49e151e
Revert
alrz Apr 25, 2021
6e5b186
Merge remote-tracking branch 'origin/features/extended-property-patte…
alrz Apr 25, 2021
4d0a51d
Address feedback
alrz Apr 27, 2021
371c4f4
Typo
alrz Apr 27, 2021
8e82fe7
Revert code
alrz Apr 29, 2021
13eb678
Remove ERR_ConditionalAccessInSubpattern
alrz Apr 29, 2021
359681f
Add PROTOTYPE comments
alrz Apr 29, 2021
8996089
Add suggested test
alrz Apr 29, 2021
7c487de
Add backcompat APIs
alrz Apr 29, 2021
81037c3
Typo
alrz Apr 29, 2021
eaa5c5d
Attempt to disable RS0016
alrz Apr 29, 2021
ffcb24e
Renamings
alrz Apr 29, 2021
6d5f488
Fix typo
alrz Apr 29, 2021
ae153fd
Add missing PROTOTYPE comment
alrz Apr 29, 2021
732486c
Create ExpressionColon when a NameColon.WithName is called with an ex…
alrz Apr 29, 2021
7d7bd53
Pass IOp verification
alrz Apr 29, 2021
11f1ea8
Add missing member
alrz Apr 29, 2021
a6ba2fe
Attempt to fix build
alrz Apr 29, 2021
35ea5b3
Typo
alrz May 4, 2021
b6b7bd3
Address feedback on syntax
alrz May 4, 2021
6f518d7
Add more parsing tests
alrz May 4, 2021
d0e80c4
Add suggested tests
alrz May 4, 2021
3a83ba0
Fix build
alrz May 4, 2021
4897191
Add test
alrz May 4, 2021
d3f1e7f
Move langversion check to the parser
alrz May 4, 2021
05f9a88
Add suggested tests
alrz May 4, 2021
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
39 changes: 30 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -780,6 +780,7 @@ deconstructMethod is null &&
bool isError = hasErrors || outPlaceholders.IsDefaultOrEmpty || i >= outPlaceholders.Length;
TypeSymbol elementType = isError ? CreateErrorType() : outPlaceholders[i].Type;
ParameterSymbol? parameter = null;
// PROTOTYPE(extended-property-patterns)
alrz marked this conversation as resolved.
Show resolved Hide resolved
if (subPattern.NameColon != null && !isError)
{
// Check that the given name is the same as the corresponding parameter of the method.
Expand Down Expand Up @@ -818,6 +819,7 @@ private void BindITupleSubpatterns(
var objectType = Compilation.GetSpecialType(SpecialType.System_Object);
foreach (var subpatternSyntax in node.Subpatterns)
{
// PROTOTYPE(extended-property-patterns)
if (subpatternSyntax.NameColon != null)
{
// error: name not permitted in ITuple deconstruction
Expand Down Expand Up @@ -874,6 +876,7 @@ private void BindValueTupleSubpatterns(
bool isError = i >= elementTypesWithAnnotations.Length;
TypeSymbol elementType = isError ? CreateErrorType() : elementTypesWithAnnotations[i].Type;
FieldSymbol? foundField = null;
// PROTOTYPE(extended-property-patterns)
if (subpatternSyntax.NameColon != null && !isError)
{
string name = subpatternSyntax.NameColon.Name.Identifier.ValueText;
Expand Down Expand Up @@ -1163,34 +1166,52 @@ private ImmutableArray<BoundSubpattern> BindPropertyPatternClause(
var builder = ArrayBuilder<BoundSubpattern>.GetInstance(node.Subpatterns.Count);
foreach (SubpatternSyntax p in node.Subpatterns)
{
IdentifierNameSyntax? name = p.NameColon?.Name;
ExpressionSyntax? expr = p.NameColon is not null ? p.NameColon.Name : p.ExpressionColon?.Expression;
PatternSyntax pattern = p.Pattern;
Symbol? member = null;
ImmutableArray<Symbol> members;
TypeSymbol memberType;
if (name == null)
if (expr == null)
{
if (!hasErrors)
diagnostics.Add(ErrorCode.ERR_PropertyPatternNameMissing, pattern.Location, pattern);

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

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

return builder.ToImmutableAndFree();
}

private Symbol? LookupMemberForPropertyPattern(
TypeSymbol inputType, IdentifierNameSyntax name, BindingDiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType)
private void LookupMemberForPropertyPattern(
alrz marked this conversation as resolved.
Show resolved Hide resolved
TypeSymbol inputType, ExpressionSyntax expr, ArrayBuilder<Symbol> builder, BindingDiagnosticBag diagnostics, ref bool hasErrors, out TypeSymbol memberType)
{
Symbol? symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);
Symbol? symbol;
switch (expr)
{
case IdentifierNameSyntax name:
symbol = BindPropertyPatternMember(inputType, name, ref hasErrors, diagnostics);
break;
case MemberAccessExpressionSyntax { Name: IdentifierNameSyntax name } memberAccess when memberAccess.IsKind(SyntaxKind.SimpleMemberAccessExpression):
LookupMemberForPropertyPattern(inputType, memberAccess.Expression, builder, diagnostics, ref hasErrors, out memberType);
symbol = BindPropertyPatternMember(memberType.StrippedType(), name, ref hasErrors, diagnostics);
break;
default:
Error(diagnostics, expr is ConditionalAccessExpressionSyntax ? ErrorCode.ERR_ConditionalAccessInSubpattern : ErrorCode.ERR_InvalidNameInSubpattern, expr);
symbol = null;
hasErrors = true;
break;
}

memberType = symbol switch
{
Expand All @@ -1199,7 +1220,7 @@ private ImmutableArray<BoundSubpattern> BindPropertyPatternClause(
_ => CreateErrorType()
};

return symbol;
builder.AddIfNotNull(symbol);
}

private Symbol? BindPropertyPatternMember(
Expand Down
56 changes: 37 additions & 19 deletions src/Compilers/CSharp/Portable/Binder/DecisionDagBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -527,29 +527,47 @@ private Tests MakeTestsAndBindingsForRecursivePattern(
if (!recursive.Properties.IsDefault)
{
// we have a "property" form
for (int i = 0; i < recursive.Properties.Length; i++)
foreach (var subpattern in recursive.Properties)
{
var subPattern = recursive.Properties[i];
Symbol? symbol = subPattern.Symbol;
BoundPattern pattern = subPattern.Pattern;
BoundDagEvaluation evaluation;
switch (symbol)
BoundPattern pattern = subpattern.Pattern;
BoundDagTemp currentInput = input;
if (subpattern.Symbols.IsEmpty)
{
case PropertySymbol property:
evaluation = new BoundDagPropertyEvaluation(pattern.Syntax, property, OriginalInput(input, property));
break;
case FieldSymbol field:
evaluation = new BoundDagFieldEvaluation(pattern.Syntax, field, OriginalInput(input, field));
break;
default:
RoslynDebug.Assert(recursive.HasAnyErrors);
tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true)));
continue;
RoslynDebug.Assert(recursive.HasAnyErrors);
alrz marked this conversation as resolved.
Show resolved Hide resolved
tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), input, hasErrors: true)));
goto done;
}

tests.Add(new Tests.One(evaluation));
var element = new BoundDagTemp(pattern.Syntax, symbol.GetTypeOrReturnType().Type, evaluation);
tests.Add(MakeTestsAndBindings(element, pattern, bindings));
Symbol last = subpattern.Symbols.Last();
foreach (Symbol symbol in subpattern.Symbols)
{
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:
RoslynDebug.Assert(recursive.HasAnyErrors);
tests.Add(new Tests.One(new BoundDagTypeTest(recursive.Syntax, ErrorType(), currentInput, hasErrors: true)));
goto done;
}

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

if (!ReferenceEquals(symbol, last))
alrz marked this conversation as resolved.
Show resolved Hide resolved
{
currentInput = MakeConvertToType(currentInput, pattern.Syntax, type.StrippedType(), isExplicitTest: false, tests);
}
}

done:
tests.Add(MakeTestsAndBindings(currentInput, pattern, bindings));
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2092,7 +2092,7 @@
<!-- 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="Symbol" Type="Symbol?"/>
<Field Name="Symbols" Type="ImmutableArray&lt;Symbol&gt;"/>
<Field Name="Pattern" Type="BoundPattern" Null="disallow"/>
</Node>

Expand Down
32 changes: 32 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundSubpattern.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable enable

using System.Collections.Immutable;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
// PROTOTYPE(extended-property-patterns) Split BoundPropertySubpattern and remove this (requires IOperation changes)
partial class BoundSubpattern
{
internal BoundSubpattern(SyntaxNode syntax, Symbol? symbol, BoundPattern pattern, bool hasErrors = false)
: this(syntax, symbol is null ? ImmutableArray<Symbol>.Empty : ImmutableArray.Create(symbol), pattern, hasErrors)
{
}

internal Symbol? Symbol
{
get
{
if (this.Symbols.IsEmpty)
return null;
if (this.Symbols.Length == 1)
return this.Symbols[0];
throw ExceptionUtilities.Unreachable;
}
}
}
}
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6600,4 +6600,10 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_FunctionPointerTypesInAttributeNotSupported" xml:space="preserve">
<value>Using a function pointer type in a 'typeof' in an attribute is not supported.</value>
</data>
<data name="ERR_InvalidNameInSubpattern" xml:space="preserve">
<value>Identifier or a simple member access expected.</value>
</data>
<data name="ERR_ConditionalAccessInSubpattern" xml:space="preserve">
<value>Conditional access may not be used in property subpatterns. Use a simple member access instead.</value>
</data>
</root>
4 changes: 4 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1932,6 +1932,10 @@ internal enum ErrorCode

#endregion diagnostics introduced for C# 9.0

// PROTOTYPE(extended-property-patterns)
ERR_InvalidNameInSubpattern = 9000,
ERR_ConditionalAccessInSubpattern,

// Note: you will need to re-generate compiler code after adding warnings (eng\generate-compiler-code.cmd)
}
}
7 changes: 7 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ internal enum MessageID

IDS_FeatureDiscards = MessageBase + 12688,

// PROTOTYPE(extended-property-patterns)
IDS_FeatureExtendedPropertyPatterns = MessageBase + 12800,

IDS_FeatureDefaultTypeParameterConstraint = MessageBase + 12689,
IDS_FeatureNullPropagatingOperator = MessageBase + 12690,
IDS_FeatureExpressionBodiedMethod = MessageBase + 12691,
Expand Down Expand Up @@ -483,6 +486,10 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureSwitchOnBool: // Checked in the binder.
return LanguageVersion.CSharp2;

// PROTOTYPE(extended-property-patterns)
case MessageID.IDS_FeatureExtendedPropertyPatterns:
alrz marked this conversation as resolved.
Show resolved Hide resolved
return LanguageVersion.Preview;

// Special C# 2 feature: only a warning in C# 1.
case MessageID.IDS_FeatureModuleAttrLoc:
return LanguageVersion.CSharp1;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,13 +165,17 @@ private void LearnFromAnyNullPatterns(
// for property part
if (!rp.Properties.IsDefault)
{
for (int i = 0, n = rp.Properties.Length; i < n; i++)
foreach (BoundSubpattern subpattern in rp.Properties)
{
BoundSubpattern item = rp.Properties[i];
Symbol symbol = item.Symbol;
if (symbol?.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions) == true)
if (subpattern.Symbols.IsEmpty)
continue;

// Obtain the last symbol's slot which is actually the one being matched.
Symbol last = subpattern.Symbols.Last();
// PROTOTYPE(extended-property-patterns) For a chain of members, the input type is no longer `inputType`
if (last.ContainingType.Equals(inputType, TypeCompareKind.AllIgnoreOptions))
{
LearnFromAnyNullPatterns(GetOrCreateSlot(symbol, inputSlot), symbol.GetTypeOrReturnType().Type, item.Pattern);
LearnFromAnyNullPatterns(GetOrCreateSlot(last, inputSlot), last.GetTypeOrReturnType().Type, subpattern.Pattern);
}
}
}
Expand Down
21 changes: 11 additions & 10 deletions src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion src/Compilers/CSharp/Portable/Generated/CSharp.Generated.g4

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading