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 (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);
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
: 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?",
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
);
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 ());
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
));
127 if (!TypeManager
.IsGenericMethod (method
))
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 ());
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
);
145 public AQueryClause next
;
146 protected ToplevelBlock block
;
148 protected AQueryClause (ToplevelBlock block
, Expression expr
, Location loc
)
155 protected override void CloneTo (CloneContext clonectx
, Expression target
)
157 base.CloneTo (clonectx
, target
);
159 AQueryClause t
= (AQueryClause
) target
;
162 t
.block
= (ToplevelBlock
) block
.Clone (clonectx
);
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
)
176 CreateArguments (ec
, out args
);
177 lSide
= CreateQueryExpression (lSide
, args
);
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
);
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
{
222 public AQueryClause Tail
{
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}'",
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
)
293 this.type_expr
= type
;
296 public override Expression
BuildQueryClause (ResolveContext ec
, Expression lSide
)
298 lSide
= CreateQueryExpression (expr
, new TypeArguments (type_expr
), null);
300 return next
.BuildQueryClause (ec
, 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
)
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
;
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 ()
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
)
431 protected override LocatedToken
GetIntoVariable ()
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
{
466 SimpleName sn
= expr
as SimpleName
;
470 return sn
.Name
!= block
.Parameters
.FixedParameters
[0].Name
;
474 protected override string MethodName
{
475 get { return "Select"; }
479 class SelectMany
: ARangeVariableQueryClause
483 public SelectMany (ToplevelBlock block
, LocatedToken lt
, Expression expr
)
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
;
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
)
559 protected override string MethodName
{
560 get { return "ThenBy"; }
564 class ThenByDescending
: OrderByDescending
566 public ThenByDescending (ToplevelBlock block
, Expression 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
)
597 Identifier
= identifier
.Value
;
600 public static void Reset ()
606 public sealed class ImplicitQueryParameter
: ImplicitLambdaParameter
608 public ImplicitQueryParameter (string name
, Location 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
)
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
)
644 // Query parameter reference can include transparent parameters
646 protected override Expression
GetParameterReferenceExpression (string name
, Location loc
)
648 Expression expr
= base.GetParameterReferenceExpression (name
, loc
);
652 TransparentParameter tp
= parameters
[0] as TransparentParameter
;
654 if (tp
.Identifier
== name
)
657 TransparentParameter tp_next
= tp
.Parent
[0] as TransparentParameter
;
658 if (tp_next
== null) {
659 if (tp
.Parent
.GetParameterIndexByName (name
) >= 0)
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
);
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}'",
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",
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",