예제 #1
0
        private BoundExpression BindQueryInternal2(QueryTranslationState state, BindingDiagnosticBag diagnostics)
        {
            // we continue reducing the query until it is reduced away.
            while (true)
            {
                if (state.clauses.IsEmpty())
                {
                    if (state.selectOrGroup == null)
                    {
                        return(state.fromExpression); // already reduced away
                    }
                    if (IsDegenerateQuery(state))
                    {
                        // A query expression of the form
                        //     from x in e select x
                        // is translated into
                        //     ( e )
                        var result = state.fromExpression;

                        // ignore missing or malformed Select method
                        BoundExpression?unoptimized = FinalTranslation(state, BindingDiagnosticBag.Discarded);

                        if (unoptimized.HasAnyErrors && !result.HasAnyErrors)
                        {
                            unoptimized = null;
                        }
                        return(MakeQueryClause(state.selectOrGroup, result, unoptimizedForm: unoptimized));
                    }

                    return(FinalTranslation(state, diagnostics));
                }

                ReduceQuery(state, diagnostics);
            }
        }
예제 #2
0
        private void ReduceQuery(QueryTranslationState state, DiagnosticBag diagnostics)
        {
            var topClause = state.clauses.Pop();

            switch (topClause.Kind())
            {
            case SyntaxKind.WhereClause:
                ReduceWhere((WhereClauseSyntax)topClause, state, diagnostics);
                break;

            case SyntaxKind.JoinClause:
                ReduceJoin((JoinClauseSyntax)topClause, state, diagnostics);
                break;

            case SyntaxKind.OrderByClause:
                ReduceOrderBy((OrderByClauseSyntax)topClause, state, diagnostics);
                break;

            case SyntaxKind.FromClause:
                ReduceFrom((FromClauseSyntax)topClause, state, diagnostics);
                break;

            case SyntaxKind.LetClause:
                ReduceLet((LetClauseSyntax)topClause, state, diagnostics);
                break;

            default:
                throw ExceptionUtilities.UnexpectedValue(topClause.Kind());
            }
        }
예제 #3
0
        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);
        }
예제 #4
0
        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);
        }
예제 #5
0
 private BoundExpression BindQueryInternal1(QueryTranslationState state, DiagnosticBag diagnostics)
 {
     // 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.
     return(IsDegenerateQuery(state) ? FinalTranslation(state, diagnostics) : BindQueryInternal2(state, diagnostics));
 }
예제 #6
0
 private BoundExpression BindQueryInternal1(QueryTranslationState state, DiagnosticBag diagnostics)
 {
     // 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.
     return IsDegenerateQuery(state) ? FinalTranslation(state, diagnostics) : BindQueryInternal2(state, diagnostics);
 }
예제 #7
0
        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);
        }
예제 #8
0
        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));
        }
예제 #9
0
        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);
            }
        }
예제 #10
0
        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);
        }
예제 #11
0
        private static bool IsDegenerateQuery(QueryTranslationState state)
        {
            if (!state.clauses.IsEmpty())
            {
                return(false);
            }

            // A degenerate query is of the form "from x in e select x".
            var select = state.selectOrGroup as SelectClauseSyntax;

            if (select == null)
            {
                return(false);
            }
            var name = select.Expression as IdentifierNameSyntax;

            return(name != null && state.rangeVariable.Name == name.Identifier.ValueText);
        }
예제 #12
0
            BoundExpression BindQueryInternal2(QueryTranslationState state, DiagnosticBag diagnostics)
            {
                // we continue reducing the query until it is reduced away.
                while (true)
                {
                    if (state.clauses.IsEmpty())
                    {
                        if (IsDegenerateQuery(state))
                        {
                            // A query expression of the form
                            //     from x in e select x
                            // is translated into
                            //     ( e )
                            return(state.fromExpression);
                        }

                        return(FinalTranslation(state, diagnostics));
                    }

                    ReduceQuery(state, diagnostics);
                }
            }
예제 #13
0
            private bool IsDegenerateQuery(QueryTranslationState state)
            {
                if (!state.clauses.IsEmpty())
                {
                    return(false);
                }

                // Some translations complete the whole query, which they flag by setting selectOrGroup to null.
                if (state.selectOrGroup == null)
                {
                    return(true);
                }

                // A degenerate query is of the form "from x in e select x".
                var select = state.selectOrGroup as SelectClauseSyntax;

                if (select == null)
                {
                    return(false);
                }
                var identifier = select.Expression as IdentifierNameSyntax;

                return(identifier != null && state.queryVariable.Name == identifier.Identifier.ValueText);
            }
예제 #14
0
        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);
        }
예제 #15
0
 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);
 }
예제 #16
0
        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);
            }
        }
예제 #17
0
        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));
        }
예제 #18
0
        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);
            }
        }
예제 #19
0
        private BoundExpression MakePair(CSharpSyntaxNode node, string field1Name, BoundExpression field1Value, string field2Name, BoundExpression field2Value, QueryTranslationState state, DiagnosticBag diagnostics)
        {
            if (field1Name == field2Name)
            {
                // we will generate a diagnostic elsewhere
                field2Name = state.TransparentRangeVariableName();
                field2Value = new BoundBadExpression(field2Value.Syntax, LookupResultKind.Empty, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(field2Value), field2Value.Type, true);
            }

            AnonymousTypeDescriptor typeDescriptor = new AnonymousTypeDescriptor(
                                                            ImmutableArray.Create<AnonymousTypeField>(
                                                                new AnonymousTypeField(field1Name, field1Value.Syntax.Location, TypeOrError(field1Value)),
                                                                new AnonymousTypeField(field2Name, field2Value.Syntax.Location, TypeOrError(field2Value))
                                                            ),
                                                            node.Location
                                                     );

            AnonymousTypeManager manager = this.Compilation.AnonymousTypeManager;
            NamedTypeSymbol anonymousType = manager.ConstructAnonymousTypeSymbol(typeDescriptor);
            return MakeConstruction(node, anonymousType, ImmutableArray.Create(field1Value, field2Value), diagnostics);
        }
예제 #20
0
        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));
            }
            }
        }
예제 #21
0
        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);
                    }
            }
        }
예제 #22
0
        private BoundExpression BindQueryInternal2(QueryTranslationState state, DiagnosticBag diagnostics)
        {
            // we continue reducing the query until it is reduced away.
            while (true)
            {
                if (state.clauses.IsEmpty())
                {
                    if (state.selectOrGroup == null)
                    {
                        return state.fromExpression; // already reduced away
                    }
                    if (IsDegenerateQuery(state))
                    {
                        // A query expression of the form
                        //     from x in e select x
                        // is translated into
                        //     ( e )
                        var result = state.fromExpression;

                        // ignore missing or malformed Select method
                        DiagnosticBag discarded = DiagnosticBag.GetInstance();
                        var unoptimized = FinalTranslation(state, discarded);
                        discarded.Free();

                        if (unoptimized.HasAnyErrors && !result.HasAnyErrors) unoptimized = null;
                        return MakeQueryClause(state.selectOrGroup, result, unoptimizedForm: unoptimized);
                    }

                    return FinalTranslation(state, diagnostics);
                }

                ReduceQuery(state, diagnostics);
            }
        }
예제 #23
0
            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;
                }
            }
예제 #24
0
 private void ReduceQuery(QueryTranslationState state, DiagnosticBag diagnostics)
 {
     var topClause = state.clauses.Pop();
     switch (topClause.Kind())
     {
         case SyntaxKind.WhereClause:
             ReduceWhere((WhereClauseSyntax)topClause, state, diagnostics);
             break;
         case SyntaxKind.JoinClause:
             ReduceJoin((JoinClauseSyntax)topClause, state, diagnostics);
             break;
         case SyntaxKind.OrderByClause:
             ReduceOrderBy((OrderByClauseSyntax)topClause, state, diagnostics);
             break;
         case SyntaxKind.FromClause:
             ReduceFrom((FromClauseSyntax)topClause, state, diagnostics);
             break;
         case SyntaxKind.LetClause:
             ReduceLet((LetClauseSyntax)topClause, state, diagnostics);
             break;
         default:
             throw ExceptionUtilities.UnexpectedValue(topClause.Kind());
     }
 }
예제 #25
0
            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);
            }
예제 #26
0
        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);
        }
예제 #27
0
        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;
        }
예제 #28
0
        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);
            }
        }
예제 #29
0
        private static bool IsDegenerateQuery(QueryTranslationState state)
        {
            if (!state.clauses.IsEmpty()) return false;

            // A degenerate query is of the form "from x in e select x".
            var select = state.selectOrGroup as SelectClauseSyntax;
            if (select == null) return false;
            var name = select.Expression as IdentifierNameSyntax;
            return name != null && state.rangeVariable.Name == name.Identifier.ValueText;
        }
예제 #30
0
        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);
        }
예제 #31
0
        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);
        }
예제 #32
0
        private BoundExpression MakePair(CSharpSyntaxNode node, string field1Name, BoundExpression field1Value, string field2Name, BoundExpression field2Value, QueryTranslationState state, DiagnosticBag diagnostics)
        {
            if (field1Name == field2Name)
            {
                // we will generate a diagnostic elsewhere
                field2Name  = state.TransparentRangeVariableName();
                field2Value = new BoundBadExpression(field2Value.Syntax, LookupResultKind.Empty, ImmutableArray <Symbol> .Empty, ImmutableArray.Create(field2Value), field2Value.Type, true);
            }

            AnonymousTypeDescriptor typeDescriptor = new AnonymousTypeDescriptor(
                ImmutableArray.Create <AnonymousTypeField>(
                    new AnonymousTypeField(field1Name, field1Value.Syntax.Location, TypeOrError(field1Value)),
                    new AnonymousTypeField(field2Name, field2Value.Syntax.Location, TypeOrError(field2Value))
                    ),
                node.Location
                );

            AnonymousTypeManager manager       = this.Compilation.AnonymousTypeManager;
            NamedTypeSymbol      anonymousType = manager.ConstructAnonymousTypeSymbol(typeDescriptor);

            return(MakeConstruction(node, anonymousType, ImmutableArray.Create(field1Value, field2Value), diagnostics));
        }
예제 #33
0
        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);
        }
예제 #34
0
            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.queryVariable;
                    var e            = state.fromExpression;
                    var v            = selectClause.Expression;
                    var lambda       = MakeQueryUnboundLambda(state.QueryVariableMap(), x, v);
                    var result       = MakeInvocation(state.selectOrGroup, e, "Select", lambda, diagnostics);
                    return(new BoundQueryClause(
                               syntax: selectClause,
                               syntaxTree: SyntaxTree,
                               value: result,
                               definedSymbol: null,
                               queryMethod: null,
                               castMethod: null,
                               type: result.Type));
                }

                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.queryVariable;
                    var       e           = state.fromExpression;
                    var       v           = groupClause.GroupExpression;
                    var       k           = groupClause.ByExpression;
                    var       vId         = v as IdentifierNameSyntax;
                    BoundCall result;
                    if (vId != null && vId.Identifier.ValueText == x.Name)
                    {
                        var lambda = MakeQueryUnboundLambda(state.QueryVariableMap(), x, k);
                        result = MakeInvocation(state.selectOrGroup, e, "GroupBy", lambda, diagnostics);
                    }
                    else
                    {
                        BoundExpression lambdaLeft  = MakeQueryUnboundLambda(state.QueryVariableMap(), x, k);
                        BoundExpression lambdaRight = MakeQueryUnboundLambda(state.QueryVariableMap(), x, v);
                        result = MakeInvocation(state.selectOrGroup, e, "GroupBy", Args(lambdaLeft, lambdaRight), diagnostics);
                    }
                    return(new BoundQueryClause(
                               syntax: groupClause,
                               syntaxTree: syntaxTree,
                               value: result,
                               definedSymbol: null,
                               queryMethod: result.Method,
                               castMethod: null,
                               type: result.Type));
                }

                default:
                {
                    // there should have been a syntax error if we get here.
                    return(new BoundBadExpression(
                               state.selectOrGroup, SyntaxTree, LookupResultKind.OverloadResolutionFailure, ReadOnlyArray <Symbol> .Empty,
                               Args <BoundNode>(state.fromExpression), state.fromExpression.Type));
                }
                }
            }
예제 #35
0
        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);
        }