2010-04-29 Marek Safar <marek.safar@gmail.com>
[mono.git] / mcs / mcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Martin Baulig (martin@ximian.com)
7 //   Marek Safar (marek.safar@seznam.cz)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 //
12
13 using System;
14 using System.Text;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections.Generic;
19
20 namespace Mono.CSharp {
21         
22         public abstract class Statement {
23                 public Location loc;
24                 
25                 /// <summary>
26                 ///   Resolves the statement, true means that all sub-statements
27                 ///   did resolve ok.
28                 //  </summary>
29                 public virtual bool Resolve (BlockContext ec)
30                 {
31                         return true;
32                 }
33
34                 /// <summary>
35                 ///   We already know that the statement is unreachable, but we still
36                 ///   need to resolve it to catch errors.
37                 /// </summary>
38                 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
39                 {
40                         //
41                         // This conflicts with csc's way of doing this, but IMHO it's
42                         // the right thing to do.
43                         //
44                         // If something is unreachable, we still check whether it's
45                         // correct.  This means that you cannot use unassigned variables
46                         // in unreachable code, for instance.
47                         //
48
49                         if (warn)
50                                 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
51
52                         ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53                         bool ok = Resolve (ec);
54                         ec.KillFlowBranching ();
55
56                         return ok;
57                 }
58                                 
59                 /// <summary>
60                 ///   Return value indicates whether all code paths emitted return.
61                 /// </summary>
62                 protected abstract void DoEmit (EmitContext ec);
63
64                 public virtual void Emit (EmitContext ec)
65                 {
66                         ec.Mark (loc);
67                         DoEmit (ec);
68                 }
69
70                 //
71                 // This routine must be overrided in derived classes and make copies
72                 // of all the data that might be modified if resolved
73                 // 
74                 protected abstract void CloneTo (CloneContext clonectx, Statement target);
75
76                 public Statement Clone (CloneContext clonectx)
77                 {
78                         Statement s = (Statement) this.MemberwiseClone ();
79                         CloneTo (clonectx, s);
80                         return s;
81                 }
82
83                 public virtual Expression CreateExpressionTree (ResolveContext ec)
84                 {
85                         ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
86                         return null;
87                 }
88
89                 public Statement PerformClone ()
90                 {
91                         CloneContext clonectx = new CloneContext ();
92
93                         return Clone (clonectx);
94                 }
95         }
96
97         public sealed class EmptyStatement : Statement
98         {
99                 public EmptyStatement (Location loc)
100                 {
101                         this.loc = loc;
102                 }
103                 
104                 public override bool Resolve (BlockContext ec)
105                 {
106                         return true;
107                 }
108
109                 public override bool ResolveUnreachable (BlockContext ec, bool warn)
110                 {
111                         return true;
112                 }
113
114                 public override void Emit (EmitContext ec)
115                 {
116                 }
117
118                 protected override void DoEmit (EmitContext ec)
119                 {
120                         throw new NotSupportedException ();
121                 }
122
123                 protected override void CloneTo (CloneContext clonectx, Statement target)
124                 {
125                         // nothing needed.
126                 }
127         }
128         
129         public class If : Statement {
130                 Expression expr;
131                 public Statement TrueStatement;
132                 public Statement FalseStatement;
133
134                 bool is_true_ret;
135
136                 public If (Expression bool_expr, Statement true_statement, Location l)
137                         : this (bool_expr, true_statement, null, l)
138                 {
139                 }
140
141                 public If (Expression bool_expr,
142                            Statement true_statement,
143                            Statement false_statement,
144                            Location l)
145                 {
146                         this.expr = bool_expr;
147                         TrueStatement = true_statement;
148                         FalseStatement = false_statement;
149                         loc = l;
150                 }
151
152                 public override bool Resolve (BlockContext ec)
153                 {
154                         bool ok = true;
155
156                         Report.Debug (1, "START IF BLOCK", loc);
157
158                         expr = expr.Resolve (ec);
159                         if (expr == null) {
160                                 ok = false;
161                         } else {
162                                 //
163                                 // Dead code elimination
164                                 //
165                                 if (expr is Constant) {
166                                         bool take = !((Constant) expr).IsDefaultValue;
167
168                                         if (take) {
169                                                 if (!TrueStatement.Resolve (ec))
170                                                         return false;
171
172                                                 if ((FalseStatement != null) &&
173                                                         !FalseStatement.ResolveUnreachable (ec, true))
174                                                         return false;
175                                                 FalseStatement = null;
176                                         } else {
177                                                 if (!TrueStatement.ResolveUnreachable (ec, true))
178                                                         return false;
179                                                 TrueStatement = null;
180
181                                                 if ((FalseStatement != null) &&
182                                                         !FalseStatement.Resolve (ec))
183                                                         return false;
184                                         }
185
186                                         return true;
187                                 }
188                         }
189
190                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
191                         
192                         ok &= TrueStatement.Resolve (ec);
193
194                         is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
195
196                         ec.CurrentBranching.CreateSibling ();
197
198                         if (FalseStatement != null)
199                                 ok &= FalseStatement.Resolve (ec);
200                                         
201                         ec.EndFlowBranching ();
202
203                         Report.Debug (1, "END IF BLOCK", loc);
204
205                         return ok;
206                 }
207                 
208                 protected override void DoEmit (EmitContext ec)
209                 {
210                         Label false_target = ec.DefineLabel ();
211                         Label end;
212
213                         //
214                         // If we're a boolean constant, Resolve() already
215                         // eliminated dead code for us.
216                         //
217                         Constant c = expr as Constant;
218                         if (c != null){
219                                 c.EmitSideEffect (ec);
220
221                                 if (!c.IsDefaultValue)
222                                         TrueStatement.Emit (ec);
223                                 else if (FalseStatement != null)
224                                         FalseStatement.Emit (ec);
225
226                                 return;
227                         }                       
228                         
229                         expr.EmitBranchable (ec, false_target, false);
230                         
231                         TrueStatement.Emit (ec);
232
233                         if (FalseStatement != null){
234                                 bool branch_emitted = false;
235                                 
236                                 end = ec.DefineLabel ();
237                                 if (!is_true_ret){
238                                         ec.Emit (OpCodes.Br, end);
239                                         branch_emitted = true;
240                                 }
241
242                                 ec.MarkLabel (false_target);
243                                 FalseStatement.Emit (ec);
244
245                                 if (branch_emitted)
246                                         ec.MarkLabel (end);
247                         } else {
248                                 ec.MarkLabel (false_target);
249                         }
250                 }
251
252                 protected override void CloneTo (CloneContext clonectx, Statement t)
253                 {
254                         If target = (If) t;
255
256                         target.expr = expr.Clone (clonectx);
257                         target.TrueStatement = TrueStatement.Clone (clonectx);
258                         if (FalseStatement != null)
259                                 target.FalseStatement = FalseStatement.Clone (clonectx);
260                 }
261         }
262
263         public class Do : Statement {
264                 public Expression expr;
265                 public Statement  EmbeddedStatement;
266
267                 public Do (Statement statement, BooleanExpression bool_expr, Location l)
268                 {
269                         expr = bool_expr;
270                         EmbeddedStatement = statement;
271                         loc = l;
272                 }
273
274                 public override bool Resolve (BlockContext ec)
275                 {
276                         bool ok = true;
277
278                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
279
280                         bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
281
282                         ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
283                         if (!EmbeddedStatement.Resolve (ec))
284                                 ok = false;
285                         ec.EndFlowBranching ();
286
287                         if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
288                                 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
289
290                         expr = expr.Resolve (ec);
291                         if (expr == null)
292                                 ok = false;
293                         else if (expr is Constant){
294                                 bool infinite = !((Constant) expr).IsDefaultValue;
295                                 if (infinite)
296                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
297                         }
298
299                         ec.EndFlowBranching ();
300
301                         return ok;
302                 }
303                 
304                 protected override void DoEmit (EmitContext ec)
305                 {
306                         Label loop = ec.DefineLabel ();
307                         Label old_begin = ec.LoopBegin;
308                         Label old_end = ec.LoopEnd;
309                         
310                         ec.LoopBegin = ec.DefineLabel ();
311                         ec.LoopEnd = ec.DefineLabel ();
312                                 
313                         ec.MarkLabel (loop);
314                         EmbeddedStatement.Emit (ec);
315                         ec.MarkLabel (ec.LoopBegin);
316
317                         //
318                         // Dead code elimination
319                         //
320                         if (expr is Constant){
321                                 bool res = !((Constant) expr).IsDefaultValue;
322
323                                 expr.EmitSideEffect (ec);
324                                 if (res)
325                                         ec.Emit (OpCodes.Br, loop); 
326                         } else
327                                 expr.EmitBranchable (ec, loop, true);
328                         
329                         ec.MarkLabel (ec.LoopEnd);
330
331                         ec.LoopBegin = old_begin;
332                         ec.LoopEnd = old_end;
333                 }
334
335                 protected override void CloneTo (CloneContext clonectx, Statement t)
336                 {
337                         Do target = (Do) t;
338
339                         target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
340                         target.expr = expr.Clone (clonectx);
341                 }
342         }
343
344         public class While : Statement {
345                 public Expression expr;
346                 public Statement Statement;
347                 bool infinite, empty;
348
349                 public While (BooleanExpression bool_expr, Statement statement, Location l)
350                 {
351                         this.expr = bool_expr;
352                         Statement = statement;
353                         loc = l;
354                 }
355
356                 public override bool Resolve (BlockContext ec)
357                 {
358                         bool ok = true;
359
360                         expr = expr.Resolve (ec);
361                         if (expr == null)
362                                 ok = false;
363
364                         //
365                         // Inform whether we are infinite or not
366                         //
367                         if (expr is Constant){
368                                 bool value = !((Constant) expr).IsDefaultValue;
369
370                                 if (value == false){
371                                         if (!Statement.ResolveUnreachable (ec, true))
372                                                 return false;
373                                         empty = true;
374                                         return true;
375                                 } else
376                                         infinite = true;
377                         }
378
379                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
380                         if (!infinite)
381                                 ec.CurrentBranching.CreateSibling ();
382
383                         ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384                         if (!Statement.Resolve (ec))
385                                 ok = false;
386                         ec.EndFlowBranching ();
387
388                         // There's no direct control flow from the end of the embedded statement to the end of the loop
389                         ec.CurrentBranching.CurrentUsageVector.Goto ();
390
391                         ec.EndFlowBranching ();
392
393                         return ok;
394                 }
395                 
396                 protected override void DoEmit (EmitContext ec)
397                 {
398                         if (empty) {
399                                 expr.EmitSideEffect (ec);
400                                 return;
401                         }
402
403                         Label old_begin = ec.LoopBegin;
404                         Label old_end = ec.LoopEnd;
405                         
406                         ec.LoopBegin = ec.DefineLabel ();
407                         ec.LoopEnd = ec.DefineLabel ();
408
409                         //
410                         // Inform whether we are infinite or not
411                         //
412                         if (expr is Constant){
413                                 // expr is 'true', since the 'empty' case above handles the 'false' case
414                                 ec.MarkLabel (ec.LoopBegin);
415                                 expr.EmitSideEffect (ec);
416                                 Statement.Emit (ec);
417                                 ec.Emit (OpCodes.Br, ec.LoopBegin);
418                                         
419                                 //
420                                 // Inform that we are infinite (ie, `we return'), only
421                                 // if we do not `break' inside the code.
422                                 //
423                                 ec.MarkLabel (ec.LoopEnd);
424                         } else {
425                                 Label while_loop = ec.DefineLabel ();
426
427                                 ec.Emit (OpCodes.Br, ec.LoopBegin);
428                                 ec.MarkLabel (while_loop);
429
430                                 Statement.Emit (ec);
431                         
432                                 ec.MarkLabel (ec.LoopBegin);
433                                 ec.Mark (loc);
434
435                                 expr.EmitBranchable (ec, while_loop, true);
436                                 
437                                 ec.MarkLabel (ec.LoopEnd);
438                         }       
439
440                         ec.LoopBegin = old_begin;
441                         ec.LoopEnd = old_end;
442                 }
443
444                 public override void Emit (EmitContext ec)
445                 {
446                         DoEmit (ec);
447                 }
448
449                 protected override void CloneTo (CloneContext clonectx, Statement t)
450                 {
451                         While target = (While) t;
452
453                         target.expr = expr.Clone (clonectx);
454                         target.Statement = Statement.Clone (clonectx);
455                 }
456         }
457
458         public class For : Statement {
459                 Expression Test;
460                 Statement InitStatement;
461                 Statement Increment;
462                 public Statement Statement;
463                 bool infinite, empty;
464                 
465                 public For (Statement init_statement,
466                             BooleanExpression test,
467                             Statement increment,
468                             Statement statement,
469                             Location l)
470                 {
471                         InitStatement = init_statement;
472                         Test = test;
473                         Increment = increment;
474                         Statement = statement;
475                         loc = l;
476                 }
477
478                 public override bool Resolve (BlockContext ec)
479                 {
480                         bool ok = true;
481
482                         if (InitStatement != null){
483                                 if (!InitStatement.Resolve (ec))
484                                         ok = false;
485                         }
486
487                         if (Test != null){
488                                 Test = Test.Resolve (ec);
489                                 if (Test == null)
490                                         ok = false;
491                                 else if (Test is Constant){
492                                         bool value = !((Constant) Test).IsDefaultValue;
493
494                                         if (value == false){
495                                                 if (!Statement.ResolveUnreachable (ec, true))
496                                                         return false;
497                                                 if ((Increment != null) &&
498                                                     !Increment.ResolveUnreachable (ec, false))
499                                                         return false;
500                                                 empty = true;
501                                                 return true;
502                                         } else
503                                                 infinite = true;
504                                 }
505                         } else
506                                 infinite = true;
507
508                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
509                         if (!infinite)
510                                 ec.CurrentBranching.CreateSibling ();
511
512                         bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
513
514                         ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
515                         if (!Statement.Resolve (ec))
516                                 ok = false;
517                         ec.EndFlowBranching ();
518
519                         if (Increment != null){
520                                 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
521                                         if (!Increment.ResolveUnreachable (ec, !was_unreachable))
522                                                 ok = false;
523                                 } else {
524                                         if (!Increment.Resolve (ec))
525                                                 ok = false;
526                                 }
527                         }
528
529                         // There's no direct control flow from the end of the embedded statement to the end of the loop
530                         ec.CurrentBranching.CurrentUsageVector.Goto ();
531
532                         ec.EndFlowBranching ();
533
534                         return ok;
535                 }
536
537                 protected override void DoEmit (EmitContext ec)
538                 {
539                         if (InitStatement != null)
540                                 InitStatement.Emit (ec);
541
542                         if (empty) {
543                                 Test.EmitSideEffect (ec);
544                                 return;
545                         }
546
547                         Label old_begin = ec.LoopBegin;
548                         Label old_end = ec.LoopEnd;
549                         Label loop = ec.DefineLabel ();
550                         Label test = ec.DefineLabel ();
551
552                         ec.LoopBegin = ec.DefineLabel ();
553                         ec.LoopEnd = ec.DefineLabel ();
554
555                         ec.Emit (OpCodes.Br, test);
556                         ec.MarkLabel (loop);
557                         Statement.Emit (ec);
558
559                         ec.MarkLabel (ec.LoopBegin);
560                         Increment.Emit (ec);
561
562                         ec.MarkLabel (test);
563                         //
564                         // If test is null, there is no test, and we are just
565                         // an infinite loop
566                         //
567                         if (Test != null){
568                                 //
569                                 // The Resolve code already catches the case for
570                                 // Test == Constant (false) so we know that
571                                 // this is true
572                                 //
573                                 if (Test is Constant) {
574                                         Test.EmitSideEffect (ec);
575                                         ec.Emit (OpCodes.Br, loop);
576                                 } else {
577                                         Test.EmitBranchable (ec, loop, true);
578                                 }
579                                 
580                         } else
581                                 ec.Emit (OpCodes.Br, loop);
582                         ec.MarkLabel (ec.LoopEnd);
583
584                         ec.LoopBegin = old_begin;
585                         ec.LoopEnd = old_end;
586                 }
587
588                 protected override void CloneTo (CloneContext clonectx, Statement t)
589                 {
590                         For target = (For) t;
591
592                         if (InitStatement != null)
593                                 target.InitStatement = InitStatement.Clone (clonectx);
594                         if (Test != null)
595                                 target.Test = Test.Clone (clonectx);
596                         if (Increment != null)
597                                 target.Increment = Increment.Clone (clonectx);
598                         target.Statement = Statement.Clone (clonectx);
599                 }
600         }
601         
602         public class StatementExpression : Statement {
603                 ExpressionStatement expr;
604                 
605                 public StatementExpression (ExpressionStatement expr)
606                 {
607                         this.expr = expr;
608                         loc = expr.Location;
609                 }
610
611                 public override bool Resolve (BlockContext ec)
612                 {
613                         expr = expr.ResolveStatement (ec);
614                         return expr != null;
615                 }
616                 
617                 protected override void DoEmit (EmitContext ec)
618                 {
619                         expr.EmitStatement (ec);
620                 }
621
622                 public override string ToString ()
623                 {
624                         return "StatementExpression (" + expr + ")";
625                 }
626
627                 protected override void CloneTo (CloneContext clonectx, Statement t)
628                 {
629                         StatementExpression target = (StatementExpression) t;
630
631                         target.expr = (ExpressionStatement) expr.Clone (clonectx);
632                 }
633         }
634
635         // A 'return' or a 'yield break'
636         public abstract class ExitStatement : Statement
637         {
638                 protected bool unwind_protect;
639                 protected abstract bool DoResolve (BlockContext ec);
640
641                 public virtual void Error_FinallyClause (Report Report)
642                 {
643                         Report.Error (157, loc, "Control cannot leave the body of a finally clause");
644                 }
645
646                 public sealed override bool Resolve (BlockContext ec)
647                 {
648                         if (!DoResolve (ec))
649                                 return false;
650
651                         unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
652                         if (unwind_protect)
653                                 ec.NeedReturnLabel ();
654                         ec.CurrentBranching.CurrentUsageVector.Goto ();
655                         return true;
656                 }
657         }
658
659         /// <summary>
660         ///   Implements the return statement
661         /// </summary>
662         public class Return : ExitStatement {
663                 protected Expression Expr;
664                 public Return (Expression expr, Location l)
665                 {
666                         Expr = expr;
667                         loc = l;
668                 }
669                 
670                 protected override bool DoResolve (BlockContext ec)
671                 {
672                         if (Expr == null) {
673                                 if (ec.ReturnType == TypeManager.void_type)
674                                         return true;
675                                 
676                                 ec.Report.Error (126, loc,
677                                         "An object of a type convertible to `{0}' is required for the return statement",
678                                         TypeManager.CSharpName (ec.ReturnType));
679                                 return false;
680                         }
681
682                         if (ec.CurrentBlock.Toplevel.IsIterator) {
683                                 ec.Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
684                                                   "statement to return a value, or yield break to end the iteration");
685                         }
686
687                         AnonymousExpression am = ec.CurrentAnonymousMethod;
688                         if (am == null && ec.ReturnType == TypeManager.void_type) {
689                                 ec.Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
690                                         ec.GetSignatureForError ());
691                         }
692
693                         Expr = Expr.Resolve (ec);
694                         if (Expr == null)
695                                 return false;
696
697                         if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
698                                 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
699                                 return true;
700                         }
701
702                         if (Expr.Type != ec.ReturnType) {
703                                 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
704
705                                 if (Expr == null) {
706                                         if (am != null) {
707                                                 ec.Report.Error (1662, loc,
708                                                         "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
709                                                         am.ContainerType, am.GetSignatureForError ());
710                                         }
711                                         return false;
712                                 }
713                         }
714
715                         return true;                    
716                 }
717                 
718                 protected override void DoEmit (EmitContext ec)
719                 {
720                         if (Expr != null) {
721                                 Expr.Emit (ec);
722
723                                 if (unwind_protect)
724                                         ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
725                         }
726
727                         if (unwind_protect)
728                                 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
729                         else
730                                 ec.Emit (OpCodes.Ret);
731                 }
732
733                 protected override void CloneTo (CloneContext clonectx, Statement t)
734                 {
735                         Return target = (Return) t;
736                         // It's null for simple return;
737                         if (Expr != null)
738                                 target.Expr = Expr.Clone (clonectx);
739                 }
740         }
741
742         public class Goto : Statement {
743                 string target;
744                 LabeledStatement label;
745                 bool unwind_protect;
746
747                 public override bool Resolve (BlockContext ec)
748                 {
749                         unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
750                         ec.CurrentBranching.CurrentUsageVector.Goto ();
751                         return true;
752                 }
753                 
754                 public Goto (string label, Location l)
755                 {
756                         loc = l;
757                         target = label;
758                 }
759
760                 public string Target {
761                         get { return target; }
762                 }
763
764                 public void SetResolvedTarget (LabeledStatement label)
765                 {
766                         this.label = label;
767                         label.AddReference ();
768                 }
769
770                 protected override void CloneTo (CloneContext clonectx, Statement target)
771                 {
772                         // Nothing to clone
773                 }
774
775                 protected override void DoEmit (EmitContext ec)
776                 {
777                         if (label == null)
778                                 throw new InternalErrorException ("goto emitted before target resolved");
779                         Label l = label.LabelTarget (ec);
780                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
781                 }
782         }
783
784         public class LabeledStatement : Statement {
785                 string name;
786                 bool defined;
787                 bool referenced;
788                 Label label;
789
790                 FlowBranching.UsageVector vectors;
791                 
792                 public LabeledStatement (string name, Location l)
793                 {
794                         this.name = name;
795                         this.loc = l;
796                 }
797
798                 public Label LabelTarget (EmitContext ec)
799                 {
800                         if (defined)
801                                 return label;
802
803                         label = ec.DefineLabel ();
804                         defined = true;
805                         return label;
806                 }
807
808                 public string Name {
809                         get { return name; }
810                 }
811
812                 public bool IsDefined {
813                         get { return defined; }
814                 }
815
816                 public bool HasBeenReferenced {
817                         get { return referenced; }
818                 }
819
820                 public FlowBranching.UsageVector JumpOrigins {
821                         get { return vectors; }
822                 }
823
824                 public void AddUsageVector (FlowBranching.UsageVector vector)
825                 {
826                         vector = vector.Clone ();
827                         vector.Next = vectors;
828                         vectors = vector;
829                 }
830
831                 protected override void CloneTo (CloneContext clonectx, Statement target)
832                 {
833                         // nothing to clone
834                 }
835
836                 public override bool Resolve (BlockContext ec)
837                 {
838                         // this flow-branching will be terminated when the surrounding block ends
839                         ec.StartFlowBranching (this);
840                         return true;
841                 }
842
843                 protected override void DoEmit (EmitContext ec)
844                 {
845                         LabelTarget (ec);
846                         ec.MarkLabel (label);
847                 }
848
849                 public void AddReference ()
850                 {
851                         referenced = true;
852                 }
853         }
854         
855
856         /// <summary>
857         ///   `goto default' statement
858         /// </summary>
859         public class GotoDefault : Statement {
860                 
861                 public GotoDefault (Location l)
862                 {
863                         loc = l;
864                 }
865
866                 protected override void CloneTo (CloneContext clonectx, Statement target)
867                 {
868                         // nothing to clone
869                 }
870
871                 public override bool Resolve (BlockContext ec)
872                 {
873                         ec.CurrentBranching.CurrentUsageVector.Goto ();
874
875                         if (ec.Switch == null) {
876                                 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
877                                 return false;
878                         }
879
880                         if (!ec.Switch.GotDefault) {
881                                 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
882                                 return false;
883                         }
884
885                         return true;
886                 }
887
888                 protected override void DoEmit (EmitContext ec)
889                 {
890                         ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
891                 }
892         }
893
894         /// <summary>
895         ///   `goto case' statement
896         /// </summary>
897         public class GotoCase : Statement {
898                 Expression expr;
899                 SwitchLabel sl;
900                 
901                 public GotoCase (Expression e, Location l)
902                 {
903                         expr = e;
904                         loc = l;
905                 }
906
907                 public override bool Resolve (BlockContext ec)
908                 {
909                         if (ec.Switch == null){
910                                 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
911                                 return false;
912                         }
913
914                         ec.CurrentBranching.CurrentUsageVector.Goto ();
915
916                         expr = expr.Resolve (ec);
917                         if (expr == null)
918                                 return false;
919
920                         Constant c = expr as Constant;
921                         if (c == null) {
922                                 ec.Report.Error (150, expr.Location, "A constant value is expected");
923                                 return false;
924                         }
925
926                         TypeSpec type = ec.Switch.SwitchType;
927                         Constant res = c.TryReduce (ec, type, c.Location);
928                         if (res == null) {
929                                 c.Error_ValueCannotBeConverted (ec, loc, type, true);
930                                 return false;
931                         }
932
933                         if (!Convert.ImplicitStandardConversionExists (c, type))
934                                 ec.Report.Warning (469, 2, loc,
935                                         "The `goto case' value is not implicitly convertible to type `{0}'",
936                                         TypeManager.CSharpName (type));
937
938                         object val = res.GetValue ();
939                         if (val == null)
940                                 val = SwitchLabel.NullStringCase;
941                                         
942                         if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
943                                 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + 
944                                         (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
945                                 return false;
946                         }
947
948                         return true;
949                 }
950
951                 protected override void DoEmit (EmitContext ec)
952                 {
953                         ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
954                 }
955
956                 protected override void CloneTo (CloneContext clonectx, Statement t)
957                 {
958                         GotoCase target = (GotoCase) t;
959
960                         target.expr = expr.Clone (clonectx);
961                 }
962         }
963         
964         public class Throw : Statement {
965                 Expression expr;
966                 
967                 public Throw (Expression expr, Location l)
968                 {
969                         this.expr = expr;
970                         loc = l;
971                 }
972
973                 public override bool Resolve (BlockContext ec)
974                 {
975                         if (expr == null) {
976                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
977                                 return ec.CurrentBranching.CheckRethrow (loc);
978                         }
979
980                         expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
981                         ec.CurrentBranching.CurrentUsageVector.Goto ();
982
983                         if (expr == null)
984                                 return false;
985
986                         if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
987                                 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
988                         else
989                                 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
990
991                         return true;
992                 }
993                         
994                 protected override void DoEmit (EmitContext ec)
995                 {
996                         if (expr == null)
997                                 ec.Emit (OpCodes.Rethrow);
998                         else {
999                                 expr.Emit (ec);
1000
1001                                 ec.Emit (OpCodes.Throw);
1002                         }
1003                 }
1004
1005                 protected override void CloneTo (CloneContext clonectx, Statement t)
1006                 {
1007                         Throw target = (Throw) t;
1008
1009                         if (expr != null)
1010                                 target.expr = expr.Clone (clonectx);
1011                 }
1012         }
1013
1014         public class Break : Statement {
1015                 
1016                 public Break (Location l)
1017                 {
1018                         loc = l;
1019                 }
1020
1021                 bool unwind_protect;
1022
1023                 public override bool Resolve (BlockContext ec)
1024                 {
1025                         unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1026                         ec.CurrentBranching.CurrentUsageVector.Goto ();
1027                         return true;
1028                 }
1029
1030                 protected override void DoEmit (EmitContext ec)
1031                 {
1032                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1033                 }
1034
1035                 protected override void CloneTo (CloneContext clonectx, Statement t)
1036                 {
1037                         // nothing needed
1038                 }
1039         }
1040
1041         public class Continue : Statement {
1042                 
1043                 public Continue (Location l)
1044                 {
1045                         loc = l;
1046                 }
1047
1048                 bool unwind_protect;
1049
1050                 public override bool Resolve (BlockContext ec)
1051                 {
1052                         unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1053                         ec.CurrentBranching.CurrentUsageVector.Goto ();
1054                         return true;
1055                 }
1056
1057                 protected override void DoEmit (EmitContext ec)
1058                 {
1059                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1060                 }
1061
1062                 protected override void CloneTo (CloneContext clonectx, Statement t)
1063                 {
1064                         // nothing needed.
1065                 }
1066         }
1067
1068         public interface ILocalVariable
1069         {
1070                 void Emit (EmitContext ec);
1071                 void EmitAssign (EmitContext ec);
1072                 void EmitAddressOf (EmitContext ec);
1073         }
1074
1075         public interface IKnownVariable {
1076                 Block Block { get; }
1077                 Location Location { get; }
1078         }
1079
1080         //
1081         // The information about a user-perceived local variable
1082         //
1083         public class LocalInfo : IKnownVariable, ILocalVariable {
1084                 public readonly FullNamedExpression Type;
1085
1086                 public TypeSpec VariableType;
1087                 public readonly string Name;
1088                 public readonly Location Location;
1089                 public readonly Block Block;
1090
1091                 public VariableInfo VariableInfo;
1092                 HoistedVariable hoisted_variant;
1093
1094                 [Flags]
1095                 enum Flags : byte {
1096                         Used = 1,
1097                         ReadOnly = 2,
1098                         Pinned = 4,
1099                         IsThis = 8,
1100                         AddressTaken = 32,
1101                         CompilerGenerated = 64,
1102                         IsConstant = 128
1103                 }
1104
1105                 public enum ReadOnlyContext: byte {
1106                         Using,
1107                         Foreach,
1108                         Fixed
1109                 }
1110
1111                 Flags flags;
1112                 ReadOnlyContext ro_context;
1113                 LocalBuilder builder;
1114
1115                 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1116                 {
1117                         Type = type;
1118                         Name = name;
1119                         Block = block;
1120                         Location = l;
1121                 }
1122
1123                 public LocalInfo (TypeContainer ds, Block block, Location l)
1124                 {
1125                         VariableType = ds.IsGeneric ? ds.CurrentType : ds.Definition;
1126                         Block = block;
1127                         Location = l;
1128                 }
1129
1130                 public void ResolveVariable (EmitContext ec)
1131                 {
1132                         if (HoistedVariant != null)
1133                                 return;
1134
1135                         if (builder == null) {
1136                                 builder = ec.DeclareLocal (VariableType, Pinned);
1137                         }
1138                 }
1139
1140                 public void Emit (EmitContext ec)
1141                 {
1142                         ec.Emit (OpCodes.Ldloc, builder);
1143                 }
1144
1145                 public void EmitAssign (EmitContext ec)
1146                 {
1147                         ec.Emit (OpCodes.Stloc, builder);
1148                 }
1149
1150                 public void EmitAddressOf (EmitContext ec)
1151                 {
1152                         ec.Emit (OpCodes.Ldloca, builder);
1153                 }
1154
1155                 public void EmitSymbolInfo (EmitContext ec)
1156                 {
1157                         if (builder != null)
1158                                 ec.DefineLocalVariable (Name, builder);
1159                 }
1160
1161                 //
1162                 // Hoisted local variable variant
1163                 //
1164                 public HoistedVariable HoistedVariant {
1165                         get {
1166                                 return hoisted_variant;
1167                         }
1168                         set {
1169                                 hoisted_variant = value;
1170                         }
1171                 }
1172
1173                 public bool IsThisAssigned (BlockContext ec, Block block)
1174                 {
1175                         if (VariableInfo == null)
1176                                 throw new Exception ();
1177
1178                         if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1179                                 return true;
1180
1181                         return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1182                 }
1183
1184                 public bool IsAssigned (BlockContext ec)
1185                 {
1186                         if (VariableInfo == null)
1187                                 throw new Exception ();
1188
1189                         return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1190                 }
1191
1192                 public bool Resolve (ResolveContext ec)
1193                 {
1194                         if (VariableType != null)
1195                                 return true;
1196
1197                         TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1198                         if (texpr == null)
1199                                 return false;
1200                                 
1201                         VariableType = texpr.Type;
1202
1203                         if (VariableType.IsStatic) {
1204                                 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType, ec.Report);
1205                                 return false;
1206                         }
1207
1208                         if (VariableType.IsPointer && !ec.IsUnsafe)
1209                                 Expression.UnsafeError (ec, Location);
1210
1211                         return true;
1212                 }
1213
1214                 public bool IsConstant {
1215                         get { return (flags & Flags.IsConstant) != 0; }
1216                         set { flags |= Flags.IsConstant; }
1217                 }
1218
1219                 public bool AddressTaken {
1220                         get { return (flags & Flags.AddressTaken) != 0; }
1221                         set { flags |= Flags.AddressTaken; }
1222                 }
1223
1224                 public bool CompilerGenerated {
1225                         get { return (flags & Flags.CompilerGenerated) != 0; }
1226                         set { flags |= Flags.CompilerGenerated; }
1227                 }
1228
1229                 public override string ToString ()
1230                 {
1231                         return String.Format ("LocalInfo ({0},{1},{2},{3})",
1232                                               Name, Type, VariableInfo, Location);
1233                 }
1234
1235                 public bool Used {
1236                         get { return (flags & Flags.Used) != 0; }
1237                         set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1238                 }
1239
1240                 public bool ReadOnly {
1241                         get { return (flags & Flags.ReadOnly) != 0; }
1242                 }
1243
1244                 public void SetReadOnlyContext (ReadOnlyContext context)
1245                 {
1246                         flags |= Flags.ReadOnly;
1247                         ro_context = context;
1248                 }
1249
1250                 public string GetReadOnlyContext ()
1251                 {
1252                         if (!ReadOnly)
1253                                 throw new InternalErrorException ("Variable is not readonly");
1254
1255                         switch (ro_context) {
1256                         case ReadOnlyContext.Fixed:
1257                                 return "fixed variable";
1258                         case ReadOnlyContext.Foreach:
1259                                 return "foreach iteration variable";
1260                         case ReadOnlyContext.Using:
1261                                 return "using variable";
1262                         }
1263                         throw new NotImplementedException ();
1264                 }
1265
1266                 //
1267                 // Whether the variable is pinned, if Pinned the variable has been 
1268                 // allocated in a pinned slot with DeclareLocal.
1269                 //
1270                 public bool Pinned {
1271                         get { return (flags & Flags.Pinned) != 0; }
1272                         set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1273                 }
1274
1275                 public bool IsThis {
1276                         get { return (flags & Flags.IsThis) != 0; }
1277                         set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1278                 }
1279
1280                 Block IKnownVariable.Block {
1281                         get { return Block; }
1282                 }
1283
1284                 Location IKnownVariable.Location {
1285                         get { return Location; }
1286                 }
1287
1288                 public LocalInfo Clone (CloneContext clonectx)
1289                 {
1290                         //
1291                         // Variables in anonymous block are not resolved yet
1292                         //
1293                         if (VariableType == null)
1294                                 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1295
1296                         //
1297                         // Variables in method block are resolved
1298                         //
1299                         LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1300                         li.VariableType = VariableType;
1301                         return li;                      
1302                 }
1303         }
1304
1305         /// <summary>
1306         ///   Block represents a C# block.
1307         /// </summary>
1308         ///
1309         /// <remarks>
1310         ///   This class is used in a number of places: either to represent
1311         ///   explicit blocks that the programmer places or implicit blocks.
1312         ///
1313         ///   Implicit blocks are used as labels or to introduce variable
1314         ///   declarations.
1315         ///
1316         ///   Top-level blocks derive from Block, and they are called ToplevelBlock
1317         ///   they contain extra information that is not necessary on normal blocks.
1318         /// </remarks>
1319         public class Block : Statement {
1320                 public Block    Parent;
1321                 public Location StartLocation;
1322                 public Location EndLocation = Location.Null;
1323
1324                 public ExplicitBlock Explicit;
1325                 public ToplevelBlock Toplevel; // TODO: Use Explicit
1326
1327                 [Flags]
1328                 public enum Flags
1329                 {
1330                         Unchecked = 1,
1331                         BlockUsed = 2,
1332                         VariablesInitialized = 4,
1333                         HasRet = 8,
1334                         Unsafe = 16,
1335                         IsIterator = 32,
1336                         HasCapturedVariable = 64,
1337                         HasCapturedThis = 1 << 7,
1338                         IsExpressionTree = 1 << 8
1339                 }
1340
1341                 protected Flags flags;
1342
1343                 public bool Unchecked {
1344                         get { return (flags & Flags.Unchecked) != 0; }
1345                         set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1346                 }
1347
1348                 public bool Unsafe {
1349                         get { return (flags & Flags.Unsafe) != 0; }
1350                         set { flags |= Flags.Unsafe; }
1351                 }
1352
1353                 //
1354                 // The statements in this block
1355                 //
1356                 protected List<Statement> statements;
1357
1358                 //
1359                 // An array of Blocks.  We keep track of children just
1360                 // to generate the local variable declarations.
1361                 //
1362                 // Statements and child statements are handled through the
1363                 // statements.
1364                 //
1365                 List<Block> children;
1366
1367                 //
1368                 // Labels.  (label, block) pairs.
1369                 //
1370                 protected Dictionary<string, LabeledStatement> labels;
1371
1372                 //
1373                 // Keeps track of (name, type) pairs
1374                 //
1375                 Dictionary<string, LocalInfo> variables;
1376
1377                 //
1378                 // Keeps track of constants
1379                 Dictionary<string, Expression> constants;
1380
1381                 //
1382                 // Temporary variables.
1383                 //
1384                 List<LocalInfo> temporary_variables;
1385                 
1386                 //
1387                 // If this is a switch section, the enclosing switch block.
1388                 //
1389                 Block switch_block;
1390
1391                 protected List<Statement> scope_initializers;
1392
1393                 List<ToplevelBlock> anonymous_children;
1394
1395                 int? resolving_init_idx;
1396
1397                 protected static int id;
1398
1399                 int this_id;
1400
1401                 int assignable_slots;
1402                 bool unreachable_shown;
1403                 bool unreachable;
1404                 
1405                 public Block (Block parent)
1406                         : this (parent, (Flags) 0, Location.Null, Location.Null)
1407                 { }
1408
1409                 public Block (Block parent, Flags flags)
1410                         : this (parent, flags, Location.Null, Location.Null)
1411                 { }
1412
1413                 public Block (Block parent, Location start, Location end)
1414                         : this (parent, (Flags) 0, start, end)
1415                 { }
1416
1417                 //
1418                 // Useful when TopLevel block is downgraded to normal block
1419                 //
1420                 public Block (ToplevelBlock parent, ToplevelBlock source)
1421                         : this (parent, source.flags, source.StartLocation, source.EndLocation)
1422                 {
1423                         statements = source.statements;
1424                         children = source.children;
1425                         labels = source.labels;
1426                         variables = source.variables;
1427                         constants = source.constants;
1428                         switch_block = source.switch_block;
1429                 }
1430
1431                 public Block (Block parent, Flags flags, Location start, Location end)
1432                 {
1433                         if (parent != null) {
1434                                 parent.AddChild (this);
1435
1436                                 // the appropriate constructors will fixup these fields
1437                                 Toplevel = parent.Toplevel;
1438                                 Explicit = parent.Explicit;
1439                         }
1440                         
1441                         this.Parent = parent;
1442                         this.flags = flags;
1443                         this.StartLocation = start;
1444                         this.EndLocation = end;
1445                         this.loc = start;
1446                         this_id = id++;
1447                         statements = new List<Statement> (4);
1448                 }
1449
1450                 public Block CreateSwitchBlock (Location start)
1451                 {
1452                         // FIXME: should this be implicit?
1453                         Block new_block = new ExplicitBlock (this, start, start);
1454                         new_block.switch_block = this;
1455                         return new_block;
1456                 }
1457
1458                 public int ID {
1459                         get { return this_id; }
1460                 }
1461
1462                 public IDictionary<string, LocalInfo> Variables {
1463                         get {
1464                                 if (variables == null)
1465                                         variables = new Dictionary<string, LocalInfo> ();
1466                                 return variables;
1467                         }
1468                 }
1469
1470                 void AddChild (Block b)
1471                 {
1472                         if (children == null)
1473                                 children = new List<Block> (1);
1474                         
1475                         children.Add (b);
1476                 }
1477
1478                 public void SetEndLocation (Location loc)
1479                 {
1480                         EndLocation = loc;
1481                 }
1482
1483                 protected void Error_158 (string name, Location loc)
1484                 {
1485                         Toplevel.Report.Error (158, loc, "The label `{0}' shadows another label " +
1486                                       "by the same name in a contained scope", name);
1487                 }
1488
1489                 /// <summary>
1490                 ///   Adds a label to the current block. 
1491                 /// </summary>
1492                 ///
1493                 /// <returns>
1494                 ///   false if the name already exists in this block. true
1495                 ///   otherwise.
1496                 /// </returns>
1497                 ///
1498                 public bool AddLabel (LabeledStatement target)
1499                 {
1500                         if (switch_block != null)
1501                                 return switch_block.AddLabel (target);
1502
1503                         string name = target.Name;
1504
1505                         Block cur = this;
1506                         while (cur != null) {
1507                                 LabeledStatement s = cur.DoLookupLabel (name);
1508                                 if (s != null) {
1509                                         Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1510                                         Toplevel.Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1511                                         return false;
1512                                 }
1513
1514                                 if (this == Explicit)
1515                                         break;
1516
1517                                 cur = cur.Parent;
1518                         }
1519
1520                         while (cur != null) {
1521                                 if (cur.DoLookupLabel (name) != null) {
1522                                         Error_158 (name, target.loc);
1523                                         return false;
1524                                 }
1525
1526                                 if (children != null) {
1527                                         foreach (Block b in children) {
1528                                                 LabeledStatement s = b.DoLookupLabel (name);
1529                                                 if (s == null)
1530                                                         continue;
1531
1532                                                 Toplevel.Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1533                                                 Error_158 (name, target.loc);
1534                                                 return false;
1535                                         }
1536                                 }
1537
1538                                 cur = cur.Parent;
1539                         }
1540
1541                         Toplevel.CheckError158 (name, target.loc);
1542
1543                         if (labels == null)
1544                                 labels = new Dictionary<string, LabeledStatement> ();
1545
1546                         labels.Add (name, target);
1547                         return true;
1548                 }
1549
1550                 public LabeledStatement LookupLabel (string name)
1551                 {
1552                         LabeledStatement s = DoLookupLabel (name);
1553                         if (s != null)
1554                                 return s;
1555
1556                         if (children == null)
1557                                 return null;
1558
1559                         foreach (Block child in children) {
1560                                 if (Explicit != child.Explicit)
1561                                         continue;
1562
1563                                 s = child.LookupLabel (name);
1564                                 if (s != null)
1565                                         return s;
1566                         }
1567
1568                         return null;
1569                 }
1570
1571                 LabeledStatement DoLookupLabel (string name)
1572                 {
1573                         if (switch_block != null)
1574                                 return switch_block.LookupLabel (name);
1575
1576                         if (labels != null)
1577                                 if (labels.ContainsKey (name))
1578                                         return labels [name];
1579
1580                         return null;
1581                 }
1582
1583                 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1584                 {
1585                         Block b = this;
1586                         IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1587                         while (kvi == null) {
1588                                 b = b.Explicit.Parent;
1589                                 if (b == null)
1590                                         return true;
1591                                 kvi = b.Explicit.GetKnownVariable (name);
1592                         }
1593
1594                         if (kvi.Block == b)
1595                                 return true;
1596
1597                         // Is kvi.Block nested inside 'b'
1598                         if (b.Explicit != kvi.Block.Explicit) {
1599                                 //
1600                                 // If a variable by the same name it defined in a nested block of this
1601                                 // block, we violate the invariant meaning in a block.
1602                                 //
1603                                 if (b == this) {
1604                                         Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1605                                         Toplevel.Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1606                                         return false;
1607                                 }
1608
1609                                 //
1610                                 // It's ok if the definition is in a nested subblock of b, but not
1611                                 // nested inside this block -- a definition in a sibling block
1612                                 // should not affect us.
1613                                 //
1614                                 return true;
1615                         }
1616
1617                         //
1618                         // Block 'b' and kvi.Block are the same textual block.
1619                         // However, different variables are extant.
1620                         //
1621                         // Check if the variable is in scope in both blocks.  We use
1622                         // an indirect check that depends on AddVariable doing its
1623                         // part in maintaining the invariant-meaning-in-block property.
1624                         //
1625                         if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1626                                 return true;
1627
1628                         if (this is ToplevelBlock) {
1629                                 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1630                                 e.Error_VariableIsUsedBeforeItIsDeclared (Toplevel.Report, name);
1631                                 return false;
1632                         }
1633
1634                         //
1635                         // Even though we detected the error when the name is used, we
1636                         // treat it as if the variable declaration was in error.
1637                         //
1638                         Toplevel.Report.SymbolRelatedToPreviousError (loc, name);
1639                         Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1640                         return false;
1641                 }
1642
1643                 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1644                 {
1645                         LocalInfo vi = GetLocalInfo (name);
1646                         if (vi != null) {
1647                                 block.Report.SymbolRelatedToPreviousError (vi.Location, name);
1648                                 if (Explicit == vi.Block.Explicit) {
1649                                         Error_AlreadyDeclared (l, name, null);
1650                                 } else {
1651                                         Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1652                                                 "parent or current" : "parent");
1653                                 }
1654                                 return false;
1655                         }
1656
1657                         if (block != null) {
1658                                 Expression e = block.GetParameterReference (name, Location.Null);
1659                                 if (e != null) {
1660                                         ParameterReference pr = e as ParameterReference;
1661                                         if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1662                                                 Error_AlreadyDeclared (loc, name);
1663                                         else
1664                                                 Error_AlreadyDeclared (loc, name, "parent or current");
1665                                         return false;
1666                                 }
1667                         }
1668
1669                         return true;
1670                 }
1671
1672                 public LocalInfo AddVariable (Expression type, string name, Location l)
1673                 {
1674                         if (!CheckParentConflictName (Toplevel, name, l))
1675                                 return null;
1676
1677                         if (Toplevel.GenericMethod != null) {
1678                                 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1679                                         if (tp.Name == name) {
1680                                                 Toplevel.Report.SymbolRelatedToPreviousError (tp);
1681                                                 Error_AlreadyDeclaredTypeParameter (Toplevel.Report, loc, name, "local variable");
1682                                                 return null;
1683                                         }
1684                                 }
1685                         }                       
1686
1687                         IKnownVariable kvi = Explicit.GetKnownVariable (name);
1688                         if (kvi != null) {
1689                                 Toplevel.Report.SymbolRelatedToPreviousError (kvi.Location, name);
1690                                 Error_AlreadyDeclared (l, name, "child");
1691                                 return null;
1692                         }
1693
1694                         LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1695                         AddVariable (vi);
1696
1697                         if ((flags & Flags.VariablesInitialized) != 0)
1698                                 throw new InternalErrorException ("block has already been resolved");
1699
1700                         return vi;
1701                 }
1702                 
1703                 protected virtual void AddVariable (LocalInfo li)
1704                 {
1705                         Variables.Add (li.Name, li);
1706                         Explicit.AddKnownVariable (li.Name, li);
1707                 }
1708
1709                 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1710                 {
1711                         if (reason == null) {
1712                                 Error_AlreadyDeclared (loc, var);
1713                                 return;
1714                         }
1715                         
1716                         Toplevel.Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1717                                       "in this scope because it would give a different meaning " +
1718                                       "to `{0}', which is already used in a `{1}' scope " +
1719                                       "to denote something else", var, reason);
1720                 }
1721
1722                 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1723                 {
1724                         Toplevel.Report.Error (128, loc,
1725                                 "A local variable named `{0}' is already defined in this scope", name);
1726                 }
1727                                         
1728                 public virtual void Error_AlreadyDeclaredTypeParameter (Report r, Location loc, string name, string conflict)
1729                 {
1730                         r.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1731                                 name, conflict);
1732                 }
1733
1734                 public bool AddConstant (Expression type, string name, Expression value, Location l)
1735                 {
1736                         if (AddVariable (type, name, l) == null)
1737                                 return false;
1738                         
1739                         if (constants == null)
1740                                 constants = new Dictionary<string, Expression> ();
1741
1742                         constants.Add (name, value);
1743
1744                         // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1745                         Use ();
1746                         return true;
1747                 }
1748
1749                 static int next_temp_id = 0;
1750
1751                 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1752                 {
1753                         Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1754
1755                         if (temporary_variables == null)
1756                                 temporary_variables = new List<LocalInfo> ();
1757
1758                         int id = ++next_temp_id;
1759                         string name = "$s_" + id.ToString ();
1760
1761                         LocalInfo li = new LocalInfo (te, name, this, loc);
1762                         li.CompilerGenerated = true;
1763                         temporary_variables.Add (li);
1764                         return li;
1765                 }
1766
1767                 public LocalInfo GetLocalInfo (string name)
1768                 {
1769                         LocalInfo ret;
1770                         for (Block b = this; b != null; b = b.Parent) {
1771                                 if (b.variables != null) {
1772                                         if (b.variables.TryGetValue (name, out ret))
1773                                                 return ret;
1774                                 }
1775                         }
1776
1777                         return null;
1778                 }
1779
1780                 public Expression GetVariableType (string name)
1781                 {
1782                         LocalInfo vi = GetLocalInfo (name);
1783                         return vi == null ? null : vi.Type;
1784                 }
1785
1786                 public Expression GetConstantExpression (string name)
1787                 {
1788                         Expression ret;
1789                         for (Block b = this; b != null; b = b.Parent) {
1790                                 if (b.constants != null) {
1791                                         if (b.constants.TryGetValue (name, out ret))
1792                                                 return ret;
1793                                 }
1794                         }
1795                         return null;
1796                 }
1797
1798                 //
1799                 // It should be used by expressions which require to
1800                 // register a statement during resolve process.
1801                 //
1802                 public void AddScopeStatement (Statement s)
1803                 {
1804                         if (scope_initializers == null)
1805                                 scope_initializers = new List<Statement> ();
1806
1807                         //
1808                         // Simple recursive helper, when resolve scope initializer another
1809                         // new scope initializer can be added, this ensures it's initialized
1810                         // before existing one. For now this can happen with expression trees
1811                         // in base ctor initializer only
1812                         //
1813                         if (resolving_init_idx.HasValue) {
1814                                 scope_initializers.Insert (resolving_init_idx.Value, s);
1815                                 ++resolving_init_idx;
1816                         } else {
1817                                 scope_initializers.Add (s);
1818                         }
1819                 }
1820                 
1821                 public void AddStatement (Statement s)
1822                 {
1823                         statements.Add (s);
1824                         flags |= Flags.BlockUsed;
1825                 }
1826
1827                 public bool Used {
1828                         get { return (flags & Flags.BlockUsed) != 0; }
1829                 }
1830
1831                 public void Use ()
1832                 {
1833                         flags |= Flags.BlockUsed;
1834                 }
1835
1836                 public bool HasRet {
1837                         get { return (flags & Flags.HasRet) != 0; }
1838                 }
1839
1840                 public int AssignableSlots {
1841                         get {
1842 // TODO: Re-enable                      
1843 //                              if ((flags & Flags.VariablesInitialized) == 0)
1844 //                                      throw new Exception ("Variables have not been initialized yet");
1845                                 return assignable_slots;
1846                         }
1847                 }
1848
1849                 public IList<ToplevelBlock> AnonymousChildren {
1850                         get { return anonymous_children; }
1851                 }
1852
1853                 public void AddAnonymousChild (ToplevelBlock b)
1854                 {
1855                         if (anonymous_children == null)
1856                                 anonymous_children = new List<ToplevelBlock> ();
1857
1858                         anonymous_children.Add (b);
1859                 }
1860
1861                 void DoResolveConstants (BlockContext ec)
1862                 {
1863                         if (constants == null)
1864                                 return;
1865
1866                         if (variables == null)
1867                                 throw new InternalErrorException ("cannot happen");
1868
1869                         foreach (var de in variables) {
1870                                 string name = de.Key;
1871                                 LocalInfo vi = de.Value;
1872                                 TypeSpec variable_type = vi.VariableType;
1873
1874                                 if (variable_type == null) {
1875                                         if (vi.Type is VarExpr)
1876                                                 ec.Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
1877
1878                                         continue;
1879                                 }
1880
1881                                 Expression cv;
1882                                 if (!constants.TryGetValue (name, out cv))
1883                                         continue;
1884
1885                                 // Don't let 'const int Foo = Foo;' succeed.
1886                                 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
1887                                 // which in turn causes the 'must be constant' error to be triggered.
1888                                 constants.Remove (name);
1889
1890                                 if (!variable_type.IsConstantCompatible) {
1891                                         Const.Error_InvalidConstantType (variable_type, loc, ec.Report);
1892                                         continue;
1893                                 }
1894
1895                                 ec.CurrentBlock = this;
1896                                 Expression e;
1897                                 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
1898                                         e = cv.Resolve (ec);
1899                                 }
1900                                 if (e == null)
1901                                         continue;
1902
1903                                 Constant ce = e as Constant;
1904                                 if (ce == null) {
1905                                         e.Error_ExpressionMustBeConstant (ec, vi.Location, name);
1906                                         continue;
1907                                 }
1908
1909                                 e = ce.ConvertImplicitly (ec, variable_type);
1910                                 if (e == null) {
1911                                         if (TypeManager.IsReferenceType (variable_type))
1912                                                 ce.Error_ConstantCanBeInitializedWithNullOnly (ec, variable_type, vi.Location, vi.Name);
1913                                         else
1914                                                 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
1915                                         continue;
1916                                 }
1917
1918                                 constants.Add (name, e);
1919                                 vi.IsConstant = true;
1920                         }
1921                 }
1922
1923                 protected void ResolveMeta (BlockContext ec, int offset)
1924                 {
1925                         Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
1926
1927                         // If some parent block was unsafe, we remain unsafe even if this block
1928                         // isn't explicitly marked as such.
1929                         using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
1930                                 flags |= Flags.VariablesInitialized;
1931
1932                                 if (variables != null) {
1933                                         foreach (LocalInfo li in variables.Values) {
1934                                                 if (!li.Resolve (ec))
1935                                                         continue;
1936                                                 li.VariableInfo = new VariableInfo (li, offset);
1937                                                 offset += li.VariableInfo.Length;
1938                                         }
1939                                 }
1940                                 assignable_slots = offset;
1941
1942                                 DoResolveConstants (ec);
1943
1944                                 if (children == null)
1945                                         return;
1946                                 foreach (Block b in children)
1947                                         b.ResolveMeta (ec, offset);
1948                         }
1949                 }
1950
1951                 //
1952                 // Emits the local variable declarations for a block
1953                 //
1954                 public virtual void EmitMeta (EmitContext ec)
1955                 {
1956                         if (variables != null){
1957                                 foreach (LocalInfo vi in variables.Values)
1958                                         vi.ResolveVariable (ec);
1959                         }
1960
1961                         if (temporary_variables != null) {
1962                                 for (int i = 0; i < temporary_variables.Count; i++)
1963                                         ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
1964                         }
1965
1966                         if (children != null) {
1967                                 for (int i = 0; i < children.Count; i++)
1968                                         ((Block)children[i]).EmitMeta(ec);
1969                         }
1970                 }
1971
1972                 void UsageWarning (BlockContext ec)
1973                 {
1974                         if (variables == null || ec.Report.WarningLevel < 3)
1975                                 return;
1976
1977                         foreach (var de in variables) {
1978                                 LocalInfo vi = de.Value;
1979
1980                                 if (!vi.Used) {
1981                                         string name = de.Key;
1982
1983                                         // vi.VariableInfo can be null for 'catch' variables
1984                                         if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
1985                                                 ec.Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
1986                                         else
1987                                                 ec.Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
1988                                 }
1989                         }
1990                 }
1991
1992                 static void CheckPossibleMistakenEmptyStatement (BlockContext ec, Statement s)
1993                 {
1994                         Statement body;
1995
1996                         // Some statements are wrapped by a Block. Since
1997                         // others' internal could be changed, here I treat
1998                         // them as possibly wrapped by Block equally.
1999                         Block b = s as Block;
2000                         if (b != null && b.statements.Count == 1)
2001                                 s = (Statement) b.statements [0];
2002
2003                         if (s is Lock)
2004                                 body = ((Lock) s).Statement;
2005                         else if (s is For)
2006                                 body = ((For) s).Statement;
2007                         else if (s is Foreach)
2008                                 body = ((Foreach) s).Statement;
2009                         else if (s is While)
2010                                 body = ((While) s).Statement;
2011                         else if (s is Fixed)
2012                                 body = ((Fixed) s).Statement;
2013                         else if (s is Using)
2014                                 body = ((Using) s).EmbeddedStatement;
2015                         else if (s is UsingTemporary)
2016                                 body = ((UsingTemporary) s).Statement;
2017                         else
2018                                 return;
2019
2020                         if (body == null || body is EmptyStatement)
2021                                 ec.Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2022                 }
2023
2024                 public override bool Resolve (BlockContext ec)
2025                 {
2026                         Block prev_block = ec.CurrentBlock;
2027                         bool ok = true;
2028
2029                         int errors = ec.Report.Errors;
2030
2031                         ec.CurrentBlock = this;
2032                         ec.StartFlowBranching (this);
2033
2034                         Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2035
2036                         //
2037                         // Compiler generated scope statements
2038                         //
2039                         if (scope_initializers != null) {
2040                                 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2041                                         scope_initializers[resolving_init_idx.Value].Resolve (ec);
2042                                 }
2043
2044                                 resolving_init_idx = null;
2045                         }
2046
2047                         //
2048                         // This flag is used to notate nested statements as unreachable from the beginning of this block.
2049                         // For the purposes of this resolution, it doesn't matter that the whole block is unreachable 
2050                         // from the beginning of the function.  The outer Resolve() that detected the unreachability is
2051                         // responsible for handling the situation.
2052                         //
2053                         int statement_count = statements.Count;
2054                         for (int ix = 0; ix < statement_count; ix++){
2055                                 Statement s = statements [ix];
2056                                 // Check possible empty statement (CS0642)
2057                                 if (ix + 1 < statement_count && ec.Report.WarningLevel >= 3 &&
2058                                         statements [ix + 1] is ExplicitBlock)
2059                                         CheckPossibleMistakenEmptyStatement (ec, s);
2060
2061                                 //
2062                                 // Warn if we detect unreachable code.
2063                                 //
2064                                 if (unreachable) {
2065                                         if (s is EmptyStatement)
2066                                                 continue;
2067
2068                                         if (!unreachable_shown && !(s is LabeledStatement)) {
2069                                                 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2070                                                 unreachable_shown = true;
2071                                         }
2072
2073                                         Block c_block = s as Block;
2074                                         if (c_block != null)
2075                                                 c_block.unreachable = c_block.unreachable_shown = true;
2076                                 }
2077
2078                                 //
2079                                 // Note that we're not using ResolveUnreachable() for unreachable
2080                                 // statements here.  ResolveUnreachable() creates a temporary
2081                                 // flow branching and kills it afterwards.  This leads to problems
2082                                 // if you have two unreachable statements where the first one
2083                                 // assigns a variable and the second one tries to access it.
2084                                 //
2085
2086                                 if (!s.Resolve (ec)) {
2087                                         ok = false;
2088                                         if (ec.IsInProbingMode)
2089                                                 break;
2090
2091                                         statements [ix] = new EmptyStatement (s.loc);
2092                                         continue;
2093                                 }
2094
2095                                 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2096                                         statements [ix] = new EmptyStatement (s.loc);
2097
2098                                 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2099                                 if (unreachable && s is LabeledStatement)
2100                                         throw new InternalErrorException ("should not happen");
2101                         }
2102
2103                         Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2104                                       ec.CurrentBranching, statement_count);
2105
2106                         while (ec.CurrentBranching is FlowBranchingLabeled)
2107                                 ec.EndFlowBranching ();
2108
2109                         bool flow_unreachable = ec.EndFlowBranching ();
2110
2111                         ec.CurrentBlock = prev_block;
2112
2113                         if (flow_unreachable)
2114                                 flags |= Flags.HasRet;
2115
2116                         // If we're a non-static `struct' constructor which doesn't have an
2117                         // initializer, then we must initialize all of the struct's fields.
2118                         if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2119                                 ok = false;
2120
2121                         if ((labels != null) && (ec.Report.WarningLevel >= 2)) {
2122                                 foreach (LabeledStatement label in labels.Values)
2123                                         if (!label.HasBeenReferenced)
2124                                                 ec.Report.Warning (164, 2, label.loc, "This label has not been referenced");
2125                         }
2126
2127                         if (ok && errors == ec.Report.Errors)
2128                                 UsageWarning (ec);
2129
2130                         return ok;
2131                 }
2132
2133                 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2134                 {
2135                         unreachable_shown = true;
2136                         unreachable = true;
2137
2138                         if (warn)
2139                                 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2140
2141                         ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2142                         bool ok = Resolve (ec);
2143                         ec.KillFlowBranching ();
2144
2145                         return ok;
2146                 }
2147                 
2148                 protected override void DoEmit (EmitContext ec)
2149                 {
2150                         for (int ix = 0; ix < statements.Count; ix++){
2151                                 Statement s = (Statement) statements [ix];
2152                                 s.Emit (ec);
2153                         }
2154                 }
2155
2156                 public override void Emit (EmitContext ec)
2157                 {
2158                         if (scope_initializers != null)
2159                                 EmitScopeInitializers (ec);
2160
2161                         ec.Mark (StartLocation);
2162                         DoEmit (ec);
2163
2164                         if (SymbolWriter.HasSymbolWriter)
2165                                 EmitSymbolInfo (ec);
2166                 }
2167
2168                 protected void EmitScopeInitializers (EmitContext ec)
2169                 {
2170                         SymbolWriter.OpenCompilerGeneratedBlock (ec);
2171
2172                         using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2173                                 foreach (Statement s in scope_initializers)
2174                                         s.Emit (ec);
2175                         }
2176
2177                         SymbolWriter.CloseCompilerGeneratedBlock (ec);
2178                 }
2179
2180                 protected virtual void EmitSymbolInfo (EmitContext ec)
2181                 {
2182                         if (variables != null) {
2183                                 foreach (LocalInfo vi in variables.Values) {
2184                                         vi.EmitSymbolInfo (ec);
2185                                 }
2186                         }
2187                 }
2188
2189                 public override string ToString ()
2190                 {
2191                         return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2192                 }
2193
2194                 protected override void CloneTo (CloneContext clonectx, Statement t)
2195                 {
2196                         Block target = (Block) t;
2197
2198                         clonectx.AddBlockMap (this, target);
2199
2200                         //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2201                         target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2202                         if (Parent != null)
2203                                 target.Parent = clonectx.RemapBlockCopy (Parent);
2204
2205                         if (variables != null){
2206                                 target.variables = new Dictionary<string, LocalInfo> ();
2207
2208                                 foreach (var de in variables){
2209                                         LocalInfo newlocal = de.Value.Clone (clonectx);
2210                                         target.variables [de.Key] = newlocal;
2211                                         clonectx.AddVariableMap (de.Value, newlocal);
2212                                 }
2213                         }
2214
2215                         target.statements = new List<Statement> (statements.Count);
2216                         foreach (Statement s in statements)
2217                                 target.statements.Add (s.Clone (clonectx));
2218
2219                         if (target.children != null){
2220                                 target.children = new List<Block> (children.Count);
2221                                 foreach (Block b in children){
2222                                         target.children.Add (clonectx.LookupBlock (b));
2223                                 }
2224                         }
2225
2226                         //
2227                         // TODO: labels, switch_block, constants (?), anonymous_children
2228                         //
2229                 }
2230         }
2231
2232         public class ExplicitBlock : Block
2233         {
2234                 Dictionary<string, IKnownVariable> known_variables;
2235                 protected AnonymousMethodStorey am_storey;
2236
2237                 public ExplicitBlock (Block parent, Location start, Location end)
2238                         : this (parent, (Flags) 0, start, end)
2239                 {
2240                 }
2241
2242                 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2243                         : base (parent, flags, start, end)
2244                 {
2245                         this.Explicit = this;
2246                 }
2247
2248                 // <summary>
2249                 //   Marks a variable with name @name as being used in this or a child block.
2250                 //   If a variable name has been used in a child block, it's illegal to
2251                 //   declare a variable with the same name in the current block.
2252                 // </summary>
2253                 internal void AddKnownVariable (string name, IKnownVariable info)
2254                 {
2255                         if (known_variables == null)
2256                                 known_variables = new Dictionary<string, IKnownVariable> ();
2257
2258                         known_variables [name] = info;
2259
2260                         if (Parent != null)
2261                                 Parent.Explicit.AddKnownVariable (name, info);
2262                 }
2263
2264                 public AnonymousMethodStorey AnonymousMethodStorey {
2265                         get { return am_storey; }
2266                 }
2267
2268                 //
2269                 // Creates anonymous method storey in current block
2270                 //
2271                 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2272                 {
2273                         //
2274                         // When referencing a variable in iterator storey from children anonymous method
2275                         //
2276                         if (Toplevel.am_storey is IteratorStorey) {
2277                                 return Toplevel.am_storey;
2278                         }
2279
2280                         //
2281                         // An iterator has only 1 storey block
2282                         //
2283                         if (ec.CurrentIterator != null)
2284                             return ec.CurrentIterator.Storey;
2285
2286                         if (am_storey == null) {
2287                                 MemberBase mc = ec.MemberContext as MemberBase;
2288                                 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2289
2290                                 //
2291                                 // Creates anonymous method storey for this block
2292                                 //
2293                                 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, gm, "AnonStorey");
2294                         }
2295
2296                         return am_storey;
2297                 }
2298
2299                 public override void Emit (EmitContext ec)
2300                 {
2301                         if (am_storey != null)
2302                                 am_storey.EmitStoreyInstantiation (ec);
2303
2304                         bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2305                         if (emit_debug_info)
2306                                 ec.BeginScope ();
2307
2308                         base.Emit (ec);
2309
2310                         if (emit_debug_info)
2311                                 ec.EndScope ();
2312                 }
2313
2314                 public override void EmitMeta (EmitContext ec)
2315                 {
2316                         //
2317                         // Creates anonymous method storey
2318                         //
2319                         if (am_storey != null) {
2320                                 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2321                                         //
2322                                         // Creates parent storey reference when hoisted this is accessible
2323                                         //
2324                                         if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2325                                                 ExplicitBlock parent = Toplevel.Parent.Explicit;
2326
2327                                                 //
2328                                                 // Hoisted this exists in top-level parent storey only
2329                                                 //
2330                                                 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2331                                                         parent = parent.Parent.Explicit;
2332
2333                                                 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2334                                         }
2335
2336                                         am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2337
2338                                         // TODO MemberCache: Review
2339                                         am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2340                                 }
2341
2342                                 am_storey.CreateType ();
2343                                 if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2344                                         am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2345
2346                                 am_storey.DefineType ();
2347                                 am_storey.ResolveTypeParameters ();
2348                                 am_storey.Define ();
2349                                 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2350
2351                                 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2352                                 if (ref_blocks != null) {
2353                                         foreach (ExplicitBlock ref_block in ref_blocks) {
2354                                                 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2355                                                         if (b.am_storey != null) {
2356                                                                 b.am_storey.AddParentStoreyReference (ec, am_storey);
2357
2358                                                                 // Stop propagation inside same top block
2359                                                                 if (b.Toplevel == Toplevel)
2360                                                                         break;
2361
2362                                                                 b = b.Toplevel;
2363                                                     }
2364                                                         b.HasCapturedVariable = true;
2365                                                 }
2366                                         }
2367                                 }
2368                         }
2369
2370                         base.EmitMeta (ec);
2371                 }
2372
2373                 internal IKnownVariable GetKnownVariable (string name)
2374                 {
2375                         if (known_variables == null)
2376                                 return null;
2377
2378                         IKnownVariable kw;
2379                         if (!known_variables.TryGetValue (name, out kw))
2380                                 return null;
2381
2382                         return kw;
2383                 }
2384
2385                 public bool HasCapturedThis
2386                 {
2387                         set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2388                         get { return (flags & Flags.HasCapturedThis) != 0; }
2389                 }
2390
2391                 public bool HasCapturedVariable
2392                 {
2393                         set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2394                         get { return (flags & Flags.HasCapturedVariable) != 0; }
2395                 }
2396
2397                 protected override void CloneTo (CloneContext clonectx, Statement t)
2398                 {
2399                         ExplicitBlock target = (ExplicitBlock) t;
2400                         target.known_variables = null;
2401                         base.CloneTo (clonectx, t);
2402                 }
2403         }
2404
2405         public class ToplevelParameterInfo : IKnownVariable {
2406                 public readonly ToplevelBlock Block;
2407                 public readonly int Index;
2408                 public VariableInfo VariableInfo;
2409
2410                 Block IKnownVariable.Block {
2411                         get { return Block; }
2412                 }
2413                 public Parameter Parameter {
2414                         get { return Block.Parameters [Index]; }
2415                 }
2416
2417                 public TypeSpec ParameterType {
2418                         get { return Block.Parameters.Types [Index]; }
2419                 }
2420
2421                 public Location Location {
2422                         get { return Parameter.Location; }
2423                 }
2424
2425                 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2426                 {
2427                         this.Block = block;
2428                         this.Index = idx;
2429                 }
2430         }
2431
2432         //
2433         // A toplevel block contains extra information, the split is done
2434         // only to separate information that would otherwise bloat the more
2435         // lightweight Block.
2436         //
2437         // In particular, this was introduced when the support for Anonymous
2438         // Methods was implemented. 
2439         // 
2440         public class ToplevelBlock : ExplicitBlock
2441         {
2442                 // 
2443                 // Block is converted to an expression
2444                 //
2445                 sealed class BlockScopeExpression : Expression
2446                 {
2447                         Expression child;
2448                         readonly ToplevelBlock block;
2449
2450                         public BlockScopeExpression (Expression child, ToplevelBlock block)
2451                         {
2452                                 this.child = child;
2453                                 this.block = block;
2454                         }
2455
2456                         public override Expression CreateExpressionTree (ResolveContext ec)
2457                         {
2458                                 throw new NotSupportedException ();
2459                         }
2460
2461                         protected override Expression DoResolve (ResolveContext ec)
2462                         {
2463                                 if (child == null)
2464                                         return null;
2465                                 
2466                                 child = child.Resolve (ec);
2467                                 if (child == null)
2468                                         return null;
2469
2470                                 eclass = child.eclass;
2471                                 type = child.Type;
2472                                 return this;
2473                         }
2474
2475                         public override void Emit (EmitContext ec)
2476                         {
2477                                 block.EmitMeta (ec);
2478                                 block.EmitScopeInitializers (ec);
2479                                 child.Emit (ec);
2480                         }
2481                 }
2482
2483                 GenericMethod generic;
2484                 protected ParametersCompiled parameters;
2485                 ToplevelParameterInfo[] parameter_info;
2486                 LocalInfo this_variable;
2487                 bool resolved;
2488                 bool unreachable;
2489                 CompilerContext compiler;
2490
2491                 public HoistedVariable HoistedThisVariable;
2492
2493                 public bool Resolved {
2494                         get {
2495                                 return resolved;
2496                         }
2497                 }
2498
2499                 //
2500                 // The parameters for the block.
2501                 //
2502                 public ParametersCompiled Parameters {
2503                         get { return parameters; }
2504                 }
2505
2506                 public Report Report {
2507                         get { return compiler.Report; }
2508                 }
2509
2510                 public GenericMethod GenericMethod {
2511                         get { return generic; }
2512                 }
2513
2514                 public ToplevelBlock Container {
2515                         get { return Parent == null ? null : Parent.Toplevel; }
2516                 }
2517
2518                 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, Location start) :
2519                         this (ctx, parent, (Flags) 0, parameters, start)
2520                 {
2521                 }
2522
2523                 public ToplevelBlock (CompilerContext ctx, Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2524                         this (ctx, parent, parameters, start)
2525                 {
2526                         this.generic = generic;
2527                 }
2528
2529                 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start) :
2530                         this (ctx, null, (Flags) 0, parameters, start)
2531                 {
2532                 }
2533
2534                 ToplevelBlock (CompilerContext ctx, Flags flags, ParametersCompiled parameters, Location start) :
2535                         this (ctx, null, flags, parameters, start)
2536                 {
2537                 }
2538
2539                 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2540                 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2541                 public ToplevelBlock (CompilerContext ctx, Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2542                         base (null, flags, start, Location.Null)
2543                 {
2544                         this.compiler = ctx;
2545                         this.Toplevel = this;
2546
2547                         this.parameters = parameters;
2548                         this.Parent = parent;
2549                         if (parent != null)
2550                                 parent.AddAnonymousChild (this);
2551
2552                         if (!this.parameters.IsEmpty)
2553                                 ProcessParameters ();
2554                 }
2555
2556                 public ToplevelBlock (CompilerContext ctx, Location loc)
2557                         : this (ctx, null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2558                 {
2559                 }
2560
2561                 protected override void CloneTo (CloneContext clonectx, Statement t)
2562                 {
2563                         ToplevelBlock target = (ToplevelBlock) t;
2564                         base.CloneTo (clonectx, t);
2565
2566                         if (parameters.Count != 0)
2567                                 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2568                         for (int i = 0; i < parameters.Count; ++i)
2569                                 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2570                 }
2571
2572                 public bool CheckError158 (string name, Location loc)
2573                 {
2574                         if (AnonymousChildren != null) {
2575                                 foreach (ToplevelBlock child in AnonymousChildren) {
2576                                         if (!child.CheckError158 (name, loc))
2577                                                 return false;
2578                                 }
2579                         }
2580
2581                         for (ToplevelBlock c = Container; c != null; c = c.Container) {
2582                                 if (!c.DoCheckError158 (name, loc))
2583                                         return false;
2584                         }
2585
2586                         return true;
2587                 }
2588
2589                 void ProcessParameters ()
2590                 {
2591                         int n = parameters.Count;
2592                         parameter_info = new ToplevelParameterInfo [n];
2593                         ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2594                         for (int i = 0; i < n; ++i) {
2595                                 parameter_info [i] = new ToplevelParameterInfo (this, i);
2596
2597                                 Parameter p = parameters [i];
2598                                 if (p == null)
2599                                         continue;
2600
2601                                 string name = p.Name;
2602                                 if (CheckParentConflictName (top_parent, name, loc))
2603                                         AddKnownVariable (name, parameter_info [i]);
2604                         }
2605
2606                         // mark this block as "used" so that we create local declarations in a sub-block
2607                         // FIXME: This appears to uncover a lot of bugs
2608                         //this.Use ();
2609                 }
2610
2611                 bool DoCheckError158 (string name, Location loc)
2612                 {
2613                         LabeledStatement s = LookupLabel (name);
2614                         if (s != null) {
2615                                 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2616                                 Error_158 (name, loc);
2617                                 return false;
2618                         }
2619
2620                         return true;
2621                 }
2622
2623                 public override Expression CreateExpressionTree (ResolveContext ec)
2624                 {
2625                         if (statements.Count == 1) {
2626                                 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2627                                 if (scope_initializers != null)
2628                                         expr = new BlockScopeExpression (expr, this);
2629
2630                                 return expr;
2631                         }
2632
2633                         return base.CreateExpressionTree (ec);
2634                 }
2635
2636                 //
2637                 // Reformats this block to be top-level iterator block
2638                 //
2639                 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2640                 {
2641                         IsIterator = true;
2642
2643                         // Creates block with original statements
2644                         AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2645
2646                         source.statements = new List<Statement> (1);
2647                         source.AddStatement (new Return (iterator, iterator.Location));
2648                         source.IsIterator = false;
2649
2650                         IteratorStorey iterator_storey = new IteratorStorey (iterator);
2651                         source.am_storey = iterator_storey;
2652                         return iterator_storey;
2653                 }
2654
2655                 //
2656                 // Returns a parameter reference expression for the given name,
2657                 // or null if there is no such parameter
2658                 //
2659                 public Expression GetParameterReference (string name, Location loc)
2660                 {
2661                         for (ToplevelBlock t = this; t != null; t = t.Container) {
2662                                 Expression expr = t.GetParameterReferenceExpression (name, loc);
2663                                 if (expr != null)
2664                                         return expr;
2665                         }
2666
2667                         return null;
2668                 }
2669
2670                 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2671                 {
2672                         int idx = parameters.GetParameterIndexByName (name);
2673                         return idx < 0 ?
2674                                 null : new ParameterReference (parameter_info [idx], loc);
2675                 }
2676
2677                 // <summary>
2678                 //   Returns the "this" instance variable of this block.
2679                 //   See AddThisVariable() for more information.
2680                 // </summary>
2681                 public LocalInfo ThisVariable {
2682                         get { return this_variable; }
2683                 }
2684
2685                 // <summary>
2686                 //   This is used by non-static `struct' constructors which do not have an
2687                 //   initializer - in this case, the constructor must initialize all of the
2688                 //   struct's fields.  To do this, we add a "this" variable and use the flow
2689                 //   analysis code to ensure that it's been fully initialized before control
2690                 //   leaves the constructor.
2691                 // </summary>
2692                 public LocalInfo AddThisVariable (TypeContainer ds, Location l)
2693                 {
2694                         if (this_variable == null) {
2695                                 this_variable = new LocalInfo (ds, this, l);
2696                                 this_variable.Used = true;
2697                                 this_variable.IsThis = true;
2698
2699                                 Variables.Add ("this", this_variable);
2700                         }
2701
2702                         return this_variable;
2703                 }
2704
2705                 public bool IsIterator {
2706                         get { return (flags & Flags.IsIterator) != 0; }
2707                         set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2708                 }
2709
2710                 //
2711                 // Block has been converted to expression tree
2712                 //
2713                 public bool IsExpressionTree {
2714                         get { return (flags & Flags.IsExpressionTree) != 0; }
2715                 }
2716
2717                 public bool IsThisAssigned (BlockContext ec)
2718                 {
2719                         return this_variable == null || this_variable.IsThisAssigned (ec, this);
2720                 }
2721
2722                 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2723                 {
2724                         if (resolved)
2725                                 return true;
2726
2727                         resolved = true;
2728
2729                         if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2730                                 flags |= Flags.IsExpressionTree;
2731
2732                         try {
2733                                 if (!ResolveMeta (rc, ip))
2734                                         return false;
2735
2736                                 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2737                                         FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2738
2739                                         if (!Resolve (rc))
2740                                                 return false;
2741
2742                                         unreachable = top_level.End ();
2743                                 }
2744                         } catch (Exception) {
2745 #if PRODUCTION
2746                                 if (rc.CurrentBlock != null) {
2747                                         ec.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2748                                 } else {
2749                                         ec.Report.Error (587, "Internal compiler error: Phase Resolve");
2750                                 }
2751 #endif
2752                                 throw;
2753                         }
2754
2755                         if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2756                                 if (rc.CurrentAnonymousMethod == null) {
2757                                         rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2758                                         return false;
2759                                 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2760                                         rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2761                                                           rc.CurrentAnonymousMethod.GetSignatureForError ());
2762                                         return false;
2763                                 }
2764                         }
2765
2766                         return true;
2767                 }
2768
2769                 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2770                 {
2771                         int errors = ec.Report.Errors;
2772                         int orig_count = parameters.Count;
2773
2774                         if (ip != null)
2775                                 parameters = ip;
2776
2777                         // Assert: orig_count != parameter.Count => orig_count == 0
2778                         if (orig_count != 0 && orig_count != parameters.Count)
2779                                 throw new InternalErrorException ("parameter information mismatch");
2780
2781                         int offset = Parent == null ? 0 : Parent.AssignableSlots;
2782
2783                         for (int i = 0; i < orig_count; ++i) {
2784                                 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2785
2786                                 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2787                                         continue;
2788
2789                                 VariableInfo vi = new VariableInfo (ip, i, offset);
2790                                 parameter_info [i].VariableInfo = vi;
2791                                 offset += vi.Length;
2792                         }
2793
2794                         ResolveMeta (ec, offset);
2795
2796                         return ec.Report.Errors == errors;
2797                 }
2798
2799                 // <summary>
2800                 //   Check whether all `out' parameters have been assigned.
2801                 // </summary>
2802                 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2803                 {
2804                         if (vector.IsUnreachable)
2805                                 return;
2806
2807                         int n = parameter_info == null ? 0 : parameter_info.Length;
2808
2809                         for (int i = 0; i < n; i++) {
2810                                 VariableInfo var = parameter_info [i].VariableInfo;
2811
2812                                 if (var == null)
2813                                         continue;
2814
2815                                 if (vector.IsAssigned (var, false))
2816                                         continue;
2817
2818                                 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2819                                         var.Name);
2820                         }
2821                 }
2822
2823                 public override void Emit (EmitContext ec)
2824                 {
2825                         if (Report.Errors > 0)
2826                                 return;
2827
2828 #if PRODUCTION
2829                         try {
2830 #endif
2831                         EmitMeta (ec);
2832
2833                         if (ec.HasReturnLabel)
2834                                 ec.ReturnLabel = ec.DefineLabel ();
2835
2836                         base.Emit (ec);
2837
2838                         ec.Mark (EndLocation);
2839
2840                         if (ec.HasReturnLabel)
2841                                 ec.MarkLabel (ec.ReturnLabel);
2842
2843                         if (ec.return_value != null) {
2844                                 ec.Emit (OpCodes.Ldloc, ec.return_value);
2845                                 ec.Emit (OpCodes.Ret);
2846                         } else {
2847                                 //
2848                                 // If `HasReturnLabel' is set, then we already emitted a
2849                                 // jump to the end of the method, so we must emit a `ret'
2850                                 // there.
2851                                 //
2852                                 // Unfortunately, System.Reflection.Emit automatically emits
2853                                 // a leave to the end of a finally block.  This is a problem
2854                                 // if no code is following the try/finally block since we may
2855                                 // jump to a point after the end of the method.
2856                                 // As a workaround, we're always creating a return label in
2857                                 // this case.
2858                                 //
2859
2860                                 if (ec.HasReturnLabel || !unreachable) {
2861                                         if (ec.ReturnType != TypeManager.void_type)
2862                                                 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2863                                         ec.Emit (OpCodes.Ret);
2864                                 }
2865                         }
2866
2867 #if PRODUCTION
2868                         } catch (Exception e){
2869                                 Console.WriteLine ("Exception caught by the compiler while emitting:");
2870                                 Console.WriteLine ("   Block that caused the problem begin at: " + block.loc);
2871                                         
2872                                 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2873                                 throw;
2874                         }
2875 #endif
2876                 }
2877
2878                 public override void EmitMeta (EmitContext ec)
2879                 {
2880                         // Avoid declaring an IL variable for this_variable since it is not accessed
2881                         // from the generated IL
2882                         if (this_variable != null)
2883                                 Variables.Remove ("this");
2884                         base.EmitMeta (ec);
2885                 }
2886
2887                 protected override void EmitSymbolInfo (EmitContext ec)
2888                 {
2889                         AnonymousExpression ae = ec.CurrentAnonymousMethod;
2890                         if ((ae != null) && (ae.Storey != null))
2891                                 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2892
2893                         base.EmitSymbolInfo (ec);
2894                 }
2895         }
2896         
2897         public class SwitchLabel {
2898                 Expression label;
2899                 object converted;
2900                 Location loc;
2901
2902                 Label il_label;
2903                 bool  il_label_set;
2904                 Label il_label_code;
2905                 bool  il_label_code_set;
2906
2907                 public static readonly object NullStringCase = new object ();
2908
2909                 //
2910                 // if expr == null, then it is the default case.
2911                 //
2912                 public SwitchLabel (Expression expr, Location l)
2913                 {
2914                         label = expr;
2915                         loc = l;
2916                 }
2917
2918                 public Expression Label {
2919                         get {
2920                                 return label;
2921                         }
2922                 }
2923
2924                 public Location Location {
2925                         get { return loc; }
2926                 }
2927
2928                 public object Converted {
2929                         get {
2930                                 return converted;
2931                         }
2932                 }
2933
2934                 public Label GetILLabel (EmitContext ec)
2935                 {
2936                         if (!il_label_set){
2937                                 il_label = ec.DefineLabel ();
2938                                 il_label_set = true;
2939                         }
2940                         return il_label;
2941                 }
2942
2943                 public Label GetILLabelCode (EmitContext ec)
2944                 {
2945                         if (!il_label_code_set){
2946                                 il_label_code = ec.DefineLabel ();
2947                                 il_label_code_set = true;
2948                         }
2949                         return il_label_code;
2950                 }                               
2951                 
2952                 //
2953                 // Resolves the expression, reduces it to a literal if possible
2954                 // and then converts it to the requested type.
2955                 //
2956                 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
2957                 {       
2958                         Expression e = label.Resolve (ec);
2959
2960                         if (e == null)
2961                                 return false;
2962
2963                         Constant c = e as Constant;
2964                         if (c == null){
2965                                 ec.Report.Error (150, loc, "A constant value is expected");
2966                                 return false;
2967                         }
2968
2969                         if (required_type == TypeManager.string_type && c.GetValue () == null) {
2970                                 converted = NullStringCase;
2971                                 return true;
2972                         }
2973
2974                         if (allow_nullable && c.GetValue () == null) {
2975                                 converted = NullStringCase;
2976                                 return true;
2977                         }
2978                         
2979                         c = c.ImplicitConversionRequired (ec, required_type, loc);
2980                         if (c == null)
2981                                 return false;
2982
2983                         converted = c.GetValue ();
2984                         return true;
2985                 }
2986
2987                 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
2988                 {
2989                         string label;
2990                         if (converted == null)
2991                                 label = "default";
2992                         else if (converted == NullStringCase)
2993                                 label = "null";
2994                         else
2995                                 label = converted.ToString ();
2996                         
2997                         ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2998                         ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2999                 }
3000
3001                 public SwitchLabel Clone (CloneContext clonectx)
3002                 {
3003                         return new SwitchLabel (label.Clone (clonectx), loc);
3004                 }
3005         }
3006
3007         public class SwitchSection {
3008                 // An array of SwitchLabels.
3009                 public readonly List<SwitchLabel> Labels;
3010                 public readonly Block Block;
3011                 
3012                 public SwitchSection (List<SwitchLabel> labels, Block block)
3013                 {
3014                         Labels = labels;
3015                         Block = block;
3016                 }
3017
3018                 public SwitchSection Clone (CloneContext clonectx)
3019                 {
3020                         var cloned_labels = new List<SwitchLabel> ();
3021
3022                         foreach (SwitchLabel sl in cloned_labels)
3023                                 cloned_labels.Add (sl.Clone (clonectx));
3024                         
3025                         return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3026                 }
3027         }
3028         
3029         public class Switch : Statement {
3030                 public List<SwitchSection> Sections;
3031                 public Expression Expr;
3032
3033                 /// <summary>
3034                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
3035                 /// </summary>
3036                 public IDictionary<object, SwitchLabel> Elements;
3037
3038                 /// <summary>
3039                 ///   The governing switch type
3040                 /// </summary>
3041                 public TypeSpec SwitchType;
3042
3043                 //
3044                 // Computed
3045                 //
3046                 Label default_target;
3047                 Label null_target;
3048                 Expression new_expr;
3049                 bool is_constant;
3050                 bool has_null_case;
3051                 SwitchSection constant_section;
3052                 SwitchSection default_section;
3053
3054                 ExpressionStatement string_dictionary;
3055                 FieldExpr switch_cache_field;
3056                 static int unique_counter;
3057
3058                 //
3059                 // Nullable Types support
3060                 //
3061                 Nullable.Unwrap unwrap;
3062
3063                 protected bool HaveUnwrap {
3064                         get { return unwrap != null; }
3065                 }
3066
3067                 //
3068                 // The types allowed to be implicitly cast from
3069                 // on the governing type
3070                 //
3071                 static TypeSpec [] allowed_types;
3072
3073                 public Switch (Expression e, List<SwitchSection> sects, Location l)
3074                 {
3075                         Expr = e;
3076                         Sections = sects;
3077                         loc = l;
3078                 }
3079
3080                 public bool GotDefault {
3081                         get {
3082                                 return default_section != null;
3083                         }
3084                 }
3085
3086                 public Label DefaultTarget {
3087                         get {
3088                                 return default_target;
3089                         }
3090                 }
3091
3092                 //
3093                 // Determines the governing type for a switch.  The returned
3094                 // expression might be the expression from the switch, or an
3095                 // expression that includes any potential conversions to the
3096                 // integral types or to string.
3097                 //
3098                 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3099                 {
3100                         TypeSpec t = expr.Type;
3101
3102                         if (t == TypeManager.byte_type ||
3103                             t == TypeManager.sbyte_type ||
3104                             t == TypeManager.ushort_type ||
3105                             t == TypeManager.short_type ||
3106                             t == TypeManager.uint32_type ||
3107                             t == TypeManager.int32_type ||
3108                             t == TypeManager.uint64_type ||
3109                             t == TypeManager.int64_type ||
3110                             t == TypeManager.char_type ||
3111                             t == TypeManager.string_type ||
3112                             t == TypeManager.bool_type ||
3113                             TypeManager.IsEnumType (t))
3114                                 return expr;
3115
3116                         if (allowed_types == null){
3117                                 allowed_types = new TypeSpec [] {
3118                                         TypeManager.sbyte_type,
3119                                         TypeManager.byte_type,
3120                                         TypeManager.short_type,
3121                                         TypeManager.ushort_type,
3122                                         TypeManager.int32_type,
3123                                         TypeManager.uint32_type,
3124                                         TypeManager.int64_type,
3125                                         TypeManager.uint64_type,
3126                                         TypeManager.char_type,
3127                                         TypeManager.string_type
3128                                 };
3129                         }
3130
3131                         //
3132                         // Try to find a *user* defined implicit conversion.
3133                         //
3134                         // If there is no implicit conversion, or if there are multiple
3135                         // conversions, we have to report an error
3136                         //
3137                         Expression converted = null;
3138                         foreach (TypeSpec tt in allowed_types){
3139                                 Expression e;
3140                                 
3141                                 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3142                                 if (e == null)
3143                                         continue;
3144
3145                                 //
3146                                 // Ignore over-worked ImplicitUserConversions that do
3147                                 // an implicit conversion in addition to the user conversion.
3148                                 // 
3149                                 if (!(e is UserCast))
3150                                         continue;
3151
3152                                 if (converted != null){
3153                                         ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3154                                         return null;
3155                                 }
3156
3157                                 converted = e;
3158                         }
3159                         return converted;
3160                 }
3161
3162                 //
3163                 // Performs the basic sanity checks on the switch statement
3164                 // (looks for duplicate keys and non-constant expressions).
3165                 //
3166                 // It also returns a hashtable with the keys that we will later
3167                 // use to compute the switch tables
3168                 //
3169                 bool CheckSwitch (ResolveContext ec)
3170                 {
3171                         bool error = false;
3172                         Elements = new Dictionary<object, SwitchLabel> ();
3173                                 
3174                         foreach (SwitchSection ss in Sections){
3175                                 foreach (SwitchLabel sl in ss.Labels){
3176                                         if (sl.Label == null){
3177                                                 if (default_section != null){
3178                                                         sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3179                                                         error = true;
3180                                                 }
3181                                                 default_section = ss;
3182                                                 continue;
3183                                         }
3184
3185                                         if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3186                                                 error = true;
3187                                                 continue;
3188                                         }
3189                                         
3190                                         object key = sl.Converted;
3191                                         if (key == SwitchLabel.NullStringCase)
3192                                                 has_null_case = true;
3193
3194                                         try {
3195                                                 Elements.Add (key, sl);
3196                                         } catch (ArgumentException) {
3197                                                 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3198                                                 error = true;
3199                                         }
3200                                 }
3201                         }
3202                         return !error;
3203                 }
3204
3205                 void EmitObjectInteger (EmitContext ec, object k)
3206                 {
3207                         if (k is int)
3208                                 ec.EmitInt ((int) k);
3209                         else if (k is Constant) {
3210                                 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3211                         } 
3212                         else if (k is uint)
3213                                 ec.EmitInt (unchecked ((int) (uint) k));
3214                         else if (k is long)
3215                         {
3216                                 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3217                                 {
3218                                         ec.EmitInt ((int) (long) k);
3219                                         ec.Emit (OpCodes.Conv_I8);
3220                                 }
3221                                 else
3222                                         ec.EmitLong ((long) k);
3223                         }
3224                         else if (k is ulong)
3225                         {
3226                                 ulong ul = (ulong) k;
3227                                 if (ul < (1L<<32))
3228                                 {
3229                                         ec.EmitInt (unchecked ((int) ul));
3230                                         ec.Emit (OpCodes.Conv_U8);
3231                                 }
3232                                 else
3233                                 {
3234                                         ec.EmitLong (unchecked ((long) ul));
3235                                 }
3236                         }
3237                         else if (k is char)
3238                                 ec.EmitInt ((int) ((char) k));
3239                         else if (k is sbyte)
3240                                 ec.EmitInt ((int) ((sbyte) k));
3241                         else if (k is byte)
3242                                 ec.EmitInt ((int) ((byte) k));
3243                         else if (k is short)
3244                                 ec.EmitInt ((int) ((short) k));
3245                         else if (k is ushort)
3246                                 ec.EmitInt ((int) ((ushort) k));
3247                         else if (k is bool)
3248                                 ec.EmitInt (((bool) k) ? 1 : 0);
3249                         else
3250                                 throw new Exception ("Unhandled case");
3251                 }
3252                 
3253                 // structure used to hold blocks of keys while calculating table switch
3254                 class KeyBlock : IComparable
3255                 {
3256                         public KeyBlock (long _first)
3257                         {
3258                                 first = last = _first;
3259                         }
3260                         public long first;
3261                         public long last;
3262                         public List<object> element_keys;
3263                         // how many items are in the bucket
3264                         public int Size = 1;
3265                         public int Length
3266                         {
3267                                 get { return (int) (last - first + 1); }
3268                         }
3269                         public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3270                         {
3271                                 return kb_last.last - kb_first.first + 1;
3272                         }
3273                         public int CompareTo (object obj)
3274                         {
3275                                 KeyBlock kb = (KeyBlock) obj;
3276                                 int nLength = Length;
3277                                 int nLengthOther = kb.Length;
3278                                 if (nLengthOther == nLength)
3279                                         return (int) (kb.first - first);
3280                                 return nLength - nLengthOther;
3281                         }
3282                 }
3283
3284                 /// <summary>
3285                 /// This method emits code for a lookup-based switch statement (non-string)
3286                 /// Basically it groups the cases into blocks that are at least half full,
3287                 /// and then spits out individual lookup opcodes for each block.
3288                 /// It emits the longest blocks first, and short blocks are just
3289                 /// handled with direct compares.
3290                 /// </summary>
3291                 /// <param name="ec"></param>
3292                 /// <param name="val"></param>
3293                 /// <returns></returns>
3294                 void TableSwitchEmit (EmitContext ec, Expression val)
3295                 {
3296                         int element_count = Elements.Count;
3297                         object [] element_keys = new object [element_count];
3298                         Elements.Keys.CopyTo (element_keys, 0);
3299                         Array.Sort (element_keys);
3300
3301                         // initialize the block list with one element per key
3302                         var key_blocks = new List<KeyBlock> (element_count);
3303                         foreach (object key in element_keys)
3304                                 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3305
3306                         KeyBlock current_kb;
3307                         // iteratively merge the blocks while they are at least half full
3308                         // there's probably a really cool way to do this with a tree...
3309                         while (key_blocks.Count > 1)
3310                         {
3311                                 var key_blocks_new = new List<KeyBlock> ();
3312                                 current_kb = (KeyBlock) key_blocks [0];
3313                                 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3314                                 {
3315                                         KeyBlock kb = (KeyBlock) key_blocks [ikb];
3316                                         if ((current_kb.Size + kb.Size) * 2 >=  KeyBlock.TotalLength (current_kb, kb))
3317                                         {
3318                                                 // merge blocks
3319                                                 current_kb.last = kb.last;
3320                                                 current_kb.Size += kb.Size;
3321                                         }
3322                                         else
3323                                         {
3324                                                 // start a new block
3325                                                 key_blocks_new.Add (current_kb);
3326                                                 current_kb = kb;
3327                                         }
3328                                 }
3329                                 key_blocks_new.Add (current_kb);
3330                                 if (key_blocks.Count == key_blocks_new.Count)
3331                                         break;
3332                                 key_blocks = key_blocks_new;
3333                         }
3334
3335                         // initialize the key lists
3336                         foreach (KeyBlock kb in key_blocks)
3337                                 kb.element_keys = new List<object> ();
3338
3339                         // fill the key lists
3340                         int iBlockCurr = 0;
3341                         if (key_blocks.Count > 0) {
3342                                 current_kb = (KeyBlock) key_blocks [0];
3343                                 foreach (object key in element_keys)
3344                                 {
3345                                         bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3346                                                 System.Convert.ToInt64 (key) > current_kb.last;
3347                                         if (next_block)
3348                                                 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3349                                         current_kb.element_keys.Add (key);
3350                                 }
3351                         }
3352
3353                         // sort the blocks so we can tackle the largest ones first
3354                         key_blocks.Sort ();
3355
3356                         // okay now we can start...
3357                         Label lbl_end = ec.DefineLabel ();      // at the end ;-)
3358                         Label lbl_default = default_target;
3359
3360                         Type type_keys = null;
3361                         if (element_keys.Length > 0)
3362                                 type_keys = element_keys [0].GetType ();        // used for conversions
3363
3364                         TypeSpec compare_type;
3365                         
3366                         if (TypeManager.IsEnumType (SwitchType))
3367                                 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3368                         else
3369                                 compare_type = SwitchType;
3370                         
3371                         for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3372                         {
3373                                 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3374                                 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3375                                 if (kb.Length <= 2)
3376                                 {
3377                                         foreach (object key in kb.element_keys) {
3378                                                 SwitchLabel sl = (SwitchLabel) Elements [key];
3379                                                 if (key is int && (int) key == 0) {
3380                                                         val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3381                                                 } else {
3382                                                         val.Emit (ec);
3383                                                         EmitObjectInteger (ec, key);
3384                                                         ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3385                                                 }
3386                                         }
3387                                 }
3388                                 else
3389                                 {
3390                                         // TODO: if all the keys in the block are the same and there are
3391                                         //       no gaps/defaults then just use a range-check.
3392                                         if (compare_type == TypeManager.int64_type ||
3393                                                 compare_type == TypeManager.uint64_type)
3394                                         {
3395                                                 // TODO: optimize constant/I4 cases
3396
3397                                                 // check block range (could be > 2^31)
3398                                                 val.Emit (ec);
3399                                                 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3400                                                 ec.Emit (OpCodes.Blt, lbl_default);
3401                                                 val.Emit (ec);
3402                                                 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3403                                                 ec.Emit (OpCodes.Bgt, lbl_default);
3404
3405                                                 // normalize range
3406                                                 val.Emit (ec);
3407                                                 if (kb.first != 0)
3408                                                 {
3409                                                         EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3410                                                         ec.Emit (OpCodes.Sub);
3411                                                 }
3412                                                 ec.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
3413                                         }
3414                                         else
3415                                         {
3416                                                 // normalize range
3417                                                 val.Emit (ec);
3418                                                 int first = (int) kb.first;
3419                                                 if (first > 0)
3420                                                 {
3421                                                         ec.EmitInt (first);
3422                                                         ec.Emit (OpCodes.Sub);
3423                                                 }
3424                                                 else if (first < 0)
3425                                                 {
3426                                                         ec.EmitInt (-first);
3427                                                         ec.Emit (OpCodes.Add);
3428                                                 }
3429                                         }
3430
3431                                         // first, build the list of labels for the switch
3432                                         int iKey = 0;
3433                                         int cJumps = kb.Length;
3434                                         Label [] switch_labels = new Label [cJumps];
3435                                         for (int iJump = 0; iJump < cJumps; iJump++)
3436                                         {
3437                                                 object key = kb.element_keys [iKey];
3438                                                 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3439                                                 {
3440                                                         SwitchLabel sl = (SwitchLabel) Elements [key];
3441                                                         switch_labels [iJump] = sl.GetILLabel (ec);
3442                                                         iKey++;
3443                                                 }
3444                                                 else
3445                                                         switch_labels [iJump] = lbl_default;
3446                                         }
3447                                         // emit the switch opcode
3448                                         ec.Emit (OpCodes.Switch, switch_labels);
3449                                 }
3450
3451                                 // mark the default for this block
3452                                 if (iBlock != 0)
3453                                         ec.MarkLabel (lbl_default);
3454                         }
3455
3456                         // TODO: find the default case and emit it here,
3457                         //       to prevent having to do the following jump.
3458                         //       make sure to mark other labels in the default section
3459
3460                         // the last default just goes to the end
3461                         if (element_keys.Length > 0)
3462                                 ec.Emit (OpCodes.Br, lbl_default);
3463
3464                         // now emit the code for the sections
3465                         bool found_default = false;
3466
3467                         foreach (SwitchSection ss in Sections) {
3468                                 foreach (SwitchLabel sl in ss.Labels) {
3469                                         if (sl.Converted == SwitchLabel.NullStringCase) {
3470                                                 ec.MarkLabel (null_target);
3471                                         } else if (sl.Label == null) {
3472                                                 ec.MarkLabel (lbl_default);
3473                                                 found_default = true;
3474                                                 if (!has_null_case)
3475                                                         ec.MarkLabel (null_target);
3476                                         }
3477                                         ec.MarkLabel (sl.GetILLabel (ec));
3478                                         ec.MarkLabel (sl.GetILLabelCode (ec));
3479                                 }
3480                                 ss.Block.Emit (ec);
3481                         }
3482                         
3483                         if (!found_default) {
3484                                 ec.MarkLabel (lbl_default);
3485                                 if (!has_null_case) {
3486                                         ec.MarkLabel (null_target);
3487                                 }
3488                         }
3489                         
3490                         ec.MarkLabel (lbl_end);
3491                 }
3492
3493                 SwitchSection FindSection (SwitchLabel label)
3494                 {
3495                         foreach (SwitchSection ss in Sections){
3496                                 foreach (SwitchLabel sl in ss.Labels){
3497                                         if (label == sl)
3498                                                 return ss;
3499                                 }
3500                         }
3501
3502                         return null;
3503                 }
3504
3505                 public static void Reset ()
3506                 {
3507                         unique_counter = 0;
3508                         allowed_types = null;
3509                 }
3510
3511                 public override bool Resolve (BlockContext ec)
3512                 {
3513                         Expr = Expr.Resolve (ec);
3514                         if (Expr == null)
3515                                 return false;
3516
3517                         new_expr = SwitchGoverningType (ec, Expr);
3518
3519                         if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3520                                 unwrap = Nullable.Unwrap.Create (Expr, false);
3521                                 if (unwrap == null)
3522                                         return false;
3523
3524                                 new_expr = SwitchGoverningType (ec, unwrap);
3525                         }
3526
3527                         if (new_expr == null){
3528                                 ec.Report.Error (151, loc,
3529                                         "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3530                                         TypeManager.CSharpName (Expr.Type));
3531                                 return false;
3532                         }
3533
3534                         // Validate switch.
3535                         SwitchType = new_expr.Type;
3536
3537                         if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3538                                 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3539                                 return false;
3540                         }
3541
3542                         if (!CheckSwitch (ec))
3543                                 return false;
3544
3545                         if (HaveUnwrap)
3546                                 Elements.Remove (SwitchLabel.NullStringCase);
3547
3548                         Switch old_switch = ec.Switch;
3549                         ec.Switch = this;
3550                         ec.Switch.SwitchType = SwitchType;
3551
3552                         Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3553                         ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3554
3555                         var constant = new_expr as Constant;
3556                         if (constant != null) {
3557                                 is_constant = true;
3558                                 object key = constant.GetValue ();
3559                                 SwitchLabel label;
3560                                 if (Elements.TryGetValue (key, out label))
3561                                         constant_section = FindSection (label);
3562
3563                                 if (constant_section == null)
3564                                         constant_section = default_section;
3565                         }
3566
3567                         bool first = true;
3568                         bool ok = true;
3569                         foreach (SwitchSection ss in Sections){
3570                                 if (!first)
3571                                         ec.CurrentBranching.CreateSibling (
3572                                                 null, FlowBranching.SiblingType.SwitchSection);
3573                                 else
3574                                         first = false;
3575
3576                                 if (is_constant && (ss != constant_section)) {
3577                                         // If we're a constant switch, we're only emitting
3578                                         // one single section - mark all the others as
3579                                         // unreachable.
3580                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
3581                                         if (!ss.Block.ResolveUnreachable (ec, true)) {
3582                                                 ok = false;
3583                                         }
3584                                 } else {
3585                                         if (!ss.Block.Resolve (ec))
3586                                                 ok = false;
3587                                 }
3588                         }
3589
3590                         if (default_section == null)
3591                                 ec.CurrentBranching.CreateSibling (
3592                                         null, FlowBranching.SiblingType.SwitchSection);
3593
3594                         ec.EndFlowBranching ();
3595                         ec.Switch = old_switch;
3596
3597                         Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3598
3599                         if (!ok)
3600                                 return false;
3601
3602                         if (SwitchType == TypeManager.string_type && !is_constant) {
3603                                 // TODO: Optimize single case, and single+default case
3604                                 ResolveStringSwitchMap (ec);
3605                         }
3606
3607                         return true;
3608                 }
3609
3610                 void ResolveStringSwitchMap (ResolveContext ec)
3611                 {
3612                         FullNamedExpression string_dictionary_type;
3613                         if (TypeManager.generic_ienumerable_type != null) {
3614                                 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3615                                         new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3616
3617                                 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3618                                         new TypeArguments (
3619                                                 new TypeExpression (TypeManager.string_type, loc),
3620                                                 new TypeExpression (TypeManager.int32_type, loc)), loc);
3621                         } else {
3622                                 MemberAccess system_collections_generic = new MemberAccess (
3623                                         new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3624
3625                                 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3626                         }
3627
3628                         var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3629                         Field field = new Field (ctype, string_dictionary_type,
3630                                 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3631                                 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3632                         if (!field.Define ())
3633                                 return;
3634                         ctype.AddField (field);
3635
3636                         var init = new List<Expression> ();
3637                         int counter = 0;
3638                         Elements.Clear ();
3639                         string value = null;
3640                         foreach (SwitchSection section in Sections) {
3641                                 int last_count = init.Count;
3642                                 foreach (SwitchLabel sl in section.Labels) {
3643                                         if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3644                                                 continue;
3645
3646                                         value = (string) sl.Converted;
3647                                         var init_args = new List<Expression> (2);
3648                                         init_args.Add (new StringLiteral (value, sl.Location));
3649                                         init_args.Add (new IntConstant (counter, loc));
3650                                         init.Add (new CollectionElementInitializer (init_args, loc));
3651                                 }
3652
3653                                 //
3654                                 // Don't add empty sections
3655                                 //
3656                                 if (last_count == init.Count)
3657                                         continue;
3658
3659                                 Elements.Add (counter, section.Labels [0]);
3660                                 ++counter;
3661                         }
3662
3663                         Arguments args = new Arguments (1);
3664                         args.Add (new Argument (new IntConstant (init.Count, loc)));
3665                         Expression initializer = new NewInitialize (string_dictionary_type, args,
3666                                 new CollectionOrObjectInitializers (init, loc), loc);
3667
3668                         switch_cache_field = new FieldExpr (field, loc);
3669                         string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3670                 }
3671
3672                 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3673                 {
3674                         Label l_initialized = ec.DefineLabel ();
3675
3676                         //
3677                         // Skip initialization when value is null
3678                         //
3679                         value.EmitBranchable (ec, null_target, false);
3680
3681                         //
3682                         // Check if string dictionary is initialized and initialize
3683                         //
3684                         switch_cache_field.EmitBranchable (ec, l_initialized, true);
3685                         string_dictionary.EmitStatement (ec);
3686                         ec.MarkLabel (l_initialized);
3687
3688                         LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3689
3690                         ResolveContext rc = new ResolveContext (ec.MemberContext);
3691
3692                         if (TypeManager.generic_ienumerable_type != null) {
3693                                 Arguments get_value_args = new Arguments (2);
3694                                 get_value_args.Add (new Argument (value));
3695                                 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3696                                 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3697                                 if (get_item == null)
3698                                         return;
3699
3700                                 //
3701                                 // A value was not found, go to default case
3702                                 //
3703                                 get_item.EmitBranchable (ec, default_target, false);
3704                         } else {
3705                                 Arguments get_value_args = new Arguments (1);
3706                                 get_value_args.Add (new Argument (value));
3707
3708                                 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3709                                 if (get_item == null)
3710                                         return;
3711
3712                                 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3713                                 get_item_object.EmitAssign (ec, get_item, true, false);
3714                                 ec.Emit (OpCodes.Brfalse, default_target);
3715
3716                                 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3717                                         new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3718
3719                                 get_item_int.EmitStatement (ec);
3720                                 get_item_object.Release (ec);
3721                         }
3722
3723                         TableSwitchEmit (ec, string_switch_variable);
3724                         string_switch_variable.Release (ec);
3725                 }
3726                 
3727                 protected override void DoEmit (EmitContext ec)
3728                 {
3729                         default_target = ec.DefineLabel ();
3730                         null_target = ec.DefineLabel ();
3731
3732                         // Store variable for comparission purposes
3733                         // TODO: Don't duplicate non-captured VariableReference
3734                         LocalTemporary value;
3735                         if (HaveUnwrap) {
3736                                 value = new LocalTemporary (SwitchType);
3737                                 unwrap.EmitCheck (ec);
3738                                 ec.Emit (OpCodes.Brfalse, null_target);
3739                                 new_expr.Emit (ec);
3740                                 value.Store (ec);
3741                         } else if (!is_constant) {
3742                                 value = new LocalTemporary (SwitchType);
3743                                 new_expr.Emit (ec);
3744                                 value.Store (ec);
3745                         } else
3746                                 value = null;
3747
3748                         //
3749                         // Setup the codegen context
3750                         //
3751                         Label old_end = ec.LoopEnd;
3752                         Switch old_switch = ec.Switch;
3753                         
3754                         ec.LoopEnd = ec.DefineLabel ();
3755                         ec.Switch = this;
3756
3757                         // Emit Code.
3758                         if (is_constant) {
3759                                 if (constant_section != null)
3760                                         constant_section.Block.Emit (ec);
3761                         } else if (string_dictionary != null) {
3762                                 DoEmitStringSwitch (value, ec);
3763                         } else {
3764                                 TableSwitchEmit (ec, value);
3765                         }
3766
3767                         if (value != null)
3768                                 value.Release (ec);
3769
3770                         // Restore context state. 
3771                         ec.MarkLabel (ec.LoopEnd);
3772
3773                         //
3774                         // Restore the previous context
3775                         //
3776                         ec.LoopEnd = old_end;
3777                         ec.Switch = old_switch;
3778                 }
3779
3780                 protected override void CloneTo (CloneContext clonectx, Statement t)
3781                 {
3782                         Switch target = (Switch) t;
3783
3784                         target.Expr = Expr.Clone (clonectx);
3785                         target.Sections = new List<SwitchSection> ();
3786                         foreach (SwitchSection ss in Sections){
3787                                 target.Sections.Add (ss.Clone (clonectx));
3788                         }
3789                 }
3790         }
3791
3792         // A place where execution can restart in an iterator
3793         public abstract class ResumableStatement : Statement
3794         {
3795                 bool prepared;
3796                 protected Label resume_point;
3797
3798                 public Label PrepareForEmit (EmitContext ec)
3799                 {
3800                         if (!prepared) {
3801                                 prepared = true;
3802                                 resume_point = ec.DefineLabel ();
3803                         }
3804                         return resume_point;
3805                 }
3806
3807                 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3808                 {
3809                         return end;
3810                 }
3811                 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3812                 {
3813                 }
3814         }
3815
3816         // Base class for statements that are implemented in terms of try...finally
3817         public abstract class ExceptionStatement : ResumableStatement
3818         {
3819                 bool code_follows;
3820                 Iterator iter;
3821                 List<ResumableStatement> resume_points;
3822                 int first_resume_pc;
3823
3824                 protected abstract void EmitPreTryBody (EmitContext ec);
3825                 protected abstract void EmitTryBody (EmitContext ec);
3826                 protected abstract void EmitFinallyBody (EmitContext ec);
3827
3828                 protected sealed override void DoEmit (EmitContext ec)
3829                 {
3830                         EmitPreTryBody (ec);
3831
3832                         if (resume_points != null) {
3833                                 ec.EmitInt ((int) Iterator.State.Running);
3834                                 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3835                         }
3836
3837                         ec.BeginExceptionBlock ();
3838
3839                         if (resume_points != null) {
3840                                 ec.MarkLabel (resume_point);
3841
3842                                 // For normal control flow, we want to fall-through the Switch
3843                                 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3844                                 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3845                                 ec.EmitInt (first_resume_pc);
3846                                 ec.Emit (OpCodes.Sub);
3847
3848                                 Label [] labels = new Label [resume_points.Count];
3849                                 for (int i = 0; i < resume_points.Count; ++i)
3850                                         labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3851                                 ec.Emit (OpCodes.Switch, labels);
3852                         }
3853
3854                         EmitTryBody (ec);
3855
3856                         ec.BeginFinallyBlock ();
3857
3858                         Label start_finally = ec.DefineLabel ();
3859                         if (resume_points != null) {
3860                                 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3861                                 ec.Emit (OpCodes.Brfalse_S, start_finally);
3862                                 ec.Emit (OpCodes.Endfinally);
3863                         }
3864
3865                         ec.MarkLabel (start_finally);
3866                         EmitFinallyBody (ec);
3867
3868                         ec.EndExceptionBlock ();
3869                 }
3870
3871                 public void SomeCodeFollows ()
3872                 {
3873                         code_follows = true;
3874                 }
3875
3876                 public override bool Resolve (BlockContext ec)
3877                 {
3878                         // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3879                         // So, ensure there's some IL code after this statement.
3880                         if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3881                                 ec.NeedReturnLabel ();
3882
3883                         iter = ec.CurrentIterator;
3884                         return true;
3885                 }
3886
3887                 public void AddResumePoint (ResumableStatement stmt, int pc)
3888                 {
3889                         if (resume_points == null) {
3890                                 resume_points = new List<ResumableStatement> ();
3891                                 first_resume_pc = pc;
3892                         }
3893
3894                         if (pc != first_resume_pc + resume_points.Count)
3895                                 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3896
3897                         resume_points.Add (stmt);
3898                 }
3899
3900                 Label dispose_try_block;
3901                 bool prepared_for_dispose, emitted_dispose;
3902                 public override Label PrepareForDispose (EmitContext ec, Label end)
3903                 {
3904                         if (!prepared_for_dispose) {
3905                                 prepared_for_dispose = true;
3906                                 dispose_try_block = ec.DefineLabel ();
3907                         }
3908                         return dispose_try_block;
3909                 }
3910
3911                 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3912                 {
3913                         if (emitted_dispose)
3914                                 return;
3915
3916                         emitted_dispose = true;
3917
3918                         Label end_of_try = ec.DefineLabel ();
3919
3920                         // Ensure that the only way we can get into this code is through a dispatcher
3921                         if (have_dispatcher)
3922                                 ec.Emit (OpCodes.Br, end);
3923
3924                         ec.BeginExceptionBlock ();
3925
3926                         ec.MarkLabel (dispose_try_block);
3927
3928                         Label [] labels = null;
3929                         for (int i = 0; i < resume_points.Count; ++i) {
3930                                 ResumableStatement s = (ResumableStatement) resume_points [i];
3931                                 Label ret = s.PrepareForDispose (ec, end_of_try);
3932                                 if (ret.Equals (end_of_try) && labels == null)
3933                                         continue;
3934                                 if (labels == null) {
3935                                         labels = new Label [resume_points.Count];
3936                                         for (int j = 0; j < i; ++j)
3937                                                 labels [j] = end_of_try;
3938                                 }
3939                                 labels [i] = ret;
3940                         }
3941
3942                         if (labels != null) {
3943                                 int j;
3944                                 for (j = 1; j < labels.Length; ++j)
3945                                         if (!labels [0].Equals (labels [j]))
3946                                                 break;
3947                                 bool emit_dispatcher = j < labels.Length;
3948
3949                                 if (emit_dispatcher) {
3950                                         //SymbolWriter.StartIteratorDispatcher (ec.ig);
3951                                         ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3952                                         ec.EmitInt (first_resume_pc);
3953                                         ec.Emit (OpCodes.Sub);
3954                                         ec.Emit (OpCodes.Switch, labels);
3955                                         //SymbolWriter.EndIteratorDispatcher (ec.ig);
3956                                 }
3957
3958                                 foreach (ResumableStatement s in resume_points)
3959                                         s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3960                         }
3961
3962                         ec.MarkLabel (end_of_try);
3963
3964                         ec.BeginFinallyBlock ();
3965
3966                         EmitFinallyBody (ec);
3967
3968                         ec.EndExceptionBlock ();
3969                 }
3970         }
3971
3972         public class Lock : ExceptionStatement {
3973                 Expression expr;
3974                 public Statement Statement;
3975                 TemporaryVariable temp;
3976                         
3977                 public Lock (Expression expr, Statement stmt, Location l)
3978                 {
3979                         this.expr = expr;
3980                         Statement = stmt;
3981                         loc = l;
3982                 }
3983
3984                 public override bool Resolve (BlockContext ec)
3985                 {
3986                         expr = expr.Resolve (ec);
3987                         if (expr == null)
3988                                 return false;
3989
3990                         if (!TypeManager.IsReferenceType (expr.Type)){
3991                                 ec.Report.Error (185, loc,
3992                                               "`{0}' is not a reference type as required by the lock statement",
3993                                               TypeManager.CSharpName (expr.Type));
3994                                 return false;
3995                         }
3996
3997                         ec.StartFlowBranching (this);
3998                         bool ok = Statement.Resolve (ec);
3999                         ec.EndFlowBranching ();
4000
4001                         ok &= base.Resolve (ec);
4002
4003                         // Avoid creating libraries that reference the internal
4004                         // mcs NullType:
4005                         TypeSpec t = expr.Type;
4006                         if (t == TypeManager.null_type)
4007                                 t = TypeManager.object_type;
4008                         
4009                         temp = new TemporaryVariable (t, loc);
4010                         temp.Resolve (ec);
4011
4012                         if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4013                                 TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4014                                 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4015                                         monitor_type, "Enter", loc, TypeManager.object_type);
4016                                 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4017                                         monitor_type, "Exit", loc, TypeManager.object_type);
4018                         }
4019                         
4020                         return ok;
4021                 }
4022                 
4023                 protected override void EmitPreTryBody (EmitContext ec)
4024                 {
4025                         temp.EmitAssign (ec, expr);
4026                         temp.Emit (ec);
4027                         ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4028                 }
4029
4030                 protected override void EmitTryBody (EmitContext ec)
4031                 {
4032                         Statement.Emit (ec);
4033                 }
4034
4035                 protected override void EmitFinallyBody (EmitContext ec)
4036                 {
4037                         temp.Emit (ec);
4038                         ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4039                 }
4040
4041                 protected override void CloneTo (CloneContext clonectx, Statement t)
4042                 {
4043                         Lock target = (Lock) t;
4044
4045                         target.expr = expr.Clone (clonectx);
4046                         target.Statement = Statement.Clone (clonectx);
4047                 }
4048         }
4049
4050         public class Unchecked : Statement {
4051                 public Block Block;
4052                 
4053                 public Unchecked (Block b)
4054                 {
4055                         Block = b;
4056                         b.Unchecked = true;
4057                 }
4058
4059                 public override bool Resolve (BlockContext ec)
4060                 {
4061                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4062                                 return Block.Resolve (ec);
4063                 }
4064                 
4065                 protected override void DoEmit (EmitContext ec)
4066                 {
4067                         using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4068                                 Block.Emit (ec);
4069                 }
4070
4071                 protected override void CloneTo (CloneContext clonectx, Statement t)
4072                 {
4073                         Unchecked target = (Unchecked) t;
4074
4075                         target.Block = clonectx.LookupBlock (Block);
4076                 }
4077         }
4078
4079         public class Checked : Statement {
4080                 public Block Block;
4081                 
4082                 public Checked (Block b)
4083                 {
4084                         Block = b;
4085                         b.Unchecked = false;
4086                 }
4087
4088                 public override bool Resolve (BlockContext ec)
4089                 {
4090                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4091                                 return Block.Resolve (ec);
4092                 }
4093
4094                 protected override void DoEmit (EmitContext ec)
4095                 {
4096                         using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4097                                 Block.Emit (ec);
4098                 }
4099
4100                 protected override void CloneTo (CloneContext clonectx, Statement t)
4101                 {
4102                         Checked target = (Checked) t;
4103
4104                         target.Block = clonectx.LookupBlock (Block);
4105                 }
4106         }
4107
4108         public class Unsafe : Statement {
4109                 public Block Block;
4110
4111                 public Unsafe (Block b)
4112                 {
4113                         Block = b;
4114                         Block.Unsafe = true;
4115                         loc = b.StartLocation;
4116                 }
4117
4118                 public override bool Resolve (BlockContext ec)
4119                 {
4120                         if (ec.CurrentIterator != null)
4121                                 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4122
4123                         using (ec.Set (ResolveContext.Options.UnsafeScope))
4124                                 return Block.Resolve (ec);
4125                 }
4126                 
4127                 protected override void DoEmit (EmitContext ec)
4128                 {
4129                         Block.Emit (ec);
4130                 }
4131
4132                 protected override void CloneTo (CloneContext clonectx, Statement t)
4133                 {
4134                         Unsafe target = (Unsafe) t;
4135
4136                         target.Block = clonectx.LookupBlock (Block);
4137                 }
4138         }
4139
4140         // 
4141         // Fixed statement
4142         //
4143         public class Fixed : Statement {
4144                 Expression type;
4145                 List<KeyValuePair<LocalInfo, Expression>> declarators;
4146                 Statement statement;
4147                 TypeSpec expr_type;
4148                 Emitter[] data;
4149                 bool has_ret;
4150
4151                 abstract class Emitter
4152                 {
4153                         protected LocalInfo vi;
4154                         protected Expression converted;
4155
4156                         protected Emitter (Expression expr, LocalInfo li)
4157                         {
4158                                 converted = expr;
4159                                 vi = li;
4160                         }
4161
4162                         public abstract void Emit (EmitContext ec);
4163                         public abstract void EmitExit (EmitContext ec);
4164                 }
4165
4166                 class ExpressionEmitter : Emitter {
4167                         public ExpressionEmitter (Expression converted, LocalInfo li) :
4168                                 base (converted, li)
4169                         {
4170                         }
4171
4172                         public override void Emit (EmitContext ec) {
4173                                 //
4174                                 // Store pointer in pinned location
4175                                 //
4176                                 converted.Emit (ec);
4177                                 vi.EmitAssign (ec);
4178                         }
4179
4180                         public override void EmitExit (EmitContext ec)
4181                         {
4182                                 ec.Emit (OpCodes.Ldc_I4_0);
4183                                 ec.Emit (OpCodes.Conv_U);
4184                                 vi.EmitAssign (ec);
4185                         }
4186                 }
4187
4188                 class StringEmitter : Emitter
4189                 {
4190                         LocalInfo pinned_string;
4191
4192                         public StringEmitter (Expression expr, LocalInfo li, Location loc):
4193                                 base (expr, li)
4194                         {
4195                                 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4196                                 pinned_string.Pinned = true;
4197                         }
4198
4199                         public StringEmitter Resolve (ResolveContext rc)
4200                         {
4201                                 pinned_string.Resolve (rc);
4202
4203                                 if (TypeManager.int_get_offset_to_string_data == null) {
4204                                         TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4205                                                 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4206                                 }
4207
4208                                 return this;
4209                         }
4210
4211                         public override void Emit (EmitContext ec)
4212                         {
4213                                 pinned_string.ResolveVariable (ec);
4214
4215                                 converted.Emit (ec);
4216                                 pinned_string.EmitAssign (ec);
4217
4218                                 // TODO: Should use Binary::Add
4219                                 pinned_string.Emit (ec);
4220                                 ec.Emit (OpCodes.Conv_I);
4221
4222                                 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4223                                 //pe.InstanceExpression = pinned_string;
4224                                 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4225
4226                                 ec.Emit (OpCodes.Add);
4227                                 vi.EmitAssign (ec);
4228                         }
4229
4230                         public override void EmitExit (EmitContext ec)
4231                         {
4232                                 ec.Emit (OpCodes.Ldnull);
4233                                 pinned_string.EmitAssign (ec);
4234                         }
4235                 }
4236
4237                 public Fixed (Expression type, List<KeyValuePair<LocalInfo, Expression>> decls, Statement stmt, Location l)
4238                 {
4239                         this.type = type;
4240                         declarators = decls;
4241                         statement = stmt;
4242                         loc = l;
4243                 }
4244
4245                 public Statement Statement {
4246                         get { return statement; }
4247                 }
4248
4249                 public override bool Resolve (BlockContext ec)
4250                 {
4251                         if (!ec.IsUnsafe){
4252                                 Expression.UnsafeError (ec, loc);
4253                                 return false;
4254                         }
4255                         
4256                         TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4257                         if (texpr == null) {
4258                                 if (type is VarExpr)
4259                                         ec.Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4260
4261                                 return false;
4262                         }
4263
4264                         expr_type = texpr.Type;
4265
4266                         data = new Emitter [declarators.Count];
4267
4268                         if (!expr_type.IsPointer){
4269                                 ec.Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4270                                 return false;
4271                         }
4272                         
4273                         int i = 0;
4274                         foreach (var p in declarators){
4275                                 LocalInfo vi = p.Key;
4276                                 Expression e = p.Value;
4277                                 
4278                                 vi.VariableInfo.SetAssigned (ec);
4279                                 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4280
4281                                 //
4282                                 // The rules for the possible declarators are pretty wise,
4283                                 // but the production on the grammar is more concise.
4284                                 //
4285                                 // So we have to enforce these rules here.
4286                                 //
4287                                 // We do not resolve before doing the case 1 test,
4288                                 // because the grammar is explicit in that the token &
4289                                 // is present, so we need to test for this particular case.
4290                                 //
4291
4292                                 if (e is Cast){
4293                                         ec.Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4294                                         return false;
4295                                 }
4296
4297                                 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4298                                         e = e.Resolve (ec);
4299                                 }
4300
4301                                 if (e == null)
4302                                         return false;
4303
4304                                 //
4305                                 // Case 2: Array
4306                                 //
4307                                 if (e.Type.IsArray){
4308                                         TypeSpec array_type = TypeManager.GetElementType (e.Type);
4309                                         
4310                                         //
4311                                         // Provided that array_type is unmanaged,
4312                                         //
4313                                         if (!TypeManager.VerifyUnmanaged (ec.Compiler, array_type, loc))
4314                                                 return false;
4315
4316                                         //
4317                                         // and T* is implicitly convertible to the
4318                                         // pointer type given in the fixed statement.
4319                                         //
4320                                         ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4321                                         
4322                                         Expression converted = Convert.ImplicitConversionRequired (
4323                                                 ec, array_ptr, vi.VariableType, loc);
4324                                         if (converted == null)
4325                                                 return false;
4326                                         
4327                                         //
4328                                         // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4329                                         //
4330                                         converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4331                                                 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc), loc),
4332                                                 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc), loc), loc)),
4333                                                         new NullPointer (loc),
4334                                                         converted);
4335
4336                                         converted = converted.Resolve (ec);                                     
4337
4338                                         data [i] = new ExpressionEmitter (converted, vi);
4339                                         i++;
4340
4341                                         continue;
4342                                 }
4343
4344                                 //
4345                                 // Case 3: string
4346                                 //
4347                                 if (e.Type == TypeManager.string_type){
4348                                         data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4349                                         i++;
4350                                         continue;
4351                                 }
4352
4353                                 // Case 4: fixed buffer
4354                                 if (e is FixedBufferPtr) {
4355                                         data [i++] = new ExpressionEmitter (e, vi);
4356                                         continue;
4357                                 }
4358
4359                                 //
4360                                 // Case 1: & object.
4361                                 //
4362                                 Unary u = e as Unary;
4363                                 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4364                                         IVariableReference vr = u.Expr as IVariableReference;
4365                                         if (vr == null || !vr.IsFixed) {
4366                                                 data [i] = new ExpressionEmitter (e, vi);
4367                                         }
4368                                 }
4369
4370                                 if (data [i++] == null)
4371                                         ec.Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4372
4373                                 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4374                         }
4375
4376                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4377                         bool ok = statement.Resolve (ec);
4378                         bool flow_unreachable = ec.EndFlowBranching ();
4379                         has_ret = flow_unreachable;
4380
4381                         return ok;
4382                 }
4383                 
4384                 protected override void DoEmit (EmitContext ec)
4385                 {
4386                         for (int i = 0; i < data.Length; i++) {
4387                                 data [i].Emit (ec);
4388                         }
4389
4390                         statement.Emit (ec);
4391
4392                         if (has_ret)
4393                                 return;
4394
4395                         //
4396                         // Clear the pinned variable
4397                         //
4398                         for (int i = 0; i < data.Length; i++) {
4399                                 data [i].EmitExit (ec);
4400                         }
4401                 }
4402
4403                 protected override void CloneTo (CloneContext clonectx, Statement t)
4404                 {
4405                         Fixed target = (Fixed) t;
4406
4407                         target.type = type.Clone (clonectx);
4408                         target.declarators = new List<KeyValuePair<LocalInfo, Expression>> (declarators.Count);
4409                         foreach (var p in declarators) {
4410                                 target.declarators.Add (new KeyValuePair<LocalInfo, Expression> (
4411                                         clonectx.LookupVariable (p.Key), p.Value.Clone (clonectx)));
4412                         }
4413                         
4414                         target.statement = statement.Clone (clonectx);
4415                 }
4416         }
4417         
4418         public class Catch : Statement {
4419                 public readonly string Name;
4420                 public Block  Block;
4421                 public Block  VarBlock;
4422
4423                 Expression type_expr;
4424                 TypeSpec type;
4425                 
4426                 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4427                 {
4428                         type_expr = type;
4429                         Name = name;
4430                         Block = block;
4431                         VarBlock = var_block;
4432                         loc = l;
4433                 }
4434
4435                 public TypeSpec CatchType {
4436                         get {
4437                                 return type;
4438                         }
4439                 }
4440
4441                 public bool IsGeneral {
4442                         get {
4443                                 return type_expr == null;
4444                         }
4445                 }
4446
4447                 protected override void DoEmit (EmitContext ec)
4448                 {
4449                         if (CatchType != null)
4450                                 ec.BeginCatchBlock (CatchType);
4451                         else
4452                                 ec.BeginCatchBlock (TypeManager.object_type);
4453
4454                         if (VarBlock != null)
4455                                 VarBlock.Emit (ec);
4456
4457                         if (Name != null) {
4458                                 // TODO: Move to resolve
4459                                 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4460                                 lvr.Resolve (new ResolveContext (ec.MemberContext));
4461                                 
4462                                 // Only to make verifier happy
4463                                 if (TypeManager.IsGenericParameter (lvr.Type))
4464                                         ec.Emit (OpCodes.Unbox_Any, lvr.Type);
4465
4466                                 Expression source;
4467                                 if (lvr.IsHoisted) {
4468                                         LocalTemporary lt = new LocalTemporary (lvr.Type);
4469                                         lt.Store (ec);
4470                                         source = lt;
4471                                 } else {
4472                                         // Variable is at the top of the stack
4473                                         source = EmptyExpression.Null;
4474                                 }
4475
4476                                 lvr.EmitAssign (ec, source, false, false);
4477                         } else
4478                                 ec.Emit (OpCodes.Pop);
4479
4480                         Block.Emit (ec);
4481                 }
4482
4483                 public override bool Resolve (BlockContext ec)
4484                 {
4485                         using (ec.With (ResolveContext.Options.CatchScope, true)) {
4486                                 if (type_expr != null) {
4487                                         TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4488                                         if (te == null)
4489                                                 return false;
4490
4491                                         type = te.Type;
4492
4493                                         if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4494                                                 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4495                                                 return false;
4496                                         }
4497                                 } else
4498                                         type = null;
4499
4500                                 if (!Block.Resolve (ec))
4501                                         return false;
4502
4503                                 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4504                                 // emit the "unused variable" warnings.
4505                                 if (VarBlock != null)
4506                                         return VarBlock.Resolve (ec);
4507
4508                                 return true;
4509                         }
4510                 }
4511
4512                 protected override void CloneTo (CloneContext clonectx, Statement t)
4513                 {
4514                         Catch target = (Catch) t;
4515
4516                         if (type_expr != null)
4517                                 target.type_expr = type_expr.Clone (clonectx);
4518                         if (VarBlock != null)
4519                                 target.VarBlock = clonectx.LookupBlock (VarBlock);                      
4520                         target.Block = clonectx.LookupBlock (Block);
4521                 }
4522         }
4523
4524         public class TryFinally : ExceptionStatement {
4525                 Statement stmt;
4526                 Block fini;
4527
4528                 public TryFinally (Statement stmt, Block fini, Location l)
4529                 {
4530                         this.stmt = stmt;
4531                         this.fini = fini;
4532                         loc = l;
4533                 }
4534
4535                 public override bool Resolve (BlockContext ec)
4536                 {
4537                         bool ok = true;
4538
4539                         ec.StartFlowBranching (this);
4540
4541                         if (!stmt.Resolve (ec))
4542                                 ok = false;
4543
4544                         if (ok)
4545                                 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4546                         using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4547                                 if (!fini.Resolve (ec))
4548                                         ok = false;
4549                         }
4550
4551                         ec.EndFlowBranching ();
4552
4553                         ok &= base.Resolve (ec);
4554
4555                         return ok;
4556                 }
4557
4558                 protected override void EmitPreTryBody (EmitContext ec)
4559                 {
4560                 }
4561
4562                 protected override void EmitTryBody (EmitContext ec)
4563                 {
4564                         stmt.Emit (ec);
4565                 }
4566
4567                 protected override void EmitFinallyBody (EmitContext ec)
4568                 {
4569                         fini.Emit (ec);
4570                 }
4571
4572                 protected override void CloneTo (CloneContext clonectx, Statement t)
4573                 {
4574                         TryFinally target = (TryFinally) t;
4575
4576                         target.stmt = (Statement) stmt.Clone (clonectx);
4577                         if (fini != null)
4578                                 target.fini = clonectx.LookupBlock (fini);
4579                 }
4580         }
4581
4582         public class TryCatch : Statement {
4583                 public Block Block;
4584                 public List<Catch> Specific;
4585                 public Catch General;
4586                 bool inside_try_finally, code_follows;
4587
4588                 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4589                 {
4590                         this.Block = block;
4591                         this.Specific = catch_clauses;
4592                         this.inside_try_finally = inside_try_finally;
4593
4594                         Catch c = catch_clauses [0];
4595                         if (c.IsGeneral) {
4596                                 this.General = c;                       
4597                                 catch_clauses.RemoveAt (0);
4598                         }
4599
4600                         loc = l;
4601                 }
4602
4603                 public override bool Resolve (BlockContext ec)
4604                 {
4605                         bool ok = true;
4606
4607                         ec.StartFlowBranching (this);
4608
4609                         if (!Block.Resolve (ec))
4610                                 ok = false;
4611
4612                         TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4613                         int last_index = 0;
4614                         foreach (Catch c in Specific){
4615                                 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4616
4617                                 if (c.Name != null) {
4618                                         LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4619                                         if (vi == null)
4620                                                 throw new Exception ();
4621
4622                                         vi.VariableInfo = null;
4623                                 }
4624
4625                                 if (!c.Resolve (ec)) {
4626                                         ok = false;
4627                                         continue;
4628                                 }
4629
4630                                 TypeSpec resolved_type = c.CatchType;
4631                                 for (int ii = 0; ii < last_index; ++ii) {
4632                                         if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4633                                                 ec.Report.Error (160, c.loc,
4634                                                         "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4635                                                         TypeManager.CSharpName (prev_catches [ii]));
4636                                                 ok = false;
4637                                         }
4638                                 }
4639
4640                                 prev_catches [last_index++] = resolved_type;
4641                         }
4642
4643                         if (General != null) {
4644                                 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4645                                         foreach (Catch c in Specific){
4646                                                 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4647                                                         ec.Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4648                                                 }
4649                                         }
4650                                 }
4651
4652                                 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4653
4654                                 if (!General.Resolve (ec))
4655                                         ok = false;
4656                         }
4657
4658                         ec.EndFlowBranching ();
4659
4660                         // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4661                         // So, ensure there's some IL code after this statement
4662                         if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4663                                 ec.NeedReturnLabel ();
4664
4665                         return ok;
4666                 }
4667
4668                 public void SomeCodeFollows ()
4669                 {
4670                         code_follows = true;
4671                 }
4672                 
4673                 protected override void DoEmit (EmitContext ec)
4674                 {
4675                         if (!inside_try_finally)
4676                                 ec.BeginExceptionBlock ();
4677
4678                         Block.Emit (ec);
4679
4680                         foreach (Catch c in Specific)
4681                                 c.Emit (ec);
4682
4683                         if (General != null)
4684                                 General.Emit (ec);
4685
4686                         if (!inside_try_finally)
4687                                 ec.EndExceptionBlock ();
4688                 }
4689
4690                 protected override void CloneTo (CloneContext clonectx, Statement t)
4691                 {
4692                         TryCatch target = (TryCatch) t;
4693
4694                         target.Block = clonectx.LookupBlock (Block);
4695                         if (General != null)
4696                                 target.General = (Catch) General.Clone (clonectx);
4697                         if (Specific != null){
4698                                 target.Specific = new List<Catch> ();
4699                                 foreach (Catch c in Specific)
4700                                         target.Specific.Add ((Catch) c.Clone (clonectx));
4701                         }
4702                 }
4703         }
4704
4705         // FIXME: Why is it almost exact copy of Using ??
4706         public class UsingTemporary : ExceptionStatement {
4707                 TemporaryVariable local_copy;
4708                 public Statement Statement;
4709                 Expression expr;
4710                 TypeSpec expr_type;
4711
4712                 public UsingTemporary (Expression expr, Statement stmt, Location l)
4713                 {
4714                         this.expr = expr;
4715                         Statement = stmt;
4716                         loc = l;
4717                 }
4718
4719                 public override bool Resolve (BlockContext ec)
4720                 {
4721                         expr = expr.Resolve (ec);
4722                         if (expr == null)
4723                                 return false;
4724
4725                         expr_type = expr.Type;
4726
4727                         if (!expr_type.ImplementsInterface (TypeManager.idisposable_type) &&
4728                                 Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4729                                 if (expr_type != InternalType.Dynamic) {
4730                                         Using.Error_IsNotConvertibleToIDisposable (ec, expr);
4731                                         return false;
4732                                 }
4733
4734                                 expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.idisposable_type, loc);
4735                                 expr_type = expr.Type;
4736                         }
4737
4738                         local_copy = new TemporaryVariable (expr_type, loc);
4739                         local_copy.Resolve (ec);
4740
4741                         ec.StartFlowBranching (this);
4742
4743                         bool ok = Statement.Resolve (ec);
4744
4745                         ec.EndFlowBranching ();
4746
4747                         ok &= base.Resolve (ec);
4748
4749                         if (TypeManager.void_dispose_void == null) {
4750                                 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4751                                         TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4752                         }
4753
4754                         return ok;
4755                 }
4756
4757                 protected override void EmitPreTryBody (EmitContext ec)
4758                 {
4759                         local_copy.EmitAssign (ec, expr);
4760                 }
4761
4762                 protected override void EmitTryBody (EmitContext ec)
4763                 {
4764                         Statement.Emit (ec);
4765                 }
4766
4767                 protected override void EmitFinallyBody (EmitContext ec)
4768                 {
4769                         if (!TypeManager.IsStruct (expr_type)) {
4770                                 Label skip = ec.DefineLabel ();
4771                                 local_copy.Emit (ec);
4772                                 ec.Emit (OpCodes.Brfalse, skip);
4773                                 local_copy.Emit (ec);
4774                                 ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4775                                 ec.MarkLabel (skip);
4776                                 return;
4777                         }
4778
4779                         MethodSpec ms = MemberCache.FindMember (expr_type,
4780                                 MemberFilter.Method ("Dispose", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.void_type),
4781                                 BindingRestriction.InstanceOnly) as MethodSpec;
4782
4783                         if (ms == null) {
4784                                 local_copy.Emit (ec);
4785                                 ec.Emit (OpCodes.Box, expr_type);
4786                                 ec.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4787                                 return;
4788                         }
4789
4790                         local_copy.AddressOf (ec, AddressOp.Load);
4791                         ec.Emit (OpCodes.Call, ms);
4792                 }
4793
4794                 protected override void CloneTo (CloneContext clonectx, Statement t)
4795                 {
4796                         UsingTemporary target = (UsingTemporary) t;
4797
4798                         target.expr = expr.Clone (clonectx);
4799                         target.Statement = Statement.Clone (clonectx);
4800                 }
4801         }
4802
4803         public class Using : ExceptionStatement {
4804                 Statement stmt;
4805                 public Statement EmbeddedStatement {
4806                         get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4807                 }
4808
4809                 Expression var;
4810                 Expression init;
4811
4812                 ExpressionStatement assign;
4813
4814                 public Using (Expression var, Expression init, Statement stmt, Location l)
4815                 {
4816                         this.var = var;
4817                         this.init = init;
4818                         this.stmt = stmt;
4819                         loc = l;
4820                 }
4821
4822                 static public void Error_IsNotConvertibleToIDisposable (BlockContext ec, Expression expr)
4823                 {
4824                         ec.Report.SymbolRelatedToPreviousError (expr.Type);
4825                         ec.Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4826                                 TypeManager.CSharpName (expr.Type));
4827                 }
4828
4829                 protected override void EmitPreTryBody (EmitContext ec)
4830                 {
4831                         assign.EmitStatement (ec);
4832                 }
4833
4834                 protected override void EmitTryBody (EmitContext ec)
4835                 {
4836                         stmt.Emit (ec);
4837                 }
4838
4839                 protected override void EmitFinallyBody (EmitContext ec)
4840                 {
4841                         Label skip = ec.DefineLabel ();
4842
4843                         bool emit_null_check = !TypeManager.IsValueType (var.Type);
4844                         if (emit_null_check) {
4845                                 var.Emit (ec);
4846                                 ec.Emit (OpCodes.Brfalse, skip);
4847                         }
4848
4849                         Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
4850
4851                         if (emit_null_check)
4852                                 ec.MarkLabel (skip);
4853                 }
4854
4855                 public override bool Resolve (BlockContext ec)
4856                 {
4857                         if (!ResolveVariable (ec))
4858                                 return false;
4859
4860                         ec.StartFlowBranching (this);
4861
4862                         bool ok = stmt.Resolve (ec);
4863
4864                         ec.EndFlowBranching ();
4865
4866                         ok &= base.Resolve (ec);
4867
4868                         if (TypeManager.void_dispose_void == null) {
4869                                 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4870                                         TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
4871                         }
4872
4873                         return ok;
4874                 }
4875
4876                 bool ResolveVariable (BlockContext ec)
4877                 {
4878                         assign = new SimpleAssign (var, init, loc);
4879                         assign = assign.ResolveStatement (ec);
4880                         if (assign == null)
4881                                 return false;
4882
4883                         if (assign.Type == TypeManager.idisposable_type || assign.Type.ImplementsInterface (TypeManager.idisposable_type)) {
4884                                 return true;
4885                         }
4886
4887                         Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
4888                         if (e == null) {
4889                                 if (assign.Type == InternalType.Dynamic) {
4890                                         e = Convert.ImplicitConversionRequired (ec, assign, TypeManager.idisposable_type, loc);
4891                                         var = new TemporaryVariable (e.Type, loc);
4892                                         assign = new SimpleAssign (var, e, loc).ResolveStatement (ec);
4893                                         return true;
4894                                 }
4895
4896                                 Error_IsNotConvertibleToIDisposable (ec, var);
4897                                 return false;
4898                         }
4899
4900                         throw new NotImplementedException ("covariance?");
4901                 }
4902
4903                 protected override void CloneTo (CloneContext clonectx, Statement t)
4904                 {
4905                         Using target = (Using) t;
4906
4907                         target.var = var.Clone (clonectx);
4908                         target.init = init.Clone (clonectx);
4909                         target.stmt = stmt.Clone (clonectx);
4910                 }
4911         }
4912
4913         /// <summary>
4914         ///   Implementation of the foreach C# statement
4915         /// </summary>
4916         public class Foreach : Statement {
4917
4918                 sealed class ArrayForeach : Statement
4919                 {
4920                         class ArrayCounter : TemporaryVariable
4921                         {
4922                                 StatementExpression increment;
4923
4924                                 public ArrayCounter (Location loc)
4925                                         : base (TypeManager.int32_type, loc)
4926                                 {
4927                                 }
4928
4929                                 public void ResolveIncrement (BlockContext ec)
4930                                 {
4931                                         increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
4932                                         increment.Resolve (ec);
4933                                 }
4934
4935                                 public void EmitIncrement (EmitContext ec)
4936                                 {
4937                                         increment.Emit (ec);
4938                                 }
4939                         }
4940
4941                         readonly Foreach for_each;
4942                         readonly Statement statement;
4943
4944                         Expression conv;
4945                         TemporaryVariable[] lengths;
4946                         Expression [] length_exprs;
4947                         ArrayCounter[] counter;
4948
4949                         TemporaryVariable copy;
4950                         Expression access;
4951
4952                         public ArrayForeach (Foreach @foreach, int rank)
4953                         {
4954                                 for_each = @foreach;
4955                                 statement = for_each.statement;
4956                                 loc = @foreach.loc;
4957
4958                                 counter = new ArrayCounter [rank];
4959                                 length_exprs = new Expression [rank];
4960
4961                                 //
4962                                 // Only use temporary length variables when dealing with
4963                                 // multi-dimensional arrays
4964                                 //
4965                                 if (rank > 1)
4966                                         lengths = new TemporaryVariable [rank];
4967                         }
4968
4969                         protected override void CloneTo (CloneContext clonectx, Statement target)
4970                         {
4971                                 throw new NotImplementedException ();
4972                         }
4973
4974                         public override bool Resolve (BlockContext ec)
4975                         {
4976                                 copy = new TemporaryVariable (for_each.expr.Type, loc);
4977                                 copy.Resolve (ec);
4978
4979                                 int rank = length_exprs.Length;
4980                                 Arguments list = new Arguments (rank);
4981                                 for (int i = 0; i < rank; i++) {
4982                                         counter [i] = new ArrayCounter (loc);
4983                                         counter [i].ResolveIncrement (ec);                                      
4984
4985                                         if (rank == 1) {
4986                                                 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
4987                                         } else {
4988                                                 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
4989                                                 lengths [i].Resolve (ec);
4990
4991                                                 Arguments args = new Arguments (1);
4992                                                 args.Add (new Argument (new IntConstant (i, loc)));
4993                                                 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
4994                                         }
4995
4996                                         list.Add (new Argument (counter [i]));
4997                                 }
4998
4999                                 access = new ElementAccess (copy, list).Resolve (ec);
5000                                 if (access == null)
5001                                         return false;
5002
5003                                 Expression var_type = for_each.type;
5004                                 VarExpr ve = var_type as VarExpr;
5005                                 if (ve != null) {
5006                                         // Infer implicitly typed local variable from foreach array type
5007                                         var_type = new TypeExpression (access.Type, ve.Location);
5008                                 }
5009
5010                                 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5011                                 if (var_type == null)
5012                                         return false;
5013
5014                                 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5015                                 if (conv == null)
5016                                         return false;
5017
5018                                 bool ok = true;
5019
5020                                 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5021                                 ec.CurrentBranching.CreateSibling ();
5022
5023                                 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5024                                 if (for_each.variable == null)
5025                                         ok = false;
5026
5027                                 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5028                                 if (!statement.Resolve (ec))
5029                                         ok = false;
5030                                 ec.EndFlowBranching ();
5031
5032                                 // There's no direct control flow from the end of the embedded statement to the end of the loop
5033                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
5034
5035                                 ec.EndFlowBranching ();
5036
5037                                 return ok;
5038                         }
5039
5040                         protected override void DoEmit (EmitContext ec)
5041                         {
5042                                 copy.EmitAssign (ec, for_each.expr);
5043
5044                                 int rank = length_exprs.Length;
5045                                 Label[] test = new Label [rank];
5046                                 Label[] loop = new Label [rank];
5047
5048                                 for (int i = 0; i < rank; i++) {
5049                                         test [i] = ec.DefineLabel ();
5050                                         loop [i] = ec.DefineLabel ();
5051
5052                                         if (lengths != null)
5053                                                 lengths [i].EmitAssign (ec, length_exprs [i]);
5054                                 }
5055
5056                                 IntConstant zero = new IntConstant (0, loc);
5057                                 for (int i = 0; i < rank; i++) {
5058                                         counter [i].EmitAssign (ec, zero);
5059
5060                                         ec.Emit (OpCodes.Br, test [i]);
5061                                         ec.MarkLabel (loop [i]);
5062                                 }
5063
5064                                 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5065
5066                                 statement.Emit (ec);
5067
5068                                 ec.MarkLabel (ec.LoopBegin);
5069
5070                                 for (int i = rank - 1; i >= 0; i--){
5071                                         counter [i].EmitIncrement (ec);
5072
5073                                         ec.MarkLabel (test [i]);
5074                                         counter [i].Emit (ec);
5075
5076                                         if (lengths != null)
5077                                                 lengths [i].Emit (ec);
5078                                         else
5079                                                 length_exprs [i].Emit (ec);
5080
5081                                         ec.Emit (OpCodes.Blt, loop [i]);
5082                                 }
5083
5084                                 ec.MarkLabel (ec.LoopEnd);
5085                         }
5086                 }
5087
5088                 sealed class CollectionForeach : Statement
5089                 {
5090                         class CollectionForeachStatement : Statement
5091                         {
5092                                 TypeSpec type;
5093                                 Expression variable, current, conv;
5094                                 Statement statement;
5095                                 Assign assign;
5096
5097                                 public CollectionForeachStatement (TypeSpec type, Expression variable,
5098                                                                    Expression current, Statement statement,
5099                                                                    Location loc)
5100                                 {
5101                                         this.type = type;
5102                                         this.variable = variable;
5103                                         this.current = current;
5104                                         this.statement = statement;
5105                                         this.loc = loc;
5106                                 }
5107
5108                                 protected override void CloneTo (CloneContext clonectx, Statement target)
5109                                 {
5110                                         throw new NotImplementedException ();
5111                                 }
5112
5113                                 public override bool Resolve (BlockContext ec)
5114                                 {
5115                                         current = current.Resolve (ec);
5116                                         if (current == null)
5117                                                 return false;
5118
5119                                         conv = Convert.ExplicitConversion (ec, current, type, loc);
5120                                         if (conv == null)
5121                                                 return false;
5122
5123                                         assign = new SimpleAssign (variable, conv, loc);
5124                                         if (assign.Resolve (ec) == null)
5125                                                 return false;
5126
5127                                         if (!statement.Resolve (ec))
5128                                                 return false;
5129
5130                                         return true;
5131                                 }
5132
5133                                 protected override void DoEmit (EmitContext ec)
5134                                 {
5135                                         assign.EmitStatement (ec);
5136                                         statement.Emit (ec);
5137                                 }
5138                         }
5139
5140                         Expression variable, expr;
5141                         Statement statement;
5142
5143                         TemporaryVariable enumerator;
5144                         Expression init;
5145                         Statement loop;
5146                         Statement wrapper;
5147
5148                         MethodGroupExpr get_enumerator;
5149                         PropertyExpr get_current;
5150                         MethodSpec move_next;
5151                         Expression var_type;
5152                         TypeSpec enumerator_type;
5153                         bool enumerator_found;
5154
5155                         public CollectionForeach (Expression var_type, Expression var,
5156                                                   Expression expr, Statement stmt, Location l)
5157                         {
5158                                 this.var_type = var_type;
5159                                 this.variable = var;
5160                                 this.expr = expr;
5161                                 statement = stmt;
5162                                 loc = l;
5163                         }
5164
5165                         protected override void CloneTo (CloneContext clonectx, Statement target)
5166                         {
5167                                 throw new NotImplementedException ();
5168                         }
5169
5170                         bool GetEnumeratorFilter (ResolveContext ec, MethodSpec mi)
5171                         {
5172                                 TypeSpec return_type = mi.ReturnType;
5173
5174                                 //
5175                                 // Ok, we can access it, now make sure that we can do something
5176                                 // with this `GetEnumerator'
5177                                 //
5178
5179                                 if (return_type == TypeManager.ienumerator_type ||
5180                                         return_type.ImplementsInterface (TypeManager.ienumerator_type)) {
5181                                         //
5182                                         // If it is not an interface, lets try to find the methods ourselves.
5183                                         // For example, if we have:
5184                                         // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5185                                         // We can avoid the iface call. This is a runtime perf boost.
5186                                         // even bigger if we have a ValueType, because we avoid the cost
5187                                         // of boxing.
5188                                         //
5189                                         // We have to make sure that both methods exist for us to take
5190                                         // this path. If one of the methods does not exist, we will just
5191                                         // use the interface. Sadly, this complex if statement is the only
5192                                         // way I could do this without a goto
5193                                         //
5194
5195                                         if (TypeManager.bool_movenext_void == null) {
5196                                                 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5197                                                         TypeManager.ienumerator_type, "MoveNext", loc, TypeSpec.EmptyTypes);
5198                                         }
5199
5200                                         if (TypeManager.ienumerator_getcurrent == null) {
5201                                                 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5202                                                         TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5203                                         }
5204
5205                                         //
5206                                         // Prefer a generic enumerator over a non-generic one.
5207                                         //
5208                                         if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5209                                                 enumerator_type = return_type;
5210                                                 if (!FetchGetCurrent (ec, return_type))
5211                                                         get_current = new PropertyExpr (
5212                                                                 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5213                                                 if (!FetchMoveNext (return_type))
5214                                                         move_next = TypeManager.bool_movenext_void;
5215                                                 return true;
5216                                         }
5217
5218                                         if (return_type.IsInterface ||
5219                                             !FetchMoveNext (return_type) ||
5220                                             !FetchGetCurrent (ec, return_type)) {
5221                                                 enumerator_type = return_type;
5222                                                 move_next = TypeManager.bool_movenext_void;
5223                                                 get_current = new PropertyExpr (
5224                                                         ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5225                                                 return true;
5226                                         }
5227                                 } else {
5228                                         //
5229                                         // Ok, so they dont return an IEnumerable, we will have to
5230                                         // find if they support the GetEnumerator pattern.
5231                                         //
5232
5233                                         if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5234                                                 ec.Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5235                                                         TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5236                                                 return false;
5237                                         }
5238                                 }
5239
5240                                 enumerator_type = return_type;
5241
5242                                 return true;
5243                         }
5244
5245                         //
5246                         // Retrieves a `public bool MoveNext ()' method from the Type `t'
5247                         //
5248                         bool FetchMoveNext (TypeSpec t)
5249                         {
5250                                 move_next = MemberCache.FindMember (t,
5251                                         MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5252                                         BindingRestriction.InstanceOnly) as MethodSpec;
5253
5254                                 return move_next != null && (move_next.Modifiers & Modifiers.PUBLIC) != 0;
5255                         }
5256                 
5257                         //
5258                         // Retrieves a `public T get_Current ()' method from the Type `t'
5259                         //
5260                         bool FetchGetCurrent (ResolveContext ec, TypeSpec t)
5261                         {
5262                                 PropertyExpr pe = Expression.MemberLookup (ec.Compiler,
5263                                         ec.CurrentType, t, "Current", 0, MemberKind.Property,
5264                                         BindingRestriction.AccessibleOnly, loc) as PropertyExpr;
5265                                 if (pe == null)
5266                                         return false;
5267
5268                                 get_current = pe;
5269                                 return true;
5270                         }
5271
5272                         void Error_Enumerator (BlockContext ec)
5273                         {
5274                                 if (enumerator_found) {
5275                                         return;
5276                                 }
5277
5278                             ec.Report.Error (1579, loc,
5279                                         "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5280                                         TypeManager.CSharpName (expr.Type));
5281                         }
5282
5283                         bool TryType (ResolveContext ec, TypeSpec t)
5284                         {
5285                                 var mg = Expression.MemberLookup (ec.Compiler, ec.CurrentType, null, t, "GetEnumerator", 0,
5286                                         MemberKind.Method, BindingRestriction.NoOverrides | BindingRestriction.InstanceOnly, loc) as MethodGroupExpr;
5287
5288                                 if (mg == null)
5289                                         return false;
5290
5291                                 MethodSpec result = null;
5292                                 MethodSpec tmp_move_next = null;
5293                                 PropertyExpr tmp_get_cur = null;
5294                                 TypeSpec tmp_enumerator_type = enumerator_type;
5295                                 foreach (MethodSpec mi in mg.Methods) {
5296                                         if (!mi.Parameters.IsEmpty)
5297                                                 continue;
5298                         
5299                                         // Check whether GetEnumerator is public
5300                                         if ((mi.Modifiers & Modifiers.AccessibilityMask) != Modifiers.PUBLIC)
5301                                                 continue;
5302
5303                                         enumerator_found = true;
5304
5305                                         if (!GetEnumeratorFilter (ec, mi))
5306                                                 continue;
5307
5308                                         if (result != null) {
5309                                                 if (TypeManager.IsGenericType (result.ReturnType)) {
5310                                                         if (!TypeManager.IsGenericType (mi.ReturnType))
5311                                                                 continue;
5312
5313                                                         ec.Report.SymbolRelatedToPreviousError (t);
5314                                                         ec.Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5315                                                                          "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5316                                                                          TypeManager.CSharpName (t), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5317                                                         return false;
5318                                                 }
5319
5320                                                 // Always prefer generics enumerators
5321                                                 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5322                                                         if (mi.DeclaringType.ImplementsInterface (result.DeclaringType) ||
5323                                                                 result.DeclaringType.ImplementsInterface (mi.DeclaringType))
5324                                                                 continue;
5325
5326                                                         ec.Report.SymbolRelatedToPreviousError (result);
5327                                                         ec.Report.SymbolRelatedToPreviousError (mi);
5328                                                         ec.Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5329                                                                         TypeManager.CSharpName (t), "enumerable", result.GetSignatureForError (), mi.GetSignatureForError ());
5330                                                         return false;
5331                                                 }
5332                                         }
5333                                         result = mi;
5334                                         tmp_move_next = move_next;
5335                                         tmp_get_cur = get_current;
5336                                         tmp_enumerator_type = enumerator_type;
5337                                         if (mi.DeclaringType == t)
5338                                                 break;
5339                                 }
5340
5341                                 if (result != null) {
5342                                         move_next = tmp_move_next;
5343                                         get_current = tmp_get_cur;
5344                                         enumerator_type = tmp_enumerator_type;
5345                                         get_enumerator = new MethodGroupExpr (result, enumerator_type, loc);
5346
5347                                         if (t != expr.Type) {
5348                                                 expr = Convert.ExplicitConversion (
5349                                                         ec, expr, t, loc);
5350                                                 if (expr == null)
5351                                                         throw new InternalErrorException ();
5352                                         }
5353
5354                                         get_enumerator.InstanceExpression = expr;
5355                                         get_enumerator.IsBase = t != expr.Type;
5356
5357                                         return true;
5358                                 }
5359
5360                                 return false;
5361                         }
5362
5363                         bool ProbeCollectionType (ResolveContext ec, TypeSpec t)
5364                         {
5365                                 int errors = ec.Report.Errors;
5366                                 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type;){
5367                                         if (TryType (ec, tt))
5368                                                 return true;
5369                                         tt = tt.BaseType;
5370                                 }
5371
5372                                 if (ec.Report.Errors > errors)
5373                                         return false;
5374
5375                                 //
5376                                 // Now try to find the method in the interfaces
5377                                 //
5378                                 for (TypeSpec tt = t; tt != null && tt != TypeManager.object_type; ) {
5379                                         if (tt.Interfaces != null) {
5380                                                 foreach (TypeSpec i in tt.Interfaces) {
5381                                                         if (TryType (ec, i))
5382                                                                 return true;
5383                                                 }
5384                                         }
5385                                         tt = tt.BaseType;
5386                                 }
5387
5388                                 return false;
5389                         }
5390
5391                         public override bool Resolve (BlockContext ec)
5392                         {
5393                                 enumerator_type = TypeManager.ienumerator_type;
5394
5395                                 bool is_dynamic = expr.Type == InternalType.Dynamic;
5396                                 if (is_dynamic)
5397                                         expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5398                                 
5399                                 if (!ProbeCollectionType (ec, expr.Type)) {
5400                                         Error_Enumerator (ec);
5401                                         return false;
5402                                 }
5403
5404                                 VarExpr ve = var_type as VarExpr;
5405                                 if (ve != null) {
5406                                         // Infer implicitly typed local variable from foreach enumerable type
5407                                         var_type = new TypeExpression (
5408                                                 is_dynamic ? InternalType.Dynamic : get_current.Type,
5409                                                 var_type.Location);
5410                                 }
5411
5412                                 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5413                                 if (var_type == null)
5414                                         return false;
5415                                                                 
5416                                 enumerator = new TemporaryVariable (enumerator_type, loc);
5417                                 enumerator.Resolve (ec);
5418
5419                                 init = new Invocation (get_enumerator, null);
5420                                 init = init.Resolve (ec);
5421                                 if (init == null)
5422                                         return false;
5423
5424                                 Expression move_next_expr;
5425                                 {
5426                                         var mi = new List<MemberSpec> (1) { move_next };
5427                                         MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5428                                         mg.InstanceExpression = enumerator;
5429
5430                                         move_next_expr = new Invocation (mg, null);
5431                                 }
5432
5433                                 get_current.InstanceExpression = enumerator;
5434
5435                                 Statement block = new CollectionForeachStatement (
5436                                         var_type.Type, variable, get_current, statement, loc);
5437
5438                                 loop = new While (new BooleanExpression (move_next_expr), block, loc);
5439
5440
5441                                 bool implements_idisposable = enumerator_type.ImplementsInterface (TypeManager.idisposable_type);
5442                                 if (implements_idisposable || !enumerator_type.IsSealed) {
5443                                         wrapper = new DisposableWrapper (this, implements_idisposable);
5444                                 } else {
5445                                         wrapper = new NonDisposableWrapper (this);
5446                                 }
5447
5448                                 return wrapper.Resolve (ec);
5449                         }
5450
5451                         protected override void DoEmit (EmitContext ec)
5452                         {
5453                                 wrapper.Emit (ec);
5454                         }
5455
5456                         class NonDisposableWrapper : Statement {
5457                                 CollectionForeach parent;
5458
5459                                 internal NonDisposableWrapper (CollectionForeach parent)
5460                                 {
5461                                         this.parent = parent;
5462                                 }
5463
5464                                 protected override void CloneTo (CloneContext clonectx, Statement target)
5465                                 {
5466                                         throw new NotSupportedException ();
5467                                 }
5468
5469                                 public override bool Resolve (BlockContext ec)
5470                                 {
5471                                         return parent.ResolveLoop (ec);
5472                                 }
5473
5474                                 protected override void DoEmit (EmitContext ec)
5475                                 {
5476                                         parent.EmitLoopInit (ec);
5477                                         parent.EmitLoopBody (ec);
5478                                 }
5479                         }
5480
5481                         sealed class DisposableWrapper : ExceptionStatement
5482                         {
5483                                 CollectionForeach parent;
5484                                 bool implements_idisposable;
5485
5486                                 internal DisposableWrapper (CollectionForeach parent, bool implements)
5487                                 {
5488                                         this.parent = parent;
5489                                         this.implements_idisposable = implements;
5490                                 }
5491
5492                                 protected override void CloneTo (CloneContext clonectx, Statement target)
5493                                 {
5494                                         throw new NotSupportedException ();
5495                                 }
5496
5497                                 public override bool Resolve (BlockContext ec)
5498                                 {
5499                                         bool ok = true;
5500
5501                                         ec.StartFlowBranching (this);
5502
5503                                         if (!parent.ResolveLoop (ec))
5504                                                 ok = false;
5505
5506                                         ec.EndFlowBranching ();
5507
5508                                         ok &= base.Resolve (ec);
5509
5510                                         if (TypeManager.void_dispose_void == null) {
5511                                                 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5512                                                         TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5513                                         }
5514                                         return ok;
5515                                 }
5516
5517                                 protected override void EmitPreTryBody (EmitContext ec)
5518                                 {
5519                                         parent.EmitLoopInit (ec);
5520                                 }
5521
5522                                 protected override void EmitTryBody (EmitContext ec)
5523                                 {
5524                                         parent.EmitLoopBody (ec);
5525                                 }
5526
5527                                 protected override void EmitFinallyBody (EmitContext ec)
5528                                 {
5529                                         Expression instance = parent.enumerator;
5530                                         if (!TypeManager.IsValueType (parent.enumerator_type)) {
5531
5532                                                 parent.enumerator.Emit (ec);
5533
5534                                                 Label call_dispose = ec.DefineLabel ();
5535
5536                                                 if (!implements_idisposable) {
5537                                                         ec.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5538                                                         LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5539                                                         temp.Store (ec);
5540                                                         temp.Emit (ec);
5541                                                         instance = temp;
5542                                                 }
5543                                                 
5544                                                 ec.Emit (OpCodes.Brtrue_S, call_dispose);
5545
5546                                                 // using 'endfinally' to empty the evaluation stack
5547                                                 ec.Emit (OpCodes.Endfinally);
5548                                                 ec.MarkLabel (call_dispose);
5549                                         }
5550
5551                                         Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5552                                 }
5553                         }
5554
5555                         bool ResolveLoop (BlockContext ec)
5556                         {
5557                                 return loop.Resolve (ec);
5558                         }
5559
5560                         void EmitLoopInit (EmitContext ec)
5561                         {
5562                                 enumerator.EmitAssign (ec, init);
5563                         }
5564
5565                         void EmitLoopBody (EmitContext ec)
5566                         {
5567                                 loop.Emit (ec);
5568                         }
5569                 }
5570
5571                 Expression type;
5572                 Expression variable;
5573                 Expression expr;
5574                 Statement statement;
5575
5576                 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5577                                 Statement stmt, Location l)
5578                 {
5579                         this.type = type;
5580                         this.variable = var;
5581                         this.expr = expr;
5582                         statement = stmt;
5583                         loc = l;
5584                 }
5585
5586                 public Statement Statement {
5587                         get { return statement; }
5588                 }
5589
5590                 public override bool Resolve (BlockContext ec)
5591                 {
5592                         expr = expr.Resolve (ec);
5593                         if (expr == null)
5594                                 return false;
5595
5596                         if (expr.IsNull) {
5597                                 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5598                                 return false;
5599                         }
5600
5601                         if (expr.Type == TypeManager.string_type) {
5602                                 statement = new ArrayForeach (this, 1);
5603                         } else if (expr.Type is ArrayContainer) {
5604                                 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5605                         } else {
5606                                 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5607                                         ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5608                                                 expr.ExprClassName);
5609                                         return false;
5610                                 }
5611
5612                                 statement = new CollectionForeach (type, variable, expr, statement, loc);
5613                         }
5614
5615                         return statement.Resolve (ec);
5616                 }
5617
5618                 protected override void DoEmit (EmitContext ec)
5619                 {
5620                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5621                         ec.LoopBegin = ec.DefineLabel ();
5622                         ec.LoopEnd = ec.DefineLabel ();
5623
5624                         statement.Emit (ec);
5625
5626                         ec.LoopBegin = old_begin;
5627                         ec.LoopEnd = old_end;
5628                 }
5629
5630                 protected override void CloneTo (CloneContext clonectx, Statement t)
5631                 {
5632                         Foreach target = (Foreach) t;
5633
5634                         target.type = type.Clone (clonectx);
5635                         target.variable = variable.Clone (clonectx);
5636                         target.expr = expr.Clone (clonectx);
5637                         target.statement = statement.Clone (clonectx);
5638                 }
5639         }
5640 }