350cb797c635425e547377f75339fe6756f24562
[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.Collections.Generic;
13
14 namespace Mono.CSharp.Linq
15 {
16         public class QueryExpression : AQueryClause
17         {
18                 public QueryExpression (AQueryClause start)
19                         : base (null, null, start.Location)
20                 {
21                         this.next = start;
22                 }
23
24                 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parentParameter)
25                 {
26                         return next.BuildQueryClause (ec, lSide, parentParameter);
27                 }
28
29                 protected override Expression DoResolve (ResolveContext ec)
30                 {
31                         int counter = QueryBlock.TransparentParameter.Counter;
32
33                         Expression e = BuildQueryClause (ec, null, null);
34                         if (e != null)
35                                 e = e.Resolve (ec);
36
37                         //
38                         // Reset counter in probing mode to ensure that all transparent
39                         // identifier anonymous types are created only once
40                         //
41                         if (ec.IsInProbingMode)
42                                 QueryBlock.TransparentParameter.Counter = counter;
43
44                         return e;
45                 }
46
47                 protected override string MethodName {
48                         get { throw new NotSupportedException (); }
49                 }
50         }
51
52         public abstract class AQueryClause : ShimExpression
53         {
54                 protected 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 void Error_TypeDoesNotContainDefinition (ResolveContext ec, TypeSpec type, string name)
67                         {
68                                 ec.Report.Error (1935, loc, "An implementation of `{0}' query expression pattern could not be found. " +
69                                         "Are you missing `System.Linq' using directive or `System.Core.dll' assembly reference?",
70                                         name);
71                         }
72                 }
73
74                 protected class QueryExpressionInvocation : Invocation, OverloadResolver.IErrorHandler
75                 {
76                         public QueryExpressionInvocation (QueryExpressionAccess expr, Arguments arguments)
77                                 : base (expr, arguments)
78                         {
79                         }
80
81                         protected override MethodGroupExpr DoResolveOverload (ResolveContext ec)
82                         {
83                                 MethodGroupExpr rmg = mg.OverloadResolve (ec, ref arguments, this, OverloadResolver.Restrictions.None);
84                                 return rmg;
85                         }
86
87                         #region IErrorHandler Members
88
89                         bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
90                         {
91                                 ec.Report.SymbolRelatedToPreviousError (best);
92                                 ec.Report.SymbolRelatedToPreviousError (ambiguous);
93                                 ec.Report.Error (1940, loc, "Ambiguous implementation of the query pattern `{0}' for source type `{1}'",
94                                         best.Name, mg.InstanceExpression.GetSignatureForError ());
95                                 return true;
96                         }
97
98                         bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
99                         {
100                                 return false;
101                         }
102
103                         bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
104                         {
105                                 return false;
106                         }
107
108                         bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
109                         {
110                                 var ms = (MethodSpec) best;
111                                 TypeSpec source_type = ms.Parameters.ExtensionMethodType;
112                                 if (source_type != null) {
113                                         Argument a = arguments[0];
114
115                                         if (TypeManager.IsGenericType (source_type) && InflatedTypeSpec.ContainsTypeParameter (source_type)) {
116                                                 TypeInferenceContext tic = new TypeInferenceContext (source_type.TypeArguments);
117                                                 tic.OutputTypeInference (rc, a.Expr, source_type);
118                                                 if (tic.FixAllTypes (rc)) {
119                                                         source_type = source_type.GetDefinition ().MakeGenericType (rc, tic.InferredTypeArguments);
120                                                 }
121                                         }
122
123                                         if (!Convert.ImplicitConversionExists (rc, a.Expr, source_type)) {
124                                                 rc.Report.Error (1936, loc, "An implementation of `{0}' query expression pattern for source type `{1}' could not be found",
125                                                         best.Name, TypeManager.CSharpName (a.Type));
126                                                 return true;
127                                         }
128                                 }
129
130                                 if (best.Name == "SelectMany") {
131                                         rc.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 ());
134                                 } else {
135                                         rc.Report.Error (1942, loc,
136                                                 "An expression type in `{0}' clause is incorrect. Type inference failed in the call to `{1}'",
137                                                 best.Name.ToLowerInvariant (), best.Name);
138                                 }
139
140                                 return true;
141                         }
142
143                         #endregion
144                 }
145
146                 public AQueryClause next;
147                 public QueryBlock block;
148
149                 protected AQueryClause (QueryBlock block, Expression expr, Location loc)
150                          : base (expr)
151                 {
152                         this.block = block;
153                         this.loc = loc;
154                 }
155                 
156                 protected override void CloneTo (CloneContext clonectx, Expression target)
157                 {
158                         base.CloneTo (clonectx, target);
159
160                         AQueryClause t = (AQueryClause) target;
161
162                         if (block != null)
163                                 t.block = (QueryBlock) clonectx.LookupBlock (block);
164
165                         if (next != null)
166                                 t.next = (AQueryClause) next.Clone (clonectx);
167                 }
168
169                 protected override Expression DoResolve (ResolveContext ec)
170                 {
171                         return expr.Resolve (ec);
172                 }
173
174                 public virtual Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
175                 {
176                         Arguments args = null;
177                         CreateArguments (ec, parameter, ref args);
178                         lSide = CreateQueryExpression (lSide, args);
179                         if (next != null) {
180                                 parameter = CreateChildrenParameters (parameter);
181
182                                 Select s = next as Select;
183                                 if (s == null || s.IsRequired (parameter))
184                                         return next.BuildQueryClause (ec, lSide, parameter);
185                                         
186                                 // Skip transparent select clause if any clause follows
187                                 if (next.next != null)
188                                         return next.next.BuildQueryClause (ec, lSide, parameter);
189                         }
190
191                         return lSide;
192                 }
193
194                 protected virtual Parameter CreateChildrenParameters (Parameter parameter)
195                 {
196                         // Have to clone the parameter for any children use, it carries block sensitive data
197                         return parameter.Clone ();
198                 }
199
200                 protected virtual void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
201                 {
202                         args = new Arguments (2);
203
204                         LambdaExpression selector = new LambdaExpression (loc);
205
206                         block.SetParameter (parameter);
207                         selector.Block = block;
208                         selector.Block.AddStatement (new ContextualReturn (expr));
209
210                         args.Add (new Argument (selector));
211                 }
212
213                 protected Invocation CreateQueryExpression (Expression lSide, Arguments arguments)
214                 {
215                         return new QueryExpressionInvocation (
216                                 new QueryExpressionAccess (lSide, MethodName, loc), arguments);
217                 }
218
219                 protected abstract string MethodName { get; }
220
221                 public AQueryClause Next {
222                         set {
223                                 next = value;
224                         }
225                 }
226
227                 public AQueryClause Tail {
228                         get {
229                                 return next == null ? this : next.Tail;
230                         }
231                 }
232         }
233
234         //
235         // A query clause with an identifier (range variable)
236         //
237         public abstract class ARangeVariableQueryClause : AQueryClause
238         {
239                 sealed class RangeAnonymousTypeParameter : AnonymousTypeParameter
240                 {
241                         public RangeAnonymousTypeParameter (Expression initializer, RangeVariable parameter)
242                                 : base (initializer, parameter.Name, parameter.Location)
243                         {
244                         }
245
246                         protected override void Error_InvalidInitializer (ResolveContext ec, string initializer)
247                         {
248                                 ec.Report.Error (1932, loc, "A range variable `{0}' cannot be initialized with `{1}'",
249                                         Name, initializer);
250                         }
251                 }
252
253                 class RangeParameterReference : ParameterReference
254                 {
255                         Parameter parameter;
256
257                         public RangeParameterReference (Parameter p)
258                                 : base (null, p.Location)
259                         {
260                                 this.parameter = p;
261                         }
262
263                         protected override Expression DoResolve (ResolveContext ec)
264                         {
265                                 pi = ec.CurrentBlock.ParametersBlock.GetParameterInfo (parameter);
266                                 return base.DoResolve (ec);
267                         }
268                 }
269
270                 protected RangeVariable identifier;
271
272                 protected ARangeVariableQueryClause (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
273                         : base (block, expr, loc)
274                 {
275                         this.identifier = identifier;
276                 }
277
278                 public FullNamedExpression IdentifierType { get; set; }
279
280                 protected Invocation CreateCastExpression (Expression lSide)
281                 {
282                         return new QueryExpressionInvocation (
283                                 new QueryExpressionAccess (lSide, "Cast", new TypeArguments (IdentifierType), loc), null);
284                 }
285
286                 protected override Parameter CreateChildrenParameters (Parameter parameter)
287                 {
288                         return new QueryBlock.TransparentParameter (parameter.Clone (), GetIntoVariable ());
289                 }
290
291                 protected static Expression CreateRangeVariableType (ResolveContext rc, Parameter parameter, RangeVariable name, Expression init)
292                 {
293                         var args = new List<AnonymousTypeParameter> (2);
294
295                         //
296                         // The first argument is the reference to the parameter
297                         //
298                         args.Add (new AnonymousTypeParameter (new RangeParameterReference (parameter), parameter.Name, parameter.Location));
299
300                         //
301                         // The second argument is the linq expression
302                         //
303                         args.Add (new RangeAnonymousTypeParameter (init, name));
304
305                         //
306                         // Create unique anonymous type
307                         //
308                         return new NewAnonymousType (args, rc.MemberContext.CurrentMemberDefinition.Parent, name.Location);
309                 }
310
311                 protected virtual RangeVariable GetIntoVariable ()
312                 {
313                         return identifier;
314                 }
315         }
316
317         public sealed class RangeVariable : INamedBlockVariable
318         {
319                 Block block;
320
321                 public RangeVariable (string name, Location loc)
322                 {
323                         Name = name;
324                         Location = loc;
325                 }
326
327                 #region Properties
328
329                 public Block Block {
330                         get {
331                                 return block;
332                         }
333                         set {
334                                 block = value;
335                         }
336                 }
337
338                 public bool IsDeclared {
339                         get {
340                                 return true;
341                         }
342                 }
343
344                 public bool IsParameter {
345                         get {
346                                 return false;
347                         }
348                 }
349
350                 public Location Location { get; private set; }
351
352                 public string Name { get; private set; }
353
354                 #endregion
355
356                 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
357                 {
358                         // 
359                         // We know the variable name is somewhere in the scope. This generates
360                         // an access expression from current block
361                         //
362                         var pb = rc.CurrentBlock.ParametersBlock;
363                         while (true) {
364                                 if (pb is QueryBlock) {
365                                         for (int i = pb.Parameters.Count - 1; i >= 0; --i) {
366                                                 var p = pb.Parameters[i];
367                                                 if (p.Name == Name)
368                                                         return pb.GetParameterReference (i, loc);
369
370                                                 Expression expr = null;
371                                                 var tp = p as QueryBlock.TransparentParameter;
372                                                 while (tp != null) {
373                                                         if (expr == null)
374                                                                 expr = pb.GetParameterReference (i, loc);
375                                                         else
376                                                                 expr = new TransparentMemberAccess (expr, tp.Name);
377
378                                                         if (tp.Identifier == Name)
379                                                                 return new TransparentMemberAccess (expr, Name);
380
381                                                         if (tp.Parent.Name == Name)
382                                                                 return new TransparentMemberAccess (expr, Name);
383
384                                                         tp = tp.Parent as QueryBlock.TransparentParameter;
385                                                 }
386                                         }
387                                 }
388
389                                 if (pb == block)
390                                         return null;
391
392                                 pb = pb.Parent.ParametersBlock;
393                         }
394                 }
395         }
396
397         class QueryStartClause : ARangeVariableQueryClause
398         {
399                 public QueryStartClause (QueryBlock block, Expression expr, RangeVariable identifier, Location loc)
400                         : base (block, identifier, expr, loc)
401                 {
402                         block.AddRangeVariable (identifier);
403                 }
404
405                 public override Expression BuildQueryClause (ResolveContext ec, Expression lSide, Parameter parameter)
406                 {
407 /*
408                         expr = expr.Resolve (ec);
409                         if (expr == null)
410                                 return null;
411
412                         if (expr.Type == InternalType.Dynamic || expr.Type == TypeManager.void_type) {
413                                 ec.Report.Error (1979, expr.Location,
414                                         "Query expression with a source or join sequence of type `{0}' is not allowed",
415                                         TypeManager.CSharpName (expr.Type));
416                                 return null;
417                         }
418 */
419
420                         if (IdentifierType != null)
421                                 expr = CreateCastExpression (expr);
422
423                         if (parameter == null)
424                                 lSide = expr;
425
426                         return next.BuildQueryClause (ec, lSide, new ImplicitLambdaParameter (identifier.Name, identifier.Location));
427                 }
428
429                 protected override Expression DoResolve (ResolveContext ec)
430                 {
431                         Expression e = BuildQueryClause (ec, null, null);
432                         return e.Resolve (ec);
433                 }
434
435                 protected override string MethodName {
436                         get { throw new NotSupportedException (); }
437                 }
438         }
439
440         public class GroupBy : AQueryClause
441         {
442                 Expression element_selector;
443                 QueryBlock element_block;
444
445                 public GroupBy (QueryBlock block, Expression elementSelector, QueryBlock elementBlock, Expression keySelector, Location loc)
446                         : base (block, keySelector, loc)
447                 {
448                         //
449                         // Optimizes clauses like `group A by A'
450                         //
451                         if (!elementSelector.Equals (keySelector)) {
452                                 this.element_selector = elementSelector;
453                                 this.element_block = elementBlock;
454                         }
455                 }
456
457                 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
458                 {
459                         base.CreateArguments (ec, parameter, ref args);
460
461                         if (element_selector != null) {
462                                 LambdaExpression lambda = new LambdaExpression (element_selector.Location);
463
464                                 element_block.SetParameter (parameter.Clone ());
465                                 lambda.Block = element_block;
466                                 lambda.Block.AddStatement (new ContextualReturn (element_selector));
467                                 args.Add (new Argument (lambda));
468                         }
469                 }
470
471                 protected override void CloneTo (CloneContext clonectx, Expression target)
472                 {
473                         GroupBy t = (GroupBy) target;
474                         if (element_selector != null) {
475                                 t.element_selector = element_selector.Clone (clonectx);
476                                 t.element_block = (QueryBlock) element_block.Clone (clonectx);
477                         }
478
479                         base.CloneTo (clonectx, t);
480                 }
481
482                 protected override string MethodName {
483                         get { return "GroupBy"; }
484                 }
485         }
486
487         public class Join : SelectMany
488         {
489                 QueryBlock inner_selector, outer_selector;
490
491                 public Join (QueryBlock block, RangeVariable lt, Expression inner, QueryBlock outerSelector, QueryBlock innerSelector, Location loc)
492                         : base (block, lt, inner, loc)
493                 {
494                         this.outer_selector = outerSelector;
495                         this.inner_selector = innerSelector;
496                 }
497
498                 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
499                 {
500                         args = new Arguments (4);
501
502                         if (IdentifierType != null)
503                                 expr = CreateCastExpression (expr);
504
505                         args.Add (new Argument (expr));
506
507                         outer_selector.SetParameter (parameter.Clone ());
508                         var lambda = new LambdaExpression (outer_selector.StartLocation);
509                         lambda.Block = outer_selector;
510                         args.Add (new Argument (lambda));
511
512                         inner_selector.SetParameter (new ImplicitLambdaParameter (identifier.Name, identifier.Location));
513                         lambda = new LambdaExpression (inner_selector.StartLocation);
514                         lambda.Block = inner_selector;
515                         args.Add (new Argument (lambda));
516
517                         base.CreateArguments (ec, parameter, ref args);
518                 }
519
520                 protected override void CloneTo (CloneContext clonectx, Expression target)
521                 {
522                         Join t = (Join) target;
523                         t.inner_selector = (QueryBlock) inner_selector.Clone (clonectx);
524                         t.outer_selector = (QueryBlock) outer_selector.Clone (clonectx);
525                         base.CloneTo (clonectx, t);
526                 }       
527
528                 protected override string MethodName {
529                         get { return "Join"; }
530                 }
531         }
532
533         public class GroupJoin : Join
534         {
535                 readonly RangeVariable into;
536
537                 public GroupJoin (QueryBlock block, RangeVariable lt, Expression inner,
538                         QueryBlock outerSelector, QueryBlock innerSelector, RangeVariable into, Location loc)
539                         : base (block, lt, inner, outerSelector, innerSelector, loc)
540                 {
541                         this.into = into;
542                 }
543
544                 protected override RangeVariable GetIntoVariable ()
545                 {
546                         return into;
547                 }
548
549                 protected override string MethodName {
550                         get { return "GroupJoin"; }
551                 }
552         }
553
554         public class Let : ARangeVariableQueryClause
555         {
556                 public Let (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
557                         : base (block, identifier, expr, loc)
558                 {
559                 }
560
561                 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
562                 {
563                         expr = CreateRangeVariableType (ec, parameter, identifier, expr);
564                         base.CreateArguments (ec, parameter, ref args);
565                 }
566
567                 protected override string MethodName {
568                         get { return "Select"; }
569                 }
570         }
571
572         public class Select : AQueryClause
573         {
574                 public Select (QueryBlock block, Expression expr, Location loc)
575                         : base (block, expr, loc)
576                 {
577                 }
578                 
579                 //
580                 // For queries like `from a orderby a select a'
581                 // the projection is transparent and select clause can be safely removed 
582                 //
583                 public bool IsRequired (Parameter parameter)
584                 {
585                         SimpleName sn = expr as SimpleName;
586                         if (sn == null)
587                                 return true;
588
589                         return sn.Name != parameter.Name;
590                 }
591
592                 protected override string MethodName {
593                         get { return "Select"; }
594                 }
595         }
596
597         public class SelectMany : ARangeVariableQueryClause
598         {
599                 public SelectMany (QueryBlock block, RangeVariable identifier, Expression expr, Location loc)
600                         : base (block, identifier, expr, loc)
601                 {
602                 }
603
604                 protected override void CreateArguments (ResolveContext ec, Parameter parameter, ref Arguments args)
605                 {
606                         if (args == null) {
607                                 if (IdentifierType != null)
608                                         expr = CreateCastExpression (expr);
609
610                                 base.CreateArguments (ec, parameter.Clone (), ref args);
611                         }
612
613                         Expression result_selector_expr;
614                         QueryBlock result_block;
615
616                         var target = GetIntoVariable ();
617                         var target_param = new ImplicitLambdaParameter (target.Name, target.Location);
618
619                         //
620                         // When select follows use it as a result selector
621                         //
622                         if (next is Select) {
623                                 result_selector_expr = next.Expr;
624
625                                 result_block = next.block;
626                                 result_block.SetParameters (parameter, target_param);
627
628                                 next = next.next;
629                         } else {
630                                 result_selector_expr = CreateRangeVariableType (ec, parameter, target, new SimpleName (target.Name, target.Location));
631
632                                 result_block = new QueryBlock (block.Parent, block.StartLocation);
633                                 result_block.SetParameters (parameter, target_param);
634                         }
635
636                         LambdaExpression result_selector = new LambdaExpression (Location);
637                         result_selector.Block = result_block;
638                         result_selector.Block.AddStatement (new ContextualReturn (result_selector_expr));
639
640                         args.Add (new Argument (result_selector));
641                 }
642
643                 protected override string MethodName {
644                         get { return "SelectMany"; }
645                 }
646         }
647
648         public class Where : AQueryClause
649         {
650                 public Where (QueryBlock block, Expression expr, Location loc)
651                         : base (block, expr, loc)
652                 {
653                 }
654
655                 protected override string MethodName {
656                         get { return "Where"; }
657                 }
658         }
659
660         public class OrderByAscending : AQueryClause
661         {
662                 public OrderByAscending (QueryBlock block, Expression expr)
663                         : base (block, expr, expr.Location)
664                 {
665                 }
666
667                 protected override string MethodName {
668                         get { return "OrderBy"; }
669                 }
670         }
671
672         public class OrderByDescending : AQueryClause
673         {
674                 public OrderByDescending (QueryBlock block, Expression expr)
675                         : base (block, expr, expr.Location)
676                 {
677                 }
678
679                 protected override string MethodName {
680                         get { return "OrderByDescending"; }
681                 }
682         }
683
684         public class ThenByAscending : OrderByAscending
685         {
686                 public ThenByAscending (QueryBlock block, Expression expr)
687                         : base (block, expr)
688                 {
689                 }
690
691                 protected override string MethodName {
692                         get { return "ThenBy"; }
693                 }
694         }
695
696         public class ThenByDescending : OrderByDescending
697         {
698                 public ThenByDescending (QueryBlock block, Expression expr)
699                         : base (block, expr)
700                 {
701                 }
702
703                 protected override string MethodName {
704                         get { return "ThenByDescending"; }
705                 }
706         }
707
708         //
709         // Implicit query block
710         //
711         public class QueryBlock : ParametersBlock
712         {
713                 //
714                 // Transparent parameters are used to package up the intermediate results
715                 // and pass them onto next clause
716                 //
717                 public sealed class TransparentParameter : ImplicitLambdaParameter
718                 {
719                         public static int Counter;
720                         const string ParameterNamePrefix = "<>__TranspIdent";
721
722                         public readonly Parameter Parent;
723                         public readonly string Identifier;
724
725                         public TransparentParameter (Parameter parent, RangeVariable identifier)
726                                 : base (ParameterNamePrefix + Counter++, identifier.Location)
727                         {
728                                 Parent = parent;
729                                 Identifier = identifier.Name;
730                         }
731
732                         public static void Reset ()
733                         {
734                                 Counter = 0;
735                         }
736                 }
737
738                 public QueryBlock (Block parent, Location start)
739                         : base (parent, ParametersCompiled.EmptyReadOnlyParameters, start)
740                 {
741                         flags |= Flags.CompilerGenerated;
742                 }
743
744                 public void AddRangeVariable (RangeVariable variable)
745                 {
746                         variable.Block = this;
747                         AddLocalName (variable.Name, variable);
748                 }
749
750                 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
751                 {
752                         TopBlock.Report.Error (1931, variable.Location,
753                                 "A range variable `{0}' conflicts with a previous declaration of `{0}'",
754                                 name);
755                 }
756
757                 public override void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
758                 {
759                         TopBlock.Report.Error (1930, variable.Location,
760                                 "A range variable `{0}' has already been declared in this scope",
761                                 name);          
762                 }
763
764                 public override void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
765                 {
766                         TopBlock.Report.Error (1948, loc,
767                                 "A range variable `{0}' conflicts with a method type parameter",
768                                 name);
769                 }
770
771                 public void SetParameter (Parameter parameter)
772                 {
773                         base.parameters = new ParametersCompiled (parameter);
774                         base.parameter_info = new ParameterInfo[] {
775                                 new ParameterInfo (this, 0)
776                         };
777                 }
778
779                 public void SetParameters (Parameter first, Parameter second)
780                 {
781                         base.parameters = new ParametersCompiled (first, second);
782                         base.parameter_info = new ParameterInfo[] {
783                                 new ParameterInfo (this, 0),
784                                 new ParameterInfo (this, 1)
785                         };
786                 }
787         }
788
789         sealed class TransparentMemberAccess : MemberAccess
790         {
791                 public TransparentMemberAccess (Expression expr, string name)
792                         : base (expr, name)
793                 {
794                 }
795
796                 public override Expression DoResolveLValue (ResolveContext rc, Expression right_side)
797                 {
798                         rc.Report.Error (1947, loc,
799                                 "A range variable `{0}' cannot be assigned to. Consider using `let' clause to store the value",
800                                 Name);
801
802                         return null;
803                 }
804         }
805 }