-
Notifications
You must be signed in to change notification settings - Fork 4.1k
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
Pattern-matching fixes for ITuple, short tuple patterns, and nulls #31056
Conversation
* 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
src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/Semantic/Semantics/PatternMatchingTests4.cs
Outdated
Show resolved
Hide resolved
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) |
96cb362
to
dc32a8d
Compare
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) |
@cston I think this iteration responds to all of your comments. Do you have any others? #Resolved |
…et/roslyn into rpatterns-DeconstructVsITuple
…et/roslyn into rpatterns-DeconstructVsITuple
@@ -1915,16 +1912,13 @@ public void ParenthesizedExpression_04() | |||
UsingStatement(@"switch (e) { case (((x: 3))): ; }", TestOptions.RegularWithoutRecursivePatterns, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// 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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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)); |
There was a problem hiding this comment.
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
Just to confirm, the type in a pattern cannot be a tuple type. That would be a parsing error, correct? 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) |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | ||
|
||
private BoundPattern BindVarDesignation(VarPatternSyntax node, VariableDesignationSyntax designation, TypeSymbol inputType, uint inputValEscape, bool hasErrors, DiagnosticBag diagnostics) | ||
private BoundPattern BindVarDesignation( | ||
VariableDesignationSyntax node, |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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
There was a problem hiding this 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)
Issue reported as dotnet#31279
…et/roslyn into rpatterns-DeconstructVsITuple
Implement LDM disambiguation rules for Deconstruct vs ITuple
Permit 0-element and 1-element tuple patterns
Fixes Pattern-matching changes for short tuples #30962
Replaces Support 0-element and 1-element tuple patterns #31027
Handle null and nullable input for a var pattern with a tuple designation
Fixes Matching nullables with var pattern #30906
Replaces Handle null and nullable input for a var pattern with a tuple designation #30935
Don't report missing Deconstruct when there is more than one applicable Deconstruct
Fixes "No suitable Deconstruct instance or extension method was found" is incorrect #31031