2010-01-12 Zoltan Varga <vargaz@gmail.com>
[mcs.git] / mcs / linq.cs
blob42f5147e831dcc56440ed374fa85fda8c1620386
1 //
2 // linq.cs: support for query expressions
3 //
4 // Authors: Marek Safar (marek.safar@gmail.com)
5 //
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
7 //
8 // Copyright 2007-2008 Novell, Inc
9 //
11 using System;
12 using System.Reflection;
13 using System.Collections;
15 namespace Mono.CSharp.Linq
17 // NOTES:
18 // Expression should be IExpression to save some memory and make a few things
19 // easier to read
23 class QueryExpression : AQueryClause
25 public QueryExpression (Block block, AQueryClause query)
26 : base (null, null, query.Location)
28 this.next = query;
31 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide)
33 return next.BuildQueryClause (ec, lSide);
36 public override Expression DoResolve (ResolveContext ec)
38 int counter = QueryBlock.TransparentParameter.Counter;
40 Expression e = BuildQueryClause (ec, null);
41 e = e.Resolve (ec);
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;
50 return e;
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 (ResolveContext ec, Type container_type, Type qualifier_type,
73 Type queried_type, string name, string class_name, MemberTypes mt, BindingFlags bf)
75 ec.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?",
77 name);
78 return null;
82 class QueryExpressionInvocation : Invocation, MethodGroupExpr.IErrorHandler
84 public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments)
85 : base (expr, arguments)
89 protected override MethodGroupExpr DoResolveOverload (ResolveContext ec)
91 mg.CustomErrorHandler = this;
92 MethodGroupExpr rmg = mg.OverloadResolve (ec, ref arguments, false, loc);
93 return rmg;
96 public bool AmbiguousCall (ResolveContext ec, MethodBase ambiguous)
98 ec.Report.SymbolRelatedToPreviousError ((MethodInfo) mg);
99 ec.Report.SymbolRelatedToPreviousError (ambiguous);
100 ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
101 mg.Name, mg.InstanceExpression.GetSignatureForError ());
102 return true;
105 public bool NoExactMatch (ResolveContext ec, MethodBase method)
107 AParametersCollection pd = TypeManager.GetParameterData (method);
108 Type source_type = pd.ExtensionMethodType;
109 if (source_type != null) {
110 Argument a = arguments [0];
112 if (TypeManager.IsGenericType (source_type) && TypeManager.ContainsGenericParameters (source_type)) {
113 #if GMCS_SOURCE
114 TypeInferenceContext tic = new TypeInferenceContext (TypeManager.GetTypeArguments (source_type));
115 tic.OutputTypeInference (ec, a.Expr, source_type);
116 if (tic.FixAllTypes (ec)) {
117 source_type = TypeManager.DropGenericTypeArguments (source_type).MakeGenericType (tic.InferredTypeArguments);
119 #else
120 throw new NotSupportedException ();
121 #endif
124 if (!Convert.ImplicitConversionExists (ec, a.Expr, source_type)) {
125 ec.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
126 mg.Name, TypeManager.CSharpName (a.Type));
127 return true;
131 if (!TypeManager.IsGenericMethod (method))
132 return false;
134 if (mg.Name == "SelectMany") {
135 ec.Report.Error (1943, loc,
136 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
137 arguments [0].GetSignatureForError ());
138 } else {
139 ec.Report.Error (1942, loc,
140 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
141 mg.Name.ToLower (), mg.Name);
144 return true;
148 // TODO: protected
149 public AQueryClause next;
150 public Expression expr;
151 protected ToplevelBlock block;
153 protected AQueryClause (ToplevelBlock block, Expression expr, Location loc)
155 this.block = block;
156 this.expr = expr;
157 this.loc = loc;
160 protected override void CloneTo (CloneContext clonectx, Expression target)
162 AQueryClause t = (AQueryClause) target;
163 if (expr != null)
164 t.expr = expr.Clone (clonectx);
166 if (block != null)
167 t.block = (ToplevelBlock) block.Clone (clonectx);
169 if (next != null)
170 t.next = (AQueryClause) next.Clone (clonectx);
173 public override Expression CreateExpressionTree (ResolveContext ec)
175 // Should not be reached
176 throw new NotSupportedException ("ET");
179 public override Expression DoResolve (ResolveContext ec)
181 return expr.DoResolve (ec);
184 public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide)
186 Arguments args;
187 CreateArguments (ec, out args);
188 lSide = CreateQueryExpression (lSide, args);
189 if (next != null) {
190 Select s = next as Select;
191 if (s == null || s.IsRequired)
192 return next.BuildQueryClause (ec, lSide);
194 // Skip transparent select clause if any clause follows
195 if (next.next != null)
196 return next.next.BuildQueryClause (ec, lSide);
199 return lSide;
202 protected virtual void CreateArguments (ResolveContext ec, out Arguments args)
204 args = new Arguments (2);
206 LambdaExpression selector = new LambdaExpression (loc);
207 selector.Block = block;
208 selector.Block.AddStatement (new ContextualReturn (expr));
210 args.Add (new Argument (selector));
213 protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
215 return new QueryExpressionInvocation (
216 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
219 protected Invocation CreateQueryExpression (Expression lSide, TypeArguments typeArguments, Arguments arguments)
221 return new QueryExpressionInvocation (
222 new QueryExpressionAccess (lSide, MethodName, typeArguments, loc), arguments);
225 public override void Emit (EmitContext ec)
227 throw new NotSupportedException ();
230 protected abstract string MethodName { get; }
232 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
234 // Nothing to mutate
237 public virtual AQueryClause Next {
238 set {
239 next = value;
243 public AQueryClause Tail {
244 get {
245 return next == null ? this : next.Tail;
251 // A query clause with an identifier (range variable)
253 abstract class ARangeVariableQueryClause : AQueryClause
255 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
257 public RangeAnonymousTypeParameter (Expression initializer, LocatedToken parameter)
258 : base (initializer, parameter.Value, parameter.Location)
262 protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
264 ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
265 Name, initializer);
269 protected ARangeVariableQueryClause (ToplevelBlock block, Expression expr)
270 : base (block, expr, expr.Location)
274 protected static Expression CreateRangeVariableType (ToplevelBlock block, IMemberContext context, LocatedToken name, Expression init)
276 ArrayList args = new ArrayList (2);
277 args.Add (new AnonymousTypeParameter (block.Parameters [0]));
278 args.Add (new RangeAnonymousTypeParameter (init, name));
279 return new NewAnonymousType (args, context.CurrentTypeDefinition, name.Location);
283 class QueryStartClause : AQueryClause
285 public QueryStartClause (Expression expr)
286 : base (null, expr, expr.Location)
290 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide)
292 return next.BuildQueryClause (ec, expr);
295 public override Expression DoResolve (ResolveContext ec)
297 Expression e = BuildQueryClause (ec, null);
298 return e.Resolve (ec);
301 protected override string MethodName {
302 get { throw new NotSupportedException (); }
306 class Cast : QueryStartClause
308 // We don't have to clone cast type
309 readonly FullNamedExpression type_expr;
311 public Cast (FullNamedExpression type, Expression expr)
312 : base (expr)
314 this.type_expr = type;
317 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide)
319 lSide = CreateQueryExpression (expr, new TypeArguments (type_expr), null);
320 if (next != null)
321 return next.BuildQueryClause (ec, lSide);
323 return lSide;
326 protected override string MethodName {
327 get { return "Cast"; }
331 class GroupBy : AQueryClause
333 Expression element_selector;
334 ToplevelBlock element_block;
336 public GroupBy (ToplevelBlock block, Expression elementSelector, ToplevelBlock elementBlock, Expression keySelector, Location loc)
337 : base (block, keySelector, loc)
340 // Optimizes clauses like `group A by A'
342 if (!elementSelector.Equals (keySelector)) {
343 this.element_selector = elementSelector;
344 this.element_block = elementBlock;
348 protected override void CreateArguments (ResolveContext ec, out Arguments args)
350 base.CreateArguments (ec, out args);
352 if (element_selector != null) {
353 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
354 lambda.Block = element_block;
355 lambda.Block.AddStatement (new ContextualReturn (element_selector));
356 args.Add (new Argument (lambda));
360 protected override void CloneTo (CloneContext clonectx, Expression target)
362 GroupBy t = (GroupBy) target;
363 if (element_selector != null) {
364 t.element_selector = element_selector.Clone (clonectx);
365 t.element_block = (ToplevelBlock) element_block.Clone (clonectx);
368 base.CloneTo (clonectx, t);
371 protected override string MethodName {
372 get { return "GroupBy"; }
376 class Join : ARangeVariableQueryClause
378 readonly LocatedToken lt;
379 ToplevelBlock inner_selector, outer_selector;
381 public Join (ToplevelBlock block, LocatedToken lt, Expression inner, ToplevelBlock outerSelector, ToplevelBlock innerSelector, Location loc)
382 : base (block, inner)
384 this.lt = lt;
385 this.outer_selector = outerSelector;
386 this.inner_selector = innerSelector;
389 protected override void CreateArguments (ResolveContext ec, out Arguments args)
391 args = new Arguments (4);
393 args.Add (new Argument (expr));
395 LambdaExpression lambda = new LambdaExpression (outer_selector.StartLocation);
396 lambda.Block = outer_selector;
397 args.Add (new Argument (lambda));
399 lambda = new LambdaExpression (inner_selector.StartLocation);
400 lambda.Block = inner_selector;
401 args.Add (new Argument (lambda));
403 Expression result_selector_expr;
404 LocatedToken into_variable = GetIntoVariable ();
406 // When select follows use is as result selector
408 if (next is Select) {
409 result_selector_expr = next.expr;
410 next = next.next;
411 } else {
412 result_selector_expr = CreateRangeVariableType (block, ec.MemberContext, into_variable,
413 new SimpleName (into_variable.Value, into_variable.Location));
416 LambdaExpression result_selector = new LambdaExpression (lt.Location);
417 result_selector.Block = new QueryBlock (ec.Compiler, block.Parent, block.Parameters, into_variable, block.StartLocation);
418 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
420 args.Add (new Argument (result_selector));
423 protected virtual LocatedToken GetIntoVariable ()
425 return lt;
428 protected override void CloneTo (CloneContext clonectx, Expression target)
430 Join t = (Join) target;
431 t.inner_selector = (ToplevelBlock) inner_selector.Clone (clonectx);
432 t.outer_selector = (ToplevelBlock) outer_selector.Clone (clonectx);
433 base.CloneTo (clonectx, t);
436 protected override string MethodName {
437 get { return "Join"; }
441 class GroupJoin : Join
443 readonly LocatedToken into;
445 public GroupJoin (ToplevelBlock block, LocatedToken lt, Expression inner,
446 ToplevelBlock outerSelector, ToplevelBlock innerSelector, LocatedToken into, Location loc)
447 : base (block, lt, inner, outerSelector, innerSelector, loc)
449 this.into = into;
452 protected override LocatedToken GetIntoVariable ()
454 return into;
457 protected override string MethodName {
458 get { return "GroupJoin"; }
462 class Let : ARangeVariableQueryClause
464 public Let (ToplevelBlock block, TypeContainer container, LocatedToken identifier, Expression expr)
465 : base (block, CreateRangeVariableType (block, container, identifier, expr))
469 protected override string MethodName {
470 get { return "Select"; }
474 class Select : AQueryClause
476 public Select (ToplevelBlock block, Expression expr, Location loc)
477 : base (block, expr, loc)
482 // For queries like `from a orderby a select a'
483 // the projection is transparent and select clause can be safely removed
485 public bool IsRequired {
486 get {
487 SimpleName sn = expr as SimpleName;
488 if (sn == null)
489 return true;
491 return sn.Name != block.Parameters.FixedParameters [0].Name;
495 protected override string MethodName {
496 get { return "Select"; }
500 class SelectMany : ARangeVariableQueryClause
502 LocatedToken lt;
504 public SelectMany (ToplevelBlock block, LocatedToken lt, Expression expr)
505 : base (block, expr)
507 this.lt = lt;
510 protected override void CreateArguments (ResolveContext ec, out Arguments args)
512 base.CreateArguments (ec, out args);
514 Expression result_selector_expr;
516 // When select follow use is as result selector
518 if (next is Select) {
519 result_selector_expr = next.expr;
520 next = next.next;
521 } else {
522 result_selector_expr = CreateRangeVariableType (block, ec.MemberContext, lt, new SimpleName (lt.Value, lt.Location));
525 LambdaExpression result_selector = new LambdaExpression (lt.Location);
526 result_selector.Block = new QueryBlock (ec.Compiler, block.Parent, block.Parameters, lt, block.StartLocation);
527 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
529 args.Add (new Argument (result_selector));
532 protected override string MethodName {
533 get { return "SelectMany"; }
537 class Where : AQueryClause
539 public Where (ToplevelBlock block, Expression expr, Location loc)
540 : base (block, expr, loc)
544 protected override string MethodName {
545 get { return "Where"; }
549 class OrderByAscending : AQueryClause
551 public OrderByAscending (ToplevelBlock block,Expression expr)
552 : base (block, expr, expr.Location)
556 protected override string MethodName {
557 get { return "OrderBy"; }
561 class OrderByDescending : AQueryClause
563 public OrderByDescending (ToplevelBlock block, Expression expr)
564 : base (block, expr, expr.Location)
568 protected override string MethodName {
569 get { return "OrderByDescending"; }
573 class ThenByAscending : OrderByAscending
575 public ThenByAscending (ToplevelBlock block, Expression expr)
576 : base (block, expr)
580 protected override string MethodName {
581 get { return "ThenBy"; }
585 class ThenByDescending : OrderByDescending
587 public ThenByDescending (ToplevelBlock block, Expression expr)
588 : base (block, expr)
592 protected override string MethodName {
593 get { return "ThenByDescending"; }
598 // Implicit query block
600 class QueryBlock : ToplevelBlock
603 // Transparent parameters are used to package up the intermediate results
604 // and pass them onto next clause
606 public sealed class TransparentParameter : ImplicitLambdaParameter
608 public static int Counter;
609 const string ParameterNamePrefix = "<>__TranspIdent";
611 public readonly ParametersCompiled Parent;
612 public readonly string Identifier;
614 public TransparentParameter (ParametersCompiled parent, LocatedToken identifier)
615 : base (ParameterNamePrefix + Counter++, identifier.Location)
617 Parent = parent;
618 Identifier = identifier.Value;
621 public static void Reset ()
623 Counter = 0;
627 public sealed class ImplicitQueryParameter : ImplicitLambdaParameter
629 public ImplicitQueryParameter (string name, Location loc)
630 : base (name, loc)
635 public QueryBlock (CompilerContext ctx, Block parent, LocatedToken lt, Location start)
636 : base (ctx, parent, new ParametersCompiled (new ImplicitQueryParameter (lt.Value, lt.Location)), start)
638 if (parent != null)
639 base.CheckParentConflictName (parent.Toplevel, lt.Value, lt.Location);
642 public QueryBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, LocatedToken lt, Location start)
643 : base (ctx, parent, new ParametersCompiled (parameters [0].Clone (), new ImplicitQueryParameter (lt.Value, lt.Location)), start)
647 public QueryBlock (CompilerContext ctx, Block parent, Location start)
648 : base (ctx, parent, parent.Toplevel.Parameters.Clone (), start)
652 public void AddTransparentParameter (LocatedToken name)
654 base.CheckParentConflictName (this, name.Value, name.Location);
656 parameters = new ParametersCompiled (new TransparentParameter (parameters, name));
659 protected override bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
661 return true;
665 // Query parameter reference can include transparent parameters
667 protected override Expression GetParameterReferenceExpression (string name, Location loc)
669 Expression expr = base.GetParameterReferenceExpression (name, loc);
670 if (expr != null)
671 return expr;
673 TransparentParameter tp = parameters [0] as TransparentParameter;
674 while (tp != null) {
675 if (tp.Identifier == name)
676 break;
678 TransparentParameter tp_next = tp.Parent [0] as TransparentParameter;
679 if (tp_next == null) {
680 if (tp.Parent.GetParameterIndexByName (name) >= 0)
681 break;
684 tp = tp_next;
687 if (tp != null) {
688 expr = new SimpleName (parameters[0].Name, loc);
689 TransparentParameter tp_cursor = (TransparentParameter) parameters[0];
690 while (tp_cursor != tp) {
691 tp_cursor = (TransparentParameter) tp_cursor.Parent[0];
692 expr = new MemberAccess (expr, tp_cursor.Name);
695 return new MemberAccess (expr, name);
698 return null;
701 protected override void Error_AlreadyDeclared (Location loc, string var, string reason)
703 Report.Error (1931, loc, "A range variable `{0}' conflicts with a previous declaration of `{0}'",
704 var);
707 protected override void Error_AlreadyDeclared (Location loc, string var)
709 Report.Error (1930, loc, "A range variable `{0}' has already been declared in this scope",
710 var);
713 public override void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
715 r.Error (1948, loc, "A range variable `{0}' conflicts with a method type parameter",
716 name);