more docs
[mcs.git] / mcs / linq.cs
blob610f0f499fc72ce6e4525d6a8b6d4845cbfe2b62
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 public class QueryExpression : AQueryClause
25 LocatedToken variable;
27 public QueryExpression (LocatedToken variable, AQueryClause query)
28 : base (null, query.Location)
30 this.variable = variable;
31 this.next = query;
34 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parentParameter, TransparentIdentifiersScope ti)
36 Parameter p = CreateBlockParameter (variable);
37 return next.BuildQueryClause (ec, lSide, p, ti);
40 public override Expression DoResolve (EmitContext ec)
42 int counter = TransparentParameter.Counter;
44 Expression e = BuildQueryClause (ec, null, null, null);
45 e = e.Resolve (ec);
48 // Reset counter in probing mode to ensure that all transparent
49 // identifier anonymous types are created only once
51 if (ec.IsInProbingMode)
52 TransparentParameter.Counter = counter;
54 return e;
57 protected override string MethodName {
58 get { throw new NotSupportedException (); }
62 public abstract class AQueryClause : Expression
64 class QueryExpressionAccess : MemberAccess
66 public QueryExpressionAccess (Expression expr, string methodName, Location loc)
67 : base (expr, methodName, loc)
71 public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc)
72 : base (expr, methodName, typeArguments, loc)
76 protected override Expression Error_MemberLookupFailed (Type container_type, Type qualifier_type,
77 Type queried_type, string name, string class_name, MemberTypes mt, BindingFlags bf)
79 Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " +
80 "Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?",
81 name);
82 return null;
86 class QueryExpressionInvocation : Invocation, MethodGroupExpr.IErrorHandler
88 public QueryExpressionInvocation (QueryExpressionAccess expr, ArrayList arguments)
89 : base (expr, arguments)
93 protected override MethodGroupExpr DoResolveOverload (EmitContext ec)
95 mg.CustomErrorHandler = this;
96 MethodGroupExpr rmg = mg.OverloadResolve (ec, ref Arguments, false, loc);
97 return rmg;
100 public bool NoExactMatch (EmitContext ec, MethodBase method)
102 #if GMCS_SOURCE
103 AParametersCollection pd = TypeManager.GetParameterData (method);
104 Type source_type = pd.ExtensionMethodType;
105 if (source_type != null) {
106 Argument a = (Argument) Arguments [0];
108 if (source_type.IsGenericType && source_type.ContainsGenericParameters) {
109 TypeInferenceContext tic = new TypeInferenceContext (source_type.GetGenericArguments ());
110 tic.OutputTypeInference (ec, a.Expr, source_type);
111 if (tic.FixAllTypes ()) {
112 source_type = source_type.GetGenericTypeDefinition ().MakeGenericType (tic.InferredTypeArguments);
116 if (!Convert.ImplicitConversionExists (ec, a.Expr, source_type)) {
117 Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
118 mg.Name, TypeManager.CSharpName (a.Type));
119 return true;
123 if (!method.IsGenericMethod)
124 return false;
126 Report.Error (1942, loc, "Type inference failed to infer type argument for `{0}' clause. " +
127 "Try specifying the type argument explicitly",
128 mg.Name.ToLower ());
129 return true;
130 #else
131 return false;
132 #endif
136 public AQueryClause next;
137 /*protected*/ public Expression expr;
139 protected AQueryClause (Expression expr, Location loc)
141 this.expr = expr;
142 this.loc = loc;
145 protected override void CloneTo (CloneContext clonectx, Expression target)
147 AQueryClause t = (AQueryClause) target;
148 if (expr != null)
149 t.expr = expr.Clone (clonectx);
151 if (next != null)
152 t.next = (AQueryClause)next.Clone (clonectx);
155 public override Expression CreateExpressionTree (EmitContext ec)
157 // Should not be reached
158 throw new NotSupportedException ("ET");
161 public override Expression DoResolve (EmitContext ec)
163 return expr.DoResolve (ec);
166 public virtual Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
168 ArrayList args = new ArrayList (1);
169 args.Add (CreateSelectorArgument (ec, expr, parameter, ti));
170 lSide = CreateQueryExpression (lSide, args);
171 if (next != null) {
172 Select s = next as Select;
173 if (s == null || s.IsRequired (parameter))
174 return next.BuildQueryClause (ec, lSide, parameter, ti);
176 // Skip transparent select clause if any clause follows
177 if (next.next != null)
178 return next.next.BuildQueryClause (ec, lSide, parameter, ti);
181 return lSide;
184 protected static Parameter CreateBlockParameter (LocatedToken li)
186 return new ImplicitQueryParameter (li);
189 protected Invocation CreateQueryExpression (Expression lSide, ArrayList arguments)
191 return new QueryExpressionInvocation (
192 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
195 protected Invocation CreateQueryExpression (Expression lSide, TypeArguments typeArguments, ArrayList arguments)
197 return new QueryExpressionInvocation (
198 new QueryExpressionAccess (lSide, MethodName, typeArguments, loc), arguments);
201 protected Argument CreateSelectorArgument (EmitContext ec, Expression expr, Parameter parameter, TransparentIdentifiersScope ti)
203 return CreateSelectorArgument (ec, expr, new Parameter [] { parameter }, ti);
206 protected Argument CreateSelectorArgument (EmitContext ec, Expression expr, Parameter[] parameters, TransparentIdentifiersScope ti)
208 Parameters p = new Parameters (parameters);
210 LambdaExpression selector = new LambdaExpression (p, loc);
211 selector.Block = new SelectorBlock (ec.CurrentBlock, p, ti, loc);
212 selector.Block.AddStatement (new ContextualReturn (expr));
214 return new Argument (selector);
217 public override void Emit (EmitContext ec)
219 throw new NotSupportedException ();
222 protected abstract string MethodName { get; }
224 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
226 // Nothing to mutate
229 public virtual AQueryClause Next {
230 set {
231 next = value;
235 public AQueryClause Tail {
236 get {
237 return next == null ? this : next.Tail;
243 // A query clause with an identifier (range variable)
245 public abstract class ARangeVariableQueryClause : AQueryClause
247 LocatedToken variable;
248 protected Expression element_selector;
250 protected ARangeVariableQueryClause (LocatedToken variable, Expression expr, Location loc)
251 : base (expr, loc)
253 this.variable = variable;
256 protected virtual void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
257 ref Parameter parameter, TransparentIdentifiersScope ti)
259 args.Add (CreateSelectorArgument (ec, expr, parentParameter, ti));
260 args.Add (CreateSelectorArgument (ec, element_selector,
261 new Parameter [] { parentParameter, parameter }, ti));
265 // Customization for range variables which not only creates a lambda expression but
266 // also builds a chain of range varible pairs
268 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parentParameter, TransparentIdentifiersScope ti)
270 Parameter parameter = CreateBlockParameter (variable);
272 if (next != null) {
274 // Builds transparent identifiers, each identifier includes its parent
275 // type at index 0, and new value at index 1. This is not valid for the
276 // first one which includes two values directly.
278 ArrayList transp_args = new ArrayList (2);
279 transp_args.Add (new AnonymousTypeParameter (parentParameter));
280 transp_args.Add (CreateAnonymousTypeVariable (parameter));
281 element_selector = new AnonymousTypeDeclaration (transp_args, (TypeContainer) ec.TypeContainer, loc);
284 ArrayList args = new ArrayList ();
285 AddSelectorArguments (ec, args, parentParameter, ref parameter, ti);
287 lSide = CreateQueryExpression (lSide, args);
288 if (next != null) {
290 // Parameter identifiers go to the scope
292 string[] identifiers;
293 if (ti == null) {
294 identifiers = new string [] { parentParameter.Name, parameter.Name };
295 } else {
296 identifiers = new string [] { parameter.Name };
299 TransparentParameter tp = new TransparentParameter (loc);
300 return next.BuildQueryClause (ec, lSide, tp,
301 new TransparentIdentifiersScope (ti, tp, identifiers));
304 return lSide;
307 protected override void CloneTo (CloneContext clonectx, Expression target)
309 ARangeVariableQueryClause t = (ARangeVariableQueryClause) target;
310 if (element_selector != null)
311 t.element_selector = element_selector.Clone (clonectx);
312 base.CloneTo (clonectx, t);
316 // For transparent identifiers, creates an instance of variable expression
318 protected virtual AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
320 return new AnonymousTypeParameter (parameter);
324 public class QueryStartClause : AQueryClause
326 public QueryStartClause (Expression expr)
327 : base (expr, expr.Location)
331 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
333 return next.BuildQueryClause (ec, expr, parameter, ti);
336 public override Expression DoResolve (EmitContext ec)
338 Expression e = BuildQueryClause (ec, null, null, null);
339 return e.Resolve (ec);
342 protected override string MethodName {
343 get { throw new NotSupportedException (); }
347 public class Cast : QueryStartClause
349 // We don't have to clone cast type
350 readonly Expression type_expr;
352 public Cast (Expression type, Expression expr)
353 : base (expr)
355 this.type_expr = type;
358 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
360 lSide = CreateQueryExpression (expr, new TypeArguments (loc, type_expr), null);
361 if (next != null)
362 return next.BuildQueryClause (ec, lSide, parameter, ti);
364 return lSide;
367 protected override string MethodName {
368 get { return "Cast"; }
372 public class GroupBy : AQueryClause
374 Expression element_selector;
376 public GroupBy (Expression elementSelector, Expression keySelector, Location loc)
377 : base (keySelector, loc)
379 this.element_selector = elementSelector;
382 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
384 ArrayList args = new ArrayList (2);
385 args.Add (CreateSelectorArgument (ec, expr, parameter, ti));
387 // A query can be optimized when selector is not group by specific
388 if (!element_selector.Equals (lSide))
389 args.Add (CreateSelectorArgument (ec, element_selector, parameter, ti));
391 lSide = CreateQueryExpression (lSide, args);
392 if (next != null)
393 return next.BuildQueryClause (ec, lSide, parameter, ti);
395 return lSide;
398 protected override void CloneTo (CloneContext clonectx, Expression target)
400 GroupBy t = (GroupBy) target;
401 t.element_selector = element_selector.Clone (clonectx);
402 base.CloneTo (clonectx, t);
405 protected override string MethodName {
406 get { return "GroupBy"; }
410 public class Join : ARangeVariableQueryClause
412 Expression projection;
413 Expression inner_selector, outer_selector;
415 public Join (LocatedToken variable, Expression inner, Expression outerSelector, Expression innerSelector, Location loc)
416 : base (variable, inner, loc)
418 this.outer_selector = outerSelector;
419 this.inner_selector = innerSelector;
422 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
423 ref Parameter parameter, TransparentIdentifiersScope ti)
425 args.Add (new Argument (expr));
426 args.Add (CreateSelectorArgument (ec, outer_selector, parentParameter, ti));
427 args.Add (CreateSelectorArgument (ec, inner_selector, parameter, ti));
429 parameter = CreateResultSelectorParameter (parameter);
430 if (projection == null) {
431 ArrayList join_args = new ArrayList (2);
432 join_args.Add (new AnonymousTypeParameter (parentParameter));
433 join_args.Add (new AnonymousTypeParameter (parameter));
434 projection = new AnonymousTypeDeclaration (join_args, (TypeContainer) ec.TypeContainer, loc);
437 args.Add (CreateSelectorArgument (ec, projection,
438 new Parameter [] { parentParameter, parameter }, ti));
441 protected override void CloneTo (CloneContext clonectx, Expression target)
443 Join t = (Join) target;
444 t.projection = projection.Clone (clonectx);
445 t.inner_selector = inner_selector.Clone (clonectx);
446 t.outer_selector = outer_selector.Clone (clonectx);
447 base.CloneTo (clonectx, t);
450 protected virtual Parameter CreateResultSelectorParameter (Parameter parameter)
452 return parameter;
455 public override AQueryClause Next {
456 set {
457 // Use select as join projection
458 if (value is Select) {
459 projection = value.expr;
460 next = value.next;
461 return;
464 base.Next = value;
468 protected override string MethodName {
469 get { return "Join"; }
473 public class GroupJoin : Join
475 readonly LocatedToken into_variable;
477 public GroupJoin (LocatedToken variable, Expression inner, Expression outerSelector, Expression innerSelector,
478 LocatedToken into, Location loc)
479 : base (variable, inner, outerSelector, innerSelector, loc)
481 this.into_variable = into;
484 protected override Parameter CreateResultSelectorParameter (Parameter parameter)
487 // into variable is used as result selector and it's passed as
488 // transparent identifiers to the next clause
490 return CreateBlockParameter (into_variable);
493 protected override string MethodName {
494 get { return "GroupJoin"; }
498 public class Let : ARangeVariableQueryClause
500 class RangeAnonymousTypeParameter : AnonymousTypeParameter
502 readonly Parameter parameter;
504 public RangeAnonymousTypeParameter (Expression initializer, Parameter parameter)
505 : base (initializer, parameter.Name, parameter.Location)
507 this.parameter = parameter;
510 public override Expression DoResolve (EmitContext ec)
512 Expression e = base.DoResolve (ec);
513 if (e != null) {
515 // Spread resolved initializer type
517 ((ImplicitLambdaParameter) parameter).Type = type;
520 return e;
523 protected override void Error_InvalidInitializer (string initializer)
525 Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
526 Name, initializer);
530 public Let (LocatedToken variable, Expression expr, Location loc)
531 : base (variable, expr, loc)
535 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
536 ref Parameter parameter, TransparentIdentifiersScope ti)
538 args.Add (CreateSelectorArgument (ec, element_selector, parentParameter, ti));
541 protected override AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
543 return new RangeAnonymousTypeParameter (expr, parameter);
546 protected override string MethodName {
547 get { return "Select"; }
551 public class Select : AQueryClause
553 public Select (Expression expr, Location loc)
554 : base (expr, loc)
559 // For queries like `from a orderby a select a'
560 // the projection is transparent and select clause can be safely removed
562 public bool IsRequired (Parameter parameter)
564 SimpleName sn = expr as SimpleName;
565 if (sn == null)
566 return true;
568 return sn.Name != parameter.Name;
571 protected override string MethodName {
572 get { return "Select"; }
576 public class SelectMany : ARangeVariableQueryClause
578 public SelectMany (LocatedToken variable, Expression expr)
579 : base (variable, expr, expr.Location)
583 protected override string MethodName {
584 get { return "SelectMany"; }
587 public override AQueryClause Next {
588 set {
589 element_selector = value.expr;
591 // Can be optimized as SelectMany element selector
592 if (value is Select)
593 return;
595 next = value;
600 public class Where : AQueryClause
602 public Where (Expression expr, Location loc)
603 : base (expr, loc)
607 protected override string MethodName {
608 get { return "Where"; }
612 public class OrderByAscending : AQueryClause
614 public OrderByAscending (Expression expr)
615 : base (expr, expr.Location)
619 protected override string MethodName {
620 get { return "OrderBy"; }
624 public class OrderByDescending : AQueryClause
626 public OrderByDescending (Expression expr)
627 : base (expr, expr.Location)
631 protected override string MethodName {
632 get { return "OrderByDescending"; }
636 public class ThenByAscending : OrderByAscending
638 public ThenByAscending (Expression expr)
639 : base (expr)
643 protected override string MethodName {
644 get { return "ThenBy"; }
648 public class ThenByDescending : OrderByDescending
650 public ThenByDescending (Expression expr)
651 : base (expr)
655 protected override string MethodName {
656 get { return "ThenByDescending"; }
660 class ImplicitQueryParameter : ImplicitLambdaParameter
662 public sealed class ImplicitType : Expression
664 public static ImplicitType Instance = new ImplicitType ();
666 private ImplicitType ()
670 protected override void CloneTo (CloneContext clonectx, Expression target)
672 // Nothing to clone
675 public override Expression CreateExpressionTree (EmitContext ec)
677 throw new NotSupportedException ();
680 public override Expression DoResolve (EmitContext ec)
682 throw new NotSupportedException ();
685 public override void Emit (EmitContext ec)
687 throw new NotSupportedException ();
690 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
692 throw new NotSupportedException ();
696 public ImplicitQueryParameter (LocatedToken variable)
697 : base (variable.Value, variable.Location)
703 // Transparent parameters are used to package up the intermediate results
704 // and pass them onto next clause
706 public class TransparentParameter : ImplicitLambdaParameter
708 public static int Counter;
709 const string ParameterNamePrefix = "<>__TranspIdent";
711 public TransparentParameter (Location loc)
712 : base (ParameterNamePrefix + Counter++, loc)
718 // Transparent identifiers are stored in nested anonymous types, each type can contain
719 // up to 2 identifiers or 1 identifier and parent type.
721 public class TransparentIdentifiersScope
723 readonly string [] identifiers;
724 readonly TransparentIdentifiersScope parent;
725 readonly TransparentParameter parameter;
727 public TransparentIdentifiersScope (TransparentIdentifiersScope parent,
728 TransparentParameter parameter, string [] identifiers)
730 this.parent = parent;
731 this.parameter = parameter;
732 this.identifiers = identifiers;
735 public MemberAccess GetIdentifier (string name)
737 TransparentIdentifiersScope ident = FindIdentifier (name);
738 if (ident == null)
739 return null;
741 return new MemberAccess (CreateIdentifierNestingExpression (ident), name);
744 TransparentIdentifiersScope FindIdentifier (string name)
746 foreach (string s in identifiers) {
747 if (s == name)
748 return this;
751 if (parent == null)
752 return null;
754 return parent.FindIdentifier (name);
757 Expression CreateIdentifierNestingExpression (TransparentIdentifiersScope end)
759 Expression expr = new SimpleName (parameter.Name, parameter.Location);
760 TransparentIdentifiersScope current = this;
761 while (current != end)
763 current = current.parent;
764 expr = new MemberAccess (expr, current.parameter.Name);
767 return expr;
772 // Lambda expression block which contains transparent identifiers
774 class SelectorBlock : ToplevelBlock
776 readonly TransparentIdentifiersScope transparent_identifiers;
778 public SelectorBlock (Block block, Parameters parameters,
779 TransparentIdentifiersScope transparentIdentifiers, Location loc)
780 : base (block, parameters, loc)
782 this.transparent_identifiers = transparentIdentifiers;
785 public override Expression GetTransparentIdentifier (string name)
787 Expression expr = null;
788 if (transparent_identifiers != null)
789 expr = transparent_identifiers.GetIdentifier (name);
791 if (expr != null || Container == null)
792 return expr;
794 return Container.GetTransparentIdentifier (name);
799 // This block is actually never used, it is used by parser only
801 public class QueryBlock : ExplicitBlock
803 public QueryBlock (Block parent, Location start)
804 : base (parent, start, Location.Null)
806 range_variables = new System.Collections.Specialized.HybridDictionary ();
809 protected override void AddVariable (LocalInfo li)
811 string name = li.Name;
812 if (range_variables.Contains (name)) {
813 LocalInfo conflict = (LocalInfo) range_variables [name];
814 Report.SymbolRelatedToPreviousError (conflict.Location, name);
815 Error_AlreadyDeclared (li.Location, name);
816 return;
819 range_variables.Add (name, li);
820 Explicit.AddKnownVariable (name, li);
823 protected override void Error_AlreadyDeclared (Location loc, string var, string reason)
825 Report.Error (1931, loc, "A range variable `{0}' conflicts with a previous declaration of `{0}'",
826 var);
829 protected override void Error_AlreadyDeclared (Location loc, string var)
831 Report.Error (1930, loc, "A range variable `{0}' has already been declared in this scope",
832 var);
835 protected override void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
837 Report.Error (1948, loc, "A range variable `{0}' conflicts with a method type parameter",
838 name);