2008-07-04 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / linq.cs
1 //
2 // linq.cs: support for query expressions
3 //
4 // Authors: Marek Safar (marek.safar@gmail.com)
5 //
6 // Dual licensed under the terms of the MIT X11 or GNU GPL
7 //
8 // Copyright 2007-2008 Novell, Inc
9 //
10
11 using System;
12 using System.Reflection;
13 using System.Collections;
14
15 namespace Mono.CSharp.Linq
16 {
17         // NOTES:
18         // Expression should be IExpression to save some memory and make a few things
19         // easier to read
20         //
21         //
22
23         public class QueryExpression : AQueryClause
24         {
25                 LocatedToken variable;
26
27                 public QueryExpression (LocatedToken variable, AQueryClause query)
28                         : base (null, query.Location)
29                 {
30                         this.variable = variable;
31                         this.next = query;
32                 }
33
34                 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parentParameter, TransparentIdentifiersScope ti)
35                 {
36                         Parameter p = CreateBlockParameter (variable);
37                         return next.BuildQueryClause (ec, lSide, p, ti);
38                 }
39
40                 public override Expression DoResolve (EmitContext ec)
41                 {
42                         Expression e = BuildQueryClause (ec, null, null, null);
43                         e = e.Resolve (ec);
44                         return e;
45                 }
46
47                 protected override string MethodName {
48                         get { throw new NotSupportedException (); }
49                 }
50         }
51
52         public abstract class AQueryClause : Expression
53         {
54                 class QueryExpressionAccess : MemberAccess
55                 {
56                         public QueryExpressionAccess (Expression expr, string methodName, Location loc)
57                                 : base (expr, methodName, loc)
58                         {
59                         }
60
61                         public QueryExpressionAccess (Expression expr, string methodName, TypeArguments typeArguments, Location loc)
62                                 : base (expr, methodName, typeArguments, loc)
63                         {
64                         }
65
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)
68                         {
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?",
71                                         name);
72                                 return null;
73                         }
74                 }
75
76                 class QueryExpressionInvocation : Invocation, MethodGroupExpr.IErrorHandler
77                 {
78                         public QueryExpressionInvocation (QueryExpressionAccess expr, ArrayList arguments)
79                                 : base (expr, arguments)
80                         {
81                         }
82
83                         protected override MethodGroupExpr DoResolveOverload (EmitContext ec)
84                         {
85                                 mg.CustomErrorHandler = this;
86                                 MethodGroupExpr rmg = mg.OverloadResolve (ec, ref Arguments, false, loc);
87                                 return rmg;
88                         }
89
90                         public bool NoExactMatch (EmitContext ec, MethodBase method)
91                         {
92 #if GMCS_SOURCE                         
93                                 ParameterData pd = TypeManager.GetParameterData (method);
94                                 Type source_type = pd.ExtensionMethodType;
95                                 if (source_type != null) {
96                                         Argument a = (Argument) Arguments [0];
97
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);
103                                                 }
104                                         }
105
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));
109                                                 return true;
110                                         }
111                                 }
112
113                                 if (!method.IsGenericMethod)
114                                         return false;
115
116                                 Report.Error (1942, loc, "Type inference failed to infer type argument for `{0}' clause. " +
117                                         "Try specifying the type argument explicitly",
118                                         mg.Name.ToLower ());
119                                 return true;
120 #else
121                                 return false;
122 #endif
123                         }
124                 }
125
126                 public AQueryClause next;
127                 /*protected*/ public Expression expr;
128
129                 protected AQueryClause (Expression expr, Location loc)
130                 {
131                         this.expr = expr;
132                         this.loc = loc;
133                 }
134                 
135                 protected override void CloneTo (CloneContext clonectx, Expression target)
136                 {
137                         AQueryClause t = (AQueryClause) target;
138                         if (expr != null)
139                                 t.expr = expr.Clone (clonectx);
140                         
141                         if (next != null)
142                                 t.next = (AQueryClause)next.Clone (clonectx);
143                 }
144
145                 public override Expression CreateExpressionTree (EmitContext ec)
146                 {
147                         // Should not be reached
148                         throw new NotSupportedException ("ET");
149                 }
150
151                 public override Expression DoResolve (EmitContext ec)
152                 {
153                         return expr.DoResolve (ec);
154                 }
155
156                 public virtual Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
157                 {
158                         ArrayList args = new ArrayList (1);
159                         args.Add (CreateSelectorArgument (ec, expr, parameter, ti));
160                         lSide = CreateQueryExpression (lSide, args);
161                         if (next != null) {
162                                 Select s = next as Select;
163                                 if (s == null || s.IsRequired (parameter))
164                                         return next.BuildQueryClause (ec, lSide, parameter, ti);
165                                         
166                                 // Skip transparent select clause if any clause follows
167                                 if (next.next != null)
168                                         return next.next.BuildQueryClause (ec, lSide, parameter, ti);
169                         }
170
171                         return lSide;
172                 }
173
174                 protected static Parameter CreateBlockParameter (LocatedToken li)
175                 {
176                         return new ImplicitQueryParameter (li);
177                 }
178
179                 protected Invocation CreateQueryExpression (Expression lSide, ArrayList arguments)
180                 {
181                         return new QueryExpressionInvocation (
182                                 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
183                 }
184
185                 protected Invocation CreateQueryExpression (Expression lSide, TypeArguments typeArguments, ArrayList arguments)
186                 {
187                         return new QueryExpressionInvocation (
188                                 new QueryExpressionAccess (lSide, MethodName, typeArguments, loc), arguments);
189                 }
190
191                 protected Argument CreateSelectorArgument (EmitContext ec, Expression expr, Parameter parameter, TransparentIdentifiersScope ti)
192                 {
193                         return CreateSelectorArgument (ec, expr, new Parameter [] { parameter }, ti);
194                 }
195
196                 protected Argument CreateSelectorArgument (EmitContext ec, Expression expr, Parameter[] parameters, TransparentIdentifiersScope ti)
197                 {
198                         Parameters p = new Parameters (parameters);
199
200                         LambdaExpression selector = new LambdaExpression ((TypeContainer)ec.TypeContainer, p, loc);
201                         selector.Block = new SelectorBlock (ec.CurrentBlock, p, ti, loc);
202                         selector.Block.AddStatement (new ContextualReturn (expr));
203
204                         return new Argument (selector);
205                 }
206
207                 public override void Emit (EmitContext ec)
208                 {
209                         throw new NotSupportedException ();
210                 }
211
212                 protected abstract string MethodName { get; }
213
214                 public virtual AQueryClause Next {
215                         set {
216                                 next = value;
217                         }
218                 }
219
220                 public AQueryClause Tail {
221                         get {
222                                 return next == null ? this : next.Tail;
223                         }
224                 }
225         }
226
227         //
228         // A query clause with an identifier (range variable)
229         //
230         public abstract class ARangeVariableQueryClause : AQueryClause
231         {
232                 LocatedToken variable;
233                 protected Expression element_selector;
234
235                 protected ARangeVariableQueryClause (LocatedToken variable, Expression expr, Location loc)
236                         : base (expr, loc)
237                 {
238                         this.variable = variable;
239                 }
240
241                 protected virtual void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
242                         ref Parameter parameter, TransparentIdentifiersScope ti)
243                 {
244                         args.Add (CreateSelectorArgument (ec, expr, parentParameter, ti));
245                         args.Add (CreateSelectorArgument (ec, element_selector,
246                                 new Parameter [] { parentParameter, parameter }, ti));
247                 }
248
249                 //
250                 // Customization for range variables which not only creates a lambda expression but
251                 // also builds a chain of range varible pairs
252                 //
253                 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parentParameter, TransparentIdentifiersScope ti)
254                 {
255                         Parameter parameter = CreateBlockParameter (variable);
256
257                         if (next != null) {
258                                 //
259                                 // Builds transparent identifiers, each identifier includes its parent
260                                 // type at index 0, and new value at index 1. This is not valid for the
261                                 // first one which includes two values directly.
262                                 //
263                                 ArrayList transp_args = new ArrayList (2);
264                                 transp_args.Add (new AnonymousTypeParameter (parentParameter));
265                                 transp_args.Add (CreateAnonymousTypeVariable (parameter));
266                                 element_selector = new AnonymousTypeDeclaration (transp_args, (TypeContainer) ec.TypeContainer, loc);
267                         }
268
269                         ArrayList args = new ArrayList ();
270                         AddSelectorArguments (ec, args, parentParameter, ref parameter, ti);
271
272                         lSide = CreateQueryExpression (lSide, args);
273                         if (next != null) {
274                                 //
275                                 // Parameter identifiers go to the scope
276                                 //
277                                 string[] identifiers;
278                                 if (ti == null) {
279                                         identifiers = new string [] { parentParameter.Name, parameter.Name };
280                                 } else {
281                                         identifiers = new string [] { parameter.Name };
282                                 }
283
284                                 TransparentParameter tp = new TransparentParameter (loc);
285                                 return next.BuildQueryClause (ec, lSide, tp,
286                                         new TransparentIdentifiersScope (ti, tp, identifiers));
287                         }
288
289                         return lSide;
290                 }
291                 
292                 protected override void CloneTo (CloneContext clonectx, Expression target)
293                 {
294                         ARangeVariableQueryClause t = (ARangeVariableQueryClause) target;
295                         if (element_selector != null)
296                                 t.element_selector = element_selector.Clone (clonectx);
297                         base.CloneTo (clonectx, t);
298                 }
299                 
300                 //
301                 // For transparent identifiers, creates an instance of variable expression
302                 //
303                 protected virtual AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
304                 {
305                         return new AnonymousTypeParameter (parameter);
306                 }
307         }
308
309         public class QueryStartClause : AQueryClause
310         {
311                 public QueryStartClause (Expression expr)
312                         : base (expr, expr.Location)
313                 {
314                 }
315
316                 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
317                 {
318                         return next.BuildQueryClause (ec, expr, parameter, ti);
319                 }
320
321                 public override Expression DoResolve (EmitContext ec)
322                 {
323                         Expression e = BuildQueryClause (ec, null, null, null);
324                         return e.Resolve (ec);
325                 }
326
327                 protected override string MethodName {
328                         get { throw new NotSupportedException (); }
329                 }
330         }
331
332         public class Cast : QueryStartClause
333         {
334                 // We don't have to clone cast type
335                 readonly Expression type_expr;
336
337                 public Cast (Expression type, Expression expr)
338                         : base (expr)
339                 {
340                         this.type_expr = type;
341                 }
342                 
343                 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
344                 {
345                         lSide = CreateQueryExpression (expr, new TypeArguments (loc, type_expr), null);
346                         if (next != null)
347                                 return next.BuildQueryClause (ec, lSide, parameter, ti);
348
349                         return lSide;
350                 }
351
352                 protected override string MethodName {
353                         get { return "Cast"; }
354                 }
355         }
356
357         public class GroupBy : AQueryClause
358         {
359                 Expression element_selector;
360                 
361                 public GroupBy (Expression elementSelector, Expression keySelector, Location loc)
362                         : base (keySelector, loc)
363                 {
364                         this.element_selector = elementSelector;
365                 }
366
367                 public override Expression BuildQueryClause (EmitContext ec, Expression lSide, Parameter parameter, TransparentIdentifiersScope ti)
368                 {
369                         ArrayList args = new ArrayList (2);
370                         args.Add (CreateSelectorArgument (ec, expr, parameter, ti));
371
372                         // A query can be optimized when selector is not group by specific
373                         if (!element_selector.Equals (lSide))
374                                 args.Add (CreateSelectorArgument (ec, element_selector, parameter, ti));
375
376                         lSide = CreateQueryExpression (lSide, args);
377                         if (next != null)
378                                 return next.BuildQueryClause (ec, lSide, parameter, ti);
379
380                         return lSide;
381                 }
382         
383                 protected override void CloneTo (CloneContext clonectx, Expression target)
384                 {
385                         GroupBy t = (GroupBy) target;
386                         t.element_selector = element_selector.Clone (clonectx);
387                         base.CloneTo (clonectx, t);
388                 }
389
390                 protected override string MethodName {
391                         get { return "GroupBy"; }
392                 }
393         }
394
395         public class Join : ARangeVariableQueryClause
396         {
397                 Expression projection;
398                 Expression inner_selector, outer_selector;
399
400                 public Join (LocatedToken variable, Expression inner, Expression outerSelector, Expression innerSelector, Location loc)
401                         : base (variable, inner, loc)
402                 {
403                         this.outer_selector = outerSelector;
404                         this.inner_selector = innerSelector;
405                 }
406
407                 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
408                         ref Parameter parameter, TransparentIdentifiersScope ti)
409                 {
410                         args.Add (new Argument (expr));
411                         args.Add (CreateSelectorArgument (ec, outer_selector, parentParameter, ti));
412                         args.Add (CreateSelectorArgument (ec, inner_selector, parameter, ti));
413
414                         parameter = CreateResultSelectorParameter (parameter);
415                         if (projection == null) {
416                                 ArrayList join_args = new ArrayList (2);
417                                 join_args.Add (new AnonymousTypeParameter (parentParameter));
418                                 join_args.Add (new AnonymousTypeParameter (parameter));
419                                 projection = new AnonymousTypeDeclaration (join_args, (TypeContainer) ec.TypeContainer, loc);
420                         }
421
422                         args.Add (CreateSelectorArgument (ec, projection,
423                                 new Parameter [] { parentParameter, parameter }, ti));
424                 }
425         
426                 protected override void CloneTo (CloneContext clonectx, Expression target)
427                 {
428                         Join t = (Join) target;
429                         t.projection = projection.Clone (clonectx);
430                         t.inner_selector = inner_selector.Clone (clonectx);
431                         t.outer_selector = outer_selector.Clone (clonectx);
432                         base.CloneTo (clonectx, t);
433                 }       
434
435                 protected virtual Parameter CreateResultSelectorParameter (Parameter parameter)
436                 {
437                         return parameter;
438                 }
439
440                 public override AQueryClause Next {
441                         set {
442                                 // Use select as join projection
443                                 if (value is Select) {
444                                         projection = value.expr;
445                                         next = value.next;
446                                         return;
447                                 }
448
449                                 base.Next = value;
450                         }
451                 }
452
453                 protected override string MethodName {
454                         get { return "Join"; }
455                 }
456         }
457
458         public class GroupJoin : Join
459         {
460                 readonly LocatedToken into_variable;
461
462                 public GroupJoin (LocatedToken variable, Expression inner, Expression outerSelector, Expression innerSelector,
463                         LocatedToken into, Location loc)
464                         : base (variable, inner, outerSelector, innerSelector, loc)
465                 {
466                         this.into_variable = into;
467                 }
468
469                 protected override Parameter CreateResultSelectorParameter (Parameter parameter)
470                 {
471                         //
472                         // into variable is used as result selector and it's passed as
473                         // transparent identifiers to the next clause
474                         //
475                         return CreateBlockParameter (into_variable);
476                 }
477
478                 protected override string MethodName {
479                         get { return "GroupJoin"; }
480                 }
481         }
482
483         public class Let : ARangeVariableQueryClause
484         {
485                 class RangeAnonymousTypeParameter : AnonymousTypeParameter
486                 {
487                         readonly Parameter parameter;
488
489                         public RangeAnonymousTypeParameter (Expression initializer, Parameter parameter)
490                                 : base (initializer, parameter.Name, parameter.Location)
491                         {
492                                 this.parameter = parameter;
493                         }
494
495                         public override Expression DoResolve (EmitContext ec)
496                         {
497                                 Expression e = base.DoResolve (ec);
498                                 if (e != null) {
499                                         //
500                                         // Spread resolved initializer type
501                                         //
502                                         parameter.ParameterType = type;
503                                         parameter.Resolve (ec);
504                                 }
505
506                                 return e;
507                         }
508
509                         protected override void Error_InvalidInitializer (Expression initializer)
510                         {
511                                 Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
512                                         Name, initializer.GetSignatureForError ());
513                         }                       
514                 }
515
516                 public Let (LocatedToken variable, Expression expr, Location loc)
517                         : base (variable, expr, loc)
518                 {
519                 }
520
521                 protected override void AddSelectorArguments (EmitContext ec, ArrayList args, Parameter parentParameter,
522                         ref Parameter parameter, TransparentIdentifiersScope ti)
523                 {
524                         args.Add (CreateSelectorArgument (ec, element_selector, parentParameter, ti));
525                 }
526
527                 protected override AnonymousTypeParameter CreateAnonymousTypeVariable (Parameter parameter)
528                 {
529                         return new RangeAnonymousTypeParameter (expr, parameter);
530                 }
531
532                 protected override string MethodName {
533                         get { return "Select"; }
534                 }
535         }
536
537         public class Select : AQueryClause
538         {
539                 public Select (Expression expr, Location loc)
540                         : base (expr, loc)
541                 {
542                 }
543                 
544                 //
545                 // For queries like `from a orderby a select a'
546                 // the projection is transparent and select clause can be safely removed 
547                 //
548                 public bool IsRequired (Parameter parameter)
549                 {
550                         SimpleName sn = expr as SimpleName;
551                         if (sn == null)
552                                 return true;
553
554                         return sn.Name != parameter.Name;
555                 }
556
557                 protected override string MethodName {
558                         get { return "Select"; }
559                 }
560         }
561
562         public class SelectMany : ARangeVariableQueryClause
563         {
564                 public SelectMany (LocatedToken variable, Expression expr)
565                         : base (variable, expr, expr.Location)
566                 {
567                 }
568
569                 protected override string MethodName {
570                         get { return "SelectMany"; }
571                 }
572
573                 public override AQueryClause Next {
574                         set {
575                                 element_selector = value.expr;
576
577                                 // Can be optimized as SelectMany element selector
578                                 if (value is Select)
579                                         return;
580
581                                 next = value;
582                         }
583                 }
584         }
585
586         public class Where : AQueryClause
587         {
588                 public Where (Expression expr, Location loc)
589                         : base (expr, loc)
590                 {
591                 }
592
593                 protected override string MethodName {
594                         get { return "Where"; }
595                 }
596         }
597
598         public class OrderByAscending : AQueryClause
599         {
600                 public OrderByAscending (Expression expr)
601                         : base (expr, expr.Location)
602                 {
603                 }
604
605                 protected override string MethodName {
606                         get { return "OrderBy"; }
607                 }
608         }
609
610         public class OrderByDescending : AQueryClause
611         {
612                 public OrderByDescending (Expression expr)
613                         : base (expr, expr.Location)
614                 {
615                 }
616
617                 protected override string MethodName {
618                         get { return "OrderByDescending"; }
619                 }
620         }
621
622         public class ThenByAscending : OrderByAscending
623         {
624                 public ThenByAscending (Expression expr)
625                         : base (expr)
626                 {
627                 }
628
629                 protected override string MethodName {
630                         get { return "ThenBy"; }
631                 }
632         }
633
634         public class ThenByDescending : OrderByDescending
635         {
636                 public ThenByDescending (Expression expr)
637                         : base (expr)
638                 {
639                 }
640
641                 protected override string MethodName {
642                         get { return "ThenByDescending"; }
643                 }
644         }
645
646         class ImplicitQueryParameter : ImplicitLambdaParameter
647         {
648                 public sealed class ImplicitType : Expression
649                 {
650                         public static ImplicitType Instance = new ImplicitType ();
651
652                         private ImplicitType ()
653                         {
654                         }
655
656                         protected override void CloneTo (CloneContext clonectx, Expression target)
657                         {
658                                 // Nothing to clone
659                         }
660
661                         public override Expression CreateExpressionTree (EmitContext ec)
662                         {
663                                 throw new NotSupportedException ();
664                         }
665
666                         public override Expression DoResolve (EmitContext ec)
667                         {
668                                 throw new NotSupportedException ();
669                         }
670
671                         public override void Emit (EmitContext ec)
672                         {
673                                 throw new NotSupportedException ();
674                         }
675                 }
676
677                 public ImplicitQueryParameter (LocatedToken variable)
678                         : base (variable.Value, variable.Location)
679                 {
680                 }
681         }
682
683         //
684         // Transparent parameters are used to package up the intermediate results
685         // and pass them onto next clause
686         //
687         public class TransparentParameter : ImplicitLambdaParameter
688         {
689                 static int counter;
690                 const string ParameterNamePrefix = "<>__TranspIdent";
691
692                 public TransparentParameter (Location loc)
693                         : base (ParameterNamePrefix + counter++, loc)
694                 {
695                 }
696         }
697
698         //
699         // Transparent identifiers are stored in nested anonymous types, each type can contain
700         // up to 2 identifiers or 1 identifier and parent type.
701         //
702         public class TransparentIdentifiersScope
703         {
704                 readonly string [] identifiers;
705                 readonly TransparentIdentifiersScope parent;
706                 readonly TransparentParameter parameter;
707
708                 public TransparentIdentifiersScope (TransparentIdentifiersScope parent,
709                         TransparentParameter parameter, string [] identifiers)
710                 {
711                         this.parent = parent;
712                         this.parameter = parameter;
713                         this.identifiers = identifiers;
714                 }
715
716                 public MemberAccess GetIdentifier (string name)
717                 {
718                         TransparentIdentifiersScope ident = FindIdentifier (name);
719                         if (ident == null)
720                                 return null;
721
722                         return new MemberAccess (CreateIdentifierNestingExpression (ident), name);
723                 }
724
725                 TransparentIdentifiersScope FindIdentifier (string name)
726                 {
727                         foreach (string s in identifiers) {
728                                 if (s == name)
729                                         return this;
730                         }
731
732                         if (parent == null)
733                                 return null;
734
735                         return parent.FindIdentifier (name);
736                 }
737
738                 Expression CreateIdentifierNestingExpression (TransparentIdentifiersScope end)
739                 {
740                         Expression expr = new SimpleName (parameter.Name, parameter.Location);
741                         TransparentIdentifiersScope current = this;
742                         while (current != end)
743                         {
744                                 current = current.parent;
745                                 expr = new MemberAccess (expr, current.parameter.Name);
746                         }
747
748                         return expr;
749                 }
750         }
751
752         //
753         // Lambda expression block which contains transparent identifiers
754         //
755         class SelectorBlock : ToplevelBlock
756         {
757                 readonly TransparentIdentifiersScope transparent_identifiers;
758
759                 public SelectorBlock (Block block, Parameters parameters, 
760                         TransparentIdentifiersScope transparentIdentifiers, Location loc)
761                         : base (block, parameters, loc)
762                 {
763                         this.transparent_identifiers = transparentIdentifiers;
764                 }
765
766                 public override Expression GetTransparentIdentifier (string name)
767                 {
768                         Expression expr = null;
769                         if (transparent_identifiers != null)
770                                 expr = transparent_identifiers.GetIdentifier (name);
771
772                         if (expr != null || Container == null)
773                                 return expr;
774                         
775                         return Container.GetTransparentIdentifier (name);
776                 }
777         }
778
779         //
780         // This block is actually never used, it is used by parser only
781         //
782         public class QueryBlock : Block
783         {
784                 Hashtable range_variables = new Hashtable ();
785
786                 public QueryBlock (Block parent, Location start)
787                         : base (parent, start, Location.Null)
788                 {
789                 }
790
791                 protected override void AddVariable (LocalInfo li)
792                 {
793                         string name = li.Name;
794                         if (range_variables.Contains (name)) {
795                                 Location conflict = (Location)range_variables [name];
796                                 Report.SymbolRelatedToPreviousError (conflict, name);
797                                 Error_AlreadyDeclared (li.Location, name);
798                                 return;
799                         }
800
801                         range_variables.Add (name, li.Location);
802                 }
803                 
804                 protected override void Error_AlreadyDeclared (Location loc, string var, string reason)
805                 {
806                         Report.Error (1931, loc, "A range variable `{0}' conflicts with a previous declaration of `{0}'",
807                                 var);
808                 }
809                 
810                 protected override void Error_AlreadyDeclared (Location loc, string var)
811                 {
812                         Report.Error (1930, loc, "A range variable `{0}' has already been declared in this scope",
813                                 var);           
814                 }
815                 
816                 protected override void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
817                 {
818                         Report.Error (1948, loc, "A range variable `{0}' conflicts with a method type parameter",
819                                 name);
820                 }
821         }
822 }