2 // linq.cs: support for query expressions
4 // Authors: Marek Safar (marek.safar@gmail.com)
6 // Licensed under the terms of the GNU GPL
8 // (C) 2007 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 public class QueryExpression : AQueryClause
25 LocatedToken variable;
27 public QueryExpression (LocatedToken variable, AQueryClause query)
28 : base (null, query.Location)
30 this.variable = variable;
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 Expression e = BuildQueryClause (ec, null, null, null);
47 protected override string MethodName {
48 get { throw new NotSupportedException (); }
52 public abstract class AQueryClause : Expression
54 class QueryExpressionAccess : MemberAccess
56 public QueryExpressionAccess (Expression expr, string methodName, Location loc)
57 : base (expr, methodName, loc)
61 public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc)
62 : base (expr, methodName, typeArguments, loc)
66 protected override Expression Error_MemberLookupFailed (Type container_type, Type qualifier_type,
67 Type queried_type, string name, string class_name, MemberTypes mt, BindingFlags bf)
69 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?",
76 class QueryExpressionInvocation : Invocation, MethodGroupExpr.IErrorHandler
78 public QueryExpressionInvocation (QueryExpressionAccess expr, ArrayList arguments)
79 : base (expr, arguments)
83 protected override MethodGroupExpr DoResolveOverload (EmitContext ec)
85 mg.CustomErrorHandler = this;
86 MethodGroupExpr rmg = mg.OverloadResolve (ec, Arguments, false, loc);
90 public bool NoExactMatch (EmitContext ec, MethodBase method)
93 ParameterData pd = TypeManager.GetParameterData (method);
94 Type source_type = pd.ExtensionMethodType;
95 if (source_type != null) {
96 Argument a = (Argument) Arguments [0];
98 if (source_type.IsGenericType && source_type.ContainsGenericParameters) {
99 TypeInferenceContext tic = new TypeInferenceContext (source_type.GetGenericArguments ());
100 tic.OutputTypeInference (ec, a.Expr, source_type);
101 if (tic.FixAllTypes ()) {
102 source_type = source_type.GetGenericTypeDefinition ().MakeGenericType (tic.InferredTypeArguments);
106 if (!Convert.ImplicitConversionExists (ec, a.Expr, source_type)) {
107 Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
108 mg.Name, TypeManager.CSharpName (a.Type));
113 if (!method.IsGenericMethod)
116 Report.Error (1942, loc, "Type inference failed to infer type argument for `{0}' clause. " +
117 "Try specifying the type argument explicitly",
126 public AQueryClause next;
127 /*protected*/ public Expression expr;
129 protected AQueryClause (Expression expr, Location loc)
135 protected override void CloneTo (CloneContext clonectx, Expression target)
137 AQueryClause t = (AQueryClause) target;
139 t.expr = expr.Clone (clonectx);
142 t.next = (AQueryClause)next.Clone (clonectx);
145 public override Expression DoResolve (EmitContext ec)
147 return expr.DoResolve (ec);
150 public virtual Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
152 ArrayList args = new ArrayList (1);
153 args.Add (CreateSelectorArgument (ec, expr, parameter, ti));
154 lSide = CreateQueryExpression (lSide, args);
156 Select s = next as Select;
157 if (s == null || s.IsRequired (parameter))
158 return next.BuildQueryClause (ec, lSide, parameter, ti);
160 // Skip transparent select clause if any clause follows
161 if (next.next != null)
162 return next.next.BuildQueryClause (ec, lSide, parameter, ti);
168 protected static Parameter CreateBlockParameter (LocatedToken li)
170 return new ImplicitQueryParameter (li);
173 protected Invocation CreateQueryExpression (Expression lSide, ArrayList arguments)
175 return new QueryExpressionInvocation (
176 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
179 protected Invocation CreateQueryExpression (Expression lSide, TypeArguments typeArguments, ArrayList arguments)
181 return new QueryExpressionInvocation (
182 new QueryExpressionAccess (lSide, MethodName, typeArguments, loc), arguments);
185 protected Argument CreateSelectorArgument (EmitContext ec, Expression expr, Parameter parameter, TransparentIdentifiersScope ti)
187 return CreateSelectorArgument (ec, expr, new Parameter [] { parameter }, ti);
190 protected Argument CreateSelectorArgument (EmitContext ec, Expression expr, Parameter[] parameters, TransparentIdentifiersScope ti)
192 Parameters p = new Parameters (parameters);
194 LambdaExpression selector = new LambdaExpression (
195 null, null, (TypeContainer)ec.TypeContainer, p, ec.CurrentBlock, loc);
196 selector.Block = new SelectorBlock (ec.CurrentBlock, p, ti, loc);
197 selector.Block.AddStatement (new Return (expr, loc));
199 if (!ec.IsInProbingMode) {
200 selector.CreateAnonymousHelpers ();
202 // TODO: I am not sure where this should be done to work
203 // correctly with anonymous containerss and was called only once
204 // FIXME: selector.RootScope == null for nested anonymous
206 if (selector.RootScope != null)
207 selector.RootScope.DefineType ();
210 return new Argument (selector);
213 public override void Emit (EmitContext ec)
215 throw new NotSupportedException ();
218 protected abstract string MethodName { get; }
220 public virtual AQueryClause Next {
226 public AQueryClause Tail {
228 return next == null ? this : next.Tail;
234 // A query clause with an identifier (range variable)
236 public abstract class ARangeVariableQueryClause : AQueryClause
238 LocatedToken variable;
239 protected Expression element_selector;
241 protected ARangeVariableQueryClause (LocatedToken variable, Expression expr, Location loc)
244 this.variable = variable;
247 protected virtual void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
248 ref Parameter parameter, TransparentIdentifiersScope ti)
250 args.Add (CreateSelectorArgument (ec, expr, parentParameter, ti));
251 args.Add (CreateSelectorArgument (ec, element_selector,
252 new Parameter [] { parentParameter, parameter }, ti));
256 // Customization for range variables which not only creates a lambda expression but
257 // also builds a chain of range varible pairs
259 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parentParameter, TransparentIdentifiersScope ti)
261 Parameter parameter = CreateBlockParameter (variable);
265 // Builds transparent identifiers, each identifier includes its parent
266 // type at index 0, and new value at index 1. This is not valid for the
267 // first one which includes two values directly.
269 ArrayList transp_args = new ArrayList (2);
270 transp_args.Add (new AnonymousTypeParameter (parentParameter));
271 transp_args.Add (CreateAnonymousTypeVariable (parameter));
272 element_selector = new AnonymousTypeDeclaration (transp_args, (TypeContainer) ec.TypeContainer, loc);
275 ArrayList args = new ArrayList ();
276 AddSelectorArguments (ec, args, parentParameter, ref parameter, ti);
278 lSide = CreateQueryExpression (lSide, args);
281 // Parameter identifiers go to the scope
283 string[] identifiers;
285 identifiers = new string [] { parentParameter.Name, parameter.Name };
287 identifiers = new string [] { parameter.Name };
290 TransparentParameter tp = new TransparentParameter (loc);
291 return next.BuildQueryClause (ec, lSide, tp,
292 new TransparentIdentifiersScope (ti, tp, identifiers));
298 protected override void CloneTo (CloneContext clonectx, Expression target)
300 ARangeVariableQueryClause t = (ARangeVariableQueryClause) target;
301 if (element_selector != null)
302 t.element_selector = element_selector.Clone (clonectx);
303 base.CloneTo (clonectx, t);
307 // For transparent identifiers, creates an instance of variable expression
309 protected virtual AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
311 return new AnonymousTypeParameter (parameter);
315 public class QueryStartClause : AQueryClause
317 public QueryStartClause (Expression expr)
318 : base (expr, expr.Location)
322 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
324 return next.BuildQueryClause (ec, expr, parameter, ti);
327 public override Expression DoResolve (EmitContext ec)
329 Expression e = BuildQueryClause (ec, null, null, null);
330 return e.Resolve (ec);
333 protected override string MethodName {
334 get { throw new NotSupportedException (); }
338 public class Cast : QueryStartClause
340 // We don't have to clone cast type
341 readonly Expression type_expr;
343 public Cast (Expression type, Expression expr)
346 this.type_expr = type;
349 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
351 lSide = CreateQueryExpression (expr, new TypeArguments (loc, type_expr), null);
353 return next.BuildQueryClause (ec, lSide, parameter, ti);
358 protected override string MethodName {
359 get { return "Cast"; }
363 public class GroupBy : AQueryClause
365 Expression element_selector;
367 public GroupBy (Expression elementSelector, Expression keySelector, Location loc)
368 : base (keySelector, loc)
370 this.element_selector = elementSelector;
373 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
375 ArrayList args = new ArrayList (2);
376 args.Add (CreateSelectorArgument (ec, expr, parameter, ti));
378 // A query can be optimized when selector is not group by specific
379 if (!element_selector.Equals (lSide))
380 args.Add (CreateSelectorArgument (ec, element_selector, parameter, ti));
382 lSide = CreateQueryExpression (lSide, args);
384 return next.BuildQueryClause (ec, lSide, parameter, ti);
389 protected override void CloneTo (CloneContext clonectx, Expression target)
391 GroupBy t = (GroupBy) target;
392 t.element_selector = element_selector.Clone (clonectx);
393 base.CloneTo (clonectx, t);
396 protected override string MethodName {
397 get { return "GroupBy"; }
401 public class Join : ARangeVariableQueryClause
403 Expression projection;
404 Expression inner_selector, outer_selector;
406 public Join (LocatedToken variable, Expression inner, Expression outerSelector, Expression innerSelector, Location loc)
407 : base (variable, inner, loc)
409 this.outer_selector = outerSelector;
410 this.inner_selector = innerSelector;
413 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
414 ref Parameter parameter, TransparentIdentifiersScope ti)
416 args.Add (new Argument (expr));
417 args.Add (CreateSelectorArgument (ec, outer_selector, parentParameter, ti));
418 args.Add (CreateSelectorArgument (ec, inner_selector, parameter, ti));
420 parameter = CreateResultSelectorParameter (parameter);
421 if (projection == null) {
422 ArrayList join_args = new ArrayList (2);
423 join_args.Add (new AnonymousTypeParameter (parentParameter));
424 join_args.Add (new AnonymousTypeParameter (parameter));
425 projection = new AnonymousTypeDeclaration (join_args, (TypeContainer) ec.TypeContainer, loc);
428 args.Add (CreateSelectorArgument (ec, projection,
429 new Parameter [] { parentParameter, parameter }, ti));
432 protected override void CloneTo (CloneContext clonectx, Expression target)
434 Join t = (Join) target;
435 t.projection = projection.Clone (clonectx);
436 t.inner_selector = inner_selector.Clone (clonectx);
437 t.outer_selector = outer_selector.Clone (clonectx);
438 base.CloneTo (clonectx, t);
441 protected virtual Parameter CreateResultSelectorParameter (Parameter parameter)
446 public override AQueryClause Next {
448 // Use select as join projection
449 if (value is Select) {
450 projection = value.expr;
459 protected override string MethodName {
460 get { return "Join"; }
464 public class GroupJoin : Join
466 readonly LocatedToken into_variable;
468 public GroupJoin (LocatedToken variable, Expression inner, Expression outerSelector, Expression innerSelector,
469 LocatedToken into, Location loc)
470 : base (variable, inner, outerSelector, innerSelector, loc)
472 this.into_variable = into;
475 protected override Parameter CreateResultSelectorParameter (Parameter parameter)
478 // into variable is used as result selector and it's passed as
479 // transparent identifiers to the next clause
481 return CreateBlockParameter (into_variable);
484 protected override string MethodName {
485 get { return "GroupJoin"; }
489 public class Let : ARangeVariableQueryClause
491 class RangeAnonymousTypeParameter : AnonymousTypeParameter
493 readonly Parameter parameter;
495 public RangeAnonymousTypeParameter (Expression initializer, Parameter parameter)
496 : base (initializer, parameter.Name, parameter.Location)
498 this.parameter = parameter;
501 public override Expression DoResolve (EmitContext ec)
503 Expression e = base.DoResolve (ec);
506 // Spread resolved initializer type
508 parameter.ParameterType = type;
509 parameter.Resolve (ec);
515 protected override void Error_InvalidInitializer (Expression initializer)
517 Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
518 Name, initializer.GetSignatureForError ());
522 public Let (LocatedToken variable, Expression expr, Location loc)
523 : base (variable, expr, loc)
527 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
528 ref Parameter parameter, TransparentIdentifiersScope ti)
530 args.Add (CreateSelectorArgument (ec, element_selector, parentParameter, ti));
533 protected override AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
535 return new RangeAnonymousTypeParameter (expr, parameter);
538 protected override string MethodName {
539 get { return "Select"; }
543 public class Select : AQueryClause
545 public Select (Expression expr, Location loc)
551 // For queries like `from a orderby a select a'
552 // the projection is transparent and select clause can be safely removed
554 public bool IsRequired (Parameter parameter)
556 SimpleName sn = expr as SimpleName;
560 return sn.Name != parameter.Name;
563 protected override string MethodName {
564 get { return "Select"; }
568 public class SelectMany : ARangeVariableQueryClause
570 public SelectMany (LocatedToken variable, Expression expr)
571 : base (variable, expr, expr.Location)
575 protected override string MethodName {
576 get { return "SelectMany"; }
579 public override AQueryClause Next {
581 element_selector = value.expr;
583 // Can be optimized as SelectMany element selector
592 public class Where : AQueryClause
594 public Where (Expression expr, Location loc)
599 protected override string MethodName {
600 get { return "Where"; }
604 public class OrderByAscending : AQueryClause
606 public OrderByAscending (Expression expr)
607 : base (expr, expr.Location)
611 protected override string MethodName {
612 get { return "OrderBy"; }
616 public class OrderByDescending : AQueryClause
618 public OrderByDescending (Expression expr)
619 : base (expr, expr.Location)
623 protected override string MethodName {
624 get { return "OrderByDescending"; }
628 public class ThenByAscending : OrderByAscending
630 public ThenByAscending (Expression expr)
635 protected override string MethodName {
636 get { return "ThenBy"; }
640 public class ThenByDescending : OrderByDescending
642 public ThenByDescending (Expression expr)
647 protected override string MethodName {
648 get { return "ThenByDescending"; }
652 class ImplicitQueryParameter : ImplicitLambdaParameter
654 public sealed class ImplicitType : Expression
656 public static ImplicitType Instance = new ImplicitType ();
658 private ImplicitType ()
662 protected override void CloneTo (CloneContext clonectx, Expression target)
667 public override Expression DoResolve (EmitContext ec)
669 throw new NotSupportedException ();
672 public override void Emit (EmitContext ec)
674 throw new NotSupportedException ();
678 public ImplicitQueryParameter (LocatedToken variable)
679 : base (variable.Value, variable.Location)
685 // Transparent parameters are used to package up the intermediate results
686 // and pass them onto next clause
688 public class TransparentParameter : ImplicitLambdaParameter
691 const string ParameterNamePrefix = "<>__TranspIdent";
693 public TransparentParameter (Location loc)
694 : base (ParameterNamePrefix + counter++, loc)
700 // Transparent identifiers are stored in nested anonymous types, each type can contain
701 // up to 2 identifiers or 1 identifier and parent type.
703 public class TransparentIdentifiersScope
705 readonly string [] identifiers;
706 readonly TransparentIdentifiersScope parent;
707 readonly TransparentParameter parameter;
709 public TransparentIdentifiersScope (TransparentIdentifiersScope parent,
710 TransparentParameter parameter, string [] identifiers)
712 this.parent = parent;
713 this.parameter = parameter;
714 this.identifiers = identifiers;
717 public MemberAccess GetIdentifier (string name)
719 TransparentIdentifiersScope ident = FindIdentifier (name);
723 return new MemberAccess (CreateIdentifierNestingExpression (ident), name);
726 TransparentIdentifiersScope FindIdentifier (string name)
728 foreach (string s in identifiers) {
736 return parent.FindIdentifier (name);
739 Expression CreateIdentifierNestingExpression (TransparentIdentifiersScope end)
741 Expression expr = new SimpleName (parameter.Name, parameter.Location);
742 TransparentIdentifiersScope current = this;
743 while (current != end)
745 current = current.parent;
746 expr = new MemberAccess (expr, current.parameter.Name);
754 // Lambda expression block which contains transparent identifiers
756 class SelectorBlock : ToplevelBlock
758 readonly TransparentIdentifiersScope transparent_identifiers;
760 public SelectorBlock (Block block, Parameters parameters,
761 TransparentIdentifiersScope transparentIdentifiers, Location loc)
762 : base (block, parameters, loc)
764 this.transparent_identifiers = transparentIdentifiers;
767 public override Expression GetTransparentIdentifier (string name)
769 Expression expr = null;
770 if (transparent_identifiers != null)
771 expr = transparent_identifiers.GetIdentifier (name);
773 if (expr != null || Container == null)
776 return Container.GetTransparentIdentifier (name);
781 // This block is actually never used, it is used by parser only
783 public class QueryBlock : Block
785 Hashtable range_variables = new Hashtable ();
787 public QueryBlock (Block parent, Location start)
788 : base (parent, start, Location.Null)
792 protected override void AddVariable (LocalInfo li)
794 string name = li.Name;
795 if (range_variables.Contains (name)) {
796 Location conflict = (Location)range_variables [name];
797 Report.SymbolRelatedToPreviousError (conflict, name);
798 Error_AlreadyDeclared (li.Location, name);
802 range_variables.Add (name, li.Location);
805 protected override void Error_AlreadyDeclared (Location loc, string var, string reason)
807 Report.Error (1931, loc, "A range variable `{0}' conflicts with a previous declaration of `{0}'",
811 protected override void Error_AlreadyDeclared (Location loc, string var)
813 Report.Error (1930, loc, "A range variable `{0}' has already been declared in this scope",
817 protected override void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
819 Report.Error (1948, loc, "A range variable `{0}' conflicts with a method type parameter",