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

Deconstruction assignment: basic implementation #11384

Merged
merged 7 commits into from
May 19, 2016

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented May 17, 2016

Here's a first basic implementation of deconstruction-assignment. I'm wondering if this should go into a feature branch instead as I only did limited IDE testing so far.

In terms of design:

  • The parsing was fixed and a test harness added for various potentially ambiguous scenarios with parentheses.
    • The ScanTupleType method can now return a new ScanTypeFlags type. This only affects two callers: IsPossibleLocalDeclarationStatement (see code snippet at the bottom) and
  • The binding for assignment forks off when such a tuple literal is found on the left:
    • The binding produces a new bound node (BoundDeconstructionAssignmentOperator). This bound node contains the list of variables on the left (bound and checked), the receiver for Deconstruct, the symbol for the Deconstruct method and information to complete the assignment of individual values (including conversions).
      -The Deconstruct method is resolved with restrictive rules at this point (we should find exactly one).
  • The lowering for this new bound node generates the following sequence:
    • lower the variables on the left,
    • get their side-effects and keep expressions to access them later without side-effect (like compound assignment does),
    • lower the right-hand-side expression (that will be the receiver for Deconstruct below),
    • prepare locals as out parameters,
    • invoke Deconstruct with those out parameters,
    • assign the values from those out parameters back into their proper left-hand-side variable (including conversions).
  • There is also some small change to the data flow logic, to ensure that variables on the left count as being assigned.

Vlad, the initial approach that we discussed for binding was very difficult (significant refactoring on tricky code). So the bound node holds onto the assignment expressions for each variable, as opposed to a Conversion object. Not only was this much simpler and natural, I think this will actually help later (when dealing with nesting case).

@dotnet/roslyn-compiler for review.

From IsPossibleLocalDeclarationStatement:

                ScanTypeFlags st = this.ScanType();

                // [comment removed]
                if (st == ScanTypeFlags.MustBeType && this.CurrentToken.Kind != SyntaxKind.DotToken)
                {
                    return true;
                }

ScanType used to return MustBeType or NotType, but we need to care about "possibly-tuple-type-but-might-also-be-tuple-expression" (the new TupleType option).


// bind the variables and check they can be assigned to
var checkedVariablesBuilder = ArrayBuilder<BoundExpression>.GetInstance(arguments.Count);
for (int i = 0, l = numElements; i < l; i++)
Copy link
Member

Choose a reason for hiding this comment

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

foreach

Copy link
Member Author

Choose a reason for hiding this comment

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

I can't. I need the index i below.

Copy link
Member

Choose a reason for hiding this comment

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

It looks like the index is only used to index into arguments though. i and l seem unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, yes. Sorry about that.

}

[Fact]
public void LambdaStillNotValidateStatement()
Copy link
Member

Choose a reason for hiding this comment

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

ValidStatement?

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.

if (numElements < 2)
{
// this should be a parse error already.
BoundExpression[] args = numElements == 1 ?
Copy link
Member

Choose a reason for hiding this comment

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

Actually, it should have parsed as a parenthesized expression, not a tuple expression. I think you can assert that this does not occur.

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed.

@gafter
Copy link
Member

gafter commented May 18, 2016

                    }

If any of the element types "must be a type", then the whole enclosing thing must be a type. That should be reflected in the return value of this function.


Refers to: src/Compilers/CSharp/Portable/Parser/LanguageParser.cs:6289 in 86999a5. [](commit_id = 86999a5, deletion_comment = False)


<Field Name="LeftVariables" Type="ImmutableArray&lt;BoundExpression&gt;"/>

<Field Name="DeconstructReceiver" Type="BoundExpression"/>
Copy link
Member

Choose a reason for hiding this comment

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

I'd just call it "Right"

Copy link
Member

Choose a reason for hiding this comment

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

And you actually call it loweredRight after lowering

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.

Treat deconstruction of a tuple into existing variables as a kind of assignment, using the existing AssignmentExpression.


###Deconstruction-assignment (deconstruction into into existing variables):
Copy link
Contributor

Choose a reason for hiding this comment

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

"into into"

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.

@AlekseyTs
Copy link
Contributor

:shipit:

@AlekseyTs
Copy link
Contributor

LGTM

@jcouv
Copy link
Member Author

jcouv commented May 19, 2016

Thanks all for the review and feedback. I'll merge shortly.

@VSadov
Copy link
Member

VSadov commented May 19, 2016

LGTM.

We need to figure what nesting on the left means and what is the type of the whole expression.

;

join_clause // not sure
: 'join' type? identifier 'in' expression 'on' expression 'equals' expression
Copy link
Member

Choose a reason for hiding this comment

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

This is an existing production, no? Are you intending to add an alternative with local_variable_combo_declaration_lhs? Here and below two productions.

Copy link
Member Author

Choose a reason for hiding this comment

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

The list above resulted from my fishing for syntax nodes that look like they declare variables and so would be candidates for deconstruction-declaration.
For those marked "not sure", I wasn't sure whether they are good candidates and what the updated syntax would be.

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.

None yet

6 participants