2 // linq.cs: support for query expressions
4 // Authors: Marek Safar (marek.safar@gmail.com)
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
8 // Copyright 2007-2008 Novell, Inc
12 using System.Reflection;
13 using System.Collections.Generic;
15 namespace Mono.CSharp.Linq
17 public class QueryExpression : AQueryClause
19 public QueryExpression (AQueryClause start)
20 : base (null, null, Location.Null)
25 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parentParameter)
27 return next.BuildQueryClause (ec, lSide, parentParameter);
30 protected override Expression DoResolve (ResolveContext ec)
32 int counter = QueryBlock.TransparentParameter.Counter;
34 Expression e = BuildQueryClause (ec, null, null);
39 // Reset counter in probing mode to ensure that all transparent
40 // identifier anonymous types are created only once
42 if (ec.IsInProbingMode)
43 QueryBlock.TransparentParameter.Counter = counter;
48 protected override string MethodName {
49 get { throw new NotSupportedException (); }
53 public abstract class AQueryClause : ShimExpression
55 protected class QueryExpressionAccess : MemberAccess
57 public QueryExpressionAccess (Expression expr, string methodName, Location loc)
58 : base (expr, methodName, loc)
62 public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc)
63 : base (expr, methodName, typeArguments, loc)
67 protected override void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
69 ec.Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " +
70 "Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?",
75 protected class QueryExpressionInvocation : Invocation, OverloadResolver.IErrorHandler
77 public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments)
78 : base (expr, arguments)
82 protected override MethodGroupExpr DoResolveOverload (ResolveContext ec)
84 MethodGroupExpr rmg = mg.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.None);
88 #region IErrorHandler Members
90 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
92 ec.Report.SymbolRelatedToPreviousError (best);
93 ec.Report.SymbolRelatedToPreviousError (ambiguous);
94 ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
95 best.Name, mg.InstanceExpression.GetSignatureForError ());
99 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
104 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
109 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
111 var ms = (MethodSpec) best;
112 TypeSpec source_type = ms.Parameters.ExtensionMethodType;
113 if (source_type != null) {
114 Argument a = arguments[0];
116 if (TypeManager.IsGenericType (source_type) && TypeManager.ContainsGenericParameters (source_type)) {
117 TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments);
118 tic.OutputTypeInference (rc, a.Expr, source_type);
119 if (tic.FixAllTypes (rc)) {
120 source_type = source_type.GetDefinition ().MakeGenericType (tic.InferredTypeArguments);
124 if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) {
125 rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
126 best.Name, TypeManager.CSharpName (a.Type));
131 if (best.Name == "SelectMany") {
132 rc.Report.Error (1943, loc,
133 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
134 arguments[0].GetSignatureForError ());
136 rc.Report.Error (1942, loc,
137 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
138 best.Name.ToLowerInvariant (), best.Name);
147 public AQueryClause next;
148 public QueryBlock block;
150 protected AQueryClause (QueryBlock block, Expression expr, Location loc)
157 protected override void CloneTo (CloneContext clonectx, Expression target)
159 base.CloneTo (clonectx, target);
161 AQueryClause t = (AQueryClause) target;
164 t.block = (QueryBlock) clonectx.LookupBlock (block);
167 t.next = (AQueryClause) next.Clone (clonectx);
170 protected override Expression DoResolve (ResolveContext ec)
172 return expr.Resolve (ec);
175 public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
177 Arguments args = null;
178 CreateArguments (ec, parameter, ref args);
179 lSide = CreateQueryExpression (lSide, args);
181 parameter = CreateChildrenParameters (parameter);
183 Select s = next as Select;
184 if (s == null || s.IsRequired (parameter))
185 return next.BuildQueryClause (ec, lSide, parameter);
187 // Skip transparent select clause if any clause follows
188 if (next.next != null)
189 return next.next.BuildQueryClause (ec, lSide, parameter);
195 protected virtual Parameter CreateChildrenParameters (Parameter parameter)
197 // Have to clone the parameter for any children use, it carries block sensitive data
198 return parameter.Clone ();
201 protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
203 args = new Arguments (2);
205 LambdaExpression selector = new LambdaExpression (loc);
207 block.SetParameter (parameter);
208 selector.Block = block;
209 selector.Block.AddStatement (new ContextualReturn (expr));
211 args.Add (new Argument (selector));
214 protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
216 return new QueryExpressionInvocation (
217 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
220 protected abstract string MethodName { get; }
222 public AQueryClause Next {
228 public AQueryClause Tail {
230 return next == null ? this : next.Tail;
236 // A query clause with an identifier (range variable)
238 public abstract class ARangeVariableQueryClause : AQueryClause
240 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
242 public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter)
243 : base (initializer, parameter.Name, parameter.Location)
247 protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
249 ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
254 class RangeParameterReference : ParameterReference
258 public RangeParameterReference (Parameter p)
259 : base (null, p.Location)
264 protected override Expression DoResolve (ResolveContext ec)
266 pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter);
267 return base.DoResolve (ec);
271 protected RangeVariable identifier;
273 protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
274 : base (block, expr, loc)
276 this.identifier = identifier;
279 public FullNamedExpression IdentifierType { get; set; }
281 protected Invocation CreateCastExpression (Expression lSide)
283 return new QueryExpressionInvocation (
284 new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null);
287 protected override Parameter CreateChildrenParameters (Parameter parameter)
289 return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ());
292 protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init)
294 var args = new List<AnonymousTypeParameter> (2);
297 // The first argument is the reference to the parameter
299 args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location));
302 // The second argument is the linq expression
304 args.Add (new RangeAnonymousTypeParameter (init, name));
307 // Create unique anonymous type
309 return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location);
312 protected virtual RangeVariable GetIntoVariable ()
318 public sealed class RangeVariable : INamedBlockVariable
322 public RangeVariable (string name, Location loc)
339 public bool IsDeclared {
345 public Location Location { get; private set; }
347 public string Name { get; private set; }
351 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
353 Expression expr = null;
356 // We know the variable name is somewhere in the scope. This generates
357 // an access expression from current block
359 var pb = rc.CurrentBlock.ParametersBlock;
361 if (pb is QueryBlock) {
362 for (int i = pb.Parameters.Count - 1; i >= 0; --i) {
363 var p = pb.Parameters[i];
365 return pb.GetParameterReference (i, loc);
367 var tp = p as QueryBlock.TransparentParameter;
370 expr = pb.GetParameterReference (i, loc);
372 expr = new TransparentMemberAccess (expr, tp.Name);
374 if (tp.Identifier == Name)
375 return new TransparentMemberAccess (expr, Name);
377 if (tp.Parent.Name == Name)
378 return new TransparentMemberAccess (expr, Name);
380 tp = tp.Parent as QueryBlock.TransparentParameter;
390 pb = pb.Parent.ParametersBlock;
395 class QueryStartClause : ARangeVariableQueryClause
397 public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc)
398 : base (block, identifier, expr, loc)
400 block.AddRangeVariable (identifier);
403 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
406 expr = expr.Resolve (ec);
410 if (expr.Type == InternalType.Dynamic || expr.Type == TypeManager.void_type) {
411 ec.Report.Error (1979, expr.Location,
412 "Query expression with a source or join sequence of type `{0}' is not allowed",
413 TypeManager.CSharpName (expr.Type));
418 if (IdentifierType != null)
419 expr = CreateCastExpression (expr);
421 if (parameter == null)
424 return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location));
427 protected override Expression DoResolve (ResolveContext ec)
429 Expression e = BuildQueryClause (ec, null, null);
430 return e.Resolve (ec);
433 protected override string MethodName {
434 get { throw new NotSupportedException (); }
438 public class GroupBy : AQueryClause
440 Expression element_selector;
441 QueryBlock element_block;
443 public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc)
444 : base (block, keySelector, loc)
447 // Optimizes clauses like `group A by A'
449 if (!elementSelector.Equals (keySelector)) {
450 this.element_selector = elementSelector;
451 this.element_block = elementBlock;
455 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
457 base.CreateArguments (ec, parameter, ref args);
459 if (element_selector != null) {
460 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
462 element_block.SetParameter (parameter.Clone ());
463 lambda.Block = element_block;
464 lambda.Block.AddStatement (new ContextualReturn (element_selector));
465 args.Add (new Argument (lambda));
469 protected override void CloneTo (CloneContext clonectx, Expression target)
471 GroupBy t = (GroupBy) target;
472 if (element_selector != null) {
473 t.element_selector = element_selector.Clone (clonectx);
474 t.element_block = (QueryBlock) element_block.Clone (clonectx);
477 base.CloneTo (clonectx, t);
480 protected override string MethodName {
481 get { return "GroupBy"; }
485 public class Join : SelectMany
487 QueryBlock inner_selector, outer_selector;
489 public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc)
490 : base (block, lt, inner, loc)
492 this.outer_selector = outerSelector;
493 this.inner_selector = innerSelector;
496 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
498 args = new Arguments (4);
500 if (IdentifierType != null)
501 expr = CreateCastExpression (expr);
503 args.Add (new Argument (expr));
505 outer_selector.SetParameter (parameter.Clone ());
506 var lambda = new LambdaExpression (outer_selector.StartLocation);
507 lambda.Block = outer_selector;
508 args.Add (new Argument (lambda));
510 inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location));
511 lambda = new LambdaExpression (inner_selector.StartLocation);
512 lambda.Block = inner_selector;
513 args.Add (new Argument (lambda));
515 base.CreateArguments (ec, parameter, ref args);
518 protected override void CloneTo (CloneContext clonectx, Expression target)
520 Join t = (Join) target;
521 t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx);
522 t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx);
523 base.CloneTo (clonectx, t);
526 protected override string MethodName {
527 get { return "Join"; }
531 public class GroupJoin : Join
533 readonly RangeVariable into;
535 public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner,
536 QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc)
537 : base (block, lt, inner, outerSelector, innerSelector, loc)
542 protected override RangeVariable GetIntoVariable ()
547 protected override string MethodName {
548 get { return "GroupJoin"; }
552 public class Let : ARangeVariableQueryClause
554 public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
555 : base (block, identifier, expr, loc)
559 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
561 expr = CreateRangeVariableType (ec, parameter, identifier, expr);
562 base.CreateArguments (ec, parameter, ref args);
565 protected override string MethodName {
566 get { return "Select"; }
570 public class Select : AQueryClause
572 public Select (QueryBlock block, Expression expr, Location loc)
573 : base (block, expr, loc)
578 // For queries like `from a orderby a select a'
579 // the projection is transparent and select clause can be safely removed
581 public bool IsRequired (Parameter parameter)
583 SimpleName sn = expr as SimpleName;
587 return sn.Name != parameter.Name;
590 protected override string MethodName {
591 get { return "Select"; }
595 public class SelectMany : ARangeVariableQueryClause
597 public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
598 : base (block, identifier, expr, loc)
602 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
605 if (IdentifierType != null)
606 expr = CreateCastExpression (expr);
608 base.CreateArguments (ec, parameter, ref args);
611 Expression result_selector_expr;
612 QueryBlock result_block;
614 var target = GetIntoVariable ();
615 var target_param = new ImplicitLambdaParameter (target.Name, target.Location);
618 // When select follows use it as a result selector
620 if (next is Select) {
621 result_selector_expr = next.Expr;
623 result_block = next.block;
624 result_block.SetParameters (parameter, target_param);
628 result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location));
630 result_block = new QueryBlock (ec.Compiler, block.Parent, block.StartLocation);
631 result_block.SetParameters (parameter, target_param);
634 LambdaExpression result_selector = new LambdaExpression (Location);
635 result_selector.Block = result_block;
636 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
638 args.Add (new Argument (result_selector));
641 protected override string MethodName {
642 get { return "SelectMany"; }
646 public class Where : AQueryClause
648 public Where (QueryBlock block, BooleanExpression expr, Location loc)
649 : base (block, expr, loc)
653 protected override string MethodName {
654 get { return "Where"; }
658 public class OrderByAscending : AQueryClause
660 public OrderByAscending (QueryBlock block, Expression expr)
661 : base (block, expr, expr.Location)
665 protected override string MethodName {
666 get { return "OrderBy"; }
670 public class OrderByDescending : AQueryClause
672 public OrderByDescending (QueryBlock block, Expression expr)
673 : base (block, expr, expr.Location)
677 protected override string MethodName {
678 get { return "OrderByDescending"; }
682 public class ThenByAscending : OrderByAscending
684 public ThenByAscending (QueryBlock block, Expression expr)
689 protected override string MethodName {
690 get { return "ThenBy"; }
694 public class ThenByDescending : OrderByDescending
696 public ThenByDescending (QueryBlock block, Expression expr)
701 protected override string MethodName {
702 get { return "ThenByDescending"; }
707 // Implicit query block
709 public class QueryBlock : ParametersBlock
712 // Transparent parameters are used to package up the intermediate results
713 // and pass them onto next clause
715 public sealed class TransparentParameter : ImplicitLambdaParameter
717 public static int Counter;
718 const string ParameterNamePrefix = "<>__TranspIdent";
720 public readonly Parameter Parent;
721 public readonly string Identifier;
723 public TransparentParameter (Parameter parent, RangeVariable identifier)
724 : base (ParameterNamePrefix + Counter++, identifier.Location)
727 Identifier = identifier.Name;
730 public new static void Reset ()
736 public QueryBlock (CompilerContext ctx, Block parent, Location start)
737 : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start)
739 flags |= Flags.CompilerGenerated;
742 public void AddRangeVariable (RangeVariable variable)
744 variable.Block = this;
745 AddLocalName (variable.Name, variable);
748 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
750 TopBlock.Report.Error (1931, variable.Location,
751 "A range variable `{0}' conflicts with a previous declaration of `{0}'",
755 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
757 TopBlock.Report.Error (1930, variable.Location,
758 "A range variable `{0}' has already been declared in this scope",
762 public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
764 TopBlock.Report.Error (1948, loc,
765 "A range variable `{0}' conflicts with a method type parameter",
769 public void SetParameter (Parameter parameter)
771 base.parameters = new ParametersCompiled (parameter);
772 base.parameter_info = new ParameterInfo[] {
773 new ParameterInfo (this, 0)
777 public void SetParameters (Parameter first, Parameter second)
779 base.parameters = new ParametersCompiled (first, second);
780 base.parameter_info = new ParameterInfo[] {
781 new ParameterInfo (this, 0),
782 new ParameterInfo (this, 1)
787 sealed class TransparentMemberAccess : MemberAccess
789 public TransparentMemberAccess (Expression expr, string name)
794 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
796 rc.Report.Error (1947, loc,
797 "A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value",