internal BoundExpression BindQuery(QueryExpressionSyntax node, DiagnosticBag diagnostics) { var fromClause = node.FromClause; var boundFromExpression = BindLeftOfPotentialColorColorMemberAccess(fromClause.Expression, diagnostics); QueryTranslationState state = new QueryTranslationState(); state.fromExpression = MakeMemberAccessValue(boundFromExpression, diagnostics); var x = state.rangeVariable = state.AddRangeVariable(this, fromClause.Identifier, diagnostics); for (int i = node.Body.Clauses.Count - 1; i >= 0; i--) { state.clauses.Push(node.Body.Clauses[i]); } state.selectOrGroup = node.Body.SelectOrGroup; // A from clause that explicitly specifies a range variable type // from T x in e // is translated into // from x in ( e ) . Cast < T > ( ) BoundExpression cast = null; if (fromClause.Type != null) { var typeRestriction = BindTypeArgument(fromClause.Type, diagnostics); cast = MakeQueryInvocation(fromClause, state.fromExpression, "Cast", fromClause.Type, typeRestriction, diagnostics); state.fromExpression = cast; } state.fromExpression = MakeQueryClause(fromClause, state.fromExpression, x, castInvocation: cast); BoundExpression result = BindQueryInternal1(state, diagnostics); for (QueryContinuationSyntax continuation = node.Body.Continuation; continuation != null; continuation = continuation.Body.Continuation) { // A query expression with a continuation // from ... into x ... // is translated into // from x in ( from ... ) ... state.Clear(); state.fromExpression = result; x = state.rangeVariable = state.AddRangeVariable(this, continuation.Identifier, diagnostics); Debug.Assert(state.clauses.IsEmpty()); var clauses = continuation.Body.Clauses; for (int i = clauses.Count - 1; i >= 0; i--) { state.clauses.Push(clauses[i]); } state.selectOrGroup = continuation.Body.SelectOrGroup; result = BindQueryInternal1(state, diagnostics); result = MakeQueryClause(continuation.Body, result, x); result = MakeQueryClause(continuation, result, x); } state.Free(); return(MakeQueryClause(node, result)); }
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 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); } }
internal BoundExpression BindQuery(QueryExpressionSyntax node, DiagnosticBag diagnostics) { var fromClause = node.FromClause; var boundFromExpression = BindLeftOfPotentialColorColorMemberAccess(fromClause.Expression, diagnostics); // 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 (boundFromExpression.HasDynamicType()) { diagnostics.Add(ErrorCode.ERR_BadDynamicQuery, fromClause.Expression.Location); boundFromExpression = BadExpression(fromClause.Expression, boundFromExpression); } QueryTranslationState state = new QueryTranslationState(); state.fromExpression = MakeMemberAccessValue(boundFromExpression, diagnostics); var x = state.rangeVariable = state.AddRangeVariable(this, fromClause.Identifier, diagnostics); for (int i = node.Body.Clauses.Count - 1; i >= 0; i--) { state.clauses.Push(node.Body.Clauses[i]); } state.selectOrGroup = node.Body.SelectOrGroup; // A from clause that explicitly specifies a range variable type // from T x in e // is translated into // from x in ( e ) . Cast < T > ( ) BoundExpression cast = null; if (fromClause.Type != null) { var typeRestriction = BindTypeArgument(fromClause.Type, diagnostics); cast = MakeQueryInvocation(fromClause, state.fromExpression, "Cast", fromClause.Type, typeRestriction, diagnostics); state.fromExpression = cast; } state.fromExpression = MakeQueryClause(fromClause, state.fromExpression, x, castInvocation: cast); BoundExpression result = BindQueryInternal1(state, diagnostics); for (QueryContinuationSyntax continuation = node.Body.Continuation; continuation != null; continuation = continuation.Body.Continuation) { // A query expression with a continuation // from ... into x ... // is translated into // from x in ( from ... ) ... state.Clear(); state.fromExpression = result; x = state.rangeVariable = state.AddRangeVariable(this, continuation.Identifier, diagnostics); Debug.Assert(state.clauses.IsEmpty()); var clauses = continuation.Body.Clauses; for (int i = clauses.Count - 1; i >= 0; i--) { state.clauses.Push(clauses[i]); } state.selectOrGroup = continuation.Body.SelectOrGroup; result = BindQueryInternal1(state, diagnostics); result = MakeQueryClause(continuation.Body, result, x); result = MakeQueryClause(continuation, result, x); } state.Free(); return(MakeQueryClause(node, result)); }
void ReduceQuery(QueryTranslationState state, DiagnosticBag diagnostics) { var topClause = state.clauses.Pop(); if (topClause.Kind == SyntaxKind.WhereClause) { // A query expression with a where clause // from x in e // where f // … // is translated into // from x in ( e ) . Where ( x => f ) var where = topClause as WhereClauseSyntax; var lambda = MakeQueryUnboundLambda(state.QueryVariableMap(), state.queryVariable, where.Condition); var invocation = MakeInvocation(where, state.fromExpression, "Where", lambda, diagnostics); state.fromExpression = new BoundQueryClause( syntax: where, syntaxTree: SyntaxTree, value: invocation, definedSymbol: null, queryMethod: invocation.Method, castMethod: null, type: invocation.Type); } else if (topClause.Kind == SyntaxKind.JoinClause && state.clauses.IsEmpty() && state.selectOrGroup.Kind == SyntaxKind.SelectClause) { var join = topClause as JoinClauseSyntax; var select = state.selectOrGroup as SelectClauseSyntax; var joinArgs = ArrayBuilder <BoundExpression> .GetInstance(); var e2 = BindValue(join.InExpression, BindValueKind.RValue, diagnostics); MethodSymbol castMethod = null; if (join.TypeOpt != 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 = BindType(join.TypeOpt, diagnostics); var invocation = MakeInvocation(join, e2, "Cast", join.TypeOpt, castType, diagnostics); castMethod = invocation.Method; e2 = invocation; } joinArgs.Add(e2); var lambda1 = MakeQueryUnboundLambda(state.QueryVariableMap(), state.queryVariable, join.LeftExpression); joinArgs.Add(lambda1); var x2 = state.AddQueryVariable(this, join.Identifier); var lambda2 = MakeQueryUnboundLambda(state.QueryVariableMap(x2), x2, join.RightExpression); // TODO: ensure no other query variables in scope but x2. joinArgs.Add(lambda2); BoundExpression result; if (join.IntoOpt == 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 lambda3 = MakeQueryUnboundLambda(state.QueryVariableMap(), Args(state.queryVariable, x2), select.Expression); // TODO: lambda3's body should be surrounded by a BoundQueryClause for the select clause. joinArgs.Add(lambda3); var invocation = MakeInvocation(join, state.fromExpression, "Join", joinArgs.ToReadOnlyAndFree(), diagnostics); result = new BoundQueryClause( syntax: join, syntaxTree: SyntaxTree, value: invocation, definedSymbol: x2, queryMethod: invocation.Method, castMethod: castMethod, type: invocation.Type); } 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 ) var g = state.AddQueryVariable(this, join.IntoOpt.Identifier); // binder.queryVariable = g; // TODO: where to record the info symbol? var lambda3 = MakeQueryUnboundLambda(state.QueryVariableMap(), Args(state.queryVariable, g), select.Expression); // TODO: lambda3's body should be surrounded by a BoundQueryClause for the select clause. joinArgs.Add(lambda3); var invocation = MakeInvocation(join, state.fromExpression, "GroupJoin", joinArgs.ToReadOnlyAndFree(), diagnostics); var newArguments = Args(invocation.Arguments[0], invocation.Arguments[1], invocation.Arguments[2], new BoundQueryClause( // record the into clause's symbol in the bound tree syntax: join.IntoOpt, syntaxTree: SyntaxTree, value: invocation.Arguments[3], definedSymbol: g, queryMethod: null, castMethod: null, type: invocation.Arguments[3].Type)); invocation = invocation.Update( receiverOpt: invocation.ReceiverOpt, method: invocation.Method, arguments: newArguments); result = new BoundQueryClause( syntax: join, syntaxTree: SyntaxTree, value: invocation, definedSymbol: x2, queryMethod: invocation.Method, castMethod: castMethod, type: invocation.Type); } state.Clear(); // this completes the whole query state.fromExpression = result; } else if (topClause.Kind == SyntaxKind.OrderByClause) { // 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. var orderby = topClause as OrderByClauseSyntax; bool first = true; Symbol lastMethod = null; foreach (var ordering in orderby.Orderings) { string methodName = (first ? "OrderBy" : "ThenBy") + (ordering.Kind == SyntaxKind.DescendingOrdering ? "Descending" : ""); var lambda = MakeQueryUnboundLambda(state.QueryVariableMap(), state.queryVariable, ordering.Expression); var invocation = MakeInvocation(ordering, state.fromExpression, methodName, lambda, diagnostics); lastMethod = invocation.Method; state.fromExpression = new BoundQueryClause( syntax: ordering, syntaxTree: SyntaxTree, value: invocation, definedSymbol: null, queryMethod: invocation.Method, castMethod: null, type: invocation.Type); first = false; } state.fromExpression = new BoundQueryClause( syntax: orderby, syntaxTree: SyntaxTree, value: state.fromExpression, definedSymbol: null, queryMethod: lastMethod, castMethod: null, type: state.fromExpression.Type); } else if (topClause.Kind == SyntaxKind.FromClause && 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 fromClause = topClause as FromClauseSyntax; var select = state.selectOrGroup as SelectClauseSyntax; var x1 = state.queryVariable; TypeSymbol castType = fromClause.TypeOpt == null ? null : BindType(fromClause.TypeOpt, diagnostics); BoundExpression lambda1 = MakeQueryUnboundLambda(state.QueryVariableMap(), x1, fromClause.Expression, fromClause.TypeOpt, castType); // TODO: wrap the bound version of lambda1 in a BoundQueryClause for the from clause defining x2. var x2 = state.AddQueryVariable(this, fromClause.Identifier); BoundExpression lambda2 = MakeQueryUnboundLambda(state.QueryVariableMap(), Args(x1, x2), select.Expression); var result = MakeInvocation(fromClause, state.fromExpression, "SelectMany", Args(lambda1, lambda2), diagnostics); // TODO: extract the Cast<T>() operation from the bound version of lambda1 (this invocation's first argument) and store it in e2Binder.castMethod state.Clear(); state.fromExpression = new BoundQueryClause( syntax: select, syntaxTree: SyntaxTree, value: result, definedSymbol: null, queryMethod: result.Method, castMethod: null, type: result.Type); } else if (topClause.Kind == SyntaxKind.LetClause) { var let = topClause as LetClauseSyntax; var x = state.queryVariable; // 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 } ) // … // 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. We place // this mapping into the state and then, subsequently, into the binder // for any further clauses. LambdaBodyResolver resolver = (LambdaSymbol lambdaSymbol, ExecutableCodeBinder lambdaBodyBinder, DiagnosticBag d) => { var xExpression = new BoundParameter(let, lambdaBodyBinder.SyntaxTree, lambdaSymbol.Parameters[0]); var yExpression = lambdaBodyBinder.BindValue(let.Expression, BindValueKind.RValue, d); var construction = MakePair(let, xExpression, yExpression, d); return(lambdaBodyBinder.WrapExpressionLambdaBody(construction, let, d)); }; var lambda = MakeQueryUnboundLambda(state.QueryVariableMap(), x, let.Expression, resolver); state.queryVariable = state.TransparentQueryVariable(this); var invocation = MakeInvocation(let, state.fromExpression, "Select", lambda, diagnostics); state.AddTransparentIdentifier("Item1"); var y = state.AddQueryVariable(this, let.Identifier); state.allQueryVariables[y].Add("Item2"); state.fromExpression = new BoundQueryClause( syntax: let, syntaxTree: SyntaxTree, value: invocation, definedSymbol: y, queryMethod: invocation.Method, castMethod: null, type: invocation.Type); } else { diagnostics.Add(ErrorCode.ERR_NotYetImplementedInRoslyn, Location(topClause), "query expression"); var result = state.fromExpression; // short circuit any remaining reductions state.Clear(); state.fromExpression = result; } }
internal BoundExpression BindQueryInternal1(QueryExpressionSyntax node, DiagnosticBag diagnostics) { var fromClause = (FromClauseSyntax)node.Clauses[0]; QueryTranslationState state = new QueryTranslationState(); state.fromSyntax = fromClause; state.fromExpression = BindValue(fromClause.Expression, BindValueKind.RValue, diagnostics); state.queryVariable = state.AddQueryVariable(this, fromClause.Identifier); for (int i = node.Clauses.Count - 1; i > 0; i--) { state.clauses.Push(node.Clauses[i]); } state.selectOrGroup = node.SelectOrGroup; // A from clause that explicitly specifies a range variable type // from T x in e // is translated into // from x in ( e ) . Cast < T > ( ) MethodSymbol castMethod = null; if (fromClause.TypeOpt != null) { var typeRestriction = BindType(fromClause.TypeOpt, diagnostics); var cast = MakeInvocation(state.fromSyntax, state.fromExpression, "Cast", fromClause.TypeOpt, typeRestriction, diagnostics); castMethod = cast.Method; state.fromExpression = cast; } state.fromExpression = new BoundQueryClause( syntax: fromClause, syntaxTree: this.SyntaxTree, value: state.fromExpression, definedSymbol: state.queryVariable, queryMethod: null, castMethod: castMethod, type: state.fromExpression.Type); // If the query is a degenerate one the form "from x in e select x", but in source, // then we go ahead and generate the select anyway. We do this by skipping BindQueryInternal2, // whose job it is to (reduce away the whole query and) optimize away degenerate queries. BoundExpression result = (fromClause.TypeOpt == null && IsDegenerateQuery(state)) ? FinalTranslation(state, diagnostics) : BindQueryInternal2(state, diagnostics); QueryContinuationSyntax continuation = node.ContinuationOpt; while (continuation != null) { // A query expression with a continuation // from … into x … // is translated into // from x in ( from … ) … state.Clear(); state.fromExpression = result; var x = state.AddQueryVariable(this, continuation.Identifier); state.fromExpression = new BoundQueryClause( syntax: continuation, syntaxTree: SyntaxTree, value: state.fromExpression, definedSymbol: x, queryMethod: null, castMethod: null, type: state.fromExpression.Type); state.queryVariable = x; Debug.Assert(state.clauses.IsEmpty()); var clauses = continuation.Query.Clauses; for (int i = clauses.Count - 1; i >= 0; i--) { state.clauses.Push(clauses[i]); } state.selectOrGroup = continuation.Query.SelectOrGroup; result = BindQueryInternal2(state, diagnostics); continuation = continuation.Query.ContinuationOpt; } state.Free(); 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); } }
internal BoundExpression BindQuery(QueryExpressionSyntax node, DiagnosticBag diagnostics) { var fromClause = node.FromClause; var boundFromExpression = BindLeftOfPotentialColorColorMemberAccess(fromClause.Expression, diagnostics); // 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 (boundFromExpression.HasDynamicType()) { diagnostics.Add(ErrorCode.ERR_BadDynamicQuery, fromClause.Expression.Location); boundFromExpression = BadExpression(fromClause.Expression, boundFromExpression); } QueryTranslationState state = new QueryTranslationState(); state.fromExpression = MakeMemberAccessValue(boundFromExpression, diagnostics); var x = state.rangeVariable = state.AddRangeVariable(this, fromClause.Identifier, diagnostics); for (int i = node.Body.Clauses.Count - 1; i >= 0; i--) { state.clauses.Push(node.Body.Clauses[i]); } state.selectOrGroup = node.Body.SelectOrGroup; // A from clause that explicitly specifies a range variable type // from T x in e // is translated into // from x in ( e ) . Cast < T > ( ) BoundExpression cast = null; if (fromClause.Type != null) { var typeRestriction = BindTypeArgument(fromClause.Type, diagnostics); cast = MakeQueryInvocation(fromClause, state.fromExpression, "Cast", fromClause.Type, typeRestriction, diagnostics); state.fromExpression = cast; } state.fromExpression = MakeQueryClause(fromClause, state.fromExpression, x, castInvocation: cast); BoundExpression result = BindQueryInternal1(state, diagnostics); for (QueryContinuationSyntax continuation = node.Body.Continuation; continuation != null; continuation = continuation.Body.Continuation) { // A query expression with a continuation // from ... into x ... // is translated into // from x in ( from ... ) ... state.Clear(); state.fromExpression = result; x = state.rangeVariable = state.AddRangeVariable(this, continuation.Identifier, diagnostics); Debug.Assert(state.clauses.IsEmpty()); var clauses = continuation.Body.Clauses; for (int i = clauses.Count - 1; i >= 0; i--) { state.clauses.Push(clauses[i]); } state.selectOrGroup = continuation.Body.SelectOrGroup; result = BindQueryInternal1(state, diagnostics); result = MakeQueryClause(continuation.Body, result, x); result = MakeQueryClause(continuation, result, x); } state.Free(); return MakeQueryClause(node, result); }