Skip to content

Commit

Permalink
Bind the entire enclosing expression for expression variable inference.
Browse files Browse the repository at this point in the history
  • Loading branch information
gafter committed Aug 26, 2016
1 parent 113ec2a commit e4b4af9
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 19 deletions.
11 changes: 4 additions & 7 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -968,13 +968,10 @@ private ImmutableArray<BoundExpression> BindDeclaratorArguments(VariableDeclarat

if (declarator.ArgumentList != null)
{
var builder = ArrayBuilder<BoundExpression>.GetInstance();
foreach (var argument in declarator.ArgumentList.Arguments)
{
var boundArgument = BindValue(argument.Expression, diagnostics, BindValueKind.RValue);
builder.Add(boundArgument);
}
arguments = builder.ToImmutableAndFree();
AnalyzedArguments analyzedArguments = AnalyzedArguments.GetInstance();
BindArgumentsAndNames(declarator.ArgumentList, diagnostics, analyzedArguments);
arguments = BuildArgumentsForErrorRecovery(analyzedArguments);
analyzedArguments.Free();
}

return arguments;
Expand Down
48 changes: 48 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/ExpressionVariableFinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,36 @@ internal static void FindExpressionVariables(
finder._enclosingBinder = enclosingBinderOpt ?? scopeBinder;
finder._localsBuilder = builder;

#if DEBUG
// These are all of the kinds of nodes we should need to handle in this class.
// If you add to this list, make sure you handle that node kind with a visitor.
switch (node.Kind())
{
case SyntaxKind.EqualsValueClause:
case SyntaxKind.ArrowExpressionClause:
case SyntaxKind.SwitchSection:
case SyntaxKind.Attribute:
case SyntaxKind.ThrowStatement:
case SyntaxKind.ReturnStatement:
case SyntaxKind.YieldReturnStatement:
case SyntaxKind.ExpressionStatement:
case SyntaxKind.WhileStatement:
case SyntaxKind.DoStatement:
case SyntaxKind.LockStatement:
case SyntaxKind.IfStatement:
case SyntaxKind.SwitchStatement:
case SyntaxKind.DeconstructionDeclarationStatement:
case SyntaxKind.VariableDeclarator:
break;
case SyntaxKind.ArgumentList:
Debug.Assert(node.Parent is ConstructorInitializerSyntax);
break;
default:
Debug.Assert(node is ExpressionSyntax);
break;
}
#endif

finder.VisitNodeToBind(node);

finder._scopeBinder = null;
Expand All @@ -41,6 +71,19 @@ internal static void FindExpressionVariables(
s_poolInstance.Free(finder);
}

public override void VisitVariableDeclarator(VariableDeclaratorSyntax node)
{
if (node.ArgumentList != null)
{
foreach (var arg in node.ArgumentList.Arguments)
{
Visit(arg.Expression);
}
}

VisitNodeToBind(node.Initializer);
}

private void VisitNodeToBind(CSharpSyntaxNode node)
{
var previousNodeToBind = _nodeToBind;
Expand Down Expand Up @@ -163,6 +206,11 @@ public override void VisitSwitchStatement(SwitchStatementSyntax node)
VisitNodeToBind(node.Expression);
}

public override void VisitDeconstructionDeclarationStatement(DeconstructionDeclarationStatementSyntax node)
{
VisitNodeToBind(node.Assignment.Value);
}

public override void VisitDeclarationPattern(DeclarationPatternSyntax node)
{
_localsBuilder.Add(SourceLocalSymbol.MakeLocalSymbolWithEnclosingContext(
Expand Down
8 changes: 3 additions & 5 deletions src/Compilers/CSharp/Portable/Binder/LocalScopeBinder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,12 +189,10 @@ protected ImmutableArray<LocalSymbol> BuildLocals(SyntaxList<StatementSyntax> st
var localSymbol = MakeLocal(decl.Declaration, vdecl, kind, enclosingBinder);
locals.Add(localSymbol);

var value = vdecl.Initializer?.Value;
if (value != null)
{
ExpressionVariableFinder.FindExpressionVariables(this, locals, value, enclosingBinder);
}
// also gather expression-declared variables from the bracketed argument lists and the initializers
ExpressionVariableFinder.FindExpressionVariables(this, locals, vdecl, enclosingBinder);
}

}
break;

Expand Down
46 changes: 43 additions & 3 deletions src/Compilers/CSharp/Portable/Symbols/Source/SourceLocalSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,15 @@ internal Binder TypeSyntaxBinder
get { return _scopeBinder; } // Scope binder should be good enough for this.
}

// When the variable's type has not yet been inferred,
// don't let the debugger force inference.
internal new string GetDebuggerDisplay()
{
return ((object)_type != null)
? base.GetDebuggerDisplay()
: $"{this.Kind} <var> ${this.Name}";
}

public static SourceLocalSymbol MakeForeachLocal(
MethodSymbol containingMethod,
ForEachLoopBinder binder,
Expand Down Expand Up @@ -126,7 +135,6 @@ internal static LocalSymbol MakeLocalSymbolWithEnclosingContext(
SyntaxNode nodeToBind,
SyntaxNode forbiddenZone)
{
Debug.Assert(nodeToBind.Kind() != SyntaxKind.BracketedArgumentList);
return typeSyntax.IsVar
? new LocalSymbolWithEnclosingContext(containingSymbol, scopeBinder, nodeBinder, typeSyntax, identifierToken, kind, nodeToBind, forbiddenZone)
: new SourceLocalSymbol(containingSymbol, scopeBinder, false, typeSyntax, identifierToken, kind);
Expand Down Expand Up @@ -238,12 +246,21 @@ internal override SyntaxToken IdentifierToken
}
}

#if DEBUG
// We use this to detect infinite recursion in type inference.
private int concurrentTypeResolutions = 0;
#endif

public override TypeSymbol Type
{
get
{
if ((object)_type == null)
{
#if DEBUG
concurrentTypeResolutions++;
Debug.Assert(concurrentTypeResolutions < 50);
#endif
TypeSymbol localType = GetTypeSymbol();
SetType(localType);
}
Expand Down Expand Up @@ -684,6 +701,7 @@ public LocalSymbolWithEnclosingContext(
Debug.Assert(
nodeToBind.Kind() == SyntaxKind.CasePatternSwitchLabel ||
nodeToBind.Kind() == SyntaxKind.ArgumentList && nodeToBind.Parent is ConstructorInitializerSyntax ||
nodeToBind.Kind() == SyntaxKind.VariableDeclarator ||
nodeToBind is ExpressionSyntax);
this._nodeBinder = nodeBinder;
this._nodeToBind = nodeToBind;
Expand All @@ -703,10 +721,32 @@ protected override TypeSymbol InferTypeOfVarVariable(DiagnosticBag diagnostics)
{
case SyntaxKind.ArgumentList:
var invocation = (ConstructorInitializerSyntax)_nodeToBind.Parent;
ScopeBinder.BindConstructorInitializer(invocation.ArgumentList, (MethodSymbol)ContainingSymbol, diagnostics);
_nodeBinder.BindConstructorInitializer(invocation.ArgumentList, (MethodSymbol)ContainingSymbol, diagnostics);
break;
case SyntaxKind.CasePatternSwitchLabel:
ScopeBinder.BindPatternSwitchLabelForInference((CasePatternSwitchLabelSyntax)_nodeToBind, diagnostics);
_nodeBinder.BindPatternSwitchLabelForInference((CasePatternSwitchLabelSyntax)_nodeToBind, diagnostics);
break;
case SyntaxKind.VariableDeclarator:
// This occurs, for example, in
// int x, y[out var Z, 1 is int I];
// for (int x, y[out var Z, 1 is int I]; ;) {}
var declarator = (VariableDeclaratorSyntax)_nodeToBind;
if (declarator.ArgumentList != null)
{
foreach (var arg in declarator.ArgumentList.Arguments)
{
var expression = arg.Expression;
if (expression.Kind() == SyntaxKind.DeclarationExpression)
{
this.SetType(_nodeBinder.CreateErrorType("var"));
// no error is necessary, as they would be dropped on the floor by the caller
}
else
{
_nodeBinder.BindExpression(expression, diagnostics);
}
}
}
break;
default:
_nodeBinder.BindExpression((ExpressionSyntax)_nodeToBind, diagnostics);
Expand Down
49 changes: 45 additions & 4 deletions src/Compilers/CSharp/Test/Semantic/Semantics/OutVarTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2488,9 +2488,9 @@ class C
";
var compilation = CreateCompilationWithMscorlib45(source, options: TestOptions.DebugExe, parseOptions: TestOptions.Regular);
compilation.VerifyDiagnostics(
// (15,27): error CS0103: The name 'x' does not exist in the current context
// Console.WriteLine(x);
Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(15, 27)
// (15,27): error CS0103: The name 'x' does not exist in the current context
// Console.WriteLine(x);
Diagnostic(ErrorCode.ERR_NameNotInContext, "x").WithArguments("x").WithLocation(15, 27)
);
}

Expand Down Expand Up @@ -15068,10 +15068,21 @@ public static void Main()
int[out var x1] a = null; // fatal syntax error - 'out' is skipped
int b(out var x2) = null; // parsed as a local function with syntax error
int c[out var x3] = null; // fatal syntax error - 'out' is skipped

int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
x4 = 0;
}
}";
var compilation = CreateCompilationWithMscorlib(text);
Assert.Empty(compilation.SyntaxTrees[0].GetRoot().DescendantNodesAndSelf().OfType<DeclarationExpressionSyntax>());
var tree = compilation.SyntaxTrees.Single();
var model = compilation.GetSemanticModel(tree);
Assert.Equal(1, compilation.SyntaxTrees[0].GetRoot().DescendantNodesAndSelf().OfType<DeclarationExpressionSyntax>().Count());

var x4Decl = GetOutVarDeclaration(tree, "x4");
var x4Ref = GetReference(tree, "x4");
Assert.True(compilation.GetSemanticModel(tree).GetTypeInfo(x4Ref).Type.TypeKind == TypeKind.Error);
VerifyModelForOutVarWithoutDataFlow(model, x4Decl, x4Ref);

compilation.VerifyDiagnostics(
// (6,13): error CS1003: Syntax error, ',' expected
// int[out var x1] a = null; // fatal syntax error - 'out' is skipped
Expand Down Expand Up @@ -15106,6 +15117,30 @@ public static void Main()
// (8,23): error CS0270: Array size cannot be specified in a variable declaration (try initializing with a 'new' expression)
// int c[out var x3] = null; // fatal syntax error - 'out' is skipped
Diagnostic(ErrorCode.ERR_ArraySizeInDeclaration, "x3").WithLocation(8, 23),
// (10,17): error CS1528: Expected ; or = (cannot specify constructor arguments in declaration)
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_BadVarDecl, "(out var x4").WithLocation(10, 17),
// (10,17): error CS1003: Syntax error, '[' expected
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_SyntaxError, "(").WithArguments("[", "(").WithLocation(10, 17),
// (10,18): error CS1525: Invalid expression term 'out'
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "out").WithArguments("out").WithLocation(10, 18),
// (10,18): error CS1026: ) expected
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_CloseParenExpected, "out").WithLocation(10, 18),
// (10,18): error CS1003: Syntax error, ',' expected
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_SyntaxError, "out").WithArguments(",", "out").WithLocation(10, 18),
// (10,28): error CS1003: Syntax error, ']' expected
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_SyntaxError, ")").WithArguments("]", ")").WithLocation(10, 28),
// (10,28): error CS1002: ; expected
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_SemicolonExpected, ")").WithLocation(10, 28),
// (10,28): error CS1513: } expected
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.ERR_RbraceExpected, ")").WithLocation(10, 28),
// (7,13): error CS0501: 'b(out var)' must declare a body because it is not marked abstract, extern, or partial
// int b(out var x2) = null; // parsed as a local function with syntax error
Diagnostic(ErrorCode.ERR_ConcreteMissingBody, "b").WithArguments("b(out var)").WithLocation(7, 13),
Expand All @@ -15127,6 +15162,12 @@ public static void Main()
// (6,25): warning CS0219: The variable 'a' is assigned but its value is never used
// int[out var x1] a = null; // fatal syntax error - 'out' is skipped
Diagnostic(ErrorCode.WRN_UnreferencedVarAssg, "a").WithArguments("a").WithLocation(6, 25),
// (10,13): warning CS0168: The variable 'd' is declared but never used
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.WRN_UnreferencedVar, "d").WithArguments("d").WithLocation(10, 13),
// (10,16): warning CS0168: The variable 'e' is declared but never used
// int d, e(out var x4); // parsed as a broken bracketed argument list on the declarator
Diagnostic(ErrorCode.WRN_UnreferencedVar, "e").WithArguments("e").WithLocation(10, 16),
// (7,13): warning CS0168: The variable 'b' is declared but never used
// int b(out var x2) = null; // parsed as a local function with syntax error
Diagnostic(ErrorCode.WRN_UnreferencedVar, "b").WithArguments("b").WithLocation(7, 13)
Expand Down

0 comments on commit e4b4af9

Please sign in to comment.