-
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
IOperation for recursive pattern and switch expression #31967
IOperation for recursive pattern and switch expression #31967
Conversation
Work in progress dotnet#27749
src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/Operations/IDeclarationPatternOperation.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs
Outdated
Show resolved
Hide resolved
src/Compilers/Core/Portable/Operations/IRecursivePatternOperation.cs
Outdated
Show resolved
Hide resolved
@@ -13,6 +13,25 @@ namespace Microsoft.CodeAnalysis.Operations | |||
/// </remarks> | |||
public interface IRecursivePatternOperation : IPatternOperation |
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.
Testing around HasErrors
, such as when there is no DeconstructSymbol
but there are patterns. #Resolved
/// <see cref="ISymbol"/>/<see cref="IPatternOperation"/> pairs within it. | ||
/// If there is no property subpattern, this is a default immutable array. | ||
/// </summary> | ||
ImmutableArray<(ISymbol, IPatternOperation)> PropertySubpatterns { get; } |
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.
(ISymbol, IPatternOperation) [](start = 23, length = 28)
Need to get some feedback on whether we should be using Tuples here. We could potentially replace with a struct instead. #Resolved
src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IIsPatternExpression.cs
Outdated
Show resolved
Hide resolved
@@ -7854,6 +7855,9 @@ public override IEnumerable<IOperation> Children | |||
/// Constant value of the pattern. | |||
/// </summary> | |||
public abstract IOperation Value { get; } | |||
|
|||
public ITypeSymbol InputType { get; } |
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.
InputType [](start = 27, length = 9)
What does the input type represent? Could it be removed?
I'd expect the constant/value to have a type and that's it.
Also, I don't see InputType
in any of the test outputs. #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.
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 looked at the spec, but saw not mention of "input type".
In e is <pattern>
, isn't the input type just the type of e
?
Update: I think I found it: "If the type is omitted, we take it to be the static type of the input value."
In reply to: 244056434 [](ancestors = 244056434,244029510)
|
||
public ITypeSymbol MatchedType { get; } | ||
|
||
public ITypeSymbol InputType { get; } |
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.
InputType [](start = 27, length = 9)
Same question on the InputType
of the declaration pattern. #Resolved
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructSymbol.get -> Microsoft.CodeAnalysis.ISymbol | ||
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.DeconstructionSubpatterns.get -> System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.Operations.IPatternOperation> | ||
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.MatchedType.get -> Microsoft.CodeAnalysis.ITypeSymbol | ||
Microsoft.CodeAnalysis.Operations.IRecursivePatternOperation.PropertySubpatterns.get -> System.Collections.Immutable.ImmutableArray<(Microsoft.CodeAnalysis.ISymbol, Microsoft.CodeAnalysis.Operations.IPatternOperation)> |
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.
(Microsoft.CodeAnalysis.ISymbol, Microsoft.CodeAnalysis.Operations.IPatternOperation) [](start = 132, length = 85)
I believe this is the first usage of tuples in our public API.
I think we should have an IPropertySubpatternOperation
for this. Or maybe we provide two arrays.
Elsewhere in this change, I see uses of Item1
and Item2
that result from this decision. #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.
/// For example, in C# the pattern `var x` will match a null input, | ||
/// while the pattern `string x` will not. | ||
/// </summary> | ||
bool MatchesNull { get; } |
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.
MatchesNull [](start = 13, length = 11)
Do we need this property storing this information, or can we derive MatchesNull
from other parts?
If the latter, maybe we should remove the property and provide an extension method for convenience instead. #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.
@@ -47,7 +47,13 @@ internal override IOperation VisitNoneOperation(IOperation operation, object arg | |||
private ImmutableArray<T> VisitArray<T>(ImmutableArray<T> nodes) where T : IOperation | |||
{ | |||
// clone the array | |||
return nodes.SelectAsArray(n => Visit(n)); | |||
return nodes.IsDefault ? default : nodes.SelectAsArray(n => Visit(n)); |
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.
IsDefault [](start = 25, length = 9)
Are default arrays allowed in operations? I suspect that empty arrays are used presently. #Resolved
@@ -564,7 +581,9 @@ public override IOperation VisitMethodBodyOperation(IMethodBodyOperation operati | |||
|
|||
public override IOperation VisitDiscardOperation(IDiscardOperation operation, object argument) | |||
{ | |||
return new DiscardOperation(operation.DiscardSymbol, ((Operation)operation).OwningSemanticModel, operation.Syntax, operation.Type, operation.ConstantValue, operation.IsImplicit); | |||
return new DiscardOperation( | |||
operation is IPatternOperation pat ? pat.InputType : operation.DiscardSymbol?.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.
operation is IPatternOperation pat ? pat.InputType : operation.DiscardSymbol?.Type, [](start = 16, length = 83)
I didn't understand why cloning would require such logic.
Assuming we agreed that discard operation should contain an InputType
, then cloning should just copy it. #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.
@@ -1837,21 +1838,30 @@ private IDeclarationPatternOperation CreateBoundDeclarationPatternOperation(Boun | |||
variable = ((BoundDiscardExpression)boundDeclarationPattern.VariableAccess).ExpressionSymbol; | |||
} | |||
|
|||
ITypeSymbol inputType = boundDeclarationPattern.InputType; | |||
bool acceptsNull = boundDeclarationPattern.IsVar; |
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.
acceptsNull [](start = 17, length = 11)
What about x is null
? If the constant is null
, then we should probably set AcceptsNull
to true.
If the intention is to capture IsVar
, then maybe the pattern should have an IsVar
property instead.
As Fred pointed out in a test, should AcceptsNull
be true when matching types that can't be null
in the first place (like int
)? #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 is for the declaration pattern only.
The spec for a var
pattern is that it accepts null. The fact that it will never get null in some circumstances doesn't affect that fact.
In reply to: 244032712 [](ancestors = 244032712)
} | ||
|
||
AssertEx.Equal(children, operation.Children); | ||
} |
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 = 8, length = 1)
Consider verifying that the symbol part of PropertySubpatterns contains non-null symbols.
Consider verifying that DeconstructSymbol
is either a method, a type or null. #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.
if (_lazyPropertySubpatterns.IsDefault) | ||
{ | ||
var propertySubpatterns = CreatePropertySubpatterns(); | ||
foreach (var d in propertySubpatterns) |
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.
d [](start = 33, length = 1)
nit: d
should be p
or subpattern
. #Resolved
} | ||
public override ImmutableArray<IPatternOperation> CreateDeconstructionSubpatterns() | ||
{ | ||
return _deconstructionSubpatterns.SelectAsArray(p => (IPatternOperation)_operationFactory.Create(p.Pattern)); |
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.
p [](start = 60, length = 1)
nit: p
should be d
#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.
Expression: | ||
IParameterReferenceOperation: tuple (OperationKind.ParameterReference, Type: (System.Int32 X, System.Int32 Y)) (Syntax: 'tuple') | ||
Pattern: | ||
IRecursivePatternOperation (Declared Symbol: (System.Int32 X, System.Int32 Y) y, MatchedType: (System.Int32 X, System.Int32 Y), Deconstruct Symbol: 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.
(System.Int32 X, System.Int32 Y) [](start = 49, length = 32)
The declared symbol having tuple names feels unexpected. Does that reflect the language in some way?
I would expect that y
is an (int, int)
.
Did we discuss allowing names in positional patterns? I'd expect the names in output to come from there tuple is (a: 1, b: 2) y
, rather than from input.
Please also add test with Deconstruct
. I'd expect the element names to come from the Deconstruct
method, if anything. #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.
If the type is not given in the pattern, the type of the variable is its input type. Using Deconstruct
would not change that (it does not produce a tuple type).
In reply to: 244035791 [](ancestors = 244035791)
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 5)
Add and implement IDiscardPatternOperation, ISwitchExpressionOperation, ISwitchExpressionArmOperation
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 didn't understand why postpone addressing the feedback. Tracking such fine-grained feedback as github issues and without PROTOTYPE markers incurs a loss of context, and I don't see the benefit.
Two things that stand out and should be prioritized in the next PR:
- aligning printed labels with property names in test verifier
- revert handling of default arrays (assuming ioperations indeed uses empty arrays)
@jcouv I don’t want to have to deal with merge conflicts in the multiple PRs I have in progress. The items you suggest are already done in a follow up PR. |
@@ -47,7 +47,13 @@ internal override IOperation VisitNoneOperation(IOperation operation, object arg | |||
private ImmutableArray<T> VisitArray<T>(ImmutableArray<T> nodes) where T : IOperation | |||
{ | |||
// clone the array | |||
return nodes.SelectAsArray(n => Visit(n)); | |||
return nodes.IsDefault ? default : nodes.SelectAsArray(n => Visit(n)); |
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.
return nodes.IsDefault ? default : nodes.SelectAsArray(n => Visit(n)); [](start = 12, length = 70)
If we don't exposed default arrays, why is this change necessary? #Closed
private ImmutableArray<(U, T)> VisitArray<U, T>(ImmutableArray<(U, T)> nodes) where T : IOperation | ||
{ | ||
// clone the array | ||
return nodes.IsDefault ? default : nodes.SelectAsArray(n => (n.Item1, Visit(n.Item2))); |
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.
nodes.IsDefault [](start = 19, length = 15)
If we don't exposed default arrays, why this special case is necessary? #Closed
return nodes.IsDefault ? default : nodes.SelectAsArray(n => Visit(n)); | ||
} | ||
|
||
private ImmutableArray<(U, T)> VisitArray<U, T>(ImmutableArray<(U, T)> nodes) where T : IOperation |
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.
U [](start = 72, length = 1)
Please remove type parameter U
and replace its references with ISymbol. This way we can be sure that we are not skipping any IOperation nodes during the cloning operation.
It looks like this PR is subsumed by #32018. I am going to review that PR instead. Consider closing this one. |
…onArmOperation (#32018) - Adds further IOperation nodes for recursive patterns: IDiscardPatternOperation, ISwitchExpressionOperation, ISwitchExpressionArmOperation - Follow-up to #31967 per #32012 - Issue #32012 also describes additional open issues to be addressed later. - Update compiler test plan. - Also revert some changes accidentally checked in to the patterns branch.
Despite the title, I have not done switch expression nodes in this PR. That will be a later PR.
Part of #27749