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

D-declaration should use declaration expressions #14871

Merged
merged 8 commits into from
Nov 8, 2016

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented Nov 1, 2016

This implements the syntax changes (not the wildcard part) of #14832.
The first commit has the changes to the parser and parsing tests. The second commit has the rest (binding and semantic model).

From our discussion we could keep the following restrictions for now:

  • only parse the declaration form of deconstruction when it is a standalone statement (as today), which means we wouldn't parse M((int x, int y) = e); for now (I will do that next)
  • no support for mixing declarations and assignments in one deconstruction, such as (x, var y) = e;

On the other hand, deconstruction declaration is now allowed as an embedded statement.

Some follow-ups:

  • merge the parsing logic (and some of the binding logic) between d-assignment and d-declaration (which will allow mixing at some point)
  • the logic for parsing d-assignment and d-declaration is still separate (we can merge later). Some of the binding logic could also be factored further.
  • GetTypeInfo on tuple expression in the left-hand-side of d-assignment and d-declaration.

@jcouv jcouv added this to the 2.0 (RC.2) milestone Nov 1, 2016
@jcouv jcouv self-assigned this Nov 1, 2016
@jcouv jcouv changed the base branch from master to features/wildcard November 1, 2016 23:12
@gafter gafter self-assigned this Nov 2, 2016
@@ -1894,23 +1869,6 @@
</Node>
Copy link
Member

@gafter gafter Nov 2, 2016

Choose a reason for hiding this comment

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

Could you please remove the commented definition of WildcardVariableDesignationSyntax, since we no longer expect to do things that way? #Resolved

<Kind Name="ForEachComponentStatement"/>
<Field Name="ForEachKeyword" Type="SyntaxToken" Override="true">
<Kind Name="ForEachKeyword"/>
</Field>
<Field Name="OpenParenToken" Type="SyntaxToken" Override="true">
<Kind Name="OpenParenToken"/>
</Field>
<Field Name="VariableComponent" Type="VariableComponentSyntax"/>
<Field Name="VariableComponent" Type="ExpressionSyntax"/> <!-- Should this be something more specific? TODO REVIEW -->
Copy link
Member

@gafter gafter Nov 2, 2016

Choose a reason for hiding this comment

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

Since it could either be a tuple literal or a declaration expression, I don't think there is anything else more specific it could be. We probably will want to allow a simple wildcard here too (e.g. foreach (_ in e) {}), perhaps later if not now, so better not to be too restrictive on the type of this field.

Probably rename to Variable? #Resolved

@@ -2153,15 +2110,15 @@
<!-- We name this "DeclarationForEachStatementSyntax" because it can express existing foreach
loops. We may elect to represent all foreach loops using this node and deprecate (stop parsing
into) the old one. -->
<Node Name="ForEachComponentStatementSyntax" Base="CommonForEachStatementSyntax">
<Node Name="ForEachComponentStatementSyntax" Base="CommonForEachStatementSyntax"> <!-- Change name TODO REVIEW -->
Copy link
Member

@gafter gafter Nov 2, 2016

Choose a reason for hiding this comment

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

Possibly ForEachVariableStatementSyntax? #Resolved

@@ -1131,7 +1131,8 @@
</Node>
<Node Name="DeclarationExpressionSyntax" Base="ExpressionSyntax">
<Kind Name="DeclarationExpression"/>
<Field Name="VariableComponent" Type="VariableComponentSyntax">
<Field Name="Type" Type="TypeSyntax" Optional="true"/>
<Field Name="Designation" Type="VariableDesignationSyntax">
<PropertyComment>
<summary>Declaration representing the variable declared in an out parameter.</summary>
Copy link
Member

@gafter gafter Nov 2, 2016

Choose a reason for hiding this comment

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

Probably want to make this comment slightly more expansive:

         <summary>Declaration representing the variable declared in an out parameter or deconstruction.</summary>

#Resolved

@gafter
Copy link
Member

gafter commented Nov 2, 2016

Looks good so far. #Resolved

@gafter gafter removed their assignment Nov 2, 2016
@jcouv jcouv force-pushed the decon-syntax branch 8 times, most recently from c369441 to 0d95857 Compare November 3, 2016 16:10
@jcouv
Copy link
Member Author

jcouv commented Nov 3, 2016

With this second commit, the compiler tests are passing.
I'm still working on editor features (refactoring, formatting, and EnC). #Resolved

@jcouv jcouv force-pushed the decon-syntax branch 3 times, most recently from 3bae047 to 158826d Compare November 4, 2016 18:26
foreach (var v in t.Variables) AddVariableExpressions(component, expressions);
var t = (TupleExpressionSyntax)component;
foreach (ArgumentSyntax a in t.Arguments) AddVariableExpressions(a.Expression, expressions);
// TODO REVIEW I think there was a bug there. Are we missing tests?
Copy link
Member Author

@jcouv jcouv Nov 4, 2016

Choose a reason for hiding this comment

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

@gafter I'll stop by to discuss. #Closed

// issueSpan = candidateIssueSpan;
// return true;
//}
// TODO REVIEW Are we lacking tests? It seems this code should be needed for DeclarationExpression
Copy link
Member Author

@jcouv jcouv Nov 4, 2016

Choose a reason for hiding this comment

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

Still investigating how to hit this code. #Resolved

Copy link
Member

Choose a reason for hiding this comment

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

Please turn your TODO comments into PROTOTYPE(wildcard) comments so we can check this in and treat it as an open issue.


In reply to: 86607126 [](ancestors = 86607126)

if (operatorToken.Kind() == SyntaxKind.EqualsToken &&
(left.Kind() == SyntaxKind.TupleExpression || left.Kind() == SyntaxKind.DeclarationExpression))
{
// TODO REVIEW Once GetTypeInfo works on the left-hand-side expression in a deconstruction declaration,
Copy link
Member Author

@jcouv jcouv Nov 4, 2016

Choose a reason for hiding this comment

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

I'll replace this with reference to github issue once we decide on semantic model behavior. #Resolved

@jcouv
Copy link
Member Author

jcouv commented Nov 4, 2016

@dotnet/roslyn-compiler This is ready for review. There is one failing test (related to EE) which I'm still investigating. Thanks #Resolved

@@ -171,7 +171,7 @@ private void CheckOutVarDeclaration(BoundLocal node)
{
if (IsInside &&
!node.WasCompilerGenerated && node.Syntax.Kind() == SyntaxKind.DeclarationExpression &&
((DeclarationExpressionSyntax)node.Syntax).Identifier() == node.LocalSymbol.IdentifierToken)
((SingleVariableDesignationSyntax)((DeclarationExpressionSyntax)node.Syntax).Designation).Identifier == node.LocalSymbol.IdentifierToken)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

((SingleVariableDesignationSyntax)((DeclarationExpressionSyntax)node.Syntax).Designation).Identifier == node.LocalSymbol.IdentifierToken [](start = 16, length = 136)

Should probably strengthen the check by making sure it is an out argument. #Closed

comp.VerifyDiagnostics(
// (7,13): error CS1023: Embedded statement cannot be a declaration or labeled statement
// var (x, y) = (1, 2);
Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var (x, y) = (1, 2);").WithLocation(7, 13)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

Diagnostic(ErrorCode.ERR_BadEmbeddedStmt, "var (x, y) = (1, 2);").WithLocation(7, 13) [](start = 16, length = 85)

I think we might still want to report this error. #Closed

@jcouv jcouv force-pushed the decon-syntax branch 2 times, most recently from 9279a6b to 0ea0754 Compare November 8, 2016 18:18
if (expression.Kind == SyntaxKind.SimpleAssignmentExpression)
{
var assignment = (AssignmentExpressionSyntax)expression;
if (assignment.Left.EnumerateNodes().Any(x => x.RawKind == (int)SyntaxKind.DeclarationExpression))
Copy link
Member

@cston cston Nov 8, 2016

Choose a reason for hiding this comment

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

Could assignment.Left contain a lambda with a declaration?

while (true)
    F(() => { (int x, int y) = (1, 2); }).x = null;
``` #Resolved

Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

Can we simplify the check by simply checking if the left is a tuple or a declaration expression? #Closed

Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

Probably not, tuple expression can mean deconstruction assignment without declaration. #Closed

@@ -1131,13 +1131,14 @@
</Node>
<Node Name="DeclarationExpressionSyntax" Base="ExpressionSyntax">
<Kind Name="DeclarationExpression"/>
<Field Name="VariableComponent" Type="VariableComponentSyntax">
<Field Name="Type" Type="TypeSyntax" Optional="true"/>
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

Optional="true" [](start = 40, length = 16)

I thought the type is not optional. #Closed

/// <summary>
/// Checks if we can find at least one type in the deconstruction variables
/// </summary>
private static bool TypeFoundInDeconstructionDeclarationVariables(ExpressionSyntax node)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

TypeFoundInDeconstructionDeclarationVariables [](start = 28, length = 45)

I am not sure if this check is relevant now. Shouldn't we always require Tuples Feature when deconstruction is used? #Closed

Copy link
Member Author

@jcouv jcouv Nov 8, 2016

Choose a reason for hiding this comment

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

This is not related to the tuples features. There are two heuristics to be confident that the user typed a deconstruction declaration:

  1. we must find the expected following token (typically '=' but could be 'in' for in foreach context),
  2. the variables must include at least one specified type. That's the TypeFoundInDeconstructionDeclarationVariables method. #Resolved

{
var syntax = (TypedVariableComponentSyntax)node;
var syntax = (DeclarationExpressionSyntax)node;
if (syntax.Type.IsMissing || syntax.Designation.IsMissing) return false;
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

(syntax.Type.IsMissing || syntax.Designation.IsMissing) [](start = 27, length = 55)

Can this ever be true now? #Closed

Copy link
Member Author

@jcouv jcouv Nov 8, 2016

Choose a reason for hiding this comment

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

Yes, if we try to parse (x, var y) = e; as a d-declaration, then the type on x will be missing.
Similarly, in (int, var y) = e;, the designation will be missing. #Resolved

{
if (parent.Parent?.Kind() == SyntaxKind.TupleExpression)
{
expr = (TupleExpressionSyntax)parent.Parent;
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

(TupleExpressionSyntax) [](start = 31, length = 23)

It looks like the cast is not necessary. #Closed

Copy link
Member Author

@jcouv jcouv Nov 8, 2016

Choose a reason for hiding this comment

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

A cast is needed, at least to ExpressionSyntax (the type of expr). #Resolved

Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

Right, never mind then. #Closed

_variablesDeclared.Add(node.LocalSymbol);
var declaration = (DeclarationExpressionSyntax)node.Syntax;
if (((SingleVariableDesignationSyntax)declaration.Designation).Identifier == node.LocalSymbol.IdentifierToken &&
((ArgumentSyntax)declaration.Parent).RefOrOutKeyword.Kind() == SyntaxKind.OutKeyword)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

declaration.Parent [](start = 37, length = 18)

Probably should ensure that the cast is safe (check the kind) and that the Parent is not null. #Closed

@AlekseyTs
Copy link
Contributor

AlekseyTs commented Nov 8, 2016

        var (x, y) = (1, 2);

Consider adding a test with deconstruction assignment without declaration. #Closed


Refers to: src/Compilers/CSharp/Test/Semantic/Semantics/DeconstructionTests.cs:2445 in 7400a9f. [](commit_id = 7400a9f, deletion_comment = False)

statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt);
break;
case SyntaxKind.ExpressionStatement:
Copy link
Member

@gafter gafter Nov 8, 2016

Choose a reason for hiding this comment

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

I think we should get guidance from Mads as to whether or not an error is deserved in this situation. I thought that it isn't considered a declaration any longer (from the spec point of view) due to the syntax change.

Whichever way you leave this code, please open a separate issue to resolve this. #Resolved

Copy link
Member Author

@jcouv jcouv Nov 8, 2016

Choose a reason for hiding this comment

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

Yes. I will leave the error for now and added a note in #15049 (which tracks allow d-deconstruction in expression context).
My understanding is that we do want to allow d-deconstruction in expression context, so this error would naturally disappear then. #Resolved

@@ -2161,14 +2161,6 @@ private static bool IsThisOrTypeOrNamespace(MemberAccessExpressionSyntax memberA
return true;
}

//if (simpleName.IsParentKind(SyntaxKind.TypedVariableComponent))
Copy link
Member Author

Choose a reason for hiding this comment

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

Filed follow-up issue: #15094

@@ -136,7 +136,6 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node)
{
var t = (TupleExpressionSyntax)component;
foreach (ArgumentSyntax a in t.Arguments) AddVariableExpressions(a.Expression, expressions);
// TODO REVIEW I think there was a bug there. Are we missing tests?
Copy link
Member Author

Choose a reason for hiding this comment

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

Confirmed with Neal there was a bug.
Filed bug #15095 to improve test coverage

/// <summary>
/// Returns true if the expression is composed only of nested tuple and declaration expressions.
/// </summary>
private static bool IsDeconstructionDeclarationLeft(ExpressionSyntax node)
Copy link
Contributor

@AlekseyTs AlekseyTs Nov 8, 2016

Choose a reason for hiding this comment

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

IsDeconstructionDeclarationLeft [](start = 28, length = 31)

Is this a dupe of a method from SyntaxExtensions? Consider reusing it instead. #Closed

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 logic is the same, but one is on internal nodes, the other on public nodes.

@AlekseyTs
Copy link
Contributor

Compiler changes LGTM

}
else
{
goto default;
Copy link
Member

Choose a reason for hiding this comment

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

please do not use gotos in IDE code :)

var t = (ParenthesizedVariableComponentSyntax)component;
foreach (var v in t.Variables) AddVariableExpressions(component, expressions);
var t = (TupleExpressionSyntax)component;
foreach (ArgumentSyntax a in t.Arguments) AddVariableExpressions(a.Expression, expressions);
Copy link
Member

Choose a reason for hiding this comment

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

always use braces.

@@ -235,8 +235,7 @@ private static bool CanBindToken(SyntaxToken token)
else if (current is DeclarationExpressionSyntax)
{
var decl = (DeclarationExpressionSyntax)current;
var component = decl.VariableComponent as TypedVariableComponentSyntax;
var name = component?.Designation as SingleVariableDesignationSyntax;
var name = decl.Designation as SingleVariableDesignationSyntax;
if (name == null) break;
Copy link
Member

Choose a reason for hiding this comment

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

always use braces.

@@ -876,6 +875,13 @@ private IEnumerable<TypeInferenceInfo> InferTypeInBinaryOrAssignmentExpression(E
return SpecializedCollections.SingletonEnumerable(new TypeInferenceInfo(this.Compilation.GetSpecialType(SpecialType.System_Boolean)));
}

// Infer type for deconstruction declaration
if (operatorToken.Kind() == SyntaxKind.EqualsToken &&
(left.Kind() == SyntaxKind.TupleExpression || left.Kind() == SyntaxKind.DeclarationExpression))
Copy link
Member

Choose a reason for hiding this comment

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

didn't you introduce an IsDeconstructoin helper? Shouldn't that be used here?

Copy link
Member Author

Choose a reason for hiding this comment

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

Factored the code into an extension in CSharpWorkspace project.

@jcouv jcouv merged commit fb5d0b2 into dotnet:master Nov 8, 2016
@jcouv
Copy link
Member Author

jcouv commented Nov 8, 2016

@CyrusNajmabadi I went ahead and merged to unblock Neal. I'll make sure to address any other feedback that you have.

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