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

Binding for local deconstruction declaration statement #12224

Merged
merged 15 commits into from
Jun 30, 2016

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Jun 27, 2016

This change adds binding for local deconstruction declaration statements. The for and foreach statements will be added later.
The binding produces a BoundExpressionStatement with a BoundDeconstructionAssignmentOperator. The main difference compared to the binding for deconstruction-assignment is that now, variables in the BoundDeconstructionAssignmentOperator are BoundLocal.

Much of the binding still relies on the existing BindDeconstructionAssignment, except how the variables on the left are prepared. Going into BindDeconstructionAssignment, the variables are a new type, DeconstructionLocalPendingInference, which during the process of binding the assignment (once the types on the right-hand-side are figured out) get converted to BoundLocal with the inferred type.
Like OutVarLocalPendingInference, DeconstructionLocalPendingInference doesn't survive the early binding process.
In order to allow the tree of variables to be updated by inference, I changed the existing DeconstructionVariable to hold an ArrayBuilder instead of ImmutableArray for nested elements.

@dotnet/roslyn-compiler for review.

@gafter
Copy link
Member

gafter commented Jun 27, 2016

This change adds binding for local deconstruction declaration statements. The for and foreach statements will be added later.
The binding produces a BoundExpressionStatement with a BoundDeconstructionAssignmentOperator. The main difference compared to the binding for deconstruction-assignment is that now, variables in the BoundDeconstructionAssignmentOperator are BoundLocal.

This approach does not match the design guidelines for initial bound nodes. The shape of the initial bound tree is intended to be fairly closely isomorphic to the shape of the syntax. We have separate bound nodes for statements and expressions, and the local declarations remain in the bound tree after initial binding. The deconstruction declaration is not an expression form in the language. It is represented in the syntax as something more akin to a local variable declaration (a statement).

It could be translated "via" an expression form during lowering, but it is not syntactically or semantically an expression statement.

@jcouv
Copy link
Member Author

jcouv commented Jun 27, 2016

@gafter Thanks for pointing this out. I will introduce a new bound node for LocalDeconstructionStatement. Do you see any concerns if that new node simply packages a BoundDeconstructionAssignmentOperator (rather than replicating its fields)?

SeparatedSyntaxList<ArgumentSyntax> arguments = ((TupleExpressionSyntax)node.Left).Arguments;
ImmutableArray<DeconstructionVariable> checkedVariables = BindDeconstructionVariables(arguments, diagnostics);
ArrayBuilder<DeconstructionVariable> checkedVariables = BindDeconstructionVariables(arguments, diagnostics);
Copy link
Member

Choose a reason for hiding this comment

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

Is BindDeconstructionAssignment method below freeing checkedVariables? Please consider having the same method allocate and free.

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks for pointing this. I think when I switched to ArrayBuilder I removed the freeing and did not put it back. I'll fix.

@gafter
Copy link
Member

gafter commented Jun 27, 2016

Do you see any concerns if that new node simply packages a BoundDeconstructionAssignmentOperator (rather than replicating its fields)?

If you add a field to the new node containing the list of declared variables, I think that is a good solution.

@AlekseyTs
Copy link
Contributor

I think all tests for various forms of deconstruction declaration should also test behavior of SemanticModel.GetDeclaredSymbol API for all declared locals.

@gafter
Copy link
Member

gafter commented Jun 27, 2016

You will want to test the region analysis APIs for these new variables, but doing that in a separate PR makes sense.

@jcouv
Copy link
Member Author

jcouv commented Jun 28, 2016

Added semantic model API tests and the initial feedback.
I have not changed the statement node yet though (that will come shortly).
I took a note to test region analysis API in #11299.

return result;
}

private void FreeDeconstructionVariables(ArrayBuilder<DeconstructionVariable> variables)
Copy link
Member

Choose a reason for hiding this comment

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

static

Copy link
Member Author

Choose a reason for hiding this comment

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

Done

@jcouv
Copy link
Member Author

jcouv commented Jun 28, 2016

I pushed a commit with most feedback addressed, except: I need to add tests for errors from ValidateDeclarationNameConflictsInScope and tests for additional semantic model APIs that Aleksey suggested.
Also, I'm still investigating bug with getting the type symbol for "var" in (var x, var y) =...

Debug.Assert(((VariableDeclarationSyntax)topLevelVariableDeclaration).Deconstruction != null);
Debug.Assert(((VariableDeclarationSyntax)topLevelVariableDeclaration).Deconstruction.Value != null);

var statement = topLevelVariableDeclaration.Parent;
Copy link
Member

Choose a reason for hiding this comment

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

Perhaps extract a GetLocalDeclarationStatement(IdentifierToken) helper method that is used here and in the .ctor above and have that helper method assert there at least one pair of { VariableDeconstructionDeclarator, VariableDeclaration }.

Copy link
Member Author

Choose a reason for hiding this comment

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

Refactored all this code to minimize duplication.

if (((VariableDeclarationSyntax)parent).Deconstruction != null)
{
return true;
}
Copy link
Member

Choose a reason for hiding this comment

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

return ((VariableDeclarationSyntax)parent).Deconstruction != null;

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@cston
Copy link
Member

cston commented Jun 29, 2016

LGTM

/// </summary>
internal partial class OutVarLocalPendingInference
{
public override void Accept(OperationVisitor visitor)
{
visitor.VisitNoneOperation(this);
throw ExceptionUtilities.Unreachable;
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks!

@AlekseyTs
Copy link
Contributor

Done with review pass.

@jcouv
Copy link
Member Author

jcouv commented Jun 29, 2016

Pushed a change with addresses the feedback and suggestions.

I will add more tests on semantic model (including aliases) as well as the ValidateDeclarationNameConflictsInScope error case in subsequent PR.

@jcouv
Copy link
Member Author

jcouv commented Jun 29, 2016

The last commit makes var (x, y) = ... an error if the var type exists.
The diagnostics are not great (too many errors reported), but should be ok for this context.

@jcouv jcouv merged commit 6383d37 into dotnet:features/tuples Jun 30, 2016
@jcouv jcouv deleted the bind-d-decl branch June 30, 2016 18:25
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.

6 participants