revert 144379
[mcs.git] / mcs / linq.cs
blob65f510823724e1d3ebec1a54b93c4f004fe19eea
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 : ShimExpression
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 TypeInferenceContext tic = new TypeInferenceContext (TypeManager.GetTypeArguments (source_type));
114 tic.OutputTypeInference (ec, a.Expr, source_type);
115 if (tic.FixAllTypes (ec)) {
116 source_type = TypeManager.DropGenericTypeArguments (source_type).MakeGenericType (tic.InferredTypeArguments);
120 if (!Convert.ImplicitConversionExists (ec, a.Expr, source_type)) {
121 ec.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
122 mg.Name, TypeManager.CSharpName (a.Type));
123 return true;
127 if (!TypeManager.IsGenericMethod (method))
128 return false;
130 if (mg.Name == "SelectMany") {
131 ec.Report.Error (1943, loc,
132 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
133 arguments [0].GetSignatureForError ());
134 } else {
135 ec.Report.Error (1942, loc,
136 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
137 mg.Name.ToLower (), mg.Name);
140 return true;
144 // TODO: protected
145 public AQueryClause next;
146 protected ToplevelBlock block;
148 protected AQueryClause (ToplevelBlock block, Expression expr, Location loc)
149 : base (expr)
151 this.block = block;
152 this.loc = loc;
155 protected override void CloneTo (CloneContext clonectx, Expression target)
157 base.CloneTo (clonectx, target);
159 AQueryClause t = (AQueryClause) target;
161 if (block != null)
162 t.block = (ToplevelBlock) block.Clone (clonectx);
164 if (next != null)
165 t.next = (AQueryClause) next.Clone (clonectx);
168 public override Expression DoResolve (ResolveContext ec)
170 return expr.DoResolve (ec);
173 public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide)
175 Arguments args;
176 CreateArguments (ec, out args);
177 lSide = CreateQueryExpression (lSide, args);
178 if (next != null) {
179 Select s = next as Select;
180 if (s == null || s.IsRequired)
181 return next.BuildQueryClause (ec, lSide);
183 // Skip transparent select clause if any clause follows
184 if (next.next != null)
185 return next.next.BuildQueryClause (ec, lSide);
188 return lSide;
191 protected virtual void CreateArguments (ResolveContext ec, out Arguments args)
193 args = new Arguments (2);
195 LambdaExpression selector = new LambdaExpression (loc);
196 selector.Block = block;
197 selector.Block.AddStatement (new ContextualReturn (expr));
199 args.Add (new Argument (selector));
202 protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
204 return new QueryExpressionInvocation (
205 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
208 protected Invocation CreateQueryExpression (Expression lSide, TypeArguments typeArguments, Arguments arguments)
210 return new QueryExpressionInvocation (
211 new QueryExpressionAccess (lSide, MethodName, typeArguments, loc), arguments);
214 protected abstract string MethodName { get; }
216 public virtual AQueryClause Next {
217 set {
218 next = value;
222 public AQueryClause Tail {
223 get {
224 return next == null ? this : next.Tail;
230 // A query clause with an identifier (range variable)
232 abstract class ARangeVariableQueryClause : AQueryClause
234 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
236 public RangeAnonymousTypeParameter (Expression initializer, LocatedToken parameter)
237 : base (initializer, parameter.Value, parameter.Location)
241 protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
243 ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
244 Name, initializer);
248 protected ARangeVariableQueryClause (ToplevelBlock block, Expression expr)
249 : base (block, expr, expr.Location)
253 protected static Expression CreateRangeVariableType (ToplevelBlock block, IMemberContext context, LocatedToken name, Expression init)
255 ArrayList args = new ArrayList (2);
256 args.Add (new AnonymousTypeParameter (block.Parameters [0]));
257 args.Add (new RangeAnonymousTypeParameter (init, name));
258 return new AnonymousTypeDeclaration (args, context.CurrentTypeDefinition, name.Location);
262 class QueryStartClause : AQueryClause
264 public QueryStartClause (Expression expr)
265 : base (null, expr, expr.Location)
269 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide)
271 return next.BuildQueryClause (ec, expr);
274 public override Expression DoResolve (ResolveContext ec)
276 Expression e = BuildQueryClause (ec, null);
277 return e.Resolve (ec);
280 protected override string MethodName {
281 get { throw new NotSupportedException (); }
285 class Cast : QueryStartClause
287 // We don't have to clone cast type
288 readonly FullNamedExpression type_expr;
290 public Cast (FullNamedExpression type, Expression expr)
291 : base (expr)
293 this.type_expr = type;
296 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide)
298 lSide = CreateQueryExpression (expr, new TypeArguments (type_expr), null);
299 if (next != null)
300 return next.BuildQueryClause (ec, lSide);
302 return lSide;
305 protected override string MethodName {
306 get { return "Cast"; }
310 class GroupBy : AQueryClause
312 Expression element_selector;
313 ToplevelBlock element_block;
315 public GroupBy (ToplevelBlock block, Expression elementSelector, ToplevelBlock elementBlock, Expression keySelector, Location loc)
316 : base (block, keySelector, loc)
319 // Optimizes clauses like `group A by A'
321 if (!elementSelector.Equals (keySelector)) {
322 this.element_selector = elementSelector;
323 this.element_block = elementBlock;
327 protected override void CreateArguments (ResolveContext ec, out Arguments args)
329 base.CreateArguments (ec, out args);
331 if (element_selector != null) {
332 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
333 lambda.Block = element_block;
334 lambda.Block.AddStatement (new ContextualReturn (element_selector));
335 args.Add (new Argument (lambda));
339 protected override void CloneTo (CloneContext clonectx, Expression target)
341 GroupBy t = (GroupBy) target;
342 if (element_selector != null) {
343 t.element_selector = element_selector.Clone (clonectx);
344 t.element_block = (ToplevelBlock) element_block.Clone (clonectx);
347 base.CloneTo (clonectx, t);
350 protected override string MethodName {
351 get { return "GroupBy"; }
355 class Join : ARangeVariableQueryClause
357 readonly LocatedToken lt;
358 ToplevelBlock inner_selector, outer_selector;
360 public Join (ToplevelBlock block, LocatedToken lt, Expression inner, ToplevelBlock outerSelector, ToplevelBlock innerSelector, Location loc)
361 : base (block, inner)
363 this.lt = lt;
364 this.outer_selector = outerSelector;
365 this.inner_selector = innerSelector;
368 protected override void CreateArguments (ResolveContext ec, out Arguments args)
370 args = new Arguments (4);
372 args.Add (new Argument (expr));
374 LambdaExpression lambda = new LambdaExpression (outer_selector.StartLocation);
375 lambda.Block = outer_selector;
376 args.Add (new Argument (lambda));
378 lambda = new LambdaExpression (inner_selector.StartLocation);
379 lambda.Block = inner_selector;
380 args.Add (new Argument (lambda));
382 Expression result_selector_expr;
383 LocatedToken into_variable = GetIntoVariable ();
385 // When select follows use is as result selector
387 if (next is Select) {
388 result_selector_expr = next.Expr;
389 next = next.next;
390 } else {
391 result_selector_expr = CreateRangeVariableType (block, ec.MemberContext, into_variable,
392 new SimpleName (into_variable.Value, into_variable.Location));
395 LambdaExpression result_selector = new LambdaExpression (lt.Location);
396 result_selector.Block = new QueryBlock (ec.Compiler, block.Parent, block.Parameters, into_variable, block.StartLocation);
397 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
399 args.Add (new Argument (result_selector));
402 protected virtual LocatedToken GetIntoVariable ()
404 return lt;
407 protected override void CloneTo (CloneContext clonectx, Expression target)
409 Join t = (Join) target;
410 t.inner_selector = (ToplevelBlock) inner_selector.Clone (clonectx);
411 t.outer_selector = (ToplevelBlock) outer_selector.Clone (clonectx);
412 base.CloneTo (clonectx, t);
415 protected override string MethodName {
416 get { return "Join"; }
420 class GroupJoin : Join
422 readonly LocatedToken into;
424 public GroupJoin (ToplevelBlock block, LocatedToken lt, Expression inner,
425 ToplevelBlock outerSelector, ToplevelBlock innerSelector, LocatedToken into, Location loc)
426 : base (block, lt, inner, outerSelector, innerSelector, loc)
428 this.into = into;
431 protected override LocatedToken GetIntoVariable ()
433 return into;
436 protected override string MethodName {
437 get { return "GroupJoin"; }
441 class Let : ARangeVariableQueryClause
443 public Let (ToplevelBlock block, TypeContainer container, LocatedToken identifier, Expression expr)
444 : base (block, CreateRangeVariableType (block, container, identifier, expr))
448 protected override string MethodName {
449 get { return "Select"; }
453 class Select : AQueryClause
455 public Select (ToplevelBlock block, Expression expr, Location loc)
456 : base (block, expr, loc)
461 // For queries like `from a orderby a select a'
462 // the projection is transparent and select clause can be safely removed
464 public bool IsRequired {
465 get {
466 SimpleName sn = expr as SimpleName;
467 if (sn == null)
468 return true;
470 return sn.Name != block.Parameters.FixedParameters [0].Name;
474 protected override string MethodName {
475 get { return "Select"; }
479 class SelectMany : ARangeVariableQueryClause
481 LocatedToken lt;
483 public SelectMany (ToplevelBlock block, LocatedToken lt, Expression expr)
484 : base (block, expr)
486 this.lt = lt;
489 protected override void CreateArguments (ResolveContext ec, out Arguments args)
491 base.CreateArguments (ec, out args);
493 Expression result_selector_expr;
495 // When select follow use is as result selector
497 if (next is Select) {
498 result_selector_expr = next.Expr;
499 next = next.next;
500 } else {
501 result_selector_expr = CreateRangeVariableType (block, ec.MemberContext, lt, new SimpleName (lt.Value, lt.Location));
504 LambdaExpression result_selector = new LambdaExpression (lt.Location);
505 result_selector.Block = new QueryBlock (ec.Compiler, block.Parent, block.Parameters, lt, block.StartLocation);
506 result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
508 args.Add (new Argument (result_selector));
511 protected override string MethodName {
512 get { return "SelectMany"; }
516 class Where : AQueryClause
518 public Where (ToplevelBlock block, BooleanExpression expr, Location loc)
519 : base (block, expr, loc)
523 protected override string MethodName {
524 get { return "Where"; }
528 class OrderByAscending : AQueryClause
530 public OrderByAscending (ToplevelBlock block,Expression expr)
531 : base (block, expr, expr.Location)
535 protected override string MethodName {
536 get { return "OrderBy"; }
540 class OrderByDescending : AQueryClause
542 public OrderByDescending (ToplevelBlock block, Expression expr)
543 : base (block, expr, expr.Location)
547 protected override string MethodName {
548 get { return "OrderByDescending"; }
552 class ThenByAscending : OrderByAscending
554 public ThenByAscending (ToplevelBlock block, Expression expr)
555 : base (block, expr)
559 protected override string MethodName {
560 get { return "ThenBy"; }
564 class ThenByDescending : OrderByDescending
566 public ThenByDescending (ToplevelBlock block, Expression expr)
567 : base (block, expr)
571 protected override string MethodName {
572 get { return "ThenByDescending"; }
577 // Implicit query block
579 class QueryBlock : ToplevelBlock
582 // Transparent parameters are used to package up the intermediate results
583 // and pass them onto next clause
585 public sealed class TransparentParameter : ImplicitLambdaParameter
587 public static int Counter;
588 const string ParameterNamePrefix = "<>__TranspIdent";
590 public readonly ParametersCompiled Parent;
591 public readonly string Identifier;
593 public TransparentParameter (ParametersCompiled parent, LocatedToken identifier)
594 : base (ParameterNamePrefix + Counter++, identifier.Location)
596 Parent = parent;
597 Identifier = identifier.Value;
600 public static void Reset ()
602 Counter = 0;
606 public sealed class ImplicitQueryParameter : ImplicitLambdaParameter
608 public ImplicitQueryParameter (string name, Location loc)
609 : base (name, loc)
614 public QueryBlock (CompilerContext ctx, Block parent, LocatedToken lt, Location start)
615 : base (ctx, parent, new ParametersCompiled (new ImplicitQueryParameter (lt.Value, lt.Location)), start)
617 if (parent != null)
618 base.CheckParentConflictName (parent.Toplevel, lt.Value, lt.Location);
621 public QueryBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, LocatedToken lt, Location start)
622 : base (ctx, parent, new ParametersCompiled (parameters [0].Clone (), new ImplicitQueryParameter (lt.Value, lt.Location)), start)
626 public QueryBlock (CompilerContext ctx, Block parent, Location start)
627 : base (ctx, parent, parent.Toplevel.Parameters.Clone (), start)
631 public void AddTransparentParameter (LocatedToken name)
633 base.CheckParentConflictName (this, name.Value, name.Location);
635 parameters = new ParametersCompiled (new TransparentParameter (parameters, name));
638 protected override bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
640 return true;
644 // Query parameter reference can include transparent parameters
646 protected override Expression GetParameterReferenceExpression (string name, Location loc)
648 Expression expr = base.GetParameterReferenceExpression (name, loc);
649 if (expr != null)
650 return expr;
652 TransparentParameter tp = parameters [0] as TransparentParameter;
653 while (tp != null) {
654 if (tp.Identifier == name)
655 break;
657 TransparentParameter tp_next = tp.Parent [0] as TransparentParameter;
658 if (tp_next == null) {
659 if (tp.Parent.GetParameterIndexByName (name) >= 0)
660 break;
663 tp = tp_next;
666 if (tp != null) {
667 expr = new SimpleName (parameters[0].Name, loc);
668 TransparentParameter tp_cursor = (TransparentParameter) parameters[0];
669 while (tp_cursor != tp) {
670 tp_cursor = (TransparentParameter) tp_cursor.Parent[0];
671 expr = new MemberAccess (expr, tp_cursor.Name);
674 return new MemberAccess (expr, name);
677 return null;
680 protected override void Error_AlreadyDeclared (Location loc, string var, string reason)
682 Report.Error (1931, loc, "A range variable `{0}' conflicts with a previous declaration of `{0}'",
683 var);
686 protected override void Error_AlreadyDeclared (Location loc, string var)
688 Report.Error (1930, loc, "A range variable `{0}' has already been declared in this scope",
689 var);
692 public override void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
694 r.Error (1948, loc, "A range variable `{0}' conflicts with a method type parameter",
695 name);