Skip to content

Commit

Permalink
Binding for deconstruction-declaration in 'for' statement (#12302)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Jun 30, 2016
1 parent 6383d37 commit 1c53b7e
Show file tree
Hide file tree
Showing 11 changed files with 544 additions and 142 deletions.
19 changes: 10 additions & 9 deletions src/Compilers/CSharp/Portable/Binder/Binder_Deconstruct.cs
Original file line number Diff line number Diff line change
Expand Up @@ -479,13 +479,14 @@ private BoundExpression MissingDeconstruct(BoundExpression receiver, ExpressionS
return BadExpression(syntax, childNode);
}

private BoundLocalDeconstructionDeclaration BindDeconstructionDeclarationStatementParts(LocalDeclarationStatementSyntax node, DiagnosticBag diagnostics)
internal BoundLocalDeconstructionDeclaration BindDeconstructionDeclaration(CSharpSyntaxNode node, VariableDeclarationSyntax declaration, DiagnosticBag diagnostics)
{
Debug.Assert(node.Declaration.Deconstruction != null);
Debug.Assert(node.Kind() == SyntaxKind.LocalDeclarationStatement || node.Kind() == SyntaxKind.VariableDeclaration);
Debug.Assert(declaration.IsDeconstructionDeclaration);

ArrayBuilder<DeconstructionVariable> variables = BindDeconstructionDeclarationVariables(node.Declaration, node.Declaration.Type, diagnostics);
ArrayBuilder<DeconstructionVariable> variables = BindDeconstructionDeclarationVariables(declaration, declaration.Type, diagnostics);

var result = new BoundLocalDeconstructionDeclaration(node, BindDeconstructionAssignment(node.Declaration.Deconstruction.Value, node.Declaration.Deconstruction.Value, variables, diagnostics));
var result = new BoundLocalDeconstructionDeclaration(node, BindDeconstructionAssignment(declaration.Deconstruction.Value, declaration.Deconstruction.Value, variables, diagnostics));
FreeDeconstructionVariables(variables);

return result;
Expand All @@ -499,7 +500,7 @@ private BoundLocalDeconstructionDeclaration BindDeconstructionDeclarationStateme
/// </summary>
private ArrayBuilder<DeconstructionVariable> BindDeconstructionDeclarationVariables(VariableDeclarationSyntax node, TypeSyntax closestTypeSyntax, DiagnosticBag diagnostics)
{
Debug.Assert(node.Deconstruction != null);
Debug.Assert(node.IsDeconstructionDeclaration);
SeparatedSyntaxList<VariableDeclarationSyntax> variables = node.Deconstruction.Variables;

// There are four cases for VariableDeclaration:
Expand All @@ -514,13 +515,13 @@ private ArrayBuilder<DeconstructionVariable> BindDeconstructionDeclarationVariab
TypeSyntax typeSyntax = variable.Type ?? closestTypeSyntax;

DeconstructionVariable local;
if (variable.Deconstruction == null)
if (variable.IsDeconstructionDeclaration)
{
local = new DeconstructionVariable(BindDeconstructionDeclarationVariable(variable, typeSyntax, diagnostics));
local = new DeconstructionVariable(BindDeconstructionDeclarationVariables(variable, typeSyntax, diagnostics));
}
else
{
local = new DeconstructionVariable(BindDeconstructionDeclarationVariables(variable, typeSyntax, diagnostics));
local = new DeconstructionVariable(BindDeconstructionDeclarationVariable(variable, typeSyntax, diagnostics));
}

localsBuilder.Add(local);
Expand All @@ -534,7 +535,7 @@ private ArrayBuilder<DeconstructionVariable> BindDeconstructionDeclarationVariab
/// </summary>
private BoundExpression BindDeconstructionDeclarationVariable(VariableDeclarationSyntax node, TypeSyntax closestTypeSyntax, DiagnosticBag diagnostics)
{
Debug.Assert(node.Deconstruction == null);
Debug.Assert(!node.IsDeconstructionDeclaration);
Debug.Assert(node.Variables.Count == 1);

var declarator = node.Variables[0];
Expand Down
8 changes: 4 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -550,13 +550,13 @@ internal BoundStatement BindLocalDeclarationStatement(LocalDeclarationStatementS
Debug.Assert(binder != null);

BoundStatement bound;
if (node.Declaration.Deconstruction == null)
if (node.Declaration.IsDeconstructionDeclaration)
{
bound = binder.BindDeclarationStatementParts(node, diagnostics);
bound = binder.BindDeconstructionDeclaration(node, node.Declaration, diagnostics);
}
else
{
bound = binder.BindDeconstructionDeclarationStatementParts(node, diagnostics);
bound = binder.BindDeclarationStatementParts(node, diagnostics);
}

return binder.WrapWithVariablesIfAny(node, bound);
Expand Down Expand Up @@ -2993,7 +2993,7 @@ internal virtual BoundDoStatement BindDoParts(DiagnosticBag diagnostics, Binder
return this.Next.BindDoParts(diagnostics, originalBinder);
}

private BoundForStatement BindFor(ForStatementSyntax node, DiagnosticBag diagnostics)
internal BoundForStatement BindFor(ForStatementSyntax node, DiagnosticBag diagnostics)
{
var loopBinder = this.GetBinder(node);
Debug.Assert(loopBinder != null);
Expand Down
38 changes: 26 additions & 12 deletions src/Compilers/CSharp/Portable/Binder/ForLoopBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,28 @@ override protected ImmutableArray<LocalSymbol> BuildLocals()
var declaration = _syntax.Declaration;
if (declaration != null)
{
var refKind = _syntax.RefKeyword.Kind().GetRefKind();

foreach (var variable in declaration.Variables)
if (!declaration.IsDeconstructionDeclaration)
{
var localSymbol = MakeLocal(refKind,
declaration,
variable,
LocalDeclarationKind.ForInitializerVariable);
locals.Add(localSymbol);
var refKind = _syntax.RefKeyword.Kind().GetRefKind();

if (variable.Initializer != null)
foreach (var variable in declaration.Variables)
{
PatternVariableFinder.FindPatternVariables(this, locals, variable.Initializer.Value);
var localSymbol = MakeLocal(refKind,
declaration,
variable,
LocalDeclarationKind.ForInitializerVariable);
locals.Add(localSymbol);

if (variable.Initializer != null)
{
PatternVariableFinder.FindPatternVariables(this, locals, variable.Initializer.Value);
}
}
}
else
{
CollectLocalsFromDeconstruction(declaration, declaration.Type, LocalDeclarationKind.ForInitializerVariable, locals);
}
}
else
{
Expand Down Expand Up @@ -70,8 +77,15 @@ private BoundForStatement BindForParts(ForStatementSyntax node, Binder originalB
if (node.Declaration != null)
{
Debug.Assert(node.Initializers.Count == 0);
ImmutableArray<BoundLocalDeclaration> unused;
initializer = originalBinder.BindForOrUsingOrFixedDeclarations(node.Declaration, LocalDeclarationKind.ForInitializerVariable, diagnostics, out unused);
if (node.Declaration.IsDeconstructionDeclaration)
{
initializer = originalBinder.BindDeconstructionDeclaration(node.Declaration, node.Declaration, diagnostics);
}
else
{
ImmutableArray<BoundLocalDeclaration> unused;
initializer = originalBinder.BindForOrUsingOrFixedDeclarations(node.Declaration, LocalDeclarationKind.ForInitializerVariable, diagnostics, out unused);
}
}
else
{
Expand Down
13 changes: 7 additions & 6 deletions src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ protected ImmutableArray<LocalSymbol> BuildLocals(SyntaxList<StatementSyntax> st
locals = ArrayBuilder<LocalSymbol>.GetInstance();
}

if (decl.Declaration.Deconstruction == null)
if (!decl.Declaration.IsDeconstructionDeclaration)
{
RefKind refKind = decl.RefKeyword.Kind().GetRefKind();
LocalDeclarationKind kind = decl.IsConst ? LocalDeclarationKind.Constant : LocalDeclarationKind.RegularVariable;
Expand All @@ -176,7 +176,7 @@ protected ImmutableArray<LocalSymbol> BuildLocals(SyntaxList<StatementSyntax> st
}
else
{
CollectLocalsFromDeconstruction(decl.Declaration, decl.Declaration.Type, locals);
CollectLocalsFromDeconstruction(decl.Declaration, decl.Declaration.Type, LocalDeclarationKind.RegularVariable, locals);
}
}
break;
Expand All @@ -192,13 +192,13 @@ protected ImmutableArray<LocalSymbol> BuildLocals(SyntaxList<StatementSyntax> st
// When a VariableDeclaration is used in a deconstruction, there are two cases:
// - deconstruction is set, type may be set (for "var"), and no declarators. For instance, `var (x, ...)` or `(int x, ...)`.
// - deconstruction is null, type may be set, and there is one declarator holding the identifier. For instance, `int x` or `x`.
private void CollectLocalsFromDeconstruction(VariableDeclarationSyntax declaration, TypeSyntax closestTypeSyntax, ArrayBuilder<LocalSymbol> locals)
internal void CollectLocalsFromDeconstruction(VariableDeclarationSyntax declaration, TypeSyntax closestTypeSyntax, LocalDeclarationKind kind, ArrayBuilder<LocalSymbol> locals)
{
if (declaration.Deconstruction != null)
if (declaration.IsDeconstructionDeclaration)
{
foreach (var variable in declaration.Deconstruction.Variables)
{
CollectLocalsFromDeconstruction(variable, variable.Type ?? closestTypeSyntax, locals);
CollectLocalsFromDeconstruction(variable, variable.Type ?? closestTypeSyntax, kind, locals);
}
}
else
Expand All @@ -210,7 +210,8 @@ private void CollectLocalsFromDeconstruction(VariableDeclarationSyntax declarati
this.ContainingMemberOrLambda,
this,
closestTypeSyntax,
declarator.Identifier);
declarator.Identifier,
kind);

locals.Add(localSymbol);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -527,9 +527,13 @@ internal virtual IOperation GetOperationWorker(CSharpSyntaxNode node, GetOperati
}
else if (SyntaxFacts.IsDeconstructionType(expression, out parent))
{
Debug.Assert(((VariableDeclarationSyntax)parent).Variables.Count == 1);
var declaration = (VariableDeclarationSyntax)parent;
if (declaration.Variables.Count != 1)
{
return SymbolInfo.None;
}

return TypeFromLocal((VariableDeclarationSyntax)parent, cancellationToken);
return TypeFromLocal(declaration, cancellationToken);
}

return this.GetSymbolInfoWorker(expression, SymbolInfoOptions.DefaultOptions, cancellationToken);
Expand Down
1 change: 1 addition & 0 deletions src/Compilers/CSharp/Portable/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.WithCloseParenToken(Microso
Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.WithElements(Microsoft.CodeAnalysis.SeparatedSyntaxList<Microsoft.CodeAnalysis.CSharp.Syntax.TupleElementSyntax> elements) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax.WithOpenParenToken(Microsoft.CodeAnalysis.SyntaxToken openParenToken) -> Microsoft.CodeAnalysis.CSharp.Syntax.TupleTypeSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax.Deconstruction.get -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax.IsDeconstructionDeclaration.get -> bool
Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax.WithDeconstruction(Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax deconstruction) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax.AddVariables(params Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeclarationSyntax[] items) -> Microsoft.CodeAnalysis.CSharp.Syntax.VariableDeconstructionDeclaratorSyntax
Expand Down
25 changes: 17 additions & 8 deletions src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,18 @@ public static SourceLocalSymbol MakeDeconstructionLocal(
Symbol containingSymbol,
Binder binder,
TypeSyntax closestTypeSyntax,
SyntaxToken identifierToken)
SyntaxToken identifierToken,
LocalDeclarationKind kind)
{
Debug.Assert(closestTypeSyntax != null);

if (closestTypeSyntax.IsVar)
{
return new PossiblyImplicitlyTypedDeconstructionLocalSymbol(containingSymbol, binder, closestTypeSyntax, identifierToken, LocalDeclarationKind.RegularVariable);
return new PossiblyImplicitlyTypedDeconstructionLocalSymbol(containingSymbol, binder, closestTypeSyntax, identifierToken, kind);
}
else
{
return new SourceLocalSymbol(containingSymbol, binder, RefKind.None, closestTypeSyntax, identifierToken, LocalDeclarationKind.RegularVariable);
return new SourceLocalSymbol(containingSymbol, binder, RefKind.None, closestTypeSyntax, identifierToken, kind);
}
}

Expand Down Expand Up @@ -616,7 +617,8 @@ public PossiblyImplicitlyTypedDeconstructionLocalSymbol(
#if DEBUG
SyntaxNode parent;
Debug.Assert(SyntaxFacts.IsDeconstructionIdentifier(identifierToken, out parent));
Debug.Assert(parent.Parent?.Kind() == SyntaxKind.LocalDeclarationStatement);
Debug.Assert(parent.Parent != null);
Debug.Assert(parent.Parent.Kind() == SyntaxKind.LocalDeclarationStatement || parent.Parent.Kind() == SyntaxKind.ForStatement);
#endif
}

Expand All @@ -627,22 +629,29 @@ protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics)
bool isDeconstruction = SyntaxFacts.IsDeconstructionIdentifier(IdentifierToken, out topLevelVariableDeclaration);

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

var statement = topLevelVariableDeclaration.Parent;
switch (statement.Kind())
{
case SyntaxKind.LocalDeclarationStatement:
this.binder.BindLocalDeclarationStatement((LocalDeclarationStatementSyntax)statement, diagnostics);
break;

TypeSymbol result = this._type;
Debug.Assert((object)result != null);
return result;
case SyntaxKind.ForStatement:
var forStatement = (ForStatementSyntax)statement;
var loopBinder = this.binder.GetBinder(forStatement);
loopBinder.BindDeconstructionDeclaration(forStatement.Declaration, forStatement.Declaration, diagnostics);
break;

default:
throw ExceptionUtilities.UnexpectedValue(statement.Kind());
}

TypeSymbol result = this._type;
Debug.Assert((object)result != null);
return result;
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Syntax/SyntaxFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ private static bool IsDeconstruction(VariableDeclarationSyntax declaration, out
}
}

return ((VariableDeclarationSyntax)parent).Deconstruction != null;
return ((VariableDeclarationSyntax)parent).IsDeconstructionDeclaration;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ public VariableDeclarationSyntax Update(TypeSyntax type, SeparatedSyntaxList<Var
{
return Update(type, variables, null);
}

public bool IsDeconstructionDeclaration => Deconstruction != null;
}
}
Loading

0 comments on commit 1c53b7e

Please sign in to comment.