Skip to content

Commit

Permalink
Disallow expression variables in query clauses. (#16081)
Browse files Browse the repository at this point in the history
Fixes #15910
Also fixes a sequence point issue with the let expression. Specifically, the source range for the query lambda included more than the expression of the let, but included the entire query clause. It now includes only the expression, like other query clauses.
  • Loading branch information
gafter authored Dec 30, 2016
1 parent 5bb984b commit 8d6e20e
Show file tree
Hide file tree
Showing 10 changed files with 916 additions and 207 deletions.
21 changes: 17 additions & 4 deletions src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, Diagnos
// The bound block represents a closure scope for transparent identifiers captured in the let clause.
// Such closures shall be associated with the lambda body expression.
return lambdaBodyBinder.CreateBlockFromExpression(let.Expression, lambdaBodyBinder.GetDeclaredLocalsForScope(let.Expression), RefKind.None, construction, null, d);
return lambdaBodyBinder.CreateLambdaBlockForQueryClause(let.Expression, construction, d);
};

var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x), let.Expression, bodyFactory);
Expand All @@ -594,6 +594,17 @@ private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, Diagnos
state.fromExpression = MakeQueryClause(let, invocation, y, invocation);
}

private BoundBlock CreateLambdaBlockForQueryClause(ExpressionSyntax expression, BoundExpression result, DiagnosticBag diagnostics)
{
var locals = this.GetDeclaredLocalsForScope(expression);
if (locals.Any())
{
diagnostics.Add(ErrorCode.ERR_ExpressionVariableInQueryClause, locals[0].Locations[0]);
}

return this.CreateBlockFromExpression(expression, locals, RefKind.None, result, expression, diagnostics);
}

private BoundQueryClause MakeQueryClause(
CSharpSyntaxNode syntax,
BoundExpression expression,
Expand Down Expand Up @@ -648,7 +659,10 @@ private UnboundLambda MakeQueryUnboundLambda(RangeVariableMap qvm, ImmutableArra
{
return MakeQueryUnboundLambda(expression, new QueryUnboundLambdaState(this, qvm, parameters, (LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag diagnostics) =>
{
return lambdaBodyBinder.BindLambdaExpressionAsBlock(expression, diagnostics);
lambdaBodyBinder = lambdaBodyBinder.GetBinder(expression);
Debug.Assert(lambdaSymbol != null);
BoundExpression boundExpression = lambdaBodyBinder.BindValue(expression, diagnostics, BindValueKind.RValue);
return lambdaBodyBinder.CreateLambdaBlockForQueryClause(expression, boundExpression, diagnostics);
}));
}

Expand All @@ -658,13 +672,12 @@ private UnboundLambda MakeQueryUnboundLambdaWithCast(RangeVariableMap qvm, Range
{
lambdaBodyBinder = lambdaBodyBinder.GetBinder(expression);
Debug.Assert(lambdaBodyBinder != null);
BoundExpression boundExpression = lambdaBodyBinder.BindValue(expression, diagnostics, BindValueKind.RValue);
// We transform the expression from "expr" to "expr.Cast<castTypeOpt>()".
boundExpression = lambdaBodyBinder.MakeQueryInvocation(expression, boundExpression, "Cast", castTypeSyntax, castType, diagnostics);
return lambdaBodyBinder.CreateBlockFromExpression(expression, lambdaBodyBinder.GetDeclaredLocalsForScope(expression), RefKind.None, boundExpression, expression, diagnostics);
return lambdaBodyBinder.CreateLambdaBlockForQueryClause(expression, boundExpression, diagnostics);
}));
}

Expand Down
11 changes: 10 additions & 1 deletion src/Compilers/CSharp/Portable/CSharpResources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -4969,6 +4969,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="ERR_ExpressionVariableInConstructorOrFieldInitializer" xml:space="preserve">
<value>Out variable and pattern variable declarations are not allowed within constructor initializers, field initializers, or property initializers.</value>
</data>
<data name="ERR_ExpressionVariableInQueryClause" xml:space="preserve">
<value>Out variable and pattern variable declarations are not allowed within a query clause.</value>
</data>
<data name="ERR_SemiOrLBraceOrArrowExpected" xml:space="preserve">
<value>{ or ; or =&gt; expected</value>
</data>
Expand Down
6 changes: 5 additions & 1 deletion src/Compilers/CSharp/Portable/Errors/ErrorCode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1443,8 +1443,12 @@ internal enum ErrorCode
ERR_ImplicitlyTypedOutVariableUsedInTheSameArgumentList = 8196,
ERR_TypeInferenceFailedForImplicitlyTypedOutVariable = 8197,
ERR_ExpressionTreeContainsOutVariable = 8198,
#endregion diagnostics for out var

#region more stragglers for C# 7
ERR_VarInvocationLvalueReserved = 8199,
ERR_ExpressionVariableInConstructorOrFieldInitializer = 8200,
#endregion diagnostics for out var
ERR_ExpressionVariableInQueryClause = 8201,
#endregion more stragglers for C# 7
}
}
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Test/Emit/PDB/PDBLambdaTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ where b > 10
<forward declaringType=""C"" methodName=""M"" />
</customDebugInfo>
<sequencePoints>
<entry offset=""0x0"" startLine=""10"" startColumn=""17"" endLine=""10"" endColumn=""30"" />
<entry offset=""0x0"" startLine=""10"" startColumn=""25"" endLine=""10"" endColumn=""30"" />
</sequencePoints>
</method>
<method containingType=""C+&lt;&gt;c"" name=""&lt;M&gt;b__0_1"" parameterNames=""&lt;&gt;h__TransparentIdentifier0"">
Expand Down
Loading

0 comments on commit 8d6e20e

Please sign in to comment.