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 class QueryExpression
: AQueryClause
25 public QueryExpression (Block block
, AQueryClause query
)
26 : base (null, null, query
.Location
)
31 public override Expression
BuildQueryClause (EmitContext ec
, Expression lSide
)
33 return next
.BuildQueryClause (ec
, lSide
);
36 public override Expression
DoResolve (EmitContext ec
)
38 int counter
= QueryBlock
.TransparentParameter
.Counter
;
40 Expression e
= BuildQueryClause (ec
, null);
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
;
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 (Type container_type
, Type qualifier_type
,
73 Type queried_type
, string name
, string class_name
, MemberTypes mt
, BindingFlags bf
)
75 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?",
82 class QueryExpressionInvocation
: Invocation
, MethodGroupExpr
.IErrorHandler
84 public QueryExpressionInvocation (QueryExpressionAccess expr
, ArrayList arguments
)
85 : base (expr
, arguments
)
89 protected override MethodGroupExpr
DoResolveOverload (EmitContext ec
)
91 mg
.CustomErrorHandler
= this;
92 MethodGroupExpr rmg
= mg
.OverloadResolve (ec
, ref Arguments
, false, loc
);
96 public bool AmbiguousCall (MethodBase ambiguous
)
98 Report
.SymbolRelatedToPreviousError ((MethodInfo
) mg
);
99 Report
.SymbolRelatedToPreviousError (ambiguous
);
100 Report
.Error (1940, loc
, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
101 mg
.Name
, mg
.InstanceExpression
.GetSignatureForError ());
105 public bool NoExactMatch (EmitContext ec
, MethodBase method
)
107 AParametersCollection pd
= TypeManager
.GetParameterData (method
);
108 Type source_type
= pd
.ExtensionMethodType
;
109 if (source_type
!= null) {
110 Argument a
= (Argument
) Arguments
[0];
112 if (TypeManager
.IsGenericType (source_type
) && TypeManager
.ContainsGenericParameters (source_type
)) {
114 TypeInferenceContext tic
= new TypeInferenceContext (TypeManager
.GetTypeArguments (source_type
));
115 tic
.OutputTypeInference (ec
, a
.Expr
, source_type
);
116 if (tic
.FixAllTypes ()) {
117 source_type
= TypeManager
.DropGenericTypeArguments (source_type
).MakeGenericType (tic
.InferredTypeArguments
);
120 throw new NotSupportedException ();
124 if (!Convert
.ImplicitConversionExists (ec
, a
.Expr
, source_type
)) {
125 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
));
131 if (!TypeManager
.IsGenericMethod (method
))
134 if (mg
.Name
== "SelectMany") {
135 Report
.Error (1943, loc
,
136 "An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
137 ((Argument
) Arguments
[0]).GetSignatureForError ());
139 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
);
149 public AQueryClause next
;
150 public Expression expr
;
151 protected ToplevelBlock block
;
153 protected AQueryClause (ToplevelBlock block
, Expression expr
, Location loc
)
160 protected override void CloneTo (CloneContext clonectx
, Expression target
)
162 AQueryClause t
= (AQueryClause
) target
;
164 t
.expr
= expr
.Clone (clonectx
);
167 t
.block
= (ToplevelBlock
) block
.Clone (clonectx
);
170 t
.next
= (AQueryClause
) next
.Clone (clonectx
);
173 public override Expression
CreateExpressionTree (EmitContext ec
)
175 // Should not be reached
176 throw new NotSupportedException ("ET");
179 public override Expression
DoResolve (EmitContext ec
)
181 return expr
.DoResolve (ec
);
184 public virtual Expression
BuildQueryClause (EmitContext ec
, Expression lSide
)
187 CreateArguments (ec
, out args
);
188 lSide
= CreateQueryExpression (lSide
, args
);
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
);
202 protected virtual void CreateArguments (EmitContext ec
, out ArrayList args
)
204 args
= new ArrayList (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
, ArrayList arguments
)
215 return new QueryExpressionInvocation (
216 new QueryExpressionAccess (lSide
, MethodName
, loc
), arguments
);
219 protected Invocation
CreateQueryExpression (Expression lSide
, TypeArguments typeArguments
, ArrayList 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
)
237 public virtual AQueryClause Next
{
243 public AQueryClause Tail
{
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 (string initializer
)
264 Report
.Error (1932, loc
, "A range variable `{0}' cannot be initialized with `{1}'",
269 protected ARangeVariableQueryClause (ToplevelBlock block
, Expression expr
)
270 : base (block
, expr
, expr
.Location
)
274 protected static Expression
CreateRangeVariableType (ToplevelBlock block
, TypeContainer container
, 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 AnonymousTypeDeclaration (args
, container
, name
.Location
);
283 class QueryStartClause
: AQueryClause
285 public QueryStartClause (Expression expr
)
286 : base (null, expr
, expr
.Location
)
290 public override Expression
BuildQueryClause (EmitContext ec
, Expression lSide
)
292 return next
.BuildQueryClause (ec
, expr
);
295 public override Expression
DoResolve (EmitContext 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
)
314 this.type_expr
= type
;
317 public override Expression
BuildQueryClause (EmitContext ec
, Expression lSide
)
319 lSide
= CreateQueryExpression (expr
, new TypeArguments (type_expr
), null);
321 return next
.BuildQueryClause (ec
, 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 (EmitContext ec
, out ArrayList 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
)
385 this.outer_selector
= outerSelector
;
386 this.inner_selector
= innerSelector
;
389 protected override void CreateArguments (EmitContext ec
, out ArrayList args
)
391 args
= new ArrayList (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
;
412 result_selector_expr
= CreateRangeVariableType (block
, (TypeContainer
) ec
.TypeContainer
, 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 (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 ()
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
)
452 protected override LocatedToken
GetIntoVariable ()
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
{
487 SimpleName sn
= expr
as SimpleName
;
491 return sn
.Name
!= block
.Parameters
.FixedParameters
[0].Name
;
495 protected override string MethodName
{
496 get { return "Select"; }
500 class SelectMany
: ARangeVariableQueryClause
504 public SelectMany (ToplevelBlock block
, LocatedToken lt
, Expression expr
)
510 protected override void CreateArguments (EmitContext ec
, out ArrayList 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
;
522 result_selector_expr
= CreateRangeVariableType (block
, (TypeContainer
)ec
.TypeContainer
, lt
, new SimpleName (lt
.Value
, lt
.Location
));
525 LambdaExpression result_selector
= new LambdaExpression (lt
.Location
);
526 result_selector
.Block
= new QueryBlock (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
)
580 protected override string MethodName
{
581 get { return "ThenBy"; }
585 class ThenByDescending
: OrderByDescending
587 public ThenByDescending (ToplevelBlock block
, Expression 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
)
618 Identifier
= identifier
.Value
;
621 public static void Reset ()
627 public sealed class ImplicitQueryParameter
: ImplicitLambdaParameter
629 public ImplicitQueryParameter (string name
, Location loc
)
635 public QueryBlock (Block parent
, LocatedToken lt
, Location start
)
636 : base (parent
, new ParametersCompiled (new ImplicitQueryParameter (lt
.Value
, lt
.Location
)), start
)
639 base.CheckParentConflictName (parent
.Toplevel
, lt
.Value
, lt
.Location
);
642 public QueryBlock (Block parent
, ParametersCompiled parameters
, LocatedToken lt
, Location start
)
643 : base (parent
, new ParametersCompiled (parameters
[0].Clone (), new ImplicitQueryParameter (lt
.Value
, lt
.Location
)), start
)
647 public QueryBlock (Block parent
, Location start
)
648 : base (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
)
665 // Query parameter reference can include transparent parameters
667 protected override Expression
GetParameterReferenceExpression (string name
, Location loc
)
669 Expression expr
= base.GetParameterReferenceExpression (name
, loc
);
673 TransparentParameter tp
= parameters
[0] as TransparentParameter
;
675 if (tp
.Identifier
== name
)
678 TransparentParameter tp_next
= tp
.Parent
[0] as TransparentParameter
;
679 if (tp_next
== null) {
680 if (tp
.Parent
.GetParameterIndexByName (name
) >= 0)
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
);
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}'",
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",
713 public override void Error_AlreadyDeclaredTypeParameter (Location loc
, string name
, string conflict
)
715 Report
.Error (1948, loc
, "A range variable `{0}' conflicts with a method type parameter",