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
9
// Copyright 2011 Xamarin Inc
13
using System.Collections.Generic;
15
namespace Mono.CSharp.Linq
17
public class QueryExpression : AQueryClause
19
public QueryExpression (AQueryClause start)
20
: base (null, null, start.Location)
25
public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parentParameter)
27
return next.BuildQueryClause (ec, lSide, parentParameter);
30
protected override Expression DoResolve (ResolveContext ec)
32
int counter = QueryBlock.TransparentParameter.Counter;
34
Expression e = BuildQueryClause (ec, null, null);
39
// Reset counter in probing mode to ensure that all transparent
40
// identifier anonymous types are created only once
42
if (ec.IsInProbingMode)
43
QueryBlock.TransparentParameter.Counter = counter;
48
protected override string MethodName {
49
get { throw new NotSupportedException (); }
52
public override object Accept (StructuralVisitor visitor)
54
return visitor.Visit (this);
58
public abstract class AQueryClause : ShimExpression
60
protected 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 void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
74
ec.Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " +
75
"Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?",
80
protected class QueryExpressionInvocation : Invocation, OverloadResolver.IErrorHandler
82
public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments)
83
: base (expr, arguments)
87
protected override MethodGroupExpr DoResolveOverload (ResolveContext ec)
89
MethodGroupExpr rmg = mg.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.None);
93
protected override Expression DoResolveDynamic (ResolveContext ec, Expression memberExpr)
95
ec.Report.Error (1979, loc,
96
"Query expressions with a source or join sequence of type `dynamic' are not allowed");
100
#region IErrorHandler Members
102
bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
104
ec.Report.SymbolRelatedToPreviousError (best);
105
ec.Report.SymbolRelatedToPreviousError (ambiguous);
106
ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
107
best.Name, mg.InstanceExpression.GetSignatureForError ());
111
bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
116
bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
121
bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
123
var ms = (MethodSpec) best;
124
TypeSpec source_type = ms.Parameters.ExtensionMethodType;
125
if (source_type != null) {
126
Argument a = arguments[0];
128
if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) {
129
TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments);
130
tic.OutputTypeInference (rc, a.Expr, source_type);
131
if (tic.FixAllTypes (rc)) {
132
source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments);
136
if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) {
137
rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
138
best.Name, TypeManager.CSharpName (a.Type));
143
if (best.Name == "SelectMany") {
144
rc.Report.Error (1943, loc,
145
"An expression type is incorrect in a subsequent `from' clause in a query expression with source type `{0}'",
146
arguments[0].GetSignatureForError ());
148
rc.Report.Error (1942, loc,
149
"An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
150
best.Name.ToLowerInvariant (), best.Name);
159
public AQueryClause next;
160
public QueryBlock block;
162
protected AQueryClause (QueryBlock block, Expression expr, Location loc)
169
protected override void CloneTo (CloneContext clonectx, Expression target)
171
base.CloneTo (clonectx, target);
173
AQueryClause t = (AQueryClause) target;
176
t.block = (QueryBlock) clonectx.LookupBlock (block);
179
t.next = (AQueryClause) next.Clone (clonectx);
182
protected override Expression DoResolve (ResolveContext ec)
184
return expr.Resolve (ec);
187
public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
189
Arguments args = null;
190
CreateArguments (ec, parameter, ref args);
191
lSide = CreateQueryExpression (lSide, args);
193
parameter = CreateChildrenParameters (parameter);
195
Select s = next as Select;
196
if (s == null || s.IsRequired (parameter))
197
return next.BuildQueryClause (ec, lSide, parameter);
199
// Skip transparent select clause if any clause follows
200
if (next.next != null)
201
return next.next.BuildQueryClause (ec, lSide, parameter);
207
protected virtual Parameter CreateChildrenParameters (Parameter parameter)
209
// Have to clone the parameter for any children use, it carries block sensitive data
210
return parameter.Clone ();
213
protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
215
args = new Arguments (2);
217
LambdaExpression selector = new LambdaExpression (loc);
219
block.SetParameter (parameter);
220
selector.Block = block;
221
selector.Block.AddStatement (new ContextualReturn (expr));
223
args.Add (new Argument (selector));
226
protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
228
return new QueryExpressionInvocation (
229
new QueryExpressionAccess (lSide, MethodName, loc), arguments);
232
protected abstract string MethodName { get; }
234
public AQueryClause Next {
240
public AQueryClause Tail {
242
return next == null ? this : next.Tail;
248
// A query clause with an identifier (range variable)
250
public abstract class ARangeVariableQueryClause : AQueryClause
252
sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
254
public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter)
255
: base (initializer, parameter.Name, parameter.Location)
259
protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
261
ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
266
class RangeParameterReference : ParameterReference
270
public RangeParameterReference (Parameter p)
271
: base (null, p.Location)
276
protected override Expression DoResolve (ResolveContext ec)
278
pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter);
279
return base.DoResolve (ec);
283
protected RangeVariable identifier;
285
public RangeVariable IntoVariable {
291
protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
292
: base (block, expr, loc)
294
this.identifier = identifier;
297
public RangeVariable Identifier {
303
public FullNamedExpression IdentifierType { get; set; }
305
protected Invocation CreateCastExpression (Expression lSide)
307
return new QueryExpressionInvocation (
308
new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null);
311
protected override Parameter CreateChildrenParameters (Parameter parameter)
313
return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ());
316
protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init)
318
var args = new List<AnonymousTypeParameter> (2);
321
// The first argument is the reference to the parameter
323
args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location));
326
// The second argument is the linq expression
328
args.Add (new RangeAnonymousTypeParameter (init, name));
331
// Create unique anonymous type
333
return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location);
336
protected virtual RangeVariable GetIntoVariable ()
342
public sealed class RangeVariable : INamedBlockVariable
346
public RangeVariable (string name, Location loc)
363
public bool IsDeclared {
369
public bool IsParameter {
375
public Location Location { get; private set; }
377
public string Name { get; private set; }
381
public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
384
// We know the variable name is somewhere in the scope. This generates
385
// an access expression from current block
387
var pb = rc.CurrentBlock.ParametersBlock;
389
if (pb is QueryBlock) {
390
for (int i = pb.Parameters.Count - 1; i >= 0; --i) {
391
var p = pb.Parameters[i];
393
return pb.GetParameterReference (i, loc);
395
Expression expr = null;
396
var tp = p as QueryBlock.TransparentParameter;
399
expr = pb.GetParameterReference (i, loc);
401
expr = new TransparentMemberAccess (expr, tp.Name);
403
if (tp.Identifier == Name)
404
return new TransparentMemberAccess (expr, Name);
406
if (tp.Parent.Name == Name)
407
return new TransparentMemberAccess (expr, Name);
409
tp = tp.Parent as QueryBlock.TransparentParameter;
417
pb = pb.Parent.ParametersBlock;
422
public class QueryStartClause : ARangeVariableQueryClause
424
public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc)
425
: base (block, identifier, expr, loc)
427
block.AddRangeVariable (identifier);
430
public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
432
if (IdentifierType != null)
433
expr = CreateCastExpression (expr);
435
if (parameter == null)
438
return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location));
441
protected override Expression DoResolve (ResolveContext ec)
443
Expression e = BuildQueryClause (ec, null, null);
444
return e.Resolve (ec);
447
protected override string MethodName {
448
get { throw new NotSupportedException (); }
451
public override object Accept (StructuralVisitor visitor)
453
return visitor.Visit (this);
458
public class GroupBy : AQueryClause
460
Expression element_selector;
461
QueryBlock element_block;
463
public Expression ElementSelector {
464
get { return this.element_selector; }
467
public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc)
468
: base (block, keySelector, loc)
471
// Optimizes clauses like `group A by A'
473
if (!elementSelector.Equals (keySelector)) {
474
this.element_selector = elementSelector;
475
this.element_block = elementBlock;
479
public Expression SelectorExpression {
481
return element_selector;
485
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
487
base.CreateArguments (ec, parameter, ref args);
489
if (element_selector != null) {
490
LambdaExpression lambda = new LambdaExpression (element_selector.Location);
492
element_block.SetParameter (parameter.Clone ());
493
lambda.Block = element_block;
494
lambda.Block.AddStatement (new ContextualReturn (element_selector));
495
args.Add (new Argument (lambda));
499
protected override void CloneTo (CloneContext clonectx, Expression target)
501
GroupBy t = (GroupBy) target;
502
if (element_selector != null) {
503
t.element_selector = element_selector.Clone (clonectx);
504
t.element_block = (QueryBlock) element_block.Clone (clonectx);
507
base.CloneTo (clonectx, t);
510
protected override string MethodName {
511
get { return "GroupBy"; }
514
public override object Accept (StructuralVisitor visitor)
516
return visitor.Visit (this);
520
public class Join : SelectMany
522
QueryBlock inner_selector, outer_selector;
524
public RangeVariable JoinVariable {
525
get { return this.GetIntoVariable (); }
528
public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc)
529
: base (block, lt, inner, loc)
531
this.outer_selector = outerSelector;
532
this.inner_selector = innerSelector;
535
public QueryBlock InnerSelector {
537
return inner_selector;
541
public QueryBlock OuterSelector {
543
return outer_selector;
547
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
549
args = new Arguments (4);
551
if (IdentifierType != null)
552
expr = CreateCastExpression (expr);
554
args.Add (new Argument (expr));
556
outer_selector.SetParameter (parameter.Clone ());
557
var lambda = new LambdaExpression (outer_selector.StartLocation);
558
lambda.Block = outer_selector;
559
args.Add (new Argument (lambda));
561
inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location));
562
lambda = new LambdaExpression (inner_selector.StartLocation);
563
lambda.Block = inner_selector;
564
args.Add (new Argument (lambda));
566
base.CreateArguments (ec, parameter, ref args);
569
protected override void CloneTo (CloneContext clonectx, Expression target)
571
Join t = (Join) target;
572
t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx);
573
t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx);
574
base.CloneTo (clonectx, t);
577
protected override string MethodName {
578
get { return "Join"; }
581
public override object Accept (StructuralVisitor visitor)
583
return visitor.Visit (this);
587
public class GroupJoin : Join
589
readonly RangeVariable into;
591
public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner,
592
QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc)
593
: base (block, lt, inner, outerSelector, innerSelector, loc)
598
protected override RangeVariable GetIntoVariable ()
603
protected override string MethodName {
604
get { return "GroupJoin"; }
607
public override object Accept (StructuralVisitor visitor)
609
return visitor.Visit (this);
613
public class Let : ARangeVariableQueryClause
615
public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
616
: base (block, identifier, expr, loc)
620
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
622
expr = CreateRangeVariableType (ec, parameter, identifier, expr);
623
base.CreateArguments (ec, parameter, ref args);
626
protected override string MethodName {
627
get { return "Select"; }
630
public override object Accept (StructuralVisitor visitor)
632
return visitor.Visit (this);
636
public class Select : AQueryClause
638
public Select (QueryBlock block, Expression expr, Location loc)
639
: base (block, expr, loc)
644
// For queries like `from a orderby a select a'
645
// the projection is transparent and select clause can be safely removed
647
public bool IsRequired (Parameter parameter)
649
SimpleName sn = expr as SimpleName;
653
return sn.Name != parameter.Name;
656
protected override string MethodName {
657
get { return "Select"; }
660
public override object Accept (StructuralVisitor visitor)
662
return visitor.Visit (this);
667
public class SelectMany : ARangeVariableQueryClause
669
public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
670
: base (block, identifier, expr, loc)
674
protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
677
if (IdentifierType != null)
678
expr = CreateCastExpression (expr);
680
base.CreateArguments (ec, parameter.Clone (), ref args);
683
Expression result_selector_expr;
684
QueryBlock result_block;
686
var target = GetIntoVariable ();
687
var target_param = new ImplicitLambdaParameter (target.Name, target.Location);
690
// When select follows use it as a result selector
692
if (next is Select) {
693
result_selector_expr = next.Expr;
695
result_block = next.block;
696
result_block.SetParameters (parameter, target_param);
700
result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location));
702
result_block = new QueryBlock (block.Parent, block.StartLocation);
703
result_block.SetParameters (parameter, target_param);
706
LambdaExpression result_selector = new LambdaExpression (Location);
707
result_selector.Block = result_block;
708
result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
710
args.Add (new Argument (result_selector));
713
protected override string MethodName {
714
get { return "SelectMany"; }
717
public override object Accept (StructuralVisitor visitor)
719
return visitor.Visit (this);
723
public class Where : AQueryClause
725
public Where (QueryBlock block, Expression expr, Location loc)
726
: base (block, expr, loc)
730
protected override string MethodName {
731
get { return "Where"; }
734
public override object Accept (StructuralVisitor visitor)
736
return visitor.Visit (this);
740
public class OrderByAscending : AQueryClause
742
public OrderByAscending (QueryBlock block, Expression expr)
743
: base (block, expr, expr.Location)
747
protected override string MethodName {
748
get { return "OrderBy"; }
751
public override object Accept (StructuralVisitor visitor)
753
return visitor.Visit (this);
757
public class OrderByDescending : AQueryClause
759
public OrderByDescending (QueryBlock block, Expression expr)
760
: base (block, expr, expr.Location)
764
protected override string MethodName {
765
get { return "OrderByDescending"; }
768
public override object Accept (StructuralVisitor visitor)
770
return visitor.Visit (this);
774
public class ThenByAscending : OrderByAscending
776
public ThenByAscending (QueryBlock block, Expression expr)
781
protected override string MethodName {
782
get { return "ThenBy"; }
785
public override object Accept (StructuralVisitor visitor)
787
return visitor.Visit (this);
791
public class ThenByDescending : OrderByDescending
793
public ThenByDescending (QueryBlock block, Expression expr)
798
protected override string MethodName {
799
get { return "ThenByDescending"; }
802
public override object Accept (StructuralVisitor visitor)
804
return visitor.Visit (this);
809
// Implicit query block
811
public class QueryBlock : ParametersBlock
814
// Transparent parameters are used to package up the intermediate results
815
// and pass them onto next clause
817
public sealed class TransparentParameter : ImplicitLambdaParameter
819
public static int Counter;
820
const string ParameterNamePrefix = "<>__TranspIdent";
822
public readonly Parameter Parent;
823
public readonly string Identifier;
825
public TransparentParameter (Parameter parent, RangeVariable identifier)
826
: base (ParameterNamePrefix + Counter++, identifier.Location)
829
Identifier = identifier.Name;
832
public static void Reset ()
838
public QueryBlock (Block parent, Location start)
839
: base (parent, ParametersCompiled.EmptyReadOnlyParameters, start)
841
flags |= Flags.CompilerGenerated;
844
public void AddRangeVariable (RangeVariable variable)
846
variable.Block = this;
847
TopBlock.AddLocalName (variable.Name, variable, true);
850
public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
852
TopBlock.Report.Error (1931, variable.Location,
853
"A range variable `{0}' conflicts with a previous declaration of `{0}'",
857
public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
859
TopBlock.Report.Error (1930, variable.Location,
860
"A range variable `{0}' has already been declared in this scope",
864
public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
866
TopBlock.Report.Error (1948, loc,
867
"A range variable `{0}' conflicts with a method type parameter",
871
public void SetParameter (Parameter parameter)
873
base.parameters = new ParametersCompiled (parameter);
874
base.parameter_info = new ParameterInfo[] {
875
new ParameterInfo (this, 0)
879
public void SetParameters (Parameter first, Parameter second)
881
base.parameters = new ParametersCompiled (first, second);
882
base.parameter_info = new ParameterInfo[] {
883
new ParameterInfo (this, 0),
884
new ParameterInfo (this, 1)
889
sealed class TransparentMemberAccess : MemberAccess
891
public TransparentMemberAccess (Expression expr, string name)
896
public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
898
rc.Report.Error (1947, loc,
899
"A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value",