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;
15 namespace Mono.CSharp.Linq
18 // Expression should be IExpression to save some memory and make a few things
23 class QueryExpression : AQueryClause
25 public QueryExpression (Block block, AQueryClause query)
26 : base (null, null, query.Location)
31 public override Expression BuildQueryClause (EmitContext ec, Expression lSide)
33 return next.BuildQueryClause (ec, lSide);
36 public override Expression DoResolve (EmitContext ec)
38 int counter = QueryBlock.TransparentParameter.Counter;
40 Expression e = BuildQueryClause (ec, null);
44 // Reset counter in probing mode to ensure that all transparent
45 // identifier anonymous types are created only once
47 if (ec.IsInProbingMode)
48 QueryBlock.TransparentParameter.Counter = counter;
53 protected override string MethodName {
54 get { throw new NotSupportedException (); }
58 abstract class AQueryClause : Expression
60 class QueryExpressionAccess : MemberAccess
62 public QueryExpressionAccess (Expression expr, string methodName, Location loc)
63 : base (expr, methodName, loc)
67 public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc)
68 : base (expr, methodName, typeArguments, loc)
72 protected override Expression Error_MemberLookupFailed (Type container_type, Type qualifier_type,
73 Type queried_type, string name, string class_name, MemberTypes mt, BindingFlags bf)
75 Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " +
76 "Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?",
82 class QueryExpressionInvocation : Invocation, MethodGroupExpr.IErrorHandler
84 public QueryExpressionInvocation (QueryExpressionAccess expr, ArrayList arguments)
85 : base (expr, arguments)
89 protected override MethodGroupExpr DoResolveOverload (EmitContext ec)
91 mg.CustomErrorHandler = this;
92 MethodGroupExpr rmg = mg.OverloadResolve (ec, ref Arguments, false, loc);
96 public bool AmbiguousCall (MethodBase ambiguous)
98 Report.SymbolRelatedToPreviousError ((MethodInfo) mg);
99 Report.SymbolRelatedToPreviousError (ambiguous);
100 Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
101 mg.Name, mg.InstanceExpression.GetSignatureForError ());
105 public bool NoExactMatch (EmitContext ec, MethodBase method)
108 AParametersCollection pd = TypeManager.GetParameterData (method);
109 Type source_type = pd.ExtensionMethodType;
110 if (source_type != null) {
111 Argument a = (Argument) Arguments [0];
113 if (source_type.IsGenericType && source_type.ContainsGenericParameters) {
114 TypeInferenceContext tic = new TypeInferenceContext (source_type.GetGenericArguments ());
115 tic.OutputTypeInference (ec, a.Expr, source_type);
116 if (tic.FixAllTypes ()) {
117 source_type = source_type.GetGenericTypeDefinition ().MakeGenericType (tic.InferredTypeArguments);
121 if (!Convert.ImplicitConversionExists (ec, a.Expr, source_type)) {
122 Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
123 mg.Name, TypeManager.CSharpName (a.Type));
128 if (!method.IsGenericMethod)
131 if (mg.Name == "SelectMany") {
132 Report.Error (1943, loc,
133 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
134 ((Argument) Arguments [0]).GetSignatureForError ());
136 Report.Error (1942, loc,
137 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
138 mg.Name.ToLower (), mg.Name);
148 public AQueryClause next;
149 public Expression expr;
150 protected ToplevelBlock block;
152 protected AQueryClause (ToplevelBlock block, Expression expr, Location loc)
159 protected override void CloneTo (CloneContext clonectx, Expression target)
161 AQueryClause t = (AQueryClause) target;
163 t.expr = expr.Clone (clonectx);
166 t.block = (ToplevelBlock) block.Clone (clonectx);
169 t.next = (AQueryClause) next.Clone (clonectx);
172 public override Expression CreateExpressionTree (EmitContext ec)
174 // Should not be reached
175 throw new NotSupportedException ("ET");
178 public override Expression DoResolve (EmitContext ec)
180 return expr.DoResolve (ec);
183 public virtual Expression BuildQueryClause (EmitContext ec, Expression lSide)
186 CreateArguments (ec, out args);
187 lSide = CreateQueryExpression (lSide, args);
189 Select s = next as Select;
190 if (s == null || s.IsRequired)
191 return next.BuildQueryClause (ec, lSide);
193 // Skip transparent select clause if any clause follows
194 if (next.next != null)
195 return next.next.BuildQueryClause (ec, lSide);
201 protected virtual void CreateArguments (EmitContext ec, out ArrayList args)
203 args = new ArrayList (2);
205 LambdaExpression selector = new LambdaExpression (loc);
206 selector.Block = block;
207 selector.Block.AddStatement (new ContextualReturn (expr));
209 args.Add (new Argument (selector));
212 protected Invocation CreateQueryExpression (Expression lSide, ArrayList arguments)
214 return new QueryExpressionInvocation (
215 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
218 protected Invocation CreateQueryExpression (Expression lSide, TypeArguments typeArguments, ArrayList arguments)
220 return new QueryExpressionInvocation (
221 new QueryExpressionAccess (lSide, MethodName, typeArguments, loc), arguments);
224 public override void Emit (EmitContext ec)
226 throw new NotSupportedException ();
229 protected abstract string MethodName { get; }
231 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
236 public virtual AQueryClause Next {
242 public AQueryClause Tail {
244 return next == null ? this : next.Tail;
250 // A query clause with an identifier (range variable)
252 abstract class ARangeVariableQueryClause : AQueryClause
254 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
256 public RangeAnonymousTypeParameter (Expression initializer, LocatedToken parameter)
257 : base (initializer, parameter.Value, parameter.Location)
261 protected override void Error_InvalidInitializer (string initializer)
263 Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
268 protected ARangeVariableQueryClause (ToplevelBlock block, Expression expr)
269 : base (block, expr, expr.Location)
273 protected static Expression CreateRangeVariableType (ToplevelBlock block, TypeContainer container, LocatedToken name, Expression init)
275 ArrayList args = new ArrayList (2);
276 args.Add (new AnonymousTypeParameter (block.Parameters [0]));
277 args.Add (new RangeAnonymousTypeParameter (init, name));
278 return new AnonymousTypeDeclaration (args, container, name.Location);
282 class QueryStartClause : AQueryClause
284 public QueryStartClause (Expression expr)
285 : base (null, expr, expr.Location)
289 public override Expression BuildQueryClause (EmitContext ec, Expression lSide)
291 return next.BuildQueryClause (ec, expr);
294 public override Expression DoResolve (EmitContext ec)
296 Expression e = BuildQueryClause (ec, null);
297 return e.Resolve (ec);
300 protected override string MethodName {
301 get { throw new NotSupportedException (); }
305 class Cast : QueryStartClause
307 // We don't have to clone cast type
308 readonly FullNamedExpression type_expr;
310 public Cast (FullNamedExpression type, Expression expr)
313 this.type_expr = type;
316 public override Expression BuildQueryClause (EmitContext ec, Expression lSide)
318 lSide = CreateQueryExpression (expr, new TypeArguments (type_expr), null);
320 return next.BuildQueryClause (ec, lSide);
325 protected override string MethodName {
326 get { return "Cast"; }
330 class GroupBy : AQueryClause
332 Expression element_selector;
333 ToplevelBlock element_block;
335 public GroupBy (ToplevelBlock block, Expression elementSelector, ToplevelBlock elementBlock, Expression keySelector, Location loc)
336 : base (block, keySelector, loc)
339 // Optimizes clauses like `group A by A'
341 if (!elementSelector.Equals (keySelector)) {
342 this.element_selector = elementSelector;
343 this.element_block = elementBlock;
347 protected override void CreateArguments (EmitContext ec, out ArrayList args)
349 base.CreateArguments (ec, out args);
351 if (element_selector != null) {
352 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
353 lambda.Block = element_block;
354 lambda.Block.AddStatement (new ContextualReturn (element_selector));
355 args.Add (new Argument (lambda));
359 protected override void CloneTo (CloneContext clonectx, Expression target)
361 GroupBy t = (GroupBy) target;
362 if (element_selector != null) {
363 t.element_selector = element_selector.Clone (clonectx);
364 t.element_block = (ToplevelBlock) element_block.Clone (clonectx);
367 base.CloneTo (clonectx, t);
370 protected override string MethodName {
371 get { return "GroupBy"; }
375 class Join : ARangeVariableQueryClause
377 readonly LocatedToken lt;
378 ToplevelBlock inner_selector, outer_selector;
380 public Join (ToplevelBlock block, LocatedToken lt, Expression inner, ToplevelBlock outerSelector, ToplevelBlock innerSelector, Location loc)
381 : base (block, inner)
384 this.outer_selector = outerSelector;
385 this.inner_selector = innerSelector;
388 protected override void CreateArguments (EmitContext ec, out ArrayList args)
390 args = new ArrayList (4);
392 args.Add (new Argument (expr));
394 LambdaExpression lambda = new LambdaExpression (outer_selector.StartLocation);
395 lambda.Block = outer_selector;
396 args.Add (new Argument (lambda));
398 lambda = new LambdaExpression (inner_selector.StartLocation);
399 lambda.Block = inner_selector;
400 args.Add (new Argument (lambda));
402 Expression result_selector_expr;
403 LocatedToken into_variable = GetIntoVariable ();
405 // When select follows use is as result selector
407 if (next is Select) {
408 result_selector_expr = next.expr;
411 result_selector_expr = CreateRangeVariableType (block, (TypeContainer) ec.TypeContainer, into_variable,
412 new SimpleName (into_variable.Value, into_variable.Location));
415 LambdaExpression result_selector = new LambdaExpression (lt.Location);
416 result_selector.Block = new QueryBlock (block.Parent, block.Parameters, into_variable, block.StartLocation);
417 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
419 args.Add (new Argument (result_selector));
422 protected virtual LocatedToken GetIntoVariable ()
427 protected override void CloneTo (CloneContext clonectx, Expression target)
429 Join t = (Join) target;
430 t.inner_selector = (ToplevelBlock) inner_selector.Clone (clonectx);
431 t.outer_selector = (ToplevelBlock) outer_selector.Clone (clonectx);
432 base.CloneTo (clonectx, t);
435 protected override string MethodName {
436 get { return "Join"; }
440 class GroupJoin : Join
442 readonly LocatedToken into;
444 public GroupJoin (ToplevelBlock block, LocatedToken lt, Expression inner,
445 ToplevelBlock outerSelector, ToplevelBlock innerSelector, LocatedToken into, Location loc)
446 : base (block, lt, inner, outerSelector, innerSelector, loc)
451 protected override LocatedToken GetIntoVariable ()
456 protected override string MethodName {
457 get { return "GroupJoin"; }
461 class Let : ARangeVariableQueryClause
463 public Let (ToplevelBlock block, TypeContainer container, LocatedToken identifier, Expression expr)
464 : base (block, CreateRangeVariableType (block, container, identifier, expr))
468 protected override string MethodName {
469 get { return "Select"; }
473 class Select : AQueryClause
475 public Select (ToplevelBlock block, Expression expr, Location loc)
476 : base (block, expr, loc)
481 // For queries like `from a orderby a select a'
482 // the projection is transparent and select clause can be safely removed
484 public bool IsRequired {
486 SimpleName sn = expr as SimpleName;
490 return sn.Name != block.Parameters[0].Name;
494 protected override string MethodName {
495 get { return "Select"; }
499 class SelectMany : ARangeVariableQueryClause
503 public SelectMany (ToplevelBlock block, LocatedToken lt, Expression expr)
509 protected override void CreateArguments (EmitContext ec, out ArrayList args)
511 base.CreateArguments (ec, out args);
513 Expression result_selector_expr;
515 // When select follow use is as result selector
517 if (next is Select) {
518 result_selector_expr = next.expr;
521 result_selector_expr = CreateRangeVariableType (block, (TypeContainer)ec.TypeContainer, lt, new SimpleName (lt.Value, lt.Location));
524 LambdaExpression result_selector = new LambdaExpression (lt.Location);
525 result_selector.Block = new QueryBlock (block.Parent, block.Parameters, lt, block.StartLocation);
526 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
528 args.Add (new Argument (result_selector));
531 protected override string MethodName {
532 get { return "SelectMany"; }
536 class Where : AQueryClause
538 public Where (ToplevelBlock block, Expression expr, Location loc)
539 : base (block, expr, loc)
543 protected override string MethodName {
544 get { return "Where"; }
548 class OrderByAscending : AQueryClause
550 public OrderByAscending (ToplevelBlock block,Expression expr)
551 : base (block, expr, expr.Location)
555 protected override string MethodName {
556 get { return "OrderBy"; }
560 class OrderByDescending : AQueryClause
562 public OrderByDescending (ToplevelBlock block, Expression expr)
563 : base (block, expr, expr.Location)
567 protected override string MethodName {
568 get { return "OrderByDescending"; }
572 class ThenByAscending : OrderByAscending
574 public ThenByAscending (ToplevelBlock block, Expression expr)
579 protected override string MethodName {
580 get { return "ThenBy"; }
584 class ThenByDescending : OrderByDescending
586 public ThenByDescending (ToplevelBlock block, Expression expr)
591 protected override string MethodName {
592 get { return "ThenByDescending"; }
597 // Implicit query block
599 class QueryBlock : ToplevelBlock
602 // Transparent parameters are used to package up the intermediate results
603 // and pass them onto next clause
605 public sealed class TransparentParameter : ImplicitLambdaParameter
607 public static int Counter;
608 const string ParameterNamePrefix = "<>__TranspIdent";
610 public readonly Parameters Parent;
611 public readonly string Identifier;
613 public TransparentParameter (Parameters parent, LocatedToken identifier)
614 : base (ParameterNamePrefix + Counter++, identifier.Location)
617 Identifier = identifier.Value;
620 public static void Reset ()
626 public sealed class ImplicitQueryParameter : ImplicitLambdaParameter
628 public ImplicitQueryParameter (string name, Location loc)
634 public QueryBlock (Block parent, LocatedToken lt, Location start)
635 : base (parent, new Parameters (new ImplicitQueryParameter (lt.Value, lt.Location)), start)
638 base.CheckParentConflictName (parent.Toplevel, lt.Value, lt.Location);
641 public QueryBlock (Block parent, Parameters parameters, LocatedToken lt, Location start)
642 : base (parent, new Parameters (parameters [0].Clone (), new ImplicitQueryParameter (lt.Value, lt.Location)), start)
646 public QueryBlock (Block parent, Location start)
647 : base (parent, parent.Toplevel.Parameters.Clone (), start)
651 public void AddTransparentParameter (LocatedToken name)
653 base.CheckParentConflictName (this, name.Value, name.Location);
655 parameters = new Parameters (new TransparentParameter (parameters, name));
658 protected override bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
664 // Query parameter reference can include transparent parameters
666 protected override Expression GetParameterReferenceExpression (string name, Location loc)
668 Expression expr = base.GetParameterReferenceExpression (name, loc);
672 TransparentParameter tp = parameters [0] as TransparentParameter;
674 if (tp.Identifier == name)
677 TransparentParameter tp_next = tp.Parent [0] as TransparentParameter;
678 if (tp_next == null) {
679 if (tp.Parent.GetParameterIndexByName (name) >= 0)
687 expr = new SimpleName (parameters[0].Name, loc);
688 TransparentParameter tp_cursor = (TransparentParameter) parameters[0];
689 while (tp_cursor != tp) {
690 tp_cursor = (TransparentParameter) tp_cursor.Parent[0];
691 expr = new MemberAccess (expr, tp_cursor.Name);
694 return new MemberAccess (expr, name);
700 protected override void Error_AlreadyDeclared (Location loc, string var, string reason)
702 Report.Error (1931, loc, "A range variable `{0}' conflicts with a previous declaration of `{0}'",
706 protected override void Error_AlreadyDeclared (Location loc, string var)
708 Report.Error (1930, loc, "A range variable `{0}' has already been declared in this scope",
712 public override void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
714 Report.Error (1948, loc, "A range variable `{0}' conflicts with a method type parameter",