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