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