-
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
Deconstruction-assignment: nested scenario #11715
Changes from 5 commits
c71be15
86ca7d3
dd3b674
199c6ad
461b419
6e68b8d
3b6d012
6f354a1
f205d5f
ba975f7
1748df1
4e531c6
87ee4f4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -280,11 +280,18 @@ public override Symbol ExpressionSymbol | |
|
||
internal sealed partial class BoundDeconstructionAssignmentOperator : BoundExpression | ||
{ | ||
internal class AssignmentInfo | ||
internal class AssignmentStep | ||
{ | ||
public BoundAssignmentOperator Assignment; | ||
public BoundLValuePlaceholder LValuePlaceholder; | ||
public BoundRValuePlaceholder RValuePlaceholder; | ||
public BoundDeconstructValuePlaceholder OutputPlaceholder; | ||
public BoundDeconstructValuePlaceholder TargetPlaceholder; | ||
} | ||
|
||
internal class DeconstructStep | ||
{ | ||
public MethodSymbol DeconstructMemberOpt; // the deconstruct member, or null if tuple deconstruction | ||
public BoundDeconstructValuePlaceholder TargetPlaceholder; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would make this BoundExpression and put Right directly as the target for the first deconstruct, removing it from the parent node. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I didn't get that part. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Never mind, since we might not create the deconstruct step in case of an error, the suggestion is not an option. In reply to: 65796074 [](ancestors = 65796074) |
||
public ImmutableArray<BoundDeconstructValuePlaceholder> OutputPlaceholders; // placeholders for the outputs produced by this deconstruction | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider making these two types immutable. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can these types be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I thought about Also, from our discussion, I will move back from |
||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -84,9 +84,7 @@ | |
This node is used to represent an expression returning value of a certain type. | ||
It is used to perform intermediate binding, and will not survive the local rewriting. | ||
--> | ||
<Node Name="BoundLValuePlaceholder" Base="BoundValuePlaceholderBase" HasValidate="true"> | ||
</Node> | ||
<Node Name="BoundRValuePlaceholder" Base="BoundValuePlaceholderBase" HasValidate="true"> | ||
<Node Name="BoundDeconstructValuePlaceholder" Base="BoundValuePlaceholderBase" HasValidate="true"> | ||
</Node> | ||
|
||
<!-- only used by codegen --> | ||
|
@@ -394,13 +392,21 @@ | |
<!-- Non-null type is required for this node kind --> | ||
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/> | ||
|
||
<!-- Various assignable expressions, including some BoundDeconstructionVariables for nested variables --> | ||
<Field Name="LeftVariables" Type="ImmutableArray<BoundExpression>"/> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need BoundDeconstructionVariables in this list? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Presence of these node can potentially confuse SemanticModel, etc. In reply to: 65776149 [](ancestors = 65776149) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, given the current implementation, it looks like LeftVariable is always a flat list of targets, BoundDeconstructionVariables is never among them. Please adjust the comment accordingly. In reply to: 65778993 [](ancestors = 65778993,65776149) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AlekseyTs, from discussion with Chuck I will remove the |
||
|
||
<Field Name="Right" Type="BoundExpression"/> | ||
<Field Name="DeconstructMemberOpt" Type="MethodSymbol" Null="allow"/> | ||
|
||
<Field Name="DeconstructSteps" Type="ImmutableArray<BoundDeconstructionAssignmentOperator.DeconstructStep>" Null="NotApplicable" SkipInVisitor="true" /> | ||
|
||
<!-- The assignments have placeholders for the left and right part of the assignment --> | ||
<Field Name="Assignments" Type="ImmutableArray<BoundDeconstructionAssignmentOperator.AssignmentInfo>" Null="NotApplicable" SkipInVisitor="true"/> | ||
<Field Name="AssignmentSteps" Type="ImmutableArray<BoundDeconstructionAssignmentOperator.AssignmentStep>" Null="NotApplicable" SkipInVisitor="true"/> | ||
</Node> | ||
|
||
<Node Name="BoundDeconstructionVariables" Base="BoundExpression"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, this bound node is never output from binding at all. But during binding, I have bound expressions for the LHS, which need to be organized in a tree. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where do we use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the current implementation, it looks like BoundDeconstructionVariables never escapes the binder. This is just a helper node, used in the process of binding, never gets stored in the tree produced by the Binder. Please provide a comment reflecting that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed private class DeconstructionVariable
{
public BoundExpression Single;
public ImmutableArray<DeconstructionVariable>? Nested;
...
} |
||
<Field Name="Type" Type="TypeSymbol" Override="true" Null="always"/> | ||
|
||
<Field Name="Variables" Type="ImmutableArray<BoundExpression>"/> | ||
</Node> | ||
|
||
<Node Name="BoundVoid" Base="BoundExpression"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -33,22 +33,7 @@ Optional<object> IOperation.ConstantValue | |
public abstract TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument); | ||
} | ||
|
||
internal sealed partial class BoundLValuePlaceholder : BoundValuePlaceholderBase, IPlaceholderExpression | ||
{ | ||
protected override OperationKind ExpressionKind => OperationKind.PlaceholderExpression; | ||
|
||
public override void Accept(OperationVisitor visitor) | ||
{ | ||
visitor.VisitPlaceholderExpression(this); | ||
} | ||
|
||
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument) | ||
{ | ||
return visitor.VisitPlaceholderExpression(this, argument); | ||
} | ||
} | ||
|
||
internal sealed partial class BoundRValuePlaceholder : BoundValuePlaceholderBase, IPlaceholderExpression | ||
internal sealed partial class BoundDeconstructValuePlaceholder : BoundValuePlaceholderBase, IPlaceholderExpression | ||
{ | ||
protected override OperationKind ExpressionKind => OperationKind.PlaceholderExpression; | ||
|
||
|
@@ -1049,6 +1034,21 @@ public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, T | |
} | ||
} | ||
|
||
internal sealed partial class BoundDeconstructionVariables : BoundExpression | ||
{ | ||
protected override OperationKind ExpressionKind => OperationKind.None; | ||
|
||
public override void Accept(OperationVisitor visitor) | ||
{ | ||
visitor.VisitNoneOperation(this); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not visit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a dummy implementation, like |
||
} | ||
|
||
public override TResult Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument) | ||
{ | ||
return visitor.VisitNoneOperation(this, argument); | ||
} | ||
} | ||
|
||
internal sealed partial class BoundVoid : BoundExpression | ||
{ | ||
protected override OperationKind ExpressionKind => OperationKind.None; | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1485,7 +1485,7 @@ public override BoundNode VisitFixedStatement(BoundFixedStatement node) | |
{ | ||
foreach (LocalSymbol local in node.Locals) | ||
{ | ||
switch(local.DeclarationKind) | ||
switch (local.DeclarationKind) | ||
{ | ||
case LocalDeclarationKind.RegularVariable: | ||
case LocalDeclarationKind.PatternVariable: | ||
|
@@ -1721,14 +1721,40 @@ public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) | |
Assign(node.Left, node.Right, refKind: node.RefKind); | ||
return null; | ||
} | ||
|
||
public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstructionAssignmentOperator node) | ||
{ | ||
base.VisitDeconstructionAssignmentOperator(node); | ||
|
||
foreach(BoundExpression variable in node.LeftVariables) | ||
foreach (BoundExpression variable in node.LeftVariables) | ||
{ | ||
if (variable.Kind == BoundKind.DeconstructionVariables) | ||
{ | ||
VisitDeconstructionVariables((BoundDeconstructionVariables)variable); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doesn't There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So, I removed |
||
} | ||
else | ||
{ | ||
Assign(variable, null, refKind: RefKind.None); | ||
} | ||
} | ||
|
||
return null; | ||
} | ||
|
||
public override BoundNode VisitDeconstructionVariables(BoundDeconstructionVariables node) | ||
{ | ||
base.VisitDeconstructionVariables(node); | ||
|
||
foreach (BoundExpression variable in node.Variables) | ||
{ | ||
Assign(variable, null, refKind: RefKind.None); | ||
if (variable.Kind == BoundKind.DeconstructionVariables) | ||
{ | ||
VisitDeconstructionVariables((BoundDeconstructionVariables)variable); | ||
} | ||
else | ||
{ | ||
Assign(variable, null, refKind: RefKind.None); | ||
} | ||
} | ||
|
||
return null; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1518,14 +1518,38 @@ public override BoundNode VisitDeconstructionAssignmentOperator(BoundDeconstruct | |
{ | ||
foreach (BoundExpression variable in node.LeftVariables) | ||
{ | ||
VisitLvalue(variable); | ||
if (variable.Kind == BoundKind.DeconstructionVariables) | ||
{ | ||
VisitDeconstructionVariables((BoundDeconstructionVariables)variable); | ||
} | ||
else | ||
{ | ||
VisitLvalue(variable); | ||
} | ||
} | ||
|
||
VisitRvalue(node.Right); | ||
|
||
return null; | ||
} | ||
|
||
public override BoundNode VisitDeconstructionVariables(BoundDeconstructionVariables node) | ||
{ | ||
foreach (BoundExpression variable in node.Variables) | ||
{ | ||
if (variable.Kind == BoundKind.DeconstructionVariables) | ||
{ | ||
VisitDeconstructionVariables((BoundDeconstructionVariables)variable); | ||
} | ||
else | ||
{ | ||
VisitLvalue(variable); | ||
} | ||
} | ||
|
||
return null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it is more appropriate to throw Unreachable here. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more. Removed |
||
} | ||
|
||
public override BoundNode VisitCompoundAssignmentOperator(BoundCompoundAssignmentOperator node) | ||
{ | ||
// TODO: should events be handled specially too? | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -230,12 +230,7 @@ public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatemen | |
} | ||
} | ||
|
||
public override BoundNode VisitLValuePlaceholder(BoundLValuePlaceholder node) | ||
{ | ||
return PlaceholderReplacement(node); | ||
} | ||
|
||
public override BoundNode VisitRValuePlaceholder(BoundRValuePlaceholder node) | ||
public override BoundNode VisitDeconstructValuePlaceholder(BoundDeconstructValuePlaceholder node) | ||
{ | ||
return PlaceholderReplacement(node); | ||
} | ||
|
@@ -287,6 +282,17 @@ private void RemovePlaceholderReplacement(BoundValuePlaceholderBase placeholder) | |
Debug.Assert(removed); | ||
} | ||
|
||
/// <summary> | ||
/// Remove all the listed placeholders. | ||
/// </summary> | ||
private void RemovePlaceholderReplacements(IEnumerable<BoundValuePlaceholderBase> placeholders) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could we use more specific type rather than IEnumerable? ArrayBuilder/ImmutableArray There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Perhaps inline the method since it is only used in one place. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suspect in the future it will be handy again to be able to remove multiple placeholders. I'm not sure what is the downside of using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AlekseyTs Why not |
||
{ | ||
foreach (var placeholder in placeholders) | ||
{ | ||
RemovePlaceholderReplacement(placeholder); | ||
} | ||
} | ||
|
||
public override BoundNode VisitBadExpression(BoundBadExpression node) | ||
{ | ||
// Cannot recurse into BadExpression children since the BadExpression | ||
|
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 think AssignmentStep and DeconstructStep should be regular bound nodes
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.