-
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
D-declaration should use declaration expressions #14871
Conversation
@@ -1894,23 +1869,6 @@ | |||
</Node> |
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.
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 --> |
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.
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 --> |
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.
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> |
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.
Probably want to make this comment slightly more expansive:
<summary>Declaration representing the variable declared in an out parameter or deconstruction.</summary>
#Resolved
Looks good so far. #Resolved |
c369441
to
0d95857
Compare
With this second commit, the compiler tests are passing. |
3bae047
to
158826d
Compare
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? |
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.
@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 |
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.
Still investigating how to hit this code. #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.
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, |
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'll replace this with reference to github issue once we decide on semantic model behavior. #Resolved
@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) |
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.
((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) |
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.
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
9279a6b
to
0ea0754
Compare
if (expression.Kind == SyntaxKind.SimpleAssignmentExpression) | ||
{ | ||
var assignment = (AssignmentExpressionSyntax)expression; | ||
if (assignment.Left.EnumerateNodes().Any(x => x.RawKind == (int)SyntaxKind.DeclarationExpression)) |
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.
Could assignment.Left
contain a lambda with a declaration?
while (true)
F(() => { (int x, int y) = (1, 2); }).x = 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.
Can we simplify the check by simply checking if the left is a tuple or a declaration expression? #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.
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"/> |
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.
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) |
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.
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
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 not related to the tuples features. There are two heuristics to be confident that the user typed a deconstruction declaration:
- we must find the expected following token (typically '=' but could be 'in' for in foreach context),
- 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; |
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.
(syntax.Type.IsMissing || syntax.Designation.IsMissing) [](start = 27, length = 55)
Can this ever be true now? #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.
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; |
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.
(TupleExpressionSyntax) [](start = 31, length = 23)
It looks like the cast is not necessary. #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.
A cast is needed, at least to ExpressionSyntax
(the type of expr
). #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.
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) |
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.
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
statement = this.AddError(statement, ErrorCode.ERR_BadEmbeddedStmt); | ||
break; | ||
case SyntaxKind.ExpressionStatement: |
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 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
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.
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)) |
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.
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? |
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.
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) |
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.
IsDeconstructionDeclarationLeft [](start = 28, length = 31)
Is this a dupe of a method from SyntaxExtensions? Consider reusing it instead. #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.
The logic is the same, but one is on internal nodes, the other on public nodes.
Compiler changes LGTM |
} | ||
else | ||
{ | ||
goto default; |
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.
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); |
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.
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; |
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.
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)) |
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.
didn't you introduce an IsDeconstructoin helper? Shouldn't that be used here?
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.
Factored the code into an extension in CSharpWorkspace project.
@CyrusNajmabadi I went ahead and merged to unblock Neal. I'll make sure to address any other feedback that you have. |
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:
M((int x, int y) = e);
for now (I will do that next)(x, var y) = e;
On the other hand, deconstruction declaration is now allowed as an embedded statement.
Some follow-ups: