-
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
Implement pattern-matching via ITuple #28859
Implement pattern-matching via ITuple #28859
Conversation
Consider updating the feature document (or start one there isn't already) or feature spec to explain expected behavior. Thanks #Resolved |
@@ -1778,6 +1782,12 @@ | |||
<Field Name="VariableAccess" Type="BoundExpression" Null="allow"/> | |||
</Node> | |||
|
|||
<Node Name="BoundITuplePattern" Base="BoundPattern"> | |||
<Field Name="getLengthMethod" Type="MethodSymbol" Null="disallow"/> |
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.
getLengthMethod [](start = 17, length = 15)
nit: Not sure about capitalization #Closed
<Node Name="BoundITuplePattern" Base="BoundPattern"> | ||
<Field Name="getLengthMethod" Type="MethodSymbol" Null="disallow"/> | ||
<Field Name="getItemMethod" Type="MethodSymbol" Null="disallow"/> | ||
<Field Name="Deconstruction" Type="ImmutableArray<BoundSubpattern>" Null="disallow"/> |
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.
Deconstruction [](start = 17, length = 14)
nit: name probably can be tweaked, since no deconstruction involved. #Closed
@@ -1177,6 +1177,10 @@ | |||
<Node Name="BoundDagPropertyEvaluation" Base="BoundDagEvaluation"> | |||
<Field Name="Property" Type="PropertySymbol" Null="disallow"/> | |||
</Node> | |||
<Node Name="BoundDagIndexEvaluation" Base="BoundDagEvaluation"> |
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.
BoundDagIndexEvaluation [](start = 14, length = 23)
Do we need to customize the Display
property for the new bound nodes (in Formatting.cs
)? #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.
No, that is for nodes that extend BoundExpression
but have no type. #Closed
#endif | ||
|
||
[Fact] | ||
public void XYZZY() |
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.
XYZZY [](start = 20, length = 5)
Probably not intentional #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.
This file should be merged with PatternMatchingTests4.cs
In reply to: 205619266 [](ancestors = 205619266)
// case (1, 2): | ||
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("object", "2").WithLocation(10, 18) | ||
Diagnostic(ErrorCode.ERR_MissingDeconstruct, "(1, 2)").WithArguments("object", "2").WithLocation(8, 18) | ||
); |
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 = 16, length = 2)
Not related to this PR, but this reminded me:
I'm working on an IDE PR to generate a Deconstruct method when it's missing.
But that fixer is negatively affected by the fact that we have two diagnostics (as visible in this test). The IDE doesn't like when there are two diagnostics on the same span...
I'd like to brainstorm how to reduce this to a single diagnostic, while still providing good information to users. I'll stop by tomorrow or next week to discuss. #Closed
@@ -1093,15 +1091,15 @@ public static void M(object o) | |||
} | |||
} | |||
"; | |||
var compilation = CreatePatternCompilation(source); | |||
var compilation = CreateCompilationWithMscorlib45(source); // doesn't have ITuple |
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.
doesn't have ITuple [](start = 74, length = 19)
Would this test behave differently if the ITuple type was found? #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.
Yes; compilation would pass and it would use ITuple
for the matching. #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.
Sorry, I didn't follow. The parameter object o
doesn't implement ITuple
.
Oh, I see the description in the issue covers this.
Some documentation would be good
In reply to: 205628798 [](ancestors = 205628798)
@@ -570,6 +570,9 @@ public void AllWellKnownTypes() | |||
case WellKnownType.ExtSentinel: | |||
// Not a real type | |||
continue; | |||
case WellKnownType.System_Runtime_CompilerServices_ITuple: |
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.
Actually, ITuple has shipped (more details on where). And it may be available in ValueTupleRef which is referenced above (I don't remember exactly what that ref was based off of). #Closed
// Use a version of the platform APIs that lack ITuple | ||
var compilation = CreateCompilationWithMscorlib40AndSystemCore(source, options: TestOptions.ReleaseExe); | ||
compilation.VerifyDiagnostics( | ||
// (7,32): error CS1061: 'object' does not contain a definition for 'Deconstruct' and no accessible extension method 'Deconstruct' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) |
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.
CS1061 [](start = 33, length = 6)
Should the error message mention ITuple? #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.
Something like "Pattern matching in this case would work if we could find the type 'System.Runtime.CompilerServices.ITuple'"? If ITuple were available then we wouldn't be looking for an extension method.
In reply to: 205631111 [](ancestors = 205631111)
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.
Hum, I don't really know how to incorporate that into the message, and adding a third diagnostic would be pretty bad :-(
I don't have a good idea for this, but it feels like we're not guiding users to one of the possible solutions... :-S
In reply to: 206681158 [](ancestors = 206681158,205631111)
{ | ||
[CompilerTrait(CompilerFeature.Patterns)] | ||
public class ITuplePatternTests : PatternMatchingTestBase | ||
{ |
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 = 4, length = 1)
I'd like to see the IL produced for at least one case. Thanks #WontFix
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.
Not blocking this PR: are we going to expose the ITuple-related information through public API? (ie. what semantic model scenario should be tested?)
In reply to: 205633476 [](ancestors = 205633476)
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.
There is no design for semantic model support for pattern-matching for ITuple
. It is hard to imagine an IDE scenario in which that would be helpful.
In reply to: 205633786 [](ancestors = 205633786,205633476)
Console.WriteLine(t is (3, 0, 5)); // false | ||
Console.WriteLine(t is (3, 4, 5, 6)); // false | ||
} | ||
public void Deconstruct() {} |
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.
Could you add a variant of such scenario (where the existence of Deconstruct
interferes with binding to ITuple
) but the Deconstruct
method has a non-void return type? #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.
I'll finish reviewing tomorrow.
return false; | ||
} | ||
|
||
if (((CSharpParseOptions)node.SyntaxTree.Options).LanguageVersion < MessageID.IDS_FeatureRecursivePatterns.RequiredVersion()) |
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.
LanguageVersion [](start = 62, length = 15)
Feels like we should use the LanguageVersion from the compilation (ie. max of all trees) rather than from a single tree. #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.
The compilation's language version isn't the max. It requires that the trees have the same language version (else it throws an exception) and uses that. However, I agree that Compilation.LanguageVersion should be used here.
In reply to: 206289340 [](ancestors = 206289340)
); | ||
} | ||
} | ||
} |
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.
Could you add a test with obsolete ITuple interface? #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.
Sure, but I wouldn't expect an error. The error would appear on the API that declares that it implements the interface.
In reply to: 206290578 [](ancestors = 206290578)
// declared to implement `ITuple` but contain no `Deconstruct` methods. | ||
if (declType != (object)Compilation.GetSpecialType(SpecialType.System_Object) && | ||
declType != (object)Compilation.DynamicType && | ||
declType != (object)iTupleType && |
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 = 25, length = 2)
I'm surprised we're using reference equality. Shouldn't we be using symbol equality (.Equals
)? #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.
They are the same for "original" type symbols, which all of these are.
In reply to: 206291161 [](ancestors = 206291161)
ArrayBuilder<BoundPatternBinding> bindings) | ||
{ | ||
var syntax = pattern.Syntax; | ||
var patternLength = pattern.Deconstruction.Length; |
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 [](start = 12, length = 3)
nit: int
#WontFix
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.
Isn't it obvious that a length is an int? Why is this line different than all the others?
In reply to: 206292578 [](ancestors = 206292578)
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.
It's a nit, but I don't see a good reason to use var
for type names that are so short (like int
or bool
). For long type names I can see some benefits ;-) #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.
I see them as distracting unless they convey something relevant to the reader. #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.
Done with review pass (iteration 1).
@jcouv I believe I've responded to all of your comments. #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 Thanks (iteration 2)
@@ -3409,6 +3423,8 @@ static WellKnownMembers() | |||
"Ceiling", // System_Math__CeilingDouble | |||
"Floor", // System_Math__FloorDouble | |||
"Truncate", // System_Math__TruncateDouble | |||
"get_Item", // System_Runtime_CompilerServices_ITuple__Index |
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.
Index [](start = 103, length = 5)
Item
or get_Item
. (If get_Item
, then Length
should be get_Length
.) #Resolved
@@ -5420,4 +5420,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||
<data name="ERR_PointerTypeInPatternMatching" xml:space="preserve"> | |||
<value>Pattern-matching is not permitted for pointer types.</value> | |||
</data> | |||
<data name="ERR_ArgumentNameInITuplePattern" xml:space="preserve"> | |||
<value>Element names are not permitted when patttern-matching via 'System.Runtime.CompilerServices.ITuple'.</value> |
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.
patttern [](start = 48, length = 8)
Typo #Resolved
var objectType = Compilation.GetSpecialType(SpecialType.System_Object); | ||
var deconstruction = node.DeconstructionPatternClause; | ||
var patterns = ArrayBuilder<BoundSubpattern>.GetInstance(deconstruction.Subpatterns.Count); | ||
for (int i = 0; i < deconstruction.Subpatterns.Count; i++) |
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.
for (int i = 0; i < deconstruction.Subpatterns.Count; i++) [](start = 12, length = 58)
Consider using foreach
. #Resolved
BoundExpression deconstruct = MakeDeconstructInvocationExpression( | ||
node.Subpatterns.Count, inputPlaceholder, node, diagnostics, outPlaceholders: out ImmutableArray<BoundDeconstructValuePlaceholder> outPlaceholders); | ||
deconstructMethod = deconstruct.ExpressionSymbol as MethodSymbol; | ||
if (deconstructMethod is 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.
Consider keeping else
with a single return patterns.ToImmutableAndFree();
outside of the if
/ else
. #Resolved
public override bool Equals(object obj) => obj is BoundDagIndexEvaluation other && this.Equals(other); | ||
public override bool Equals(BoundDagEvaluation obj) => obj is BoundDagIndexEvaluation other && this.Equals(other); | ||
public override int GetHashCode() => base.GetHashCode() ^ this.Index; | ||
public bool Equals(BoundDagIndexEvaluation other) |
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.
public bool Equals(BoundDagIndexEvaluation other) [](start = 8, length = 49)
Are we calling this overload directly, other than in overloads above? If not, then it seems like we can get away with one overload in this class.
public override bool Equals(BoundDagEvaluation obj)
{
return base.Equals(other) &&
this.Index == ((BoundDagIndexEvaluation).Index;
}
``` #Resolved
Console.WriteLine(t is (3, 4, 5)); // TRUE | ||
Console.WriteLine(t is (3, 0, 5)); // false | ||
Console.WriteLine(t is (3, 4, 5, 6)); // false | ||
} |
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 = 4, length = 1)
Consider adding a case that is not ITuple
:
Console.WriteLine(new object() is (3, 4, 5)); // false
``` #Resolved
{ | ||
public class ITuple | ||
{ | ||
int Length => 3; |
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.
int [](start = 8, length = 3)
Consider making these properties public
so it's more obvious the failure is due to the class
rather than inaccessible properties. #Resolved
{ | ||
M(new C()); | ||
} | ||
public static void M<T>(T t) where T: C |
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.
where T: C [](start = 33, length = 10)
Perhaps also test where T : ITuple
and when T
is unconstrained. #Resolved
@cston I have made changes to address all of your review comments. Do you have any other comments? #Resolved |
if (subpatternSyntax.NameColon != null) | ||
{ | ||
// error: name not permitted in ITuple deconstruction | ||
diagnostics.Add(ErrorCode.ERR_ArgumentNameInITuplePattern, subpatternSyntax.NameColon.Location); |
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.
diagnostics.Add(ErrorCode.ERR_ArgumentNameInITuplePattern, subpatternSyntax.NameColon.Location); [](start = 20, length = 96)
Please add a test for this case. #Resolved
@cston I have added the test you mentioned. Do you have any other comments? #Resolved |
} | ||
} | ||
"; | ||
// Use a version of the platform APIs that lack ITuple |
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.
Is this comment correct? #Resolved
Adjust VB test for well known types.
Fixes #27746
This implements positional pattern-matching using the interface
ITuple
.