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 rc)
84 using (rc.Set (ResolveContext.Options.QueryClauseScope)) {
85 return mg.OverloadResolve (rc, ref arguments, this, OverloadResolver.Restrictions.None);
89 protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
91 ec.Report.Error (1979, loc,
92 "Query expressions with a source or join sequence of type `dynamic' are not allowed");
96 #region IErrorHandler Members
98 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
100 var emg = mg as ExtensionMethodGroupExpr;
101 var type = emg == null ? mg.InstanceExpression : emg.ExtensionExpression;
103 ec.Report.SymbolRelatedToPreviousError (best);
104 ec.Report.SymbolRelatedToPreviousError (ambiguous);
105 ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
106 best.Name, type.Type.GetSignatureForError ());
110 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
115 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
120 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
122 var ms = (MethodSpec) best;
123 TypeSpec source_type = ms.Parameters.ExtensionMethodType;
124 if (source_type != null) {
125 Argument a = arguments[0];
127 if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) {
128 TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments);
129 tic.OutputTypeInference (rc, a.Expr, source_type);
130 if (tic.FixAllTypes (rc)) {
131 source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments);
135 if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) {
136 rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
137 best.Name, a.Type.GetSignatureForError ());
142 if (best.Name == "SelectMany") {
143 rc.Report.Error (1943, loc,
144 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
145 arguments[0].GetSignatureForError ());
147 rc.Report.Error (1942, loc,
148 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
149 best.Name.ToLowerInvariant (), best.Name);
158 public AQueryClause next;
159 public QueryBlock block;
161 protected AQueryClause (QueryBlock block, Expression expr, Location loc)
168 protected override void CloneTo (CloneContext clonectx, Expression target)
170 base.CloneTo (clonectx, target);
172 AQueryClause t = (AQueryClause) target;
175 t.block = (QueryBlock) clonectx.LookupBlock (block);
178 t.next = (AQueryClause) next.Clone (clonectx);
181 protected override Expression DoResolve (ResolveContext ec)
183 return expr.Resolve (ec);
186 public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
188 Arguments args = null;
189 CreateArguments (ec, parameter, ref args);
190 lSide = CreateQueryExpression (lSide, args);
192 parameter = CreateChildrenParameters (parameter);
194 Select s = next as Select;
195 if (s == null || s.IsRequired (parameter))
196 return next.BuildQueryClause (ec, lSide, parameter);
198 // Skip transparent select clause if any clause follows
199 if (next.next != null)
200 return next.next.BuildQueryClause (ec, lSide, parameter);
206 protected virtual Parameter CreateChildrenParameters (Parameter parameter)
208 // Have to clone the parameter for any children use, it carries block sensitive data
209 return parameter.Clone ();
212 protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
214 args = new Arguments (2);
216 LambdaExpression selector = new LambdaExpression (loc);
218 block.SetParameter (parameter);
219 selector.Block = block;
220 selector.Block.AddStatement (new ContextualReturn (expr));
222 args.Add (new Argument (selector));
225 protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
227 return new QueryExpressionInvocation (
228 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
231 protected abstract string MethodName { get; }
233 public AQueryClause Next {
239 public AQueryClause Tail {
241 return next == null ? this : next.Tail;
247 // A query clause with an identifier (range variable)
249 public abstract class ARangeVariableQueryClause : AQueryClause
251 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
253 public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter)
254 : base (initializer, parameter.Name, parameter.Location)
258 protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
260 ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
265 class RangeParameterReference : ParameterReference
269 public RangeParameterReference (Parameter p)
270 : base (null, p.Location)
275 protected override Expression DoResolve (ResolveContext ec)
277 pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter);
278 return base.DoResolve (ec);
282 protected RangeVariable identifier;
284 protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
285 : base (block, expr, loc)
287 this.identifier = identifier;
290 public RangeVariable Identifier {
296 public FullNamedExpression IdentifierType { get; set; }
298 protected Invocation CreateCastExpression (Expression lSide)
300 return new QueryExpressionInvocation (
301 new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null);
304 protected override Parameter CreateChildrenParameters (Parameter parameter)
306 return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ());
309 protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init)
311 var args = new List<AnonymousTypeParameter> (2);
314 // The first argument is the reference to the parameter
316 args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location));
319 // The second argument is the linq expression
321 args.Add (new RangeAnonymousTypeParameter (init, name));
324 // Create unique anonymous type
326 return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location);
329 protected virtual RangeVariable GetIntoVariable ()
335 public sealed class RangeVariable : INamedBlockVariable
339 public RangeVariable (string name, Location loc)
356 public bool IsDeclared {
362 public bool IsParameter {
368 public Location Location { get; private set; }
370 public string Name { get; private set; }
374 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
377 // We know the variable name is somewhere in the scope. This generates
378 // an access expression from current block
380 var pb = rc.CurrentBlock.ParametersBlock;
382 if (pb is QueryBlock) {
383 for (int i = pb.Parameters.Count - 1; i >= 0; --i) {
384 var p = pb.Parameters[i];
386 return pb.GetParameterReference (i, loc);
388 Expression expr = null;
389 var tp = p as QueryBlock.TransparentParameter;
392 expr = pb.GetParameterReference (i, loc);
394 expr = new TransparentMemberAccess (expr, tp.Name);
396 if (tp.Identifier == Name)
397 return new TransparentMemberAccess (expr, Name);
399 if (tp.Parent.Name == Name)
400 return new TransparentMemberAccess (expr, Name);
402 tp = tp.Parent as QueryBlock.TransparentParameter;
410 pb = pb.Parent.ParametersBlock;
415 public class QueryStartClause : ARangeVariableQueryClause
417 public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc)
418 : base (block, identifier, expr, loc)
420 block.AddRangeVariable (identifier);
423 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
425 if (IdentifierType != null)
426 expr = CreateCastExpression (expr);
428 if (parameter == null)
431 return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location));
434 protected override Expression DoResolve (ResolveContext ec)
436 Expression e = BuildQueryClause (ec, null, null);
437 return e.Resolve (ec);
440 protected override string MethodName {
441 get { throw new NotSupportedException (); }
445 public class GroupBy : AQueryClause
447 Expression element_selector;
448 QueryBlock element_block;
450 public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc)
451 : base (block, keySelector, loc)
454 // Optimizes clauses like `group A by A'
456 if (!elementSelector.Equals (keySelector)) {
457 this.element_selector = elementSelector;
458 this.element_block = elementBlock;
462 public Expression SelectorExpression {
464 return element_selector;
468 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
470 base.CreateArguments (ec, parameter, ref args);
472 if (element_selector != null) {
473 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
475 element_block.SetParameter (parameter.Clone ());
476 lambda.Block = element_block;
477 lambda.Block.AddStatement (new ContextualReturn (element_selector));
478 args.Add (new Argument (lambda));
482 protected override void CloneTo (CloneContext clonectx, Expression target)
484 GroupBy t = (GroupBy) target;
485 if (element_selector != null) {
486 t.element_selector = element_selector.Clone (clonectx);
487 t.element_block = (QueryBlock) element_block.Clone (clonectx);
490 base.CloneTo (clonectx, t);
493 protected override string MethodName {
494 get { return "GroupBy"; }
497 public override object Accept (StructuralVisitor visitor)
499 return visitor.Visit (this);
503 public class Join : SelectMany
505 QueryBlock inner_selector, outer_selector;
507 public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc)
508 : base (block, lt, inner, loc)
510 this.outer_selector = outerSelector;
511 this.inner_selector = innerSelector;
514 public QueryBlock InnerSelector {
516 return inner_selector;
520 public QueryBlock OuterSelector {
522 return outer_selector;
526 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
528 args = new Arguments (4);
530 if (IdentifierType != null)
531 expr = CreateCastExpression (expr);
533 args.Add (new Argument (expr));
535 outer_selector.SetParameter (parameter.Clone ());
536 var lambda = new LambdaExpression (outer_selector.StartLocation);
537 lambda.Block = outer_selector;
538 args.Add (new Argument (lambda));
540 inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location));
541 lambda = new LambdaExpression (inner_selector.StartLocation);
542 lambda.Block = inner_selector;
543 args.Add (new Argument (lambda));
545 base.CreateArguments (ec, parameter, ref args);
548 protected override void CloneTo (CloneContext clonectx, Expression target)
550 Join t = (Join) target;
551 t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx);
552 t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx);
553 base.CloneTo (clonectx, t);
556 protected override string MethodName {
557 get { return "Join"; }
560 public override object Accept (StructuralVisitor visitor)
562 return visitor.Visit (this);
566 public class GroupJoin : Join
568 readonly RangeVariable into;
570 public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner,
571 QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc)
572 : base (block, lt, inner, outerSelector, innerSelector, loc)
577 protected override RangeVariable GetIntoVariable ()
582 protected override string MethodName {
583 get { return "GroupJoin"; }
586 public override object Accept (StructuralVisitor visitor)
588 return visitor.Visit (this);
592 public class Let : ARangeVariableQueryClause
594 public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
595 : base (block, identifier, expr, loc)
599 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
601 expr = CreateRangeVariableType (ec, parameter, identifier, expr);
602 base.CreateArguments (ec, parameter, ref args);
605 protected override string MethodName {
606 get { return "Select"; }
609 public override object Accept (StructuralVisitor visitor)
611 return visitor.Visit (this);
615 public class Select : AQueryClause
617 public Select (QueryBlock block, Expression expr, Location loc)
618 : base (block, expr, loc)
623 // For queries like `from a orderby a select a'
624 // the projection is transparent and select clause can be safely removed
626 public bool IsRequired (Parameter parameter)
628 SimpleName sn = expr as SimpleName;
632 return sn.Name != parameter.Name;
635 protected override string MethodName {
636 get { return "Select"; }
639 public override object Accept (StructuralVisitor visitor)
641 return visitor.Visit (this);
646 public class SelectMany : ARangeVariableQueryClause
648 public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
649 : base (block, identifier, expr, loc)
653 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
656 if (IdentifierType != null)
657 expr = CreateCastExpression (expr);
659 base.CreateArguments (ec, parameter.Clone (), ref args);
662 Expression result_selector_expr;
663 QueryBlock result_block;
665 var target = GetIntoVariable ();
666 var target_param = new ImplicitLambdaParameter (target.Name, target.Location);
669 // When select follows use it as a result selector
671 if (next is Select) {
672 result_selector_expr = next.Expr;
674 result_block = next.block;
675 result_block.SetParameters (parameter, target_param);
679 result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location));
681 result_block = new QueryBlock (block.Parent, block.StartLocation);
682 result_block.SetParameters (parameter, target_param);
685 LambdaExpression result_selector = new LambdaExpression (Location);
686 result_selector.Block = result_block;
687 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
689 args.Add (new Argument (result_selector));
692 protected override string MethodName {
693 get { return "SelectMany"; }
696 public override object Accept (StructuralVisitor visitor)
698 return visitor.Visit (this);
702 public class Where : AQueryClause
704 public Where (QueryBlock block, Expression expr, Location loc)
705 : base (block, expr, loc)
709 protected override string MethodName {
710 get { return "Where"; }
713 public override object Accept (StructuralVisitor visitor)
715 return visitor.Visit (this);
719 public class OrderByAscending : AQueryClause
721 public OrderByAscending (QueryBlock block, Expression expr)
722 : base (block, expr, expr.Location)
726 protected override string MethodName {
727 get { return "OrderBy"; }
730 public override object Accept (StructuralVisitor visitor)
732 return visitor.Visit (this);
736 public class OrderByDescending : AQueryClause
738 public OrderByDescending (QueryBlock block, Expression expr)
739 : base (block, expr, expr.Location)
743 protected override string MethodName {
744 get { return "OrderByDescending"; }
747 public override object Accept (StructuralVisitor visitor)
749 return visitor.Visit (this);
753 public class ThenByAscending : OrderByAscending
755 public ThenByAscending (QueryBlock block, Expression expr)
760 protected override string MethodName {
761 get { return "ThenBy"; }
764 public override object Accept (StructuralVisitor visitor)
766 return visitor.Visit (this);
770 public class ThenByDescending : OrderByDescending
772 public ThenByDescending (QueryBlock block, Expression expr)
777 protected override string MethodName {
778 get { return "ThenByDescending"; }
781 public override object Accept (StructuralVisitor visitor)
783 return visitor.Visit (this);
788 // Implicit query block
790 public class QueryBlock : ParametersBlock
793 // Transparent parameters are used to package up the intermediate results
794 // and pass them onto next clause
796 public sealed class TransparentParameter : ImplicitLambdaParameter
798 public static int Counter;
799 const string ParameterNamePrefix = "<>__TranspIdent";
801 public readonly Parameter Parent;
802 public readonly string Identifier;
804 public TransparentParameter (Parameter parent, RangeVariable identifier)
805 : base (ParameterNamePrefix + Counter++, identifier.Location)
808 Identifier = identifier.Name;
811 public static void Reset ()
817 public QueryBlock (Block parent, Location start)
818 : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start, Flags.CompilerGenerated)
822 public void AddRangeVariable (RangeVariable variable)
824 variable.Block = this;
825 TopBlock.AddLocalName (variable.Name, variable, true);
828 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
830 TopBlock.Report.Error (1931, variable.Location,
831 "A range variable `{0}' conflicts with a previous declaration of `{0}'",
835 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
837 TopBlock.Report.Error (1930, variable.Location,
838 "A range variable `{0}' has already been declared in this scope",
842 public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
844 TopBlock.Report.Error (1948, loc,
845 "A range variable `{0}' conflicts with a method type parameter",
849 public void SetParameter (Parameter parameter)
851 base.parameters = new ParametersCompiled (parameter);
852 base.parameter_info = new ParameterInfo[] {
853 new ParameterInfo (this, 0)
857 public void SetParameters (Parameter first, Parameter second)
859 base.parameters = new ParametersCompiled (first, second);
860 base.parameter_info = new ParameterInfo[] {
861 new ParameterInfo (this, 0),
862 new ParameterInfo (this, 1)
867 sealed class TransparentMemberAccess : MemberAccess
869 public TransparentMemberAccess (Expression expr, string name)
874 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
876 rc.Report.Error (1947, loc,
877 "A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value",