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

Pattern-matching fixes for ITuple, short tuple patterns, and nulls #31056

Merged

Conversation

gafter
Copy link
Member

@gafter gafter commented Nov 9, 2018

* Permit 0-element and 1-element tuple patterns
Fixes dotnet#30962
Replaces dotnet#31027

* Handle null and nullable input for a var pattern with a tuple designation
Fixes dotnet#30906
Replaces dotnet#30935

* Don't report missing Deconstruct when there is more than one applicable Deconstruct
Fixes dotnet#31031
@gafter gafter added this to the 16.0.P2 milestone Nov 9, 2018
@gafter gafter self-assigned this Nov 9, 2018
@gafter gafter requested review from agocke and jcouv November 9, 2018 00:02
@gafter gafter requested a review from a team as a code owner November 9, 2018 00:02
@cston
Copy link
Member

cston commented Nov 9, 2018

            if (symbol is FieldSymbol field && field.IsTupleElement())

IsTupleElement() is an extension method on IFieldSymbol. Perhaps use IsTupleField instead. #WontFix


Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs:823 in 102de2f. [](commit_id = 102de2f, deletion_comment = False)

@cston
Copy link
Member

cston commented Nov 9, 2018

    }

Consider testing static void Deconstruct(this ITuple tuple, ...) { }. #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs:649 in 102de2f. [](commit_id = 102de2f, deletion_comment = False)

@gafter
Copy link
Member Author

gafter commented Nov 13, 2018

            if (symbol is FieldSymbol field && field.IsTupleElement())

I understand that "tuple element" includes tuple fields. It isn't clear to me that the converse is true, so I'm going to leave it. If the converse is true then we should eliminate the duplicate terminology.


In reply to: 437406528 [](ancestors = 437406528)


Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs:823 in 102de2f. [](commit_id = 102de2f, deletion_comment = False)

@gafter gafter force-pushed the rpatterns-DeconstructVsITuple branch from 96cb362 to dc32a8d Compare November 13, 2018 01:19
@gafter
Copy link
Member Author

gafter commented Nov 13, 2018

            if (symbol is FieldSymbol field && field.IsTupleElement())

Added #20648 (comment) to track this


In reply to: 438087900 [](ancestors = 438087900,437406528)


Refers to: src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs:823 in 102de2f. [](commit_id = 102de2f, deletion_comment = False)

@gafter
Copy link
Member Author

gafter commented Nov 13, 2018

@cston I think this iteration responds to all of your comments. Do you have any others? #Resolved

@gafter gafter added the 4 - In Review A fix for the issue is submitted for review. label Nov 13, 2018
@@ -1915,16 +1912,13 @@ public void ParenthesizedExpression_04()
UsingStatement(@"switch (e) { case (((x: 3))): ; }", TestOptions.RegularWithoutRecursivePatterns,
Copy link
Member

@jcouv jcouv Nov 19, 2018

Choose a reason for hiding this comment

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

case (((x: 3))): [](start = 42, length = 16)

Could you add a test with pattern ((3)) too? #Closed

Copy link
Member Author

Choose a reason for hiding this comment

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

See existing test ParenthesizedExpression_01.


In reply to: 234822088 [](ancestors = 234822088)

// Work around https://github.com/dotnet/roslyn/issues/20648: The compiler's internal APIs such as `declType.IsTupleType`
// do not correctly treat the non-generic struct `System.ValueTuple` as a tuple type. We explicitly perform the tests
// required to identify it. When that bug is fixed we should be able to remove this code and its callers.
internal static bool IsZeroElementTupleType(TypeSymbol type)
Copy link
Member

@jcouv jcouv Nov 19, 2018

Choose a reason for hiding this comment

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

IsZeroElementTupleType [](start = 29, length = 22)

Would it be simpler go get the well-known type ValueTuple and compare them? #Resolved

Copy link
Member Author

Choose a reason for hiding this comment

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

No; there can be more than one tuple type imported from different assemblies.


In reply to: 234822985 [](ancestors = 234822985)

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, one more question on this: would IsTupleCompatible do the job?


In reply to: 237273866 [](ancestors = 237273866,234822985)

MethodSymbol deconstructMethod = null;
ImmutableArray<BoundSubpattern> deconstructionSubpatterns = default;
if (node.DeconstructionPatternClause != null)
{
deconstructionSubpatterns = BindDeconstructionPatternClause(node.DeconstructionPatternClause, declType, inputValEscape, diagnostics, out deconstructMethod, ref hasErrors);
DeconstructionPatternClauseSyntax deconstructClause = node.DeconstructionPatternClause;
Debug.Assert(deconstructClause != null);
Copy link
Member

@jcouv jcouv Nov 19, 2018

Choose a reason for hiding this comment

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

Debug.Assert(deconstructClause != null); [](start = 16, length = 40)

nit: assertion doesn't add much, since checked two lines above #Resolved

deconstructionSubpatterns = BindDeconstructionPatternClause(node.DeconstructionPatternClause, declType, inputValEscape, diagnostics, out deconstructMethod, ref hasErrors);
DeconstructionPatternClauseSyntax deconstructClause = node.DeconstructionPatternClause;
Debug.Assert(deconstructClause != null);
deconstructMethod = null;
Copy link
Member

@jcouv jcouv Nov 19, 2018

Choose a reason for hiding this comment

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

deconstructMethod = null; [](start = 16, length = 25)

assignment seem unnecessary since initialized with null already #Resolved

DeconstructionPatternClauseSyntax deconstructClause = node.DeconstructionPatternClause;
Debug.Assert(deconstructClause != null);
deconstructMethod = null;
var patterns = ArrayBuilder<BoundSubpattern>.GetInstance(deconstructClause.Subpatterns.Count);
Copy link
Member

@jcouv jcouv Nov 19, 2018

Choose a reason for hiding this comment

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

patterns [](start = 20, length = 8)

Rename to patternsBuilder? #Resolved

deconstructMethod = BindDeconstructSubpatterns(deconstructClause, inputValEscape, deconstruct, outPlaceholders, patterns, ref hasErrors, diagnostics);
}

deconstructionSubpatterns = patterns.ToImmutableAndFree();
Copy link
Member

@jcouv jcouv Nov 19, 2018

Choose a reason for hiding this comment

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

deconstructionSubpatterns = patterns.ToImmutableAndFree(); [](start = 16, length = 58)

The flow of this method would be easier to follow if BindValueTupleSubpatterns, BindITupleSubpatterns and BindDeconstructSubpatterns returned patterns (which we would store into deconstructionSubpatterns).
BindDeconstructSubpatterns would set deconstructMethod via a secondary output (an out parameter). #ByDesign

Copy link
Member Author

Choose a reason for hiding this comment

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

They produce multiple subpatterns, not a single pattern, which is why they go into a builder.


In reply to: 234825978 [](ancestors = 234825978)

var boundSubpattern = new BoundSubpattern(
subpatternSyntax,
null,
BindPattern(subpatternSyntax.Pattern, elementType, valEscape, false, diagnostics));
BindPattern(subpatternSyntax.Pattern, objectType, valEscape, false, diagnostics));
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

false [](start = 81, length = 5)

nit: consider naming hasErrors: false (here and below) #Resolved

@jcouv
Copy link
Member

jcouv commented Nov 20, 2018

    if (c2 is C2(1, 2) {}) {}  // [7]

Just to confirm, the type in a pattern cannot be a tuple type. That would be a parsing error, correct?
c2 is (int, int) (1, 2) ...
Do we have a test already? #Resolved


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs:844 in bbcf265. [](commit_id = bbcf265, deletion_comment = False)

if (inputType.IsTupleType)
var strippedInputType = inputType.StrippedType();

void addSubpatternsForTuple(ImmutableArray<TypeSymbolWithAnnotations> elementTypes)
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

addSubpatternsForTuple [](start = 29, length = 22)

Please move local function to the end of the method to keep the code flow clear. Thanks #Resolved

deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol;
if (!hasErrors)
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

) [](start = 42, length = 1)

braces? #ByDesign

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd rather remove unnecessary braces.


In reply to: 234839920 [](ancestors = 234839920)

}

private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignationSyntax designation, TypeSymbol inputType, uint inputValEscape, bool hasErrors, DiagnosticBag diagnostics)
private BoundPattern BindVarDesignation(
VariableDesignationSyntax node,
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

node [](start = 38, length = 4)

designation? #ByDesign

Copy link
Member Author

Choose a reason for hiding this comment

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

This is a convention used widely in the binder: node is the current syntax being processed.


In reply to: 234841661 [](ancestors = 234841661)

True
False
False";
var compVerifier = CompileAndVerify(compilation, expectedOutput: expectedOutput);
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

var compVerifier = [](start = 12, length = 19)

unused local Never mind, that's in every test #Closed

using System.Runtime.CompilerServices;
public class C : ITuple
{
int ITuple.Length => 4;
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

4; [](start = 25, length = 2)

Consider splitting this test. One test would have the ITuple implementation throw.
Also, three of the comments say the same thing but in different ways... #ByDesign

class A: IA, ITuple
{
void IA.Deconstruct(out int X, out int Y) => (X, Y) = (3, 4);
int ITuple.Length => 2;
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

2; [](start = 25, length = 2)

Consider throwing for members that don't participate in execution. (also applies in other tests below) #Resolved

// Here we test the relative priority of steps 2 and 3.
// - Found more than one applicable Deconstruct method (even though the type implements ITuple): error

// case 1: tuple pattern with var subpatterns
Copy link
Member

@jcouv jcouv Nov 20, 2018

Choose a reason for hiding this comment

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

case 1 [](start = 15, length = 6)

I didn't understand what "case 1" refers to #Resolved

Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

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

LGTM with some minor suggestions (iteration 6)

@jcouv jcouv self-assigned this Nov 20, 2018
@gafter
Copy link
Member Author

gafter commented Nov 28, 2018

    if (c2 is C2(1, 2) {}) {}  // [7]

We do have parsing tests that show this is a syntax error.


In reply to: 440095022 [](ancestors = 440095022)


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs:844 in bbcf265. [](commit_id = bbcf265, deletion_comment = False)

@gafter gafter merged commit 2ebd138 into dotnet:features/recursive-patterns Nov 29, 2018
@gafter gafter removed 4 - In Review A fix for the issue is submitted for review. labels Dec 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants