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
9 // Copyright 2011 Xamarin Inc
13 using System.Collections.Generic;
15 namespace Mono.CSharp.Linq
17 public class QueryExpression : AQueryClause
19 public QueryExpression (AQueryClause start)
20 : base (null, null, start.Location)
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 public 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 protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
90 ec.Report.Error (1979, loc,
91 "Query expressions with a source or join sequence of type `dynamic' are not allowed");
95 #region IErrorHandler Members
97 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
99 var emg = mg as ExtensionMethodGroupExpr;
100 var type = emg == null ? mg.InstanceExpression : emg.ExtensionExpression;
102 ec.Report.SymbolRelatedToPreviousError (best);
103 ec.Report.SymbolRelatedToPreviousError (ambiguous);
104 ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
105 best.Name, type.Type.GetSignatureForError ());
109 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
114 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
119 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
121 var ms = (MethodSpec) best;
122 TypeSpec source_type = ms.Parameters.ExtensionMethodType;
123 if (source_type != null) {
124 Argument a = arguments[0];
126 if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) {
127 TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments);
128 tic.OutputTypeInference (rc, a.Expr, source_type);
129 if (tic.FixAllTypes (rc)) {
130 source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments);
134 if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) {
135 rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
136 best.Name, a.Type.GetSignatureForError ());
141 if (best.Name == "SelectMany") {
142 rc.Report.Error (1943, loc,
143 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
144 arguments[0].GetSignatureForError ());
146 rc.Report.Error (1942, loc,
147 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
148 best.Name.ToLowerInvariant (), best.Name);
157 public AQueryClause next;
158 public QueryBlock block;
160 protected AQueryClause (QueryBlock block, Expression expr, Location loc)
167 protected override void CloneTo (CloneContext clonectx, Expression target)
169 base.CloneTo (clonectx, target);
171 AQueryClause t = (AQueryClause) target;
174 t.block = (QueryBlock) clonectx.LookupBlock (block);
177 t.next = (AQueryClause) next.Clone (clonectx);
180 protected override Expression DoResolve (ResolveContext ec)
182 return expr.Resolve (ec);
185 public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
187 Arguments args = null;
188 CreateArguments (ec, parameter, ref args);
189 lSide = CreateQueryExpression (lSide, args);
191 parameter = CreateChildrenParameters (parameter);
193 Select s = next as Select;
194 if (s == null || s.IsRequired (parameter))
195 return next.BuildQueryClause (ec, lSide, parameter);
197 // Skip transparent select clause if any clause follows
198 if (next.next != null)
199 return next.next.BuildQueryClause (ec, lSide, parameter);
205 protected virtual Parameter CreateChildrenParameters (Parameter parameter)
207 // Have to clone the parameter for any children use, it carries block sensitive data
208 return parameter.Clone ();
211 protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
213 args = new Arguments (2);
215 LambdaExpression selector = new LambdaExpression (loc);
217 block.SetParameter (parameter);
218 selector.Block = block;
219 selector.Block.AddStatement (new ContextualReturn (expr));
221 args.Add (new Argument (selector));
224 protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
226 return new QueryExpressionInvocation (
227 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
230 protected abstract string MethodName { get; }
232 public AQueryClause Next {
238 public AQueryClause Tail {
240 return next == null ? this : next.Tail;
246 // A query clause with an identifier (range variable)
248 public abstract class ARangeVariableQueryClause : AQueryClause
250 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
252 public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter)
253 : base (initializer, parameter.Name, parameter.Location)
257 protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
259 ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
264 class RangeParameterReference : ParameterReference
268 public RangeParameterReference (Parameter p)
269 : base (null, p.Location)
274 protected override Expression DoResolve (ResolveContext ec)
276 pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter);
277 return base.DoResolve (ec);
281 protected RangeVariable identifier;
283 protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
284 : base (block, expr, loc)
286 this.identifier = identifier;
289 public RangeVariable Identifier {
295 public FullNamedExpression IdentifierType { get; set; }
297 protected Invocation CreateCastExpression (Expression lSide)
299 return new QueryExpressionInvocation (
300 new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null);
303 protected override Parameter CreateChildrenParameters (Parameter parameter)
305 return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ());
308 protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init)
310 var args = new List<AnonymousTypeParameter> (2);
313 // The first argument is the reference to the parameter
315 args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location));
318 // The second argument is the linq expression
320 args.Add (new RangeAnonymousTypeParameter (init, name));
323 // Create unique anonymous type
325 return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location);
328 protected virtual RangeVariable GetIntoVariable ()
334 public sealed class RangeVariable : INamedBlockVariable
338 public RangeVariable (string name, Location loc)
355 public bool IsDeclared {
361 public bool IsParameter {
367 public Location Location { get; private set; }
369 public string Name { get; private set; }
373 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
376 // We know the variable name is somewhere in the scope. This generates
377 // an access expression from current block
379 var pb = rc.CurrentBlock.ParametersBlock;
381 if (pb is QueryBlock) {
382 for (int i = pb.Parameters.Count - 1; i >= 0; --i) {
383 var p = pb.Parameters[i];
385 return pb.GetParameterReference (i, loc);
387 Expression expr = null;
388 var tp = p as QueryBlock.TransparentParameter;
391 expr = pb.GetParameterReference (i, loc);
393 expr = new TransparentMemberAccess (expr, tp.Name);
395 if (tp.Identifier == Name)
396 return new TransparentMemberAccess (expr, Name);
398 if (tp.Parent.Name == Name)
399 return new TransparentMemberAccess (expr, Name);
401 tp = tp.Parent as QueryBlock.TransparentParameter;
409 pb = pb.Parent.ParametersBlock;
414 public class QueryStartClause : ARangeVariableQueryClause
416 public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc)
417 : base (block, identifier, expr, loc)
419 block.AddRangeVariable (identifier);
422 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
424 if (IdentifierType != null)
425 expr = CreateCastExpression (expr);
427 if (parameter == null)
430 return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location));
433 protected override Expression DoResolve (ResolveContext ec)
435 Expression e = BuildQueryClause (ec, null, null);
436 return e.Resolve (ec);
439 protected override string MethodName {
440 get { throw new NotSupportedException (); }
444 public class GroupBy : AQueryClause
446 Expression element_selector;
447 QueryBlock element_block;
449 public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc)
450 : base (block, keySelector, loc)
453 // Optimizes clauses like `group A by A'
455 if (!elementSelector.Equals (keySelector)) {
456 this.element_selector = elementSelector;
457 this.element_block = elementBlock;
461 public Expression SelectorExpression {
463 return element_selector;
467 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
469 base.CreateArguments (ec, parameter, ref args);
471 if (element_selector != null) {
472 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
474 element_block.SetParameter (parameter.Clone ());
475 lambda.Block = element_block;
476 lambda.Block.AddStatement (new ContextualReturn (element_selector));
477 args.Add (new Argument (lambda));
481 protected override void CloneTo (CloneContext clonectx, Expression target)
483 GroupBy t = (GroupBy) target;
484 if (element_selector != null) {
485 t.element_selector = element_selector.Clone (clonectx);
486 t.element_block = (QueryBlock) element_block.Clone (clonectx);
489 base.CloneTo (clonectx, t);
492 protected override string MethodName {
493 get { return "GroupBy"; }
496 public override object Accept (StructuralVisitor visitor)
498 return visitor.Visit (this);
502 public class Join : SelectMany
504 QueryBlock inner_selector, outer_selector;
506 public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc)
507 : base (block, lt, inner, loc)
509 this.outer_selector = outerSelector;
510 this.inner_selector = innerSelector;
513 public QueryBlock InnerSelector {
515 return inner_selector;
519 public QueryBlock OuterSelector {
521 return outer_selector;
525 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
527 args = new Arguments (4);
529 if (IdentifierType != null)
530 expr = CreateCastExpression (expr);
532 args.Add (new Argument (expr));
534 outer_selector.SetParameter (parameter.Clone ());
535 var lambda = new LambdaExpression (outer_selector.StartLocation);
536 lambda.Block = outer_selector;
537 args.Add (new Argument (lambda));
539 inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location));
540 lambda = new LambdaExpression (inner_selector.StartLocation);
541 lambda.Block = inner_selector;
542 args.Add (new Argument (lambda));
544 base.CreateArguments (ec, parameter, ref args);
547 protected override void CloneTo (CloneContext clonectx, Expression target)
549 Join t = (Join) target;
550 t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx);
551 t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx);
552 base.CloneTo (clonectx, t);
555 protected override string MethodName {
556 get { return "Join"; }
559 public override object Accept (StructuralVisitor visitor)
561 return visitor.Visit (this);
565 public class GroupJoin : Join
567 readonly RangeVariable into;
569 public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner,
570 QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc)
571 : base (block, lt, inner, outerSelector, innerSelector, loc)
576 protected override RangeVariable GetIntoVariable ()
581 protected override string MethodName {
582 get { return "GroupJoin"; }
585 public override object Accept (StructuralVisitor visitor)
587 return visitor.Visit (this);
591 public class Let : ARangeVariableQueryClause
593 public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
594 : base (block, identifier, expr, loc)
598 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
600 expr = CreateRangeVariableType (ec, parameter, identifier, expr);
601 base.CreateArguments (ec, parameter, ref args);
604 protected override string MethodName {
605 get { return "Select"; }
608 public override object Accept (StructuralVisitor visitor)
610 return visitor.Visit (this);
614 public class Select : AQueryClause
616 public Select (QueryBlock block, Expression expr, Location loc)
617 : base (block, expr, loc)
622 // For queries like `from a orderby a select a'
623 // the projection is transparent and select clause can be safely removed
625 public bool IsRequired (Parameter parameter)
627 SimpleName sn = expr as SimpleName;
631 return sn.Name != parameter.Name;
634 protected override string MethodName {
635 get { return "Select"; }
638 public override object Accept (StructuralVisitor visitor)
640 return visitor.Visit (this);
645 public class SelectMany : ARangeVariableQueryClause
647 public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
648 : base (block, identifier, expr, loc)
652 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
655 if (IdentifierType != null)
656 expr = CreateCastExpression (expr);
658 base.CreateArguments (ec, parameter.Clone (), ref args);
661 Expression result_selector_expr;
662 QueryBlock result_block;
664 var target = GetIntoVariable ();
665 var target_param = new ImplicitLambdaParameter (target.Name, target.Location);
668 // When select follows use it as a result selector
670 if (next is Select) {
671 result_selector_expr = next.Expr;
673 result_block = next.block;
674 result_block.SetParameters (parameter, target_param);
678 result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location));
680 result_block = new QueryBlock (block.Parent, block.StartLocation);
681 result_block.SetParameters (parameter, target_param);
684 LambdaExpression result_selector = new LambdaExpression (Location);
685 result_selector.Block = result_block;
686 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
688 args.Add (new Argument (result_selector));
691 protected override string MethodName {
692 get { return "SelectMany"; }
695 public override object Accept (StructuralVisitor visitor)
697 return visitor.Visit (this);
701 public class Where : AQueryClause
703 public Where (QueryBlock block, Expression expr, Location loc)
704 : base (block, expr, loc)
708 protected override string MethodName {
709 get { return "Where"; }
712 public override object Accept (StructuralVisitor visitor)
714 return visitor.Visit (this);
718 public class OrderByAscending : AQueryClause
720 public OrderByAscending (QueryBlock block, Expression expr)
721 : base (block, expr, expr.Location)
725 protected override string MethodName {
726 get { return "OrderBy"; }
729 public override object Accept (StructuralVisitor visitor)
731 return visitor.Visit (this);
735 public class OrderByDescending : AQueryClause
737 public OrderByDescending (QueryBlock block, Expression expr)
738 : base (block, expr, expr.Location)
742 protected override string MethodName {
743 get { return "OrderByDescending"; }
746 public override object Accept (StructuralVisitor visitor)
748 return visitor.Visit (this);
752 public class ThenByAscending : OrderByAscending
754 public ThenByAscending (QueryBlock block, Expression expr)
759 protected override string MethodName {
760 get { return "ThenBy"; }
763 public override object Accept (StructuralVisitor visitor)
765 return visitor.Visit (this);
769 public class ThenByDescending : OrderByDescending
771 public ThenByDescending (QueryBlock block, Expression expr)
776 protected override string MethodName {
777 get { return "ThenByDescending"; }
780 public override object Accept (StructuralVisitor visitor)
782 return visitor.Visit (this);
787 // Implicit query block
789 public class QueryBlock : ParametersBlock
792 // Transparent parameters are used to package up the intermediate results
793 // and pass them onto next clause
795 public sealed class TransparentParameter : ImplicitLambdaParameter
797 public static int Counter;
798 const string ParameterNamePrefix = "<>__TranspIdent";
800 public readonly Parameter Parent;
801 public readonly string Identifier;
803 public TransparentParameter (Parameter parent, RangeVariable identifier)
804 : base (ParameterNamePrefix + Counter++, identifier.Location)
807 Identifier = identifier.Name;
810 public static void Reset ()
816 public QueryBlock (Block parent, Location start)
817 : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start, Flags.CompilerGenerated)
821 public void AddRangeVariable (RangeVariable variable)
823 variable.Block = this;
824 TopBlock.AddLocalName (variable.Name, variable, true);
827 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
829 TopBlock.Report.Error (1931, variable.Location,
830 "A range variable `{0}' conflicts with a previous declaration of `{0}'",
834 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
836 TopBlock.Report.Error (1930, variable.Location,
837 "A range variable `{0}' has already been declared in this scope",
841 public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
843 TopBlock.Report.Error (1948, loc,
844 "A range variable `{0}' conflicts with a method type parameter",
848 public void SetParameter (Parameter parameter)
850 base.parameters = new ParametersCompiled (parameter);
851 base.parameter_info = new ParameterInfo[] {
852 new ParameterInfo (this, 0)
856 public void SetParameters (Parameter first, Parameter second)
858 base.parameters = new ParametersCompiled (first, second);
859 base.parameter_info = new ParameterInfo[] {
860 new ParameterInfo (this, 0),
861 new ParameterInfo (this, 1)
866 sealed class TransparentMemberAccess : MemberAccess
868 public TransparentMemberAccess (Expression expr, string name)
873 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
875 rc.Report.Error (1947, loc,
876 "A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value",