void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, DiagnosticBag diagnostics) { var x1 = state.rangeVariable; TypeSymbol castType = from.Type == null ? null : BindTypeArgument(from.Type, diagnostics); BoundExpression lambda1 = MakeQueryUnboundLambda(state.RangeVariableMap(), x1, from.Expression, from.Type, castType); var x2 = state.AddRangeVariable(this, from.Identifier, diagnostics); if (state.clauses.IsEmpty() && state.selectOrGroup.Kind == SyntaxKind.SelectClause) { // A query expression with a second from clause followed by a select clause // from x1 in e1 // from x2 in e2 // select v // is translated into // ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v ) var select = state.selectOrGroup as SelectClauseSyntax; BoundExpression lambda2 = MakeQueryUnboundLambda(state.RangeVariableMap(), Args(x1, x2), select.Expression); var invocation = MakeQueryInvocation(from, state.fromExpression, "SelectMany", Args(lambda1, lambda2), diagnostics); BoundExpression castInvocation = (object)castType != null?ExtractCastInvocation(invocation) : null; var arguments = invocation.Arguments.ToArray(); // Adjust the second-to-last parameter to be a query clause. (if it was an extension method, an extra parameter was added) arguments[arguments.Length - 2] = MakeQueryClause(from, arguments[arguments.Length - 2], x2, invocation, castInvocation); invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, Args(arguments)); state.Clear(); state.fromExpression = MakeQueryClause(from, invocation, definedSymbol: x2, queryInvocation: invocation); state.fromExpression = MakeQueryClause(select, state.fromExpression); } else { // A query expression with a second from clause followed by something other than a select clause: // from x1 in e1 // from x2 in e2 // ... // is translated into // from * in ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 } ) // ... // We use a slightly different translation strategy. We produce // from * in ( e ) . SelectMany ( x1 => e2, ( x1 , x2 ) => new Pair<X1,X2>(x1, x2) ) // Where X1 is the type of x1, and X2 is the type of x2. // Subsequently, x1 (or members of x1, if it is a transparent identifier) // are accessed as TRID.Item1 (or members of that), and x2 is accessed // as TRID.Item2, where TRID is the compiler-generated identifier used // to represent the transparent identifier in the result. var lambda2 = MakePairLambda(from, state, x1, x2); var invocation = MakeQueryInvocation(from, state.fromExpression, "SelectMany", Args(lambda1, lambda2), diagnostics); BoundExpression castInvocation = (object)castType != null?ExtractCastInvocation(invocation) : null; state.fromExpression = MakeQueryClause(from, invocation, x2, invocation, castInvocation); } }
private UnboundLambda MakePairLambda(CSharpSyntaxNode node, QueryTranslationState state, RangeVariableSymbol x1, RangeVariableSymbol x2) { Debug.Assert(LambdaUtilities.IsQueryPairLambda(node)); LambdaBodyFactory bodyFactory = (LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag d) => { var x1Expression = new BoundParameter(node, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; var x2Expression = new BoundParameter(node, lambdaSymbol.Parameters[1]) { WasCompilerGenerated = true }; var construction = MakePair(node, x1.Name, x1Expression, x2.Name, x2Expression, state, d); return(lambdaBodyBinder.CreateBlockFromExpression(node, ImmutableArray <LocalSymbol> .Empty, RefKind.None, construction, null, d)); }; var result = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), node, bodyFactory); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x1.Name); var x2m = state.allRangeVariables[x2]; x2m[x2m.Count - 1] = x2.Name; return(result); }
private void ReduceOrderBy(OrderByClauseSyntax orderby, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with an orderby clause // from x in e // orderby k1 , k2 , ... , kn // ... // is translated into // from x in ( e ) . // OrderBy ( x => k1 ) . // ThenBy ( x => k2 ) . // ... . // ThenBy ( x => kn ) // ... // If an ordering clause specifies a descending direction indicator, // an invocation of OrderByDescending or ThenByDescending is produced instead. bool first = true; foreach (var ordering in orderby.Orderings) { string methodName = (first ? "OrderBy" : "ThenBy") + (ordering.IsKind(SyntaxKind.DescendingOrdering) ? "Descending" : ""); var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, ordering.Expression); var invocation = MakeQueryInvocation(ordering, state.fromExpression, methodName, lambda, diagnostics); state.fromExpression = MakeQueryClause(ordering, invocation, queryInvocation: invocation); first = false; } state.fromExpression = MakeQueryClause(orderby, state.fromExpression); }
private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with a let clause // from x in e // let y = f // ... // is translated into // from * in ( e ) . Select ( x => new { x , y = f } ) // ... var x = state.rangeVariable; // We use a slightly different translation strategy. We produce // from * in ( e ) . Select ( x => new Pair<X,Y>(x, f) ) // Where X is the type of x, and Y is the type of the expression f. // Subsequently, x (or members of x, if it is a transparent identifier) // are accessed as TRID.Item1 (or members of that), and y is accessed // as TRID.Item2, where TRID is the compiler-generated identifier used // to represent the transparent identifier in the result. LambdaBodyFactory bodyFactory = (LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag d) => { var xExpression = new BoundParameter(let, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; lambdaBodyBinder = lambdaBodyBinder.GetBinder(let.Expression); Debug.Assert(lambdaBodyBinder != null); var yExpression = lambdaBodyBinder.BindValue(let.Expression, d, BindValueKind.RValue); SourceLocation errorLocation = new SourceLocation(let.SyntaxTree, new TextSpan(let.Identifier.SpanStart, let.Expression.Span.End - let.Identifier.SpanStart)); if (!yExpression.HasAnyErrors && !yExpression.HasExpressionType()) { Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Display); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(yExpression), CreateErrorType()); } else if (!yExpression.HasAnyErrors && yExpression.Type.SpecialType == SpecialType.System_Void) { Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Type); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(yExpression), yExpression.Type); } var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d); // 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.CreateLambdaBlockForQueryClause(let.Expression, construction, d)); }; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x), let.Expression, bodyFactory); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x.Name); var y = state.AddRangeVariable(this, let.Identifier, diagnostics); state.allRangeVariables[y].Add(let.Identifier.ValueText); var invocation = MakeQueryInvocation(let, state.fromExpression, "Select", lambda, diagnostics); state.fromExpression = MakeQueryClause(let, invocation, y, invocation); }
private void ReduceWhere(WhereClauseSyntax where, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with a where clause // from x in e // where f // ... // is translated into // from x in ( e ) . Where ( x => f ) var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, where.Condition); var invocation = MakeQueryInvocation(where, state.fromExpression, "Where", lambda, diagnostics); state.fromExpression = MakeQueryClause(where, invocation, queryInvocation: invocation); }
UnboundLambda MakePairLambda(CSharpSyntaxNode node, QueryTranslationState state, RangeVariableSymbol x1, RangeVariableSymbol x2) { LambdaBodyResolver resolver = (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag d) => { var x1Expression = new BoundParameter(node, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; var x2Expression = new BoundParameter(node, lambdaSymbol.Parameters[1]) { WasCompilerGenerated = true }; var construction = MakePair(node, x1.Name, x1Expression, x2.Name, x2Expression, state, d); return(lambdaBodyBinder.CreateBlockFromExpression(ImmutableArray <LocalSymbol> .Empty, construction, node, d)); }; var result = MakeQueryUnboundLambda(state.RangeVariableMap(), Args(x1, x2), node, resolver); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x1.Name); var x2m = state.allRangeVariables[x2]; x2m[x2m.Count - 1] = x2.Name; return(result); }
private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with a let clause // from x in e // let y = f // ... // is translated into // from * in ( e ) . Select ( x => new { x , y = f } ) // ... var x = state.rangeVariable; // We use a slightly different translation strategy. We produce // from * in ( e ) . Select ( x => new Pair<X,Y>(x, f) ) // Where X is the type of x, and Y is the type of the expression f. // Subsequently, x (or members of x, if it is a transparent identifier) // are accessed as TRID.Item1 (or members of that), and y is accessed // as TRID.Item2, where TRID is the compiler-generated identifier used // to represent the transparent identifier in the result. LambdaBodyFactory bodyFactory = (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag d) => { var xExpression = new BoundParameter(let, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; var yExpression = lambdaBodyBinder.BindValue(let.Expression, d, BindValueKind.RValue); SourceLocation errorLocation = new SourceLocation(let.SyntaxTree, new TextSpan(let.Identifier.SpanStart, let.Expression.Span.End - let.Identifier.SpanStart)); if (!yExpression.HasAnyErrors && !yExpression.HasExpressionType()) { MessageID id = MessageID.IDS_NULL; if (yExpression.Kind == BoundKind.UnboundLambda) { id = ((UnboundLambda)yExpression).MessageID; } else if (yExpression.Kind == BoundKind.MethodGroup) { id = MessageID.IDS_MethodGroup; } else { Debug.Assert(yExpression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); } Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, id.Localize()); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(yExpression), CreateErrorType()); } else if (!yExpression.HasAnyErrors && yExpression.Type.SpecialType == SpecialType.System_Void) { Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Type); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(yExpression), yExpression.Type); } var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d); return lambdaBodyBinder.CreateBlockFromExpression(let, lambdaBodyBinder.Locals, null, construction, d); }; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x), let.Expression, bodyFactory); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x.Name); var y = state.AddRangeVariable(this, let.Identifier, diagnostics); state.allRangeVariables[y].Add(let.Identifier.ValueText); var invocation = MakeQueryInvocation(let, state.fromExpression, "Select", lambda, diagnostics); state.fromExpression = MakeQueryClause(let, invocation, y, invocation); }
private void ReduceWhere(WhereClauseSyntax where, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with a where clause // from x in e // where f // ... // is translated into // from x in ( e ) . Where ( x => f ) var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, where.Condition); var invocation = MakeQueryInvocation(where, state.fromExpression, "Where", lambda, diagnostics); state.fromExpression = MakeQueryClause(where, invocation, queryInvocation: invocation); }
private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, DiagnosticBag diagnostics) { var inExpression = BindValue(join.InExpression, diagnostics, BindValueKind.RValue); // If the from expression is of the type dynamic we can't infer the types for any lambdas that occur in the query. // Only if there are none we could bind the query but we report an error regardless since such queries are not useful. if (inExpression.HasDynamicType()) { diagnostics.Add(ErrorCode.ERR_BadDynamicQuery, join.InExpression.Location); inExpression = BadExpression(join.InExpression, inExpression); } BoundExpression castInvocation = null; if (join.Type != null) { // A join clause that explicitly specifies a range variable type // join T x in e on k1 equals k2 // is translated into // join x in ( e ) . Cast < T > ( ) on k1 equals k2 var castType = BindTypeArgument(join.Type, diagnostics); castInvocation = MakeQueryInvocation(join, inExpression, "Cast", join.Type, castType, diagnostics); inExpression = castInvocation; } var outerKeySelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, join.LeftExpression); var x1 = state.rangeVariable; var x2 = state.AddRangeVariable(this, join.Identifier, diagnostics); var innerKeySelectorLambda = MakeQueryUnboundLambda(QueryTranslationState.RangeVariableMap(x2), x2, join.RightExpression); if (state.clauses.IsEmpty() && state.selectOrGroup.Kind() == SyntaxKind.SelectClause) { var select = state.selectOrGroup as SelectClauseSyntax; BoundCall invocation; if (join.Into == null) { // A query expression with a join clause without an into followed by a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 // select v // is translated into // ( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v ) var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), select.Expression); invocation = MakeQueryInvocation( join, state.fromExpression, "Join", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); } else { // A query expression with a join clause with an into followed by a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 into g // select v // is translated into // ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v ) state.allRangeVariables[x2].Free(); state.allRangeVariables.Remove(x2); var g = state.AddRangeVariable(this, join.Into.Identifier, diagnostics); var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, g), select.Expression); invocation = MakeQueryInvocation( join, state.fromExpression, "GroupJoin", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); // record the into clause in the bound tree var arguments = invocation.Arguments; arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments); } state.Clear(); // this completes the whole query state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation); state.fromExpression = MakeQueryClause(select, state.fromExpression); } else { BoundCall invocation; if (join.Into == null) { // A query expression with a join clause without an into followed by something other than a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 // ... // is translated into // from * in ( e1 ) . Join( // e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 }) // ... var resultSelectorLambda = MakePairLambda(join, state, x1, x2); invocation = MakeQueryInvocation( join, state.fromExpression, "Join", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); } else { // A query expression with a join clause with an into followed by something other than a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 into g // ... // is translated into // from * in ( e1 ) . GroupJoin( // e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g }) // ... state.allRangeVariables[x2].Free(); state.allRangeVariables.Remove(x2); var g = state.AddRangeVariable(this, join.Into.Identifier, diagnostics); var resultSelectorLambda = MakePairLambda(join, state, x1, g); invocation = MakeQueryInvocation( join, state.fromExpression, "GroupJoin", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); var arguments = invocation.Arguments; arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments); } state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation); } }
private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, DiagnosticBag diagnostics) { var inExpression = BindValue(join.InExpression, diagnostics, BindValueKind.RValue); // If the from expression is of the type dynamic we can't infer the types for any lambdas that occur in the query. // Only if there are none we could bind the query but we report an error regardless since such queries are not useful. if (inExpression.HasDynamicType()) { diagnostics.Add(ErrorCode.ERR_BadDynamicQuery, join.InExpression.Location); inExpression = BadExpression(join.InExpression, inExpression); } BoundExpression castInvocation = null; if (join.Type != null) { // A join clause that explicitly specifies a range variable type // join T x in e on k1 equals k2 // is translated into // join x in ( e ) . Cast < T > ( ) on k1 equals k2 var castType = BindTypeArgument(join.Type, diagnostics); castInvocation = MakeQueryInvocation(join, inExpression, "Cast", join.Type, castType, diagnostics); inExpression = castInvocation; } var outerKeySelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, join.LeftExpression); var x1 = state.rangeVariable; var x2 = state.AddRangeVariable(this, join.Identifier, diagnostics); var innerKeySelectorLambda = MakeQueryUnboundLambda(QueryTranslationState.RangeVariableMap(x2), x2, join.RightExpression); if (state.clauses.IsEmpty() && state.selectOrGroup.Kind() == SyntaxKind.SelectClause) { var select = state.selectOrGroup as SelectClauseSyntax; BoundCall invocation; if (join.Into == null) { // A query expression with a join clause without an into followed by a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 // select v // is translated into // ( e1 ) . Join( e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => v ) var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), select.Expression); invocation = MakeQueryInvocation( join, state.fromExpression, "Join", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); } else { // A query expression with a join clause with an into followed by a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 into g // select v // is translated into // ( e1 ) . GroupJoin( e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => v ) state.allRangeVariables[x2].Free(); state.allRangeVariables.Remove(x2); var g = state.AddRangeVariable(this, join.Into.Identifier, diagnostics); var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, g), select.Expression); invocation = MakeQueryInvocation( join, state.fromExpression, "GroupJoin", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); // record the into clause in the bound tree var arguments = invocation.Arguments; arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments); } state.Clear(); // this completes the whole query state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation); state.fromExpression = MakeQueryClause(select, state.fromExpression); } else { BoundCall invocation; if (join.Into == null) { // A query expression with a join clause without an into followed by something other than a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 // ... // is translated into // from * in ( e1 ) . Join( // e2 , x1 => k1 , x2 => k2 , ( x1 , x2 ) => new { x1 , x2 }) // ... var resultSelectorLambda = MakePairLambda(join, state, x1, x2); invocation = MakeQueryInvocation( join, state.fromExpression, "Join", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); } else { // A query expression with a join clause with an into followed by something other than a select clause // from x1 in e1 // join x2 in e2 on k1 equals k2 into g // ... // is translated into // from * in ( e1 ) . GroupJoin( // e2 , x1 => k1 , x2 => k2 , ( x1 , g ) => new { x1 , g }) // ... state.allRangeVariables[x2].Free(); state.allRangeVariables.Remove(x2); var g = state.AddRangeVariable(this, join.Into.Identifier, diagnostics); var resultSelectorLambda = MakePairLambda(join, state, x1, g); invocation = MakeQueryInvocation( join, state.fromExpression, "GroupJoin", ImmutableArray.Create(inExpression, outerKeySelectorLambda, innerKeySelectorLambda, resultSelectorLambda), diagnostics); var arguments = invocation.Arguments; arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments); } state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation); } }
private BoundExpression FinalTranslation(QueryTranslationState state, DiagnosticBag diagnostics) { Debug.Assert(state.clauses.IsEmpty()); switch (state.selectOrGroup.Kind()) { case SyntaxKind.SelectClause: { // A query expression of the form // from x in e select v // is translated into // ( e ) . Select ( x => v ) var selectClause = (SelectClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = selectClause.Expression; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); var result = MakeQueryInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics); return(MakeQueryClause(selectClause, result, queryInvocation: result)); } case SyntaxKind.GroupClause: { // A query expression of the form // from x in e group v by k // is translated into // ( e ) . GroupBy ( x => k , x => v ) // except when v is the identifier x, the translation is // ( e ) . GroupBy ( x => k ) var groupClause = (GroupClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = groupClause.GroupExpression; var k = groupClause.ByExpression; var vId = v as IdentifierNameSyntax; BoundCall result; var lambdaLeft = MakeQueryUnboundLambda(state.RangeVariableMap(), x, k); // this is the unoptimized form (when v is not the identifier x) var d = DiagnosticBag.GetInstance(); BoundExpression lambdaRight = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d); // k and v appear reversed in the invocation, so we reorder their evaluation result = ReverseLastTwoParameterOrder(result); BoundExpression unoptimizedForm = null; if (vId != null && vId.Identifier.ValueText == x.Name) { // The optimized form. We store the unoptimized form for analysis unoptimizedForm = result; result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", lambdaLeft, diagnostics); if (unoptimizedForm.HasAnyErrors && !result.HasAnyErrors) { unoptimizedForm = null; } } else { diagnostics.AddRange(d); } d.Free(); return(MakeQueryClause(groupClause, result, queryInvocation: result, unoptimizedForm: unoptimizedForm)); } default: { // there should have been a syntax error if we get here. return(new BoundBadExpression( state.selectOrGroup, LookupResultKind.OverloadResolutionFailure, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(state.fromExpression), state.fromExpression.Type)); } } }
void ReduceLet(LetClauseSyntax let, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with a let clause // from x in e // let y = f // ... // is translated into // from * in ( e ) . Select ( x => new { x , y = f } ) // ... var x = state.rangeVariable; // We use a slightly different translation strategy. We produce // from * in ( e ) . Select ( x => new Pair<X,Y>(x, f) ) // Where X is the type of x, and Y is the type of the expression f. // Subsequently, x (or members of x, if it is a transparent identifier) // are accessed as TRID.Item1 (or members of that), and y is accessed // as TRID.Item2, where TRID is the compiler-generated identifier used // to represent the transparent identifier in the result. LambdaBodyResolver resolver = (LambdaSymbol lambdaSymbol, ref Binder lambdaBodyBinder, DiagnosticBag d) => { var xExpression = new BoundParameter(let, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; lambdaBodyBinder = new ScopedExpressionBinder(lambdaBodyBinder, let.Expression); var yExpression = lambdaBodyBinder.BindValue(let.Expression, d, BindValueKind.RValue); SourceLocation errorLocation = new SourceLocation(let.SyntaxTree, new TextSpan(let.Identifier.SpanStart, let.Expression.Span.End - let.Identifier.SpanStart)); if (!yExpression.HasAnyErrors && !yExpression.HasExpressionType()) { MessageID id = MessageID.IDS_NULL; if (yExpression.Kind == BoundKind.UnboundLambda) { id = ((UnboundLambda)yExpression).MessageID; } else if (yExpression.Kind == BoundKind.MethodGroup) { id = MessageID.IDS_MethodGroup; } else { Debug.Assert(yExpression.IsLiteralNull(), "How did we successfully bind an expression without a type?"); } Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, id.Localize()); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(yExpression), CreateErrorType()); } else if (!yExpression.HasAnyErrors && yExpression.Type.SpecialType == SpecialType.System_Void) { Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Type); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(yExpression), yExpression.Type); } var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d); return(lambdaBodyBinder.CreateBlockFromExpression(lambdaBodyBinder.Locals, construction, let, d)); }; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, let.Expression, resolver); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x.Name); var y = state.AddRangeVariable(this, let.Identifier, diagnostics); state.allRangeVariables[y].Add(let.Identifier.ValueText); var invocation = MakeQueryInvocation(let, state.fromExpression, "Select", lambda, diagnostics); state.fromExpression = MakeQueryClause(let, invocation, y, invocation); }
private BoundExpression FinalTranslation(QueryTranslationState state, DiagnosticBag diagnostics) { Debug.Assert(state.clauses.IsEmpty()); switch (state.selectOrGroup.Kind()) { case SyntaxKind.SelectClause: { // A query expression of the form // from x in e select v // is translated into // ( e ) . Select ( x => v ) var selectClause = (SelectClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = selectClause.Expression; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); var result = MakeQueryInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics); return MakeQueryClause(selectClause, result, queryInvocation: result); } case SyntaxKind.GroupClause: { // A query expression of the form // from x in e group v by k // is translated into // ( e ) . GroupBy ( x => k , x => v ) // except when v is the identifier x, the translation is // ( e ) . GroupBy ( x => k ) var groupClause = (GroupClauseSyntax)state.selectOrGroup; var x = state.rangeVariable; var e = state.fromExpression; var v = groupClause.GroupExpression; var k = groupClause.ByExpression; var vId = v as IdentifierNameSyntax; BoundCall result; var lambdaLeft = MakeQueryUnboundLambda(state.RangeVariableMap(), x, k); // this is the unoptimized form (when v is not the identifier x) var d = DiagnosticBag.GetInstance(); BoundExpression lambdaRight = MakeQueryUnboundLambda(state.RangeVariableMap(), x, v); result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", ImmutableArray.Create(lambdaLeft, lambdaRight), d); // k and v appear reversed in the invocation, so we reorder their evaluation result = ReverseLastTwoParameterOrder(result); BoundExpression unoptimizedForm = null; if (vId != null && vId.Identifier.ValueText == x.Name) { // The optimized form. We store the unoptimized form for analysis unoptimizedForm = result; result = MakeQueryInvocation(state.selectOrGroup, e, "GroupBy", lambdaLeft, diagnostics); if (unoptimizedForm.HasAnyErrors && !result.HasAnyErrors) unoptimizedForm = null; } else { diagnostics.AddRange(d); } d.Free(); return MakeQueryClause(groupClause, result, queryInvocation: result, unoptimizedForm: unoptimizedForm); } default: { // there should have been a syntax error if we get here. return new BoundBadExpression( state.selectOrGroup, LookupResultKind.OverloadResolutionFailure, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(state.fromExpression), state.fromExpression.Type); } } }
private void ReduceLet(LetClauseSyntax let, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with a let clause // from x in e // let y = f // ... // is translated into // from * in ( e ) . Select ( x => new { x , y = f } ) // ... var x = state.rangeVariable; // We use a slightly different translation strategy. We produce // from * in ( e ) . Select ( x => new Pair<X,Y>(x, f) ) // Where X is the type of x, and Y is the type of the expression f. // Subsequently, x (or members of x, if it is a transparent identifier) // are accessed as TRID.Item1 (or members of that), and y is accessed // as TRID.Item2, where TRID is the compiler-generated identifier used // to represent the transparent identifier in the result. LambdaBodyFactory bodyFactory = (LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag d) => { var xExpression = new BoundParameter(let, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; lambdaBodyBinder = lambdaBodyBinder.GetBinder(let.Expression); Debug.Assert(lambdaBodyBinder != null); var yExpression = lambdaBodyBinder.BindValue(let.Expression, d, BindValueKind.RValue); SourceLocation errorLocation = new SourceLocation(let.SyntaxTree, new TextSpan(let.Identifier.SpanStart, let.Expression.Span.End - let.Identifier.SpanStart)); if (!yExpression.HasAnyErrors && !yExpression.HasExpressionType()) { Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Display); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(yExpression), CreateErrorType()); } else if (!yExpression.HasAnyErrors && yExpression.Type.SpecialType == SpecialType.System_Void) { Error(d, ErrorCode.ERR_QueryRangeVariableAssignedBadValue, errorLocation, yExpression.Type); yExpression = new BoundBadExpression(yExpression.Syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(yExpression), yExpression.Type); } var construction = MakePair(let, x.Name, xExpression, let.Identifier.ValueText, yExpression, state, d); // 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); }; var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x), let.Expression, bodyFactory); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x.Name); var y = state.AddRangeVariable(this, let.Identifier, diagnostics); state.allRangeVariables[y].Add(let.Identifier.ValueText); var invocation = MakeQueryInvocation(let, state.fromExpression, "Select", lambda, diagnostics); state.fromExpression = MakeQueryClause(let, invocation, y, invocation); }
private UnboundLambda MakePairLambda(CSharpSyntaxNode node, QueryTranslationState state, RangeVariableSymbol x1, RangeVariableSymbol x2) { Debug.Assert(LambdaUtilities.IsQueryPairLambda(node)); LambdaBodyFactory bodyFactory = (LambdaSymbol lambdaSymbol, Binder lambdaBodyBinder, DiagnosticBag d) => { var x1Expression = new BoundParameter(node, lambdaSymbol.Parameters[0]) { WasCompilerGenerated = true }; var x2Expression = new BoundParameter(node, lambdaSymbol.Parameters[1]) { WasCompilerGenerated = true }; var construction = MakePair(node, x1.Name, x1Expression, x2.Name, x2Expression, state, d); return lambdaBodyBinder.CreateBlockFromExpression(node, ImmutableArray<LocalSymbol>.Empty, RefKind.None, construction, null, d); }; var result = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), node, bodyFactory); state.rangeVariable = state.TransparentRangeVariable(this); state.AddTransparentIdentifier(x1.Name); var x2m = state.allRangeVariables[x2]; x2m[x2m.Count - 1] = x2.Name; return result; }
private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, DiagnosticBag diagnostics) { var x1 = state.rangeVariable; BoundExpression collectionSelectorLambda; if (from.Type == null) { collectionSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), x1, from.Expression); } else { collectionSelectorLambda = MakeQueryUnboundLambdaWithCast(state.RangeVariableMap(), x1, from.Expression, from.Type, BindTypeArgument(from.Type, diagnostics)); } var x2 = state.AddRangeVariable(this, from.Identifier, diagnostics); if (state.clauses.IsEmpty() && state.selectOrGroup.IsKind(SyntaxKind.SelectClause)) { var select = (SelectClauseSyntax)state.selectOrGroup; // A query expression with a second from clause followed by a select clause // from x1 in e1 // from x2 in e2 // select v // is translated into // ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => v ) var resultSelectorLambda = MakeQueryUnboundLambda(state.RangeVariableMap(), ImmutableArray.Create(x1, x2), select.Expression); var invocation = MakeQueryInvocation( from, state.fromExpression, "SelectMany", ImmutableArray.Create(collectionSelectorLambda, resultSelectorLambda), diagnostics); // Adjust the second-to-last parameter to be a query clause (if it was an extension method, an extra parameter was added) BoundExpression castInvocation = (from.Type != null) ? ExtractCastInvocation(invocation) : null; var arguments = invocation.Arguments; invocation = invocation.Update( invocation.ReceiverOpt, invocation.Method, arguments.SetItem(arguments.Length - 2, MakeQueryClause(from, arguments[arguments.Length - 2], x2, invocation, castInvocation))); state.Clear(); state.fromExpression = MakeQueryClause(from, invocation, definedSymbol: x2, queryInvocation: invocation); state.fromExpression = MakeQueryClause(select, state.fromExpression); } else { // A query expression with a second from clause followed by something other than a select clause: // from x1 in e1 // from x2 in e2 // ... // is translated into // from * in ( e1 ) . SelectMany( x1 => e2 , ( x1 , x2 ) => new { x1 , x2 } ) // ... // We use a slightly different translation strategy. We produce // from * in ( e ) . SelectMany ( x1 => e2, ( x1 , x2 ) => new Pair<X1,X2>(x1, x2) ) // Where X1 is the type of x1, and X2 is the type of x2. // Subsequently, x1 (or members of x1, if it is a transparent identifier) // are accessed as TRID.Item1 (or members of that), and x2 is accessed // as TRID.Item2, where TRID is the compiler-generated identifier used // to represent the transparent identifier in the result. var resultSelectorLambda = MakePairLambda(from, state, x1, x2); var invocation = MakeQueryInvocation( from, state.fromExpression, "SelectMany", ImmutableArray.Create(collectionSelectorLambda, resultSelectorLambda), diagnostics); BoundExpression castInvocation = (from.Type != null) ? ExtractCastInvocation(invocation) : null; state.fromExpression = MakeQueryClause(from, invocation, x2, invocation, castInvocation); } }
private void ReduceOrderBy(OrderByClauseSyntax orderby, QueryTranslationState state, DiagnosticBag diagnostics) { // A query expression with an orderby clause // from x in e // orderby k1 , k2 , ... , kn // ... // is translated into // from x in ( e ) . // OrderBy ( x => k1 ) . // ThenBy ( x => k2 ) . // ... . // ThenBy ( x => kn ) // ... // If an ordering clause specifies a descending direction indicator, // an invocation of OrderByDescending or ThenByDescending is produced instead. bool first = true; foreach (var ordering in orderby.Orderings) { string methodName = (first ? "OrderBy" : "ThenBy") + (ordering.IsKind(SyntaxKind.DescendingOrdering) ? "Descending" : ""); var lambda = MakeQueryUnboundLambda(state.RangeVariableMap(), state.rangeVariable, ordering.Expression); var invocation = MakeQueryInvocation(ordering, state.fromExpression, methodName, lambda, diagnostics); state.fromExpression = MakeQueryClause(ordering, invocation, queryInvocation: invocation); first = false; } state.fromExpression = MakeQueryClause(orderby, state.fromExpression); }