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 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 int counter
= TransparentParameter
.Counter
;
44 Expression e
= BuildQueryClause (ec
, null, null, null);
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
;
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?",
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
);
100 public bool NoExactMatch (EmitContext ec
, MethodBase method
)
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
));
123 if (!method
.IsGenericMethod
)
126 Report
.Error (1942, loc
, "Type inference failed to infer type argument for `{0}' clause. " +
127 "Try specifying the type argument explicitly",
136 public AQueryClause next
;
137 /*protected*/ public Expression expr
;
139 protected AQueryClause (Expression expr
, Location loc
)
145 protected override void CloneTo (CloneContext clonectx
, Expression target
)
147 AQueryClause t
= (AQueryClause
) target
;
149 t
.expr
= expr
.Clone (clonectx
);
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
);
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
);
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
)
229 public virtual AQueryClause Next
{
235 public AQueryClause Tail
{
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
)
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
);
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
);
290 // Parameter identifiers go to the scope
292 string[] identifiers
;
294 identifiers
= new string [] { parentParameter.Name, parameter.Name }
;
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
));
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
)
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);
362 return next
.BuildQueryClause (ec
, lSide
, parameter
, ti
);
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
);
393 return next
.BuildQueryClause (ec
, lSide
, parameter
, ti
);
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
)
455 public override AQueryClause Next
{
457 // Use select as join projection
458 if (value is Select
) {
459 projection
= value.expr
;
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
);
515 // Spread resolved initializer type
517 ((ImplicitLambdaParameter
) parameter
).Type
= type
;
523 protected override void Error_InvalidInitializer (string initializer
)
525 Report
.Error (1932, loc
, "A range variable `{0}' cannot be initialized with `{1}'",
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
)
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
;
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
{
589 element_selector
= value.expr
;
591 // Can be optimized as SelectMany element selector
600 public class Where
: AQueryClause
602 public Where (Expression expr
, Location 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
)
643 protected override string MethodName
{
644 get { return "ThenBy"; }
648 public class ThenByDescending
: OrderByDescending
650 public ThenByDescending (Expression 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
)
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
);
741 return new MemberAccess (CreateIdentifierNestingExpression (ident
), name
);
744 TransparentIdentifiersScope
FindIdentifier (string name
)
746 foreach (string s
in identifiers
) {
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
);
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)
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
);
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}'",
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",
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",