2003-02-23 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / mcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Martin Baulig (martin@gnome.org)
7 //
8 // (C) 2001, 2002 Ximian, Inc.
9 //
10
11 using System;
12 using System.Text;
13 using System.Reflection;
14 using System.Reflection.Emit;
15 using System.Diagnostics;
16
17 namespace Mono.CSharp {
18
19         using System.Collections;
20         
21         public abstract class Statement {
22                 public Location loc;
23                 
24                 ///
25                 /// Resolves the statement, true means that all sub-statements
26                 /// did resolve ok.
27                 //
28                 public virtual bool Resolve (EmitContext ec)
29                 {
30                         return true;
31                 }
32                 
33                 /// <summary>
34                 ///   Return value indicates whether all code paths emitted return.
35                 /// </summary>
36                 protected abstract bool DoEmit (EmitContext ec);
37
38                 /// <summary>
39                 ///   Return value indicates whether all code paths emitted return.
40                 /// </summary>
41                 public virtual bool Emit (EmitContext ec)
42                 {
43                         ec.Mark (loc);
44                         Report.Debug (8, "MARK", this, loc);
45                         return DoEmit (ec);
46                 }
47                 
48                 public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc)
49                 {
50                         e = e.Resolve (ec);
51                         if (e == null)
52                                 return null;
53                         
54                         if (e.Type != TypeManager.bool_type){
55                                 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
56                                                                 new Location (-1));
57                         }
58
59                         if (e == null){
60                                 Report.Error (
61                                         31, loc, "Can not convert the expression to a boolean");
62                         }
63
64                         ec.Mark (loc);
65
66                         return e;
67                 }
68                 
69                 /// <remarks>
70                 ///    Encapsulates the emission of a boolean test and jumping to a
71                 ///    destination.
72                 ///
73                 ///    This will emit the bool expression in `bool_expr' and if
74                 ///    `target_is_for_true' is true, then the code will generate a 
75                 ///    brtrue to the target.   Otherwise a brfalse. 
76                 /// </remarks>
77                 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
78                                                        Label target, bool target_is_for_true)
79                 {
80                         ILGenerator ig = ec.ig;
81                         
82                         bool invert = false;
83                         if (bool_expr is Unary){
84                                 Unary u = (Unary) bool_expr;
85                                 
86                                 if (u.Oper == Unary.Operator.LogicalNot){
87                                         invert = true;
88
89                                         u.EmitLogicalNot (ec);
90                                 }
91                         } else if (bool_expr is Binary){
92                                 Binary b = (Binary) bool_expr;
93
94                                 if (b.EmitBranchable (ec, target, target_is_for_true))
95                                         return;
96                         }
97
98                         if (!invert)
99                                 bool_expr.Emit (ec);
100
101                         if (target_is_for_true){
102                                 if (invert)
103                                         ig.Emit (OpCodes.Brfalse, target);
104                                 else
105                                         ig.Emit (OpCodes.Brtrue, target);
106                         } else {
107                                 if (invert)
108                                         ig.Emit (OpCodes.Brtrue, target);
109                                 else
110                                         ig.Emit (OpCodes.Brfalse, target);
111                         }
112                 }
113
114                 public static void Warning_DeadCodeFound (Location loc)
115                 {
116                         Report.Warning (162, loc, "Unreachable code detected");
117                 }
118         }
119
120         public class EmptyStatement : Statement {
121                 public override bool Resolve (EmitContext ec)
122                 {
123                         return true;
124                 }
125                 
126                 protected override bool DoEmit (EmitContext ec)
127                 {
128                         return false;
129                 }
130         }
131         
132         public class If : Statement {
133                 Expression expr;
134                 public Statement TrueStatement;
135                 public Statement FalseStatement;
136                 
137                 public If (Expression expr, Statement trueStatement, Location l)
138                 {
139                         this.expr = expr;
140                         TrueStatement = trueStatement;
141                         loc = l;
142                 }
143
144                 public If (Expression expr,
145                            Statement trueStatement,
146                            Statement falseStatement,
147                            Location l)
148                 {
149                         this.expr = expr;
150                         TrueStatement = trueStatement;
151                         FalseStatement = falseStatement;
152                         loc = l;
153                 }
154
155                 public override bool Resolve (EmitContext ec)
156                 {
157                         Report.Debug (1, "START IF BLOCK", loc);
158
159                         expr = ResolveBoolean (ec, expr, loc);
160                         if (expr == null){
161                                 return false;
162                         }
163                         
164                         ec.StartFlowBranching (FlowBranchingType.BLOCK, loc);
165                         
166                         if (!TrueStatement.Resolve (ec)) {
167                                 ec.KillFlowBranching ();
168                                 return false;
169                         }
170
171                         ec.CurrentBranching.CreateSibling ();
172
173                         if ((FalseStatement != null) && !FalseStatement.Resolve (ec)) {
174                                 ec.KillFlowBranching ();
175                                 return false;
176                         }
177                                         
178                         ec.EndFlowBranching ();
179
180                         Report.Debug (1, "END IF BLOCK", loc);
181
182                         return true;
183                 }
184                 
185                 protected override bool DoEmit (EmitContext ec)
186                 {
187                         ILGenerator ig = ec.ig;
188                         Label false_target = ig.DefineLabel ();
189                         Label end;
190                         bool is_true_ret, is_false_ret;
191
192                         //
193                         // Dead code elimination
194                         //
195                         if (expr is BoolConstant){
196                                 bool take = ((BoolConstant) expr).Value;
197
198                                 if (take){
199                                         if (FalseStatement != null){
200                                                 Warning_DeadCodeFound (FalseStatement.loc);
201                                         }
202                                         return TrueStatement.Emit (ec);
203                                 } else {
204                                         Warning_DeadCodeFound (TrueStatement.loc);
205                                         if (FalseStatement != null)
206                                                 return FalseStatement.Emit (ec);
207                                 }
208                         }
209                         
210                         EmitBoolExpression (ec, expr, false_target, false);
211
212                         is_true_ret = TrueStatement.Emit (ec);
213                         is_false_ret = is_true_ret;
214
215                         if (FalseStatement != null){
216                                 bool branch_emitted = false;
217                                 
218                                 end = ig.DefineLabel ();
219                                 if (!is_true_ret){
220                                         ig.Emit (OpCodes.Br, end);
221                                         branch_emitted = true;
222                                 }
223
224                                 ig.MarkLabel (false_target);
225                                 is_false_ret = FalseStatement.Emit (ec);
226
227                                 if (branch_emitted)
228                                         ig.MarkLabel (end);
229                         } else {
230                                 ig.MarkLabel (false_target);
231                                 is_false_ret = false;
232                         }
233
234                         return is_true_ret && is_false_ret;
235                 }
236         }
237
238         public class Do : Statement {
239                 public Expression expr;
240                 public readonly Statement  EmbeddedStatement;
241                 bool infinite, may_return;
242                 
243                 public Do (Statement statement, Expression boolExpr, Location l)
244                 {
245                         expr = boolExpr;
246                         EmbeddedStatement = statement;
247                         loc = l;
248                 }
249
250                 public override bool Resolve (EmitContext ec)
251                 {
252                         bool ok = true;
253
254                         ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
255
256                         if (!EmbeddedStatement.Resolve (ec))
257                                 ok = false;
258
259                         expr = ResolveBoolean (ec, expr, loc);
260                         if (expr == null)
261                                 ok = false;
262                         else if (expr is BoolConstant){
263                                 bool res = ((BoolConstant) expr).Value;
264
265                                 if (res)
266                                         infinite = true;
267                         }
268
269                         ec.CurrentBranching.Infinite = infinite;
270                         FlowReturns returns = ec.EndFlowBranching ();
271                         may_return = returns != FlowReturns.NEVER;
272
273                         return ok;
274                 }
275                 
276                 protected override bool DoEmit (EmitContext ec)
277                 {
278                         ILGenerator ig = ec.ig;
279                         Label loop = ig.DefineLabel ();
280                         Label old_begin = ec.LoopBegin;
281                         Label old_end = ec.LoopEnd;
282                         bool  old_inloop = ec.InLoop;
283                         int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
284                         
285                         ec.LoopBegin = ig.DefineLabel ();
286                         ec.LoopEnd = ig.DefineLabel ();
287                         ec.InLoop = true;
288                         ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
289                                 
290                         ig.MarkLabel (loop);
291                         EmbeddedStatement.Emit (ec);
292                         ig.MarkLabel (ec.LoopBegin);
293
294                         //
295                         // Dead code elimination
296                         //
297                         if (expr is BoolConstant){
298                                 bool res = ((BoolConstant) expr).Value;
299
300                                 if (res)
301                                         ec.ig.Emit (OpCodes.Br, loop); 
302                         } else
303                                 EmitBoolExpression (ec, expr, loop, true);
304                         
305                         ig.MarkLabel (ec.LoopEnd);
306
307                         ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
308                         ec.LoopBegin = old_begin;
309                         ec.LoopEnd = old_end;
310                         ec.InLoop = old_inloop;
311
312                         if (infinite)
313                                 return may_return == false;
314                         else
315                                 return false;
316                 }
317         }
318
319         public class While : Statement {
320                 public Expression expr;
321                 public readonly Statement Statement;
322                 bool may_return, empty, infinite;
323                 
324                 public While (Expression boolExpr, Statement statement, Location l)
325                 {
326                         this.expr = boolExpr;
327                         Statement = statement;
328                         loc = l;
329                 }
330
331                 public override bool Resolve (EmitContext ec)
332                 {
333                         bool ok = true;
334
335                         expr = ResolveBoolean (ec, expr, loc);
336                         if (expr == null)
337                                 return false;
338
339                         ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
340
341                         //
342                         // Inform whether we are infinite or not
343                         //
344                         if (expr is BoolConstant){
345                                 BoolConstant bc = (BoolConstant) expr;
346
347                                 if (bc.Value == false){
348                                         Warning_DeadCodeFound (Statement.loc);
349                                         empty = true;
350                                 } else
351                                         infinite = true;
352                         } else {
353                                 //
354                                 // We are not infinite, so the loop may or may not be executed.
355                                 //
356                                 ec.CurrentBranching.CreateSibling ();
357                         }
358
359                         if (!Statement.Resolve (ec))
360                                 ok = false;
361
362                         if (empty)
363                                 ec.KillFlowBranching ();
364                         else {
365                                 ec.CurrentBranching.Infinite = infinite;
366                                 FlowReturns returns = ec.EndFlowBranching ();
367                                 may_return = returns != FlowReturns.NEVER;
368                         }
369
370                         return ok;
371                 }
372                 
373                 protected override bool DoEmit (EmitContext ec)
374                 {
375                         if (empty)
376                                 return false;
377
378                         ILGenerator ig = ec.ig;
379                         Label old_begin = ec.LoopBegin;
380                         Label old_end = ec.LoopEnd;
381                         bool old_inloop = ec.InLoop;
382                         int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
383                         bool ret;
384                         
385                         ec.LoopBegin = ig.DefineLabel ();
386                         ec.LoopEnd = ig.DefineLabel ();
387                         ec.InLoop = true;
388                         ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
389
390                         //
391                         // Inform whether we are infinite or not
392                         //
393                         if (expr is BoolConstant){
394                                 BoolConstant bc = (BoolConstant) expr;
395
396                                 ig.MarkLabel (ec.LoopBegin);
397                                 Statement.Emit (ec);
398                                 ig.Emit (OpCodes.Br, ec.LoopBegin);
399                                         
400                                 //
401                                 // Inform that we are infinite (ie, `we return'), only
402                                 // if we do not `break' inside the code.
403                                 //
404                                 ret = may_return == false;
405                                 ig.MarkLabel (ec.LoopEnd);
406                         } else {
407                                 Label while_loop = ig.DefineLabel ();
408
409                                 ig.Emit (OpCodes.Br, ec.LoopBegin);
410                                 ig.MarkLabel (while_loop);
411
412                                 Statement.Emit (ec);
413                         
414                                 ig.MarkLabel (ec.LoopBegin);
415
416                                 EmitBoolExpression (ec, expr, while_loop, true);
417                                 ig.MarkLabel (ec.LoopEnd);
418
419                                 ret = false;
420                         }       
421
422                         ec.LoopBegin = old_begin;
423                         ec.LoopEnd = old_end;
424                         ec.InLoop = old_inloop;
425                         ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
426
427                         return ret;
428                 }
429         }
430
431         public class For : Statement {
432                 Expression Test;
433                 readonly Statement InitStatement;
434                 readonly Statement Increment;
435                 readonly Statement Statement;
436                 bool may_return, infinite, empty;
437                 
438                 public For (Statement initStatement,
439                             Expression test,
440                             Statement increment,
441                             Statement statement,
442                             Location l)
443                 {
444                         InitStatement = initStatement;
445                         Test = test;
446                         Increment = increment;
447                         Statement = statement;
448                         loc = l;
449                 }
450
451                 public override bool Resolve (EmitContext ec)
452                 {
453                         bool ok = true;
454
455                         if (InitStatement != null){
456                                 if (!InitStatement.Resolve (ec))
457                                         ok = false;
458                         }
459
460                         if (Test != null){
461                                 Test = ResolveBoolean (ec, Test, loc);
462                                 if (Test == null)
463                                         ok = false;
464                                 else if (Test is BoolConstant){
465                                         BoolConstant bc = (BoolConstant) Test;
466
467                                         if (bc.Value == false){
468                                                 Warning_DeadCodeFound (Statement.loc);
469                                                 empty = true;
470                                         } else
471                                                 infinite = true;
472                                 }
473                         } else
474                                 infinite = true;
475
476                         if (Increment != null){
477                                 if (!Increment.Resolve (ec))
478                                         ok = false;
479                         }
480
481                         ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
482                         if (!infinite)
483                                 ec.CurrentBranching.CreateSibling ();
484
485                         if (!Statement.Resolve (ec))
486                                 ok = false;
487
488                         if (empty)
489                                 ec.KillFlowBranching ();
490                         else {
491                                 ec.CurrentBranching.Infinite = infinite;
492                                 FlowReturns returns = ec.EndFlowBranching ();
493                                 may_return = returns != FlowReturns.NEVER;
494                         }
495
496                         return ok;
497                 }
498                 
499                 protected override bool DoEmit (EmitContext ec)
500                 {
501                         if (empty)
502                                 return false;
503
504                         ILGenerator ig = ec.ig;
505                         Label old_begin = ec.LoopBegin;
506                         Label old_end = ec.LoopEnd;
507                         bool old_inloop = ec.InLoop;
508                         int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
509                         Label loop = ig.DefineLabel ();
510                         Label test = ig.DefineLabel ();
511                         
512                         if (InitStatement != null)
513                                 if (! (InitStatement is EmptyStatement))
514                                         InitStatement.Emit (ec);
515
516                         ec.LoopBegin = ig.DefineLabel ();
517                         ec.LoopEnd = ig.DefineLabel ();
518                         ec.InLoop = true;
519                         ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
520
521                         ig.Emit (OpCodes.Br, test);
522                         ig.MarkLabel (loop);
523                         Statement.Emit (ec);
524
525                         ig.MarkLabel (ec.LoopBegin);
526                         if (!(Increment is EmptyStatement))
527                                 Increment.Emit (ec);
528
529                         ig.MarkLabel (test);
530                         //
531                         // If test is null, there is no test, and we are just
532                         // an infinite loop
533                         //
534                         if (Test != null){
535                                 //
536                                 // The Resolve code already catches the case for Test == BoolConstant (false)
537                                 // so we know that this is true
538                                 //
539                                 if (Test is BoolConstant)
540                                         ig.Emit (OpCodes.Br, loop);
541                                 else
542                                         EmitBoolExpression (ec, Test, loop, true);
543                         } else
544                                 ig.Emit (OpCodes.Br, loop);
545                         ig.MarkLabel (ec.LoopEnd);
546
547                         ec.LoopBegin = old_begin;
548                         ec.LoopEnd = old_end;
549                         ec.InLoop = old_inloop;
550                         ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
551                         
552                         //
553                         // Inform whether we are infinite or not
554                         //
555                         if (Test != null){
556                                 if (Test is BoolConstant){
557                                         BoolConstant bc = (BoolConstant) Test;
558
559                                         if (bc.Value)
560                                                 return may_return == false;
561                                 }
562                                 return false;
563                         } else
564                                 return may_return == false;
565                 }
566         }
567         
568         public class StatementExpression : Statement {
569                 Expression expr;
570                 
571                 public StatementExpression (ExpressionStatement expr, Location l)
572                 {
573                         this.expr = expr;
574                         loc = l;
575                 }
576
577                 public override bool Resolve (EmitContext ec)
578                 {
579                         expr = (Expression) expr.Resolve (ec);
580                         return expr != null;
581                 }
582                 
583                 protected override bool DoEmit (EmitContext ec)
584                 {
585                         ILGenerator ig = ec.ig;
586                         
587                         if (expr is ExpressionStatement)
588                                 ((ExpressionStatement) expr).EmitStatement (ec);
589                         else {
590                                 expr.Emit (ec);
591                                 ig.Emit (OpCodes.Pop);
592                         }
593
594                         return false;
595                 }
596
597                 public override string ToString ()
598                 {
599                         return "StatementExpression (" + expr + ")";
600                 }
601         }
602
603         /// <summary>
604         ///   Implements the return statement
605         /// </summary>
606         public class Return : Statement {
607                 public Expression Expr;
608                 
609                 public Return (Expression expr, Location l)
610                 {
611                         Expr = expr;
612                         loc = l;
613                 }
614
615                 public override bool Resolve (EmitContext ec)
616                 {
617                         if (Expr != null){
618                                 Expr = Expr.Resolve (ec);
619                                 if (Expr == null)
620                                         return false;
621                         }
622
623                         FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
624
625                         if (ec.CurrentBranching.InTryBlock ())
626                                 ec.CurrentBranching.AddFinallyVector (vector);
627                         else
628                                 vector.CheckOutParameters (ec.CurrentBranching);
629
630                         vector.Returns = FlowReturns.ALWAYS;
631                         vector.Breaks = FlowReturns.ALWAYS;
632                         return true;
633                 }
634                 
635                 protected override bool DoEmit (EmitContext ec)
636                 {
637                         if (ec.InFinally){
638                                 Report.Error (157,loc,"Control can not leave the body of the finally block");
639                                 return false;
640                         }
641                         
642                         if (ec.ReturnType == null){
643                                 if (Expr != null){
644                                         Report.Error (127, loc, "Return with a value not allowed here");
645                                         return true;
646                                 }
647                         } else {
648                                 if (Expr == null){
649                                         Report.Error (126, loc, "An object of type `" +
650                                                       TypeManager.CSharpName (ec.ReturnType) + "' is " +
651                                                       "expected for the return statement");
652                                         return true;
653                                 }
654
655                                 if (Expr.Type != ec.ReturnType)
656                                         Expr = Expression.ConvertImplicitRequired (
657                                                 ec, Expr, ec.ReturnType, loc);
658
659                                 if (Expr == null)
660                                         return true;
661
662                                 Expr.Emit (ec);
663
664                                 if (ec.InTry || ec.InCatch)
665                                         ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
666                         }
667
668                         if (ec.InTry || ec.InCatch) {
669                                 if (!ec.HasReturnLabel) {
670                                         ec.ReturnLabel = ec.ig.DefineLabel ();
671                                         ec.HasReturnLabel = true;
672                                 }
673                                 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
674                         } else
675                                 ec.ig.Emit (OpCodes.Ret);
676
677                         return true; 
678                 }
679         }
680
681         public class Goto : Statement {
682                 string target;
683                 Block block;
684                 LabeledStatement label;
685                 
686                 public override bool Resolve (EmitContext ec)
687                 {
688                         label = block.LookupLabel (target);
689                         if (label == null){
690                                 Report.Error (
691                                         159, loc,
692                                         "No such label `" + target + "' in this scope");
693                                 return false;
694                         }
695
696                         // If this is a forward goto.
697                         if (!label.IsDefined)
698                                 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
699
700                         ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
701                         ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
702
703                         return true;
704                 }
705                 
706                 public Goto (Block parent_block, string label, Location l)
707                 {
708                         block = parent_block;
709                         loc = l;
710                         target = label;
711                 }
712
713                 public string Target {
714                         get {
715                                 return target;
716                         }
717                 }
718
719                 protected override bool DoEmit (EmitContext ec)
720                 {
721                         Label l = label.LabelTarget (ec);
722                         ec.ig.Emit (OpCodes.Br, l);
723                         
724                         return false;
725                 }
726         }
727
728         public class LabeledStatement : Statement {
729                 public readonly Location Location;
730                 string label_name;
731                 bool defined;
732                 bool referenced;
733                 Label label;
734
735                 ArrayList vectors;
736                 
737                 public LabeledStatement (string label_name, Location l)
738                 {
739                         this.label_name = label_name;
740                         this.Location = l;
741                 }
742
743                 public Label LabelTarget (EmitContext ec)
744                 {
745                         if (defined)
746                                 return label;
747                         label = ec.ig.DefineLabel ();
748                         defined = true;
749
750                         return label;
751                 }
752
753                 public bool IsDefined {
754                         get {
755                                 return defined;
756                         }
757                 }
758
759                 public bool HasBeenReferenced {
760                         get {
761                                 return referenced;
762                         }
763                 }
764
765                 public void AddUsageVector (FlowBranching.UsageVector vector)
766                 {
767                         if (vectors == null)
768                                 vectors = new ArrayList ();
769
770                         vectors.Add (vector.Clone ());
771                 }
772
773                 public override bool Resolve (EmitContext ec)
774                 {
775                         if (vectors != null)
776                                 ec.CurrentBranching.CurrentUsageVector.MergeJumpOrigins (vectors);
777                         else {
778                                 ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.NEVER;
779                                 ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.NEVER;
780                         }
781
782                         referenced = true;
783
784                         return true;
785                 }
786
787                 protected override bool DoEmit (EmitContext ec)
788                 {
789                         LabelTarget (ec);
790                         ec.ig.MarkLabel (label);
791
792                         return false;
793                 }
794         }
795         
796
797         /// <summary>
798         ///   `goto default' statement
799         /// </summary>
800         public class GotoDefault : Statement {
801                 
802                 public GotoDefault (Location l)
803                 {
804                         loc = l;
805                 }
806
807                 public override bool Resolve (EmitContext ec)
808                 {
809                         ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
810                         ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
811                         return true;
812                 }
813
814                 protected override bool DoEmit (EmitContext ec)
815                 {
816                         if (ec.Switch == null){
817                                 Report.Error (153, loc, "goto default is only valid in a switch statement");
818                                 return false;
819                         }
820
821                         if (!ec.Switch.GotDefault){
822                                 Report.Error (159, loc, "No default target on switch statement");
823                                 return false;
824                         }
825                         ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
826                         return false;
827                 }
828         }
829
830         /// <summary>
831         ///   `goto case' statement
832         /// </summary>
833         public class GotoCase : Statement {
834                 Expression expr;
835                 Label label;
836                 
837                 public GotoCase (Expression e, Location l)
838                 {
839                         expr = e;
840                         loc = l;
841                 }
842
843                 public override bool Resolve (EmitContext ec)
844                 {
845                         if (ec.Switch == null){
846                                 Report.Error (153, loc, "goto case is only valid in a switch statement");
847                                 return false;
848                         }
849
850                         expr = expr.Resolve (ec);
851                         if (expr == null)
852                                 return false;
853
854                         if (!(expr is Constant)){
855                                 Report.Error (159, loc, "Target expression for goto case is not constant");
856                                 return false;
857                         }
858
859                         object val = Expression.ConvertIntLiteral (
860                                 (Constant) expr, ec.Switch.SwitchType, loc);
861
862                         if (val == null)
863                                 return false;
864                                         
865                         SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
866
867                         if (sl == null){
868                                 Report.Error (
869                                         159, loc,
870                                         "No such label 'case " + val + "': for the goto case");
871                         }
872
873                         label = sl.ILLabelCode;
874
875                         ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.UNREACHABLE;
876                         ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.ALWAYS;
877                         return true;
878                 }
879
880                 protected override bool DoEmit (EmitContext ec)
881                 {
882                         ec.ig.Emit (OpCodes.Br, label);
883                         return true;
884                 }
885         }
886         
887         public class Throw : Statement {
888                 Expression expr;
889                 
890                 public Throw (Expression expr, Location l)
891                 {
892                         this.expr = expr;
893                         loc = l;
894                 }
895
896                 public override bool Resolve (EmitContext ec)
897                 {
898                         if (expr != null){
899                                 expr = expr.Resolve (ec);
900                                 if (expr == null)
901                                         return false;
902
903                                 ExprClass eclass = expr.eclass;
904
905                                 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
906                                       eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
907                                         expr.Error118 ("value, variable, property or indexer access ");
908                                         return false;
909                                 }
910
911                                 Type t = expr.Type;
912                                 
913                                 if ((t != TypeManager.exception_type) &&
914                                     !t.IsSubclassOf (TypeManager.exception_type) &&
915                                     !(expr is NullLiteral)) {
916                                         Report.Error (155, loc,
917                                                       "The type caught or thrown must be derived " +
918                                                       "from System.Exception");
919                                         return false;
920                                 }
921                         }
922
923                         ec.CurrentBranching.CurrentUsageVector.Returns = FlowReturns.EXCEPTION;
924                         ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.EXCEPTION;
925                         return true;
926                 }
927                         
928                 protected override bool DoEmit (EmitContext ec)
929                 {
930                         if (expr == null){
931                                 if (ec.InCatch)
932                                         ec.ig.Emit (OpCodes.Rethrow);
933                                 else {
934                                         Report.Error (
935                                                 156, loc,
936                                                 "A throw statement with no argument is only " +
937                                                 "allowed in a catch clause");
938                                 }
939                                 return false;
940                         }
941
942                         expr.Emit (ec);
943
944                         ec.ig.Emit (OpCodes.Throw);
945
946                         return true;
947                 }
948         }
949
950         public class Break : Statement {
951                 
952                 public Break (Location l)
953                 {
954                         loc = l;
955                 }
956
957                 public override bool Resolve (EmitContext ec)
958                 {
959                         ec.CurrentBranching.MayLeaveLoop = true;
960                         ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
961                         return true;
962                 }
963
964                 protected override bool DoEmit (EmitContext ec)
965                 {
966                         ILGenerator ig = ec.ig;
967
968                         if (ec.InLoop == false && ec.Switch == null){
969                                 Report.Error (139, loc, "No enclosing loop or switch to continue to");
970                                 return false;
971                         }
972
973                         if (ec.InTry || ec.InCatch)
974                                 ig.Emit (OpCodes.Leave, ec.LoopEnd);
975                         else
976                                 ig.Emit (OpCodes.Br, ec.LoopEnd);
977
978                         return false;
979                 }
980         }
981
982         public class Continue : Statement {
983                 
984                 public Continue (Location l)
985                 {
986                         loc = l;
987                 }
988
989                 public override bool Resolve (EmitContext ec)
990                 {
991                         ec.CurrentBranching.CurrentUsageVector.Breaks = FlowReturns.ALWAYS;
992                         return true;
993                 }
994
995                 protected override bool DoEmit (EmitContext ec)
996                 {
997                         Label begin = ec.LoopBegin;
998                         
999                         if (!ec.InLoop){
1000                                 Report.Error (139, loc, "No enclosing loop to continue to");
1001                                 return false;
1002                         } 
1003
1004                         //
1005                         // UGH: Non trivial.  This Br might cross a try/catch boundary
1006                         // How can we tell?
1007                         //
1008                         // while () {
1009                         //   try { ... } catch { continue; }
1010                         // }
1011                         //
1012                         // From:
1013                         // try {} catch { while () { continue; }}
1014                         //
1015                         if (ec.TryCatchLevel > ec.LoopBeginTryCatchLevel)
1016                                 ec.ig.Emit (OpCodes.Leave, begin);
1017                         else if (ec.TryCatchLevel < ec.LoopBeginTryCatchLevel)
1018                                 throw new Exception ("Should never happen");
1019                         else
1020                                 ec.ig.Emit (OpCodes.Br, begin);
1021                         return false;
1022                 }
1023         }
1024
1025         // <summary>
1026         //   This is used in the control flow analysis code to specify whether the
1027         //   current code block may return to its enclosing block before reaching
1028         //   its end.
1029         // </summary>
1030         public enum FlowReturns {
1031                 // It can never return.
1032                 NEVER,
1033
1034                 // This means that the block contains a conditional return statement
1035                 // somewhere.
1036                 SOMETIMES,
1037
1038                 // The code always returns, ie. there's an unconditional return / break
1039                 // statement in it.
1040                 ALWAYS,
1041
1042                 // The code always throws an exception.
1043                 EXCEPTION,
1044
1045                 // The current code block is unreachable.  This happens if it's immediately
1046                 // following a FlowReturns.ALWAYS block.
1047                 UNREACHABLE
1048         }
1049
1050         // <summary>
1051         //   This is a special bit vector which can inherit from another bit vector doing a
1052         //   copy-on-write strategy.  The inherited vector may have a smaller size than the
1053         //   current one.
1054         // </summary>
1055         public class MyBitVector {
1056                 public readonly int Count;
1057                 public readonly MyBitVector InheritsFrom;
1058
1059                 bool is_dirty;
1060                 BitArray vector;
1061
1062                 public MyBitVector (int Count)
1063                         : this (null, Count)
1064                 { }
1065
1066                 public MyBitVector (MyBitVector InheritsFrom, int Count)
1067                 {
1068                         this.InheritsFrom = InheritsFrom;
1069                         this.Count = Count;
1070                 }
1071
1072                 // <summary>
1073                 //   Checks whether this bit vector has been modified.  After setting this to true,
1074                 //   we won't use the inherited vector anymore, but our own copy of it.
1075                 // </summary>
1076                 public bool IsDirty {
1077                         get {
1078                                 return is_dirty;
1079                         }
1080
1081                         set {
1082                                 if (!is_dirty)
1083                                         initialize_vector ();
1084                         }
1085                 }
1086
1087                 // <summary>
1088                 //   Get/set bit `index' in the bit vector.
1089                 // </summary>
1090                 public bool this [int index]
1091                 {
1092                         get {
1093                                 if (index > Count)
1094                                         throw new ArgumentOutOfRangeException ();
1095
1096                                 // We're doing a "copy-on-write" strategy here; as long
1097                                 // as nobody writes to the array, we can use our parent's
1098                                 // copy instead of duplicating the vector.
1099
1100                                 if (vector != null)
1101                                         return vector [index];
1102                                 else if (InheritsFrom != null) {
1103                                         BitArray inherited = InheritsFrom.Vector;
1104
1105                                         if (index < inherited.Count)
1106                                                 return inherited [index];
1107                                         else
1108                                                 return false;
1109                                 } else
1110                                         return false;
1111                         }
1112
1113                         set {
1114                                 if (index > Count)
1115                                         throw new ArgumentOutOfRangeException ();
1116
1117                                 // Only copy the vector if we're actually modifying it.
1118
1119                                 if (this [index] != value) {
1120                                         initialize_vector ();
1121
1122                                         vector [index] = value;
1123                                 }
1124                         }
1125                 }
1126
1127                 // <summary>
1128                 //   If you explicitly convert the MyBitVector to a BitArray, you will get a deep
1129                 //   copy of the bit vector.
1130                 // </summary>
1131                 public static explicit operator BitArray (MyBitVector vector)
1132                 {
1133                         vector.initialize_vector ();
1134                         return vector.Vector;
1135                 }
1136
1137                 // <summary>
1138                 //   Performs an `or' operation on the bit vector.  The `new_vector' may have a
1139                 //   different size than the current one.
1140                 // </summary>
1141                 public void Or (MyBitVector new_vector)
1142                 {
1143                         BitArray new_array = new_vector.Vector;
1144
1145                         initialize_vector ();
1146
1147                         int upper;
1148                         if (vector.Count < new_array.Count)
1149                                 upper = vector.Count;
1150                         else
1151                                 upper = new_array.Count;
1152
1153                         for (int i = 0; i < upper; i++)
1154                                 vector [i] = vector [i] | new_array [i];
1155                 }
1156
1157                 // <summary>
1158                 //   Perfonrms an `and' operation on the bit vector.  The `new_vector' may have
1159                 //   a different size than the current one.
1160                 // </summary>
1161                 public void And (MyBitVector new_vector)
1162                 {
1163                         BitArray new_array = new_vector.Vector;
1164
1165                         initialize_vector ();
1166
1167                         int lower, upper;
1168                         if (vector.Count < new_array.Count)
1169                                 lower = upper = vector.Count;
1170                         else {
1171                                 lower = new_array.Count;
1172                                 upper = vector.Count;
1173                         }
1174
1175                         for (int i = 0; i < lower; i++)
1176                                 vector [i] = vector [i] & new_array [i];
1177
1178                         for (int i = lower; i < upper; i++)
1179                                 vector [i] = false;
1180                 }
1181
1182                 // <summary>
1183                 //   This does a deep copy of the bit vector.
1184                 // </summary>
1185                 public MyBitVector Clone ()
1186                 {
1187                         MyBitVector retval = new MyBitVector (Count);
1188
1189                         retval.Vector = Vector;
1190
1191                         return retval;
1192                 }
1193
1194                 BitArray Vector {
1195                         get {
1196                                 if (vector != null)
1197                                         return vector;
1198                                 else if (!is_dirty && (InheritsFrom != null))
1199                                         return InheritsFrom.Vector;
1200
1201                                 initialize_vector ();
1202
1203                                 return vector;
1204                         }
1205
1206                         set {
1207                                 initialize_vector ();
1208
1209                                 for (int i = 0; i < Math.Min (vector.Count, value.Count); i++)
1210                                         vector [i] = value [i];
1211                         }
1212                 }
1213
1214                 void initialize_vector ()
1215                 {
1216                         if (vector != null)
1217                                 return;
1218
1219                         vector = new BitArray (Count, false);
1220                         if (InheritsFrom != null)
1221                                 Vector = InheritsFrom.Vector;
1222
1223                         is_dirty = true;
1224                 }
1225
1226                 public override string ToString ()
1227                 {
1228                         StringBuilder sb = new StringBuilder ("MyBitVector (");
1229
1230                         BitArray vector = Vector;
1231                         sb.Append (Count);
1232                         sb.Append (",");
1233                         if (!IsDirty)
1234                                 sb.Append ("INHERITED - ");
1235                         for (int i = 0; i < vector.Count; i++) {
1236                                 if (i > 0)
1237                                         sb.Append (",");
1238                                 sb.Append (vector [i]);
1239                         }
1240                         
1241                         sb.Append (")");
1242                         return sb.ToString ();
1243                 }
1244         }
1245
1246         // <summary>
1247         //   The type of a FlowBranching.
1248         // </summary>
1249         public enum FlowBranchingType {
1250                 // Normal (conditional or toplevel) block.
1251                 BLOCK,
1252
1253                 // A loop block.
1254                 LOOP_BLOCK,
1255
1256                 // Try/Catch block.
1257                 EXCEPTION,
1258
1259                 // Switch block.
1260                 SWITCH,
1261
1262                 // Switch section.
1263                 SWITCH_SECTION
1264         }
1265
1266         // <summary>
1267         //   A new instance of this class is created every time a new block is resolved
1268         //   and if there's branching in the block's control flow.
1269         // </summary>
1270         public class FlowBranching {
1271                 // <summary>
1272                 //   The type of this flow branching.
1273                 // </summary>
1274                 public readonly FlowBranchingType Type;
1275
1276                 // <summary>
1277                 //   The block this branching is contained in.  This may be null if it's not
1278                 //   a top-level block and it doesn't declare any local variables.
1279                 // </summary>
1280                 public readonly Block Block;
1281
1282                 // <summary>
1283                 //   The parent of this branching or null if this is the top-block.
1284                 // </summary>
1285                 public readonly FlowBranching Parent;
1286
1287                 // <summary>
1288                 //   Start-Location of this flow branching.
1289                 // </summary>
1290                 public readonly Location Location;
1291
1292                 // <summary>
1293                 //   A list of UsageVectors.  A new vector is added each time control flow may
1294                 //   take a different path.
1295                 // </summary>
1296                 public UsageVector[] Siblings;
1297
1298                 // <summary>
1299                 //   If this is an infinite loop.
1300                 // </summary>
1301                 public bool Infinite;
1302
1303                 // <summary>
1304                 //   If we may leave the current loop.
1305                 // </summary>
1306                 public bool MayLeaveLoop;
1307
1308                 //
1309                 // Private
1310                 //
1311                 InternalParameters param_info;
1312                 int[] param_map;
1313                 MyStructInfo[] struct_params;
1314                 int num_params;
1315                 ArrayList finally_vectors;
1316
1317                 static int next_id = 0;
1318                 int id;
1319
1320                 // <summary>
1321                 //   Performs an `And' operation on the FlowReturns status
1322                 //   (for instance, a block only returns ALWAYS if all its siblings
1323                 //   always return).
1324                 // </summary>
1325                 public static FlowReturns AndFlowReturns (FlowReturns a, FlowReturns b)
1326                 {
1327                         if (b == FlowReturns.UNREACHABLE)
1328                                 return a;
1329
1330                         switch (a) {
1331                         case FlowReturns.NEVER:
1332                                 if (b == FlowReturns.NEVER)
1333                                         return FlowReturns.NEVER;
1334                                 else
1335                                         return FlowReturns.SOMETIMES;
1336
1337                         case FlowReturns.SOMETIMES:
1338                                 return FlowReturns.SOMETIMES;
1339
1340                         case FlowReturns.ALWAYS:
1341                                 if ((b == FlowReturns.ALWAYS) || (b == FlowReturns.EXCEPTION))
1342                                         return FlowReturns.ALWAYS;
1343                                 else
1344                                         return FlowReturns.SOMETIMES;
1345
1346                         case FlowReturns.EXCEPTION:
1347                                 if (b == FlowReturns.EXCEPTION)
1348                                         return FlowReturns.EXCEPTION;
1349                                 else if (b == FlowReturns.ALWAYS)
1350                                         return FlowReturns.ALWAYS;
1351                                 else
1352                                         return FlowReturns.SOMETIMES;
1353                         }
1354
1355                         return b;
1356                 }
1357
1358                 // <summary>
1359                 //   The vector contains a BitArray with information about which local variables
1360                 //   and parameters are already initialized at the current code position.
1361                 // </summary>
1362                 public class UsageVector {
1363                         // <summary>
1364                         //   If this is true, then the usage vector has been modified and must be
1365                         //   merged when we're done with this branching.
1366                         // </summary>
1367                         public bool IsDirty;
1368
1369                         // <summary>
1370                         //   The number of parameters in this block.
1371                         // </summary>
1372                         public readonly int CountParameters;
1373
1374                         // <summary>
1375                         //   The number of locals in this block.
1376                         // </summary>
1377                         public readonly int CountLocals;
1378
1379                         // <summary>
1380                         //   If not null, then we inherit our state from this vector and do a
1381                         //   copy-on-write.  If null, then we're the first sibling in a top-level
1382                         //   block and inherit from the empty vector.
1383                         // </summary>
1384                         public readonly UsageVector InheritsFrom;
1385
1386                         //
1387                         // Private.
1388                         //
1389                         MyBitVector locals, parameters;
1390                         FlowReturns real_returns, real_breaks;
1391                         bool is_finally;
1392
1393                         static int next_id = 0;
1394                         int id;
1395
1396                         //
1397                         // Normally, you should not use any of these constructors.
1398                         //
1399                         public UsageVector (UsageVector parent, int num_params, int num_locals)
1400                         {
1401                                 this.InheritsFrom = parent;
1402                                 this.CountParameters = num_params;
1403                                 this.CountLocals = num_locals;
1404                                 this.real_returns = FlowReturns.NEVER;
1405                                 this.real_breaks = FlowReturns.NEVER;
1406
1407                                 if (parent != null) {
1408                                         locals = new MyBitVector (parent.locals, CountLocals);
1409                                         if (num_params > 0)
1410                                                 parameters = new MyBitVector (parent.parameters, num_params);
1411                                         real_returns = parent.Returns;
1412                                         real_breaks = parent.Breaks;
1413                                 } else {
1414                                         locals = new MyBitVector (null, CountLocals);
1415                                         if (num_params > 0)
1416                                                 parameters = new MyBitVector (null, num_params);
1417                                 }
1418
1419                                 id = ++next_id;
1420                         }
1421
1422                         public UsageVector (UsageVector parent)
1423                                 : this (parent, parent.CountParameters, parent.CountLocals)
1424                         { }
1425
1426                         // <summary>
1427                         //   This does a deep copy of the usage vector.
1428                         // </summary>
1429                         public UsageVector Clone ()
1430                         {
1431                                 UsageVector retval = new UsageVector (null, CountParameters, CountLocals);
1432
1433                                 retval.locals = locals.Clone ();
1434                                 if (parameters != null)
1435                                         retval.parameters = parameters.Clone ();
1436                                 retval.real_returns = real_returns;
1437                                 retval.real_breaks = real_breaks;
1438
1439                                 return retval;
1440                         }
1441
1442                         // 
1443                         // State of parameter `number'.
1444                         //
1445                         public bool this [int number]
1446                         {
1447                                 get {
1448                                         if (number == -1)
1449                                                 return true;
1450                                         else if (number == 0)
1451                                                 throw new ArgumentException ();
1452
1453                                         return parameters [number - 1];
1454                                 }
1455
1456                                 set {
1457                                         if (number == -1)
1458                                                 return;
1459                                         else if (number == 0)
1460                                                 throw new ArgumentException ();
1461
1462                                         parameters [number - 1] = value;
1463                                 }
1464                         }
1465
1466                         //
1467                         // State of the local variable `vi'.
1468                         // If the local variable is a struct, use a non-zero `field_idx'
1469                         // to check an individual field in it.
1470                         //
1471                         public bool this [VariableInfo vi, int field_idx]
1472                         {
1473                                 get {
1474                                         if (vi.Number == -1)
1475                                                 return true;
1476                                         else if (vi.Number == 0)
1477                                                 throw new ArgumentException ();
1478
1479                                         return locals [vi.Number + field_idx - 1];
1480                                 }
1481
1482                                 set {
1483                                         if (vi.Number == -1)
1484                                                 return;
1485                                         else if (vi.Number == 0)
1486                                                 throw new ArgumentException ();
1487
1488                                         locals [vi.Number + field_idx - 1] = value;
1489                                 }
1490                         }
1491
1492                         // <summary>
1493                         //   Specifies when the current block returns.
1494                         //   If this is FlowReturns.UNREACHABLE, then control can never reach the
1495                         //   end of the method (so that we don't need to emit a return statement).
1496                         //   The same applies for FlowReturns.EXCEPTION, but in this case the return
1497                         //   value will never be used.
1498                         // </summary>
1499                         public FlowReturns Returns {
1500                                 get {
1501                                         return real_returns;
1502                                 }
1503
1504                                 set {
1505                                         real_returns = value;
1506                                 }
1507                         }
1508
1509                         // <summary>
1510                         //   Specifies whether control may return to our containing block
1511                         //   before reaching the end of this block.  This happens if there
1512                         //   is a break/continue/goto/return in it.
1513                         //   This can also be used to find out whether the statement immediately
1514                         //   following the current block may be reached or not.
1515                         // </summary>
1516                         public FlowReturns Breaks {
1517                                 get {
1518                                         return real_breaks;
1519                                 }
1520
1521                                 set {
1522                                         real_breaks = value;
1523                                 }
1524                         }
1525
1526                         public bool AlwaysBreaks {
1527                                 get {
1528                                         return (Breaks == FlowReturns.ALWAYS) ||
1529                                                 (Breaks == FlowReturns.EXCEPTION) ||
1530                                                 (Breaks == FlowReturns.UNREACHABLE);
1531                                 }
1532                         }
1533
1534                         public bool MayBreak {
1535                                 get {
1536                                         return Breaks != FlowReturns.NEVER;
1537                                 }
1538                         }
1539
1540                         public bool AlwaysReturns {
1541                                 get {
1542                                         return (Returns == FlowReturns.ALWAYS) ||
1543                                                 (Returns == FlowReturns.EXCEPTION);
1544                                 }
1545                         }
1546
1547                         public bool MayReturn {
1548                                 get {
1549                                         return (Returns == FlowReturns.SOMETIMES) ||
1550                                                 (Returns == FlowReturns.ALWAYS);
1551                                 }
1552                         }
1553
1554                         // <summary>
1555                         //   Merge a child branching.
1556                         // </summary>
1557                         public FlowReturns MergeChildren (FlowBranching branching, UsageVector[] children)
1558                         {
1559                                 MyBitVector new_locals = null;
1560                                 MyBitVector new_params = null;
1561
1562                                 FlowReturns new_returns = FlowReturns.NEVER;
1563                                 FlowReturns new_breaks = FlowReturns.NEVER;
1564                                 bool new_returns_set = false, new_breaks_set = false;
1565
1566                                 Report.Debug (2, "MERGING CHILDREN", branching, branching.Type,
1567                                               this, children.Length);
1568
1569                                 foreach (UsageVector child in children) {
1570                                         Report.Debug (2, "  MERGING CHILD", child, child.is_finally);
1571                                         
1572                                         if (!child.is_finally) {
1573                                                 if (child.Breaks != FlowReturns.UNREACHABLE) {
1574                                                         // If Returns is already set, perform an
1575                                                         // `And' operation on it, otherwise just set just.
1576                                                         if (!new_returns_set) {
1577                                                                 new_returns = child.Returns;
1578                                                                 new_returns_set = true;
1579                                                         } else
1580                                                                 new_returns = AndFlowReturns (
1581                                                                         new_returns, child.Returns);
1582                                                 }
1583
1584                                                 // If Breaks is already set, perform an
1585                                                 // `And' operation on it, otherwise just set just.
1586                                                 if (!new_breaks_set) {
1587                                                         new_breaks = child.Breaks;
1588                                                         new_breaks_set = true;
1589                                                 } else
1590                                                         new_breaks = AndFlowReturns (
1591                                                                 new_breaks, child.Breaks);
1592                                         }
1593
1594                                         // Ignore unreachable children.
1595                                         if (child.Returns == FlowReturns.UNREACHABLE)
1596                                                 continue;
1597
1598                                         // A local variable is initialized after a flow branching if it
1599                                         // has been initialized in all its branches which do neither
1600                                         // always return or always throw an exception.
1601                                         //
1602                                         // If a branch may return, but does not always return, then we
1603                                         // can treat it like a never-returning branch here: control will
1604                                         // only reach the code position after the branching if we did not
1605                                         // return here.
1606                                         //
1607                                         // It's important to distinguish between always and sometimes
1608                                         // returning branches here:
1609                                         //
1610                                         //    1   int a;
1611                                         //    2   if (something) {
1612                                         //    3      return;
1613                                         //    4      a = 5;
1614                                         //    5   }
1615                                         //    6   Console.WriteLine (a);
1616                                         //
1617                                         // The if block in lines 3-4 always returns, so we must not look
1618                                         // at the initialization of `a' in line 4 - thus it'll still be
1619                                         // uninitialized in line 6.
1620                                         //
1621                                         // On the other hand, the following is allowed:
1622                                         //
1623                                         //    1   int a;
1624                                         //    2   if (something)
1625                                         //    3      a = 5;
1626                                         //    4   else
1627                                         //    5      return;
1628                                         //    6   Console.WriteLine (a);
1629                                         //
1630                                         // Here, `a' is initialized in line 3 and we must not look at
1631                                         // line 5 since it always returns.
1632                                         // 
1633                                         if (child.is_finally) {
1634                                                 if (new_locals == null)
1635                                                         new_locals = locals.Clone ();
1636                                                 new_locals.Or (child.locals);
1637
1638                                                 if (parameters != null) {
1639                                                         if (new_params == null)
1640                                                                 new_params = parameters.Clone ();
1641                                                         new_params.Or (child.parameters);
1642                                                 }
1643
1644                                         } else {
1645                                                 if (!child.AlwaysReturns && !child.AlwaysBreaks) {
1646                                                         if (new_locals != null)
1647                                                                 new_locals.And (child.locals);
1648                                                         else {
1649                                                                 new_locals = locals.Clone ();
1650                                                                 new_locals.Or (child.locals);
1651                                                         }
1652                                                 } else if (children.Length == 1) {
1653                                                         new_locals = locals.Clone ();
1654                                                         new_locals.Or (child.locals);
1655                                                 }
1656
1657                                                 // An `out' parameter must be assigned in all branches which do
1658                                                 // not always throw an exception.
1659                                                 if (parameters != null) {
1660                                                         if (child.Breaks != FlowReturns.EXCEPTION) {
1661                                                                 if (new_params != null)
1662                                                                         new_params.And (child.parameters);
1663                                                                 else {
1664                                                                         new_params = parameters.Clone ();
1665                                                                         new_params.Or (child.parameters);
1666                                                                 }
1667                                                         } else if (children.Length == 1) {
1668                                                                 new_params = parameters.Clone ();
1669                                                                 new_params.Or (child.parameters);
1670                                                         }
1671                                                 }
1672                                         }
1673                                 }
1674
1675                                 Returns = new_returns;
1676                                 if ((branching.Type == FlowBranchingType.BLOCK) ||
1677                                     (branching.Type == FlowBranchingType.EXCEPTION) ||
1678                                     (new_breaks == FlowReturns.UNREACHABLE) ||
1679                                     (new_breaks == FlowReturns.EXCEPTION))
1680                                         Breaks = new_breaks;
1681                                 else if (branching.Type == FlowBranchingType.SWITCH_SECTION)
1682                                         Breaks = new_returns;
1683                                 else if (branching.Type == FlowBranchingType.SWITCH){
1684                                         if (new_breaks == FlowReturns.ALWAYS)
1685                                                 Breaks = FlowReturns.ALWAYS;
1686                                 }
1687
1688                                 //
1689                                 // We've now either reached the point after the branching or we will
1690                                 // never get there since we always return or always throw an exception.
1691                                 //
1692                                 // If we can reach the point after the branching, mark all locals and
1693                                 // parameters as initialized which have been initialized in all branches
1694                                 // we need to look at (see above).
1695                                 //
1696
1697                                 if (((new_breaks != FlowReturns.ALWAYS) &&
1698                                      (new_breaks != FlowReturns.EXCEPTION) &&
1699                                      (new_breaks != FlowReturns.UNREACHABLE)) ||
1700                                     (children.Length == 1)) {
1701                                         if (new_locals != null)
1702                                                 locals.Or (new_locals);
1703
1704                                         if (new_params != null)
1705                                                 parameters.Or (new_params);
1706                                 }
1707
1708                                 Report.Debug (2, "MERGING CHILDREN DONE", branching.Type,
1709                                               new_params, new_locals, new_returns, new_breaks,
1710                                               branching.Infinite, branching.MayLeaveLoop, this);
1711
1712                                 if (branching.Type == FlowBranchingType.SWITCH_SECTION) {
1713                                         if ((new_breaks != FlowReturns.ALWAYS) &&
1714                                             (new_breaks != FlowReturns.EXCEPTION) &&
1715                                             (new_breaks != FlowReturns.UNREACHABLE))
1716                                                 Report.Error (163, branching.Location,
1717                                                               "Control cannot fall through from one " +
1718                                                               "case label to another");
1719                                 }
1720
1721                                 if (branching.Infinite && !branching.MayLeaveLoop) {
1722                                         Report.Debug (1, "INFINITE", new_returns, new_breaks,
1723                                                       Returns, Breaks, this);
1724
1725                                         // We're actually infinite.
1726                                         if (new_returns == FlowReturns.NEVER) {
1727                                                 Breaks = FlowReturns.UNREACHABLE;
1728                                                 return FlowReturns.UNREACHABLE;
1729                                         }
1730
1731                                         // If we're an infinite loop and do not break, the code after
1732                                         // the loop can never be reached.  However, if we may return
1733                                         // from the loop, then we do always return (or stay in the loop
1734                                         // forever).
1735                                         if ((new_returns == FlowReturns.SOMETIMES) ||
1736                                             (new_returns == FlowReturns.ALWAYS)) {
1737                                                 Returns = FlowReturns.ALWAYS;
1738                                                 return FlowReturns.ALWAYS;
1739                                         }
1740                                 }
1741
1742                                 if ((branching.Type == FlowBranchingType.LOOP_BLOCK) &&
1743                                     branching.MayLeaveLoop && (new_returns == FlowReturns.ALWAYS)) {
1744                                         Returns = FlowReturns.SOMETIMES;
1745                                         return FlowReturns.SOMETIMES;
1746                                 }
1747
1748                                 return new_returns;
1749                         }
1750
1751                         // <summary>
1752                         //   Tells control flow analysis that the current code position may be reached with
1753                         //   a forward jump from any of the origins listed in `origin_vectors' which is a
1754                         //   list of UsageVectors.
1755                         //
1756                         //   This is used when resolving forward gotos - in the following example, the
1757                         //   variable `a' is uninitialized in line 8 becase this line may be reached via
1758                         //   the goto in line 4:
1759                         //
1760                         //      1     int a;
1761                         //
1762                         //      3     if (something)
1763                         //      4        goto World;
1764                         //
1765                         //      6     a = 5;
1766                         //
1767                         //      7  World:
1768                         //      8     Console.WriteLine (a);
1769                         //
1770                         // </summary>
1771                         public void MergeJumpOrigins (ICollection origin_vectors)
1772                         {
1773                                 Report.Debug (1, "MERGING JUMP ORIGIN", this);
1774
1775                                 real_breaks = FlowReturns.NEVER;
1776                                 real_returns = FlowReturns.NEVER;
1777
1778                                 foreach (UsageVector vector in origin_vectors) {
1779                                         Report.Debug (1, "  MERGING JUMP ORIGIN", vector);
1780
1781                                         locals.And (vector.locals);
1782                                         if (parameters != null)
1783                                                 parameters.And (vector.parameters);
1784                                         Breaks = AndFlowReturns (Breaks, vector.Breaks);
1785                                         Returns = AndFlowReturns (Returns, vector.Returns);
1786                                 }
1787
1788                                 Report.Debug (1, "MERGING JUMP ORIGIN DONE", this);
1789                         }
1790
1791                         // <summary>
1792                         //   This is used at the beginning of a finally block if there were
1793                         //   any return statements in the try block or one of the catch blocks.
1794                         // </summary>
1795                         public void MergeFinallyOrigins (ICollection finally_vectors)
1796                         {
1797                                 Report.Debug (1, "MERGING FINALLY ORIGIN", this);
1798
1799                                 real_breaks = FlowReturns.NEVER;
1800
1801                                 foreach (UsageVector vector in finally_vectors) {
1802                                         Report.Debug (1, "  MERGING FINALLY ORIGIN", vector);
1803
1804                                         if (parameters != null)
1805                                                 parameters.And (vector.parameters);
1806                                         Breaks = AndFlowReturns (Breaks, vector.Breaks);
1807                                 }
1808
1809                                 is_finally = true;
1810
1811                                 Report.Debug (1, "MERGING FINALLY ORIGIN DONE", this);
1812                         }
1813
1814                         public void CheckOutParameters (FlowBranching branching)
1815                         {
1816                                 if (parameters != null)
1817                                         branching.CheckOutParameters (parameters, branching.Location);
1818                         }
1819
1820                         // <summary>
1821                         //   Performs an `or' operation on the locals and the parameters.
1822                         // </summary>
1823                         public void Or (UsageVector new_vector)
1824                         {
1825                                 locals.Or (new_vector.locals);
1826                                 if (parameters != null)
1827                                         parameters.Or (new_vector.parameters);
1828                         }
1829
1830                         // <summary>
1831                         //   Performs an `and' operation on the locals.
1832                         // </summary>
1833                         public void AndLocals (UsageVector new_vector)
1834                         {
1835                                 locals.And (new_vector.locals);
1836                         }
1837
1838                         // <summary>
1839                         //   Returns a deep copy of the parameters.
1840                         // </summary>
1841                         public MyBitVector Parameters {
1842                                 get {
1843                                         if (parameters != null)
1844                                                 return parameters.Clone ();
1845                                         else
1846                                                 return null;
1847                                 }
1848                         }
1849
1850                         // <summary>
1851                         //   Returns a deep copy of the locals.
1852                         // </summary>
1853                         public MyBitVector Locals {
1854                                 get {
1855                                         return locals.Clone ();
1856                                 }
1857                         }
1858
1859                         //
1860                         // Debugging stuff.
1861                         //
1862
1863                         public override string ToString ()
1864                         {
1865                                 StringBuilder sb = new StringBuilder ();
1866
1867                                 sb.Append ("Vector (");
1868                                 sb.Append (id);
1869                                 sb.Append (",");
1870                                 sb.Append (Returns);
1871                                 sb.Append (",");
1872                                 sb.Append (Breaks);
1873                                 if (parameters != null) {
1874                                         sb.Append (" - ");
1875                                         sb.Append (parameters);
1876                                 }
1877                                 sb.Append (" - ");
1878                                 sb.Append (locals);
1879                                 sb.Append (")");
1880
1881                                 return sb.ToString ();
1882                         }
1883                 }
1884
1885                 FlowBranching (FlowBranchingType type, Location loc)
1886                 {
1887                         this.Block = null;
1888                         this.Location = loc;
1889                         this.Type = type;
1890                         id = ++next_id;
1891                 }
1892
1893                 // <summary>
1894                 //   Creates a new flow branching for `block'.
1895                 //   This is used from Block.Resolve to create the top-level branching of
1896                 //   the block.
1897                 // </summary>
1898                 public FlowBranching (Block block, InternalParameters ip, Location loc)
1899                         : this (FlowBranchingType.BLOCK, loc)
1900                 {
1901                         Block = block;
1902                         Parent = null;
1903
1904                         int count = (ip != null) ? ip.Count : 0;
1905
1906                         param_info = ip;
1907                         param_map = new int [count];
1908                         struct_params = new MyStructInfo [count];
1909                         num_params = 0;
1910
1911                         for (int i = 0; i < count; i++) {
1912                                 Parameter.Modifier mod = param_info.ParameterModifier (i);
1913
1914                                 if ((mod & Parameter.Modifier.OUT) == 0)
1915                                         continue;
1916
1917                                 param_map [i] = ++num_params;
1918
1919                                 Type param_type = param_info.ParameterType (i);
1920
1921                                 struct_params [i] = MyStructInfo.GetStructInfo (param_type);
1922                                 if (struct_params [i] != null)
1923                                         num_params += struct_params [i].Count;
1924                         }
1925
1926                         AddSibling (new UsageVector (null, num_params, block.CountVariables));
1927                 }
1928
1929                 // <summary>
1930                 //   Creates a new flow branching which is contained in `parent'.
1931                 //   You should only pass non-null for the `block' argument if this block
1932                 //   introduces any new variables - in this case, we need to create a new
1933                 //   usage vector with a different size than our parent's one.
1934                 // </summary>
1935                 public FlowBranching (FlowBranching parent, FlowBranchingType type,
1936                                       Block block, Location loc)
1937                         : this (type, loc)
1938                 {
1939                         Parent = parent;
1940                         Block = block;
1941
1942                         if (parent != null) {
1943                                 param_info = parent.param_info;
1944                                 param_map = parent.param_map;
1945                                 struct_params = parent.struct_params;
1946                                 num_params = parent.num_params;
1947                         }
1948
1949                         UsageVector vector;
1950                         if (Block != null)
1951                                 vector = new UsageVector (parent.CurrentUsageVector, num_params,
1952                                                           Block.CountVariables);
1953                         else
1954                                 vector = new UsageVector (Parent.CurrentUsageVector);
1955
1956                         AddSibling (vector);
1957
1958                         switch (Type) {
1959                         case FlowBranchingType.EXCEPTION:
1960                                 finally_vectors = new ArrayList ();
1961                                 break;
1962
1963                         default:
1964                                 break;
1965                         }
1966                 }
1967
1968                 void AddSibling (UsageVector uv)
1969                 {
1970                         if (Siblings != null) {
1971                                 UsageVector[] ns = new UsageVector [Siblings.Length + 1];
1972                                 for (int i = 0; i < Siblings.Length; ++i)
1973                                         ns [i] = Siblings [i];
1974                                 Siblings = ns;
1975                         } else {
1976                                 Siblings = new UsageVector [1];
1977                         }
1978                         Siblings [Siblings.Length - 1] = uv;
1979                 }
1980
1981                 // <summary>
1982                 //   Returns the branching's current usage vector.
1983                 // </summary>
1984                 public UsageVector CurrentUsageVector
1985                 {
1986                         get {
1987                                 return Siblings [Siblings.Length - 1];
1988                         }
1989                 }
1990
1991                 // <summary>
1992                 //   Creates a sibling of the current usage vector.
1993                 // </summary>
1994                 public void CreateSibling ()
1995                 {
1996                         AddSibling (new UsageVector (Parent.CurrentUsageVector));
1997
1998                         Report.Debug (1, "CREATED SIBLING", CurrentUsageVector);
1999                 }
2000
2001                 // <summary>
2002                 //   Creates a sibling for a `finally' block.
2003                 // </summary>
2004                 public void CreateSiblingForFinally ()
2005                 {
2006                         if (Type != FlowBranchingType.EXCEPTION)
2007                                 throw new NotSupportedException ();
2008
2009                         CreateSibling ();
2010
2011                         CurrentUsageVector.MergeFinallyOrigins (finally_vectors);
2012                 }
2013
2014                 // <summary>
2015                 //   Check whether all `out' parameters have been assigned.
2016                 // </summary>
2017                 public void CheckOutParameters (MyBitVector parameters, Location loc)
2018                 {
2019                         if (InTryBlock ())
2020                                 return;
2021
2022                         for (int i = 0; i < param_map.Length; i++) {
2023                                 int index = param_map [i];
2024
2025                                 if (index == 0)
2026                                         continue;
2027
2028                                 if (parameters [index - 1])
2029                                         continue;
2030
2031                                 // If it's a struct, we must ensure that all its fields have
2032                                 // been assigned.  If the struct has any non-public fields, this
2033                                 // can only be done by assigning the whole struct.
2034
2035                                 MyStructInfo struct_info = struct_params [i];
2036                                 if ((struct_info == null) || struct_info.HasNonPublicFields) {
2037                                         Report.Error (
2038                                                 177, loc, "The out parameter `" +
2039                                                 param_info.ParameterName (i) + "' must be " +
2040                                                 "assigned before control leave the current method.");
2041                                         param_map [i] = 0;
2042                                         continue;
2043                                 }
2044
2045
2046                                 for (int j = 0; j < struct_info.Count; j++) {
2047                                         if (!parameters [index + j]) {
2048                                                 Report.Error (
2049                                                         177, loc, "The out parameter `" +
2050                                                         param_info.ParameterName (i) + "' must be " +
2051                                                         "assigned before control leaves the current method.");
2052                                                 param_map [i] = 0;
2053                                                 break;
2054                                         }
2055                                 }
2056                         }
2057                 }
2058
2059                 // <summary>
2060                 //   Merge a child branching.
2061                 // </summary>
2062                 public FlowReturns MergeChild (FlowBranching child)
2063                 {
2064                         FlowReturns returns = CurrentUsageVector.MergeChildren (child, child.Siblings);
2065
2066                         if ((child.Type != FlowBranchingType.LOOP_BLOCK) &&
2067                             (child.Type != FlowBranchingType.SWITCH_SECTION))
2068                                 MayLeaveLoop |= child.MayLeaveLoop;
2069                         else
2070                                 MayLeaveLoop = false;
2071
2072                         return returns;
2073                 }
2074  
2075                 // <summary>
2076                 //   Does the toplevel merging.
2077                 // </summary>
2078                 public FlowReturns MergeTopBlock ()
2079                 {
2080                         if ((Type != FlowBranchingType.BLOCK) || (Block == null))
2081                                 throw new NotSupportedException ();
2082
2083                         UsageVector vector = new UsageVector (null, num_params, Block.CountVariables);
2084
2085                         Report.Debug (1, "MERGING TOP BLOCK", Location, vector);
2086
2087                         vector.MergeChildren (this, Siblings);
2088
2089                         if (Siblings.Length == 1)
2090                                 Siblings [0] = vector;
2091                         else {
2092                                 Siblings = null;
2093                                 AddSibling (vector);
2094                         }
2095
2096                         Report.Debug (1, "MERGING TOP BLOCK DONE", Location, vector);
2097
2098                         if (vector.Breaks != FlowReturns.EXCEPTION) {
2099                                 if (!vector.AlwaysBreaks)
2100                                         CheckOutParameters (CurrentUsageVector.Parameters, Location);
2101                                 return vector.AlwaysBreaks ? FlowReturns.ALWAYS : vector.Returns;
2102                         } else
2103                                 return FlowReturns.EXCEPTION;
2104                 }
2105
2106                 public bool InTryBlock ()
2107                 {
2108                         if (finally_vectors != null)
2109                                 return true;
2110                         else if (Parent != null)
2111                                 return Parent.InTryBlock ();
2112                         else
2113                                 return false;
2114                 }
2115
2116                 public void AddFinallyVector (UsageVector vector)
2117                 {
2118                         if (finally_vectors != null) {
2119                                 finally_vectors.Add (vector.Clone ());
2120                                 return;
2121                         }
2122
2123                         if (Parent != null)
2124                                 Parent.AddFinallyVector (vector);
2125                         else
2126                                 throw new NotSupportedException ();
2127                 }
2128
2129                 public bool IsVariableAssigned (VariableInfo vi)
2130                 {
2131                         if (CurrentUsageVector.AlwaysBreaks)
2132                                 return true;
2133                         else
2134                                 return CurrentUsageVector [vi, 0];
2135                 }
2136
2137                 public bool IsVariableAssigned (VariableInfo vi, int field_idx)
2138                 {
2139                         if (CurrentUsageVector.AlwaysBreaks)
2140                                 return true;
2141                         else
2142                                 return CurrentUsageVector [vi, field_idx];
2143                 }
2144
2145                 public void SetVariableAssigned (VariableInfo vi)
2146                 {
2147                         if (CurrentUsageVector.AlwaysBreaks)
2148                                 return;
2149
2150                         CurrentUsageVector [vi, 0] = true;
2151                 }
2152
2153                 public void SetVariableAssigned (VariableInfo vi, int field_idx)
2154                 {
2155                         if (CurrentUsageVector.AlwaysBreaks)
2156                                 return;
2157
2158                         CurrentUsageVector [vi, field_idx] = true;
2159                 }
2160
2161                 public bool IsParameterAssigned (int number)
2162                 {
2163                         int index = param_map [number];
2164
2165                         if (index == 0)
2166                                 return true;
2167
2168                         if (CurrentUsageVector [index])
2169                                 return true;
2170
2171                         // Parameter is not assigned, so check whether it's a struct.
2172                         // If it's either not a struct or a struct which non-public
2173                         // fields, return false.
2174                         MyStructInfo struct_info = struct_params [number];
2175                         if ((struct_info == null) || struct_info.HasNonPublicFields)
2176                                 return false;
2177
2178                         // Ok, so each field must be assigned.
2179                         for (int i = 0; i < struct_info.Count; i++)
2180                                 if (!CurrentUsageVector [index + i])
2181                                         return false;
2182
2183                         return true;
2184                 }
2185
2186                 public bool IsParameterAssigned (int number, string field_name)
2187                 {
2188                         int index = param_map [number];
2189
2190                         if (index == 0)
2191                                 return true;
2192
2193                         MyStructInfo info = (MyStructInfo) struct_params [number];
2194                         if (info == null)
2195                                 return true;
2196
2197                         int field_idx = info [field_name];
2198
2199                         return CurrentUsageVector [index + field_idx];
2200                 }
2201
2202                 public void SetParameterAssigned (int number)
2203                 {
2204                         if (param_map [number] == 0)
2205                                 return;
2206
2207                         if (!CurrentUsageVector.AlwaysBreaks)
2208                                 CurrentUsageVector [param_map [number]] = true;
2209                 }
2210
2211                 public void SetParameterAssigned (int number, string field_name)
2212                 {
2213                         int index = param_map [number];
2214
2215                         if (index == 0)
2216                                 return;
2217
2218                         MyStructInfo info = (MyStructInfo) struct_params [number];
2219                         if (info == null)
2220                                 return;
2221
2222                         int field_idx = info [field_name];
2223
2224                         if (!CurrentUsageVector.AlwaysBreaks)
2225                                 CurrentUsageVector [index + field_idx] = true;
2226                 }
2227
2228                 public bool IsReachable ()
2229                 {
2230                         bool reachable;
2231
2232                         switch (Type) {
2233                         case FlowBranchingType.SWITCH_SECTION:
2234                                 // The code following a switch block is reachable unless the switch
2235                                 // block always returns.
2236                                 reachable = !CurrentUsageVector.AlwaysReturns;
2237                                 break;
2238
2239                         case FlowBranchingType.LOOP_BLOCK:
2240                                 // The code following a loop is reachable unless the loop always
2241                                 // returns or it's an infinite loop without any `break's in it.
2242                                 reachable = !CurrentUsageVector.AlwaysReturns &&
2243                                         (CurrentUsageVector.Breaks != FlowReturns.UNREACHABLE);
2244                                 break;
2245
2246                         default:
2247                                 // The code following a block or exception is reachable unless the
2248                                 // block either always returns or always breaks.
2249                                 if (MayLeaveLoop)
2250                                         reachable = true;
2251                                 else
2252                                         reachable = !CurrentUsageVector.AlwaysBreaks &&
2253                                                 !CurrentUsageVector.AlwaysReturns;
2254                                 break;
2255                         }
2256
2257                         Report.Debug (1, "REACHABLE", this, Type, CurrentUsageVector.Returns,
2258                                       CurrentUsageVector.Breaks, CurrentUsageVector, MayLeaveLoop,
2259                                       reachable);
2260
2261                         return reachable;
2262                 }
2263
2264                 public override string ToString ()
2265                 {
2266                         StringBuilder sb = new StringBuilder ("FlowBranching (");
2267
2268                         sb.Append (id);
2269                         sb.Append (",");
2270                         sb.Append (Type);
2271                         if (Block != null) {
2272                                 sb.Append (" - ");
2273                                 sb.Append (Block.ID);
2274                                 sb.Append (" - ");
2275                                 sb.Append (Block.StartLocation);
2276                         }
2277                         sb.Append (" - ");
2278                         sb.Append (Siblings.Length);
2279                         sb.Append (" - ");
2280                         sb.Append (CurrentUsageVector);
2281                         sb.Append (")");
2282                         return sb.ToString ();
2283                 }
2284         }
2285
2286         public class MyStructInfo {
2287                 public readonly Type Type;
2288                 public readonly FieldInfo[] Fields;
2289                 public readonly FieldInfo[] NonPublicFields;
2290                 public readonly int Count;
2291                 public readonly int CountNonPublic;
2292                 public readonly bool HasNonPublicFields;
2293
2294                 private static Hashtable field_type_hash = new Hashtable ();
2295                 private Hashtable field_hash;
2296
2297                 // Private constructor.  To save memory usage, we only need to create one instance
2298                 // of this class per struct type.
2299                 private MyStructInfo (Type type)
2300                 {
2301                         this.Type = type;
2302
2303                         if (type is TypeBuilder) {
2304                                 TypeContainer tc = TypeManager.LookupTypeContainer (type);
2305
2306                                 Field [] fields = tc.Fields;
2307                                 if (fields != null) {
2308                                         foreach (Field field in fields) {
2309                                                 if ((field.ModFlags & Modifiers.STATIC) != 0)
2310                                                         continue;
2311                                                 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
2312                                                         ++Count;
2313                                                 else
2314                                                         ++CountNonPublic;
2315                                         }
2316                                 }
2317
2318                                 Fields = new FieldInfo [Count];
2319                                 NonPublicFields = new FieldInfo [CountNonPublic];
2320
2321                                 Count = CountNonPublic = 0;
2322                                 if (fields != null) {
2323                                         foreach (Field field in fields) {
2324                                                 if ((field.ModFlags & Modifiers.STATIC) != 0)
2325                                                         continue;
2326                                                 if ((field.ModFlags & Modifiers.PUBLIC) != 0)
2327                                                         Fields [Count++] = field.FieldBuilder;
2328                                                 else
2329                                                         NonPublicFields [CountNonPublic++] =
2330                                                                 field.FieldBuilder;
2331                                         }
2332                                 }
2333                                 
2334                         } else {
2335                                 Fields = type.GetFields (BindingFlags.Instance|BindingFlags.Public);
2336                                 Count = Fields.Length;
2337
2338                                 NonPublicFields = type.GetFields (BindingFlags.Instance|BindingFlags.NonPublic);
2339                                 CountNonPublic = NonPublicFields.Length;
2340                         }
2341
2342                         Count += NonPublicFields.Length;
2343
2344                         int number = 0;
2345                         field_hash = new Hashtable ();
2346                         foreach (FieldInfo field in Fields)
2347                                 field_hash.Add (field.Name, ++number);
2348
2349                         if (NonPublicFields.Length != 0)
2350                                 HasNonPublicFields = true;
2351
2352                         foreach (FieldInfo field in NonPublicFields)
2353                                 field_hash.Add (field.Name, ++number);
2354                 }
2355
2356                 public int this [string name] {
2357                         get {
2358                                 if (field_hash.Contains (name))
2359                                         return (int) field_hash [name];
2360                                 else
2361                                         return 0;
2362                         }
2363                 }
2364
2365                 public FieldInfo this [int index] {
2366                         get {
2367                                 if (index >= Fields.Length)
2368                                         return NonPublicFields [index - Fields.Length];
2369                                 else
2370                                         return Fields [index];
2371                         }
2372                 }                      
2373
2374                 public static MyStructInfo GetStructInfo (Type type)
2375                 {
2376                         if (!TypeManager.IsValueType (type) || TypeManager.IsEnumType (type))
2377                                 return null;
2378
2379                         if (!(type is TypeBuilder) && TypeManager.IsBuiltinType (type))
2380                                 return null;
2381
2382                         MyStructInfo info = (MyStructInfo) field_type_hash [type];
2383                         if (info != null)
2384                                 return info;
2385
2386                         info = new MyStructInfo (type);
2387                         field_type_hash.Add (type, info);
2388                         return info;
2389                 }
2390
2391                 public static MyStructInfo GetStructInfo (TypeContainer tc)
2392                 {
2393                         MyStructInfo info = (MyStructInfo) field_type_hash [tc.TypeBuilder];
2394                         if (info != null)
2395                                 return info;
2396
2397                         info = new MyStructInfo (tc.TypeBuilder);
2398                         field_type_hash.Add (tc.TypeBuilder, info);
2399                         return info;
2400                 }
2401         }
2402         
2403         public class VariableInfo : IVariable {
2404                 public Expression Type;
2405                 public LocalBuilder LocalBuilder;
2406                 public Type VariableType;
2407                 public readonly string Name;
2408                 public readonly Location Location;
2409                 public readonly int Block;
2410
2411                 public int Number;
2412                 
2413                 public bool Used;
2414                 public bool Assigned;
2415                 public bool ReadOnly;
2416                 
2417                 public VariableInfo (Expression type, string name, int block, Location l)
2418                 {
2419                         Type = type;
2420                         Name = name;
2421                         Block = block;
2422                         LocalBuilder = null;
2423                         Location = l;
2424                 }
2425
2426                 public VariableInfo (TypeContainer tc, int block, Location l)
2427                 {
2428                         VariableType = tc.TypeBuilder;
2429                         struct_info = MyStructInfo.GetStructInfo (tc);
2430                         Block = block;
2431                         LocalBuilder = null;
2432                         Location = l;
2433                 }
2434
2435                 MyStructInfo struct_info;
2436                 public MyStructInfo StructInfo {
2437                         get {
2438                                 return struct_info;
2439                         }
2440                 }
2441
2442                 public bool IsAssigned (EmitContext ec, Location loc)
2443                 {
2444                         if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this))
2445                                 return true;
2446
2447                         MyStructInfo struct_info = StructInfo;
2448                         if ((struct_info == null) || (struct_info.HasNonPublicFields && (Name != null))) {
2449                                 Report.Error (165, loc, "Use of unassigned local variable `" + Name + "'");
2450                                 ec.CurrentBranching.SetVariableAssigned (this);
2451                                 return false;
2452                         }
2453
2454                         int count = struct_info.Count;
2455
2456                         for (int i = 0; i < count; i++) {
2457                                 if (!ec.CurrentBranching.IsVariableAssigned (this, i+1)) {
2458                                         if (Name != null) {
2459                                                 Report.Error (165, loc,
2460                                                               "Use of unassigned local variable `" +
2461                                                               Name + "'");
2462                                                 ec.CurrentBranching.SetVariableAssigned (this);
2463                                                 return false;
2464                                         }
2465
2466                                         FieldInfo field = struct_info [i];
2467                                         Report.Error (171, loc,
2468                                                       "Field `" + TypeManager.CSharpName (VariableType) +
2469                                                       "." + field.Name + "' must be fully initialized " +
2470                                                       "before control leaves the constructor");
2471                                         return false;
2472                                 }
2473                         }
2474
2475                         return true;
2476                 }
2477
2478                 public bool IsFieldAssigned (EmitContext ec, string name, Location loc)
2479                 {
2480                         if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsVariableAssigned (this) ||
2481                             (struct_info == null))
2482                                 return true;
2483
2484                         int field_idx = StructInfo [name];
2485                         if (field_idx == 0)
2486                                 return true;
2487
2488                         if (!ec.CurrentBranching.IsVariableAssigned (this, field_idx)) {
2489                                 Report.Error (170, loc,
2490                                               "Use of possibly unassigned field `" + name + "'");
2491                                 ec.CurrentBranching.SetVariableAssigned (this, field_idx);
2492                                 return false;
2493                         }
2494
2495                         return true;
2496                 }
2497
2498                 public void SetAssigned (EmitContext ec)
2499                 {
2500                         if (ec.DoFlowAnalysis)
2501                                 ec.CurrentBranching.SetVariableAssigned (this);
2502                 }
2503
2504                 public void SetFieldAssigned (EmitContext ec, string name)
2505                 {
2506                         if (ec.DoFlowAnalysis && (struct_info != null))
2507                                 ec.CurrentBranching.SetVariableAssigned (this, StructInfo [name]);
2508                 }
2509
2510                 public bool Resolve (DeclSpace decl)
2511                 {
2512                         if (struct_info != null)
2513                                 return true;
2514
2515                         if (VariableType == null)
2516                                 VariableType = decl.ResolveType (Type, false, Location);
2517
2518                         if (VariableType == null)
2519                                 return false;
2520
2521                         struct_info = MyStructInfo.GetStructInfo (VariableType);
2522
2523                         return true;
2524                 }
2525
2526                 public void MakePinned ()
2527                 {
2528                         TypeManager.MakePinned (LocalBuilder);
2529                 }
2530
2531                 public override string ToString ()
2532                 {
2533                         return "VariableInfo (" + Number + "," + Type + "," + Location + ")";
2534                 }
2535         }
2536                 
2537         /// <summary>
2538         ///   Block represents a C# block.
2539         /// </summary>
2540         ///
2541         /// <remarks>
2542         ///   This class is used in a number of places: either to represent
2543         ///   explicit blocks that the programmer places or implicit blocks.
2544         ///
2545         ///   Implicit blocks are used as labels or to introduce variable
2546         ///   declarations.
2547         /// </remarks>
2548         public class Block : Statement {
2549                 public readonly Block     Parent;
2550                 public readonly bool      Implicit;
2551                 public readonly Location  StartLocation;
2552                 public Location           EndLocation = Location.Null;
2553
2554                 //
2555                 // The statements in this block
2556                 //
2557                 ArrayList statements;
2558
2559                 //
2560                 // An array of Blocks.  We keep track of children just
2561                 // to generate the local variable declarations.
2562                 //
2563                 // Statements and child statements are handled through the
2564                 // statements.
2565                 //
2566                 ArrayList children;
2567                 
2568                 //
2569                 // Labels.  (label, block) pairs.
2570                 //
2571                 Hashtable labels;
2572
2573                 //
2574                 // Keeps track of (name, type) pairs
2575                 //
2576                 Hashtable variables;
2577
2578                 //
2579                 // Keeps track of constants
2580                 Hashtable constants;
2581
2582                 //
2583                 // Maps variable names to ILGenerator.LocalBuilders
2584                 //
2585                 Hashtable local_builders;
2586
2587                 bool used = false;
2588
2589                 static int id;
2590
2591                 int this_id;
2592                 
2593                 public Block (Block parent)
2594                         : this (parent, false, Location.Null, Location.Null)
2595                 { }
2596
2597                 public Block (Block parent, bool implicit_block)
2598                         : this (parent, implicit_block, Location.Null, Location.Null)
2599                 { }
2600
2601                 public Block (Block parent, bool implicit_block, Parameters parameters)
2602                         : this (parent, implicit_block, parameters, Location.Null, Location.Null)
2603                 { }
2604
2605                 public Block (Block parent, Location start, Location end)
2606                         : this (parent, false, start, end)
2607                 { }
2608
2609                 public Block (Block parent, Parameters parameters, Location start, Location end)
2610                         : this (parent, false, parameters, start, end)
2611                 { }
2612
2613                 public Block (Block parent, bool implicit_block, Location start, Location end)
2614                         : this (parent, implicit_block, Parameters.EmptyReadOnlyParameters,
2615                                 start, end)
2616                 { }
2617
2618                 public Block (Block parent, bool implicit_block, Parameters parameters,
2619                               Location start, Location end)
2620                 {
2621                         if (parent != null)
2622                                 parent.AddChild (this);
2623                         
2624                         this.Parent = parent;
2625                         this.Implicit = implicit_block;
2626                         this.parameters = parameters;
2627                         this.StartLocation = start;
2628                         this.EndLocation = end;
2629                         this.loc = start;
2630                         this_id = id++;
2631                         statements = new ArrayList ();
2632                 }
2633
2634                 public int ID {
2635                         get {
2636                                 return this_id;
2637                         }
2638                 }
2639
2640                 void AddChild (Block b)
2641                 {
2642                         if (children == null)
2643                                 children = new ArrayList ();
2644                         
2645                         children.Add (b);
2646                 }
2647
2648                 public void SetEndLocation (Location loc)
2649                 {
2650                         EndLocation = loc;
2651                 }
2652
2653                 /// <summary>
2654                 ///   Adds a label to the current block. 
2655                 /// </summary>
2656                 ///
2657                 /// <returns>
2658                 ///   false if the name already exists in this block. true
2659                 ///   otherwise.
2660                 /// </returns>
2661                 ///
2662                 public bool AddLabel (string name, LabeledStatement target)
2663                 {
2664                         if (labels == null)
2665                                 labels = new Hashtable ();
2666                         if (labels.Contains (name))
2667                                 return false;
2668                         
2669                         labels.Add (name, target);
2670                         return true;
2671                 }
2672
2673                 public LabeledStatement LookupLabel (string name)
2674                 {
2675                         if (labels != null){
2676                                 if (labels.Contains (name))
2677                                         return ((LabeledStatement) labels [name]);
2678                         }
2679
2680                         if (Parent != null)
2681                                 return Parent.LookupLabel (name);
2682
2683                         return null;
2684                 }
2685
2686                 VariableInfo this_variable = null;
2687
2688                 // <summary>
2689                 //   Returns the "this" instance variable of this block.
2690                 //   See AddThisVariable() for more information.
2691                 // </summary>
2692                 public VariableInfo ThisVariable {
2693                         get {
2694                                 if (this_variable != null)
2695                                         return this_variable;
2696                                 else if (Parent != null)
2697                                         return Parent.ThisVariable;
2698                                 else
2699                                         return null;
2700                         }
2701                 }
2702
2703                 Hashtable child_variable_names;
2704
2705                 // <summary>
2706                 //   Marks a variable with name @name as being used in a child block.
2707                 //   If a variable name has been used in a child block, it's illegal to
2708                 //   declare a variable with the same name in the current block.
2709                 // </summary>
2710                 public void AddChildVariableName (string name)
2711                 {
2712                         if (child_variable_names == null)
2713                                 child_variable_names = new Hashtable ();
2714
2715                         if (!child_variable_names.Contains (name))
2716                                 child_variable_names.Add (name, true);
2717                 }
2718
2719                 // <summary>
2720                 //   Marks all variables from block @block and all its children as being
2721                 //   used in a child block.
2722                 // </summary>
2723                 public void AddChildVariableNames (Block block)
2724                 {
2725                         if (block.Variables != null) {
2726                                 foreach (string name in block.Variables.Keys)
2727                                         AddChildVariableName (name);
2728                         }
2729
2730                         if (block.children != null) {
2731                                 foreach (Block child in block.children)
2732                                         AddChildVariableNames (child);
2733                         }
2734                 }
2735
2736                 // <summary>
2737                 //   Checks whether a variable name has already been used in a child block.
2738                 // </summary>
2739                 public bool IsVariableNameUsedInChildBlock (string name)
2740                 {
2741                         if (child_variable_names == null)
2742                                 return false;
2743
2744                         return child_variable_names.Contains (name);
2745                 }
2746
2747                 // <summary>
2748                 //   This is used by non-static `struct' constructors which do not have an
2749                 //   initializer - in this case, the constructor must initialize all of the
2750                 //   struct's fields.  To do this, we add a "this" variable and use the flow
2751                 //   analysis code to ensure that it's been fully initialized before control
2752                 //   leaves the constructor.
2753                 // </summary>
2754                 public VariableInfo AddThisVariable (TypeContainer tc, Location l)
2755                 {
2756                         if (this_variable != null)
2757                                 return this_variable;
2758
2759                         this_variable = new VariableInfo (tc, ID, l);
2760
2761                         if (variables == null)
2762                                 variables = new Hashtable ();
2763                         variables.Add ("this", this_variable);
2764
2765                         return this_variable;
2766                 }
2767
2768                 public VariableInfo AddVariable (Expression type, string name, Parameters pars, Location l)
2769                 {
2770                         if (variables == null)
2771                                 variables = new Hashtable ();
2772
2773                         VariableInfo vi = GetVariableInfo (name);
2774                         if (vi != null) {
2775                                 if (vi.Block != ID)
2776                                         Report.Error (136, l, "A local variable named `" + name + "' " +
2777                                                       "cannot be declared in this scope since it would " +
2778                                                       "give a different meaning to `" + name + "', which " +
2779                                                       "is already used in a `parent or current' scope to " +
2780                                                       "denote something else");
2781                                 else
2782                                         Report.Error (128, l, "A local variable `" + name + "' is already " +
2783                                                       "defined in this scope");
2784                                 return null;
2785                         }
2786
2787                         if (IsVariableNameUsedInChildBlock (name)) {
2788                                 Report.Error (136, l, "A local variable named `" + name + "' " +
2789                                               "cannot be declared in this scope since it would " +
2790                                               "give a different meaning to `" + name + "', which " +
2791                                               "is already used in a `child' scope to denote something " +
2792                                               "else");
2793                                 return null;
2794                         }
2795
2796                         if (pars != null) {
2797                                 int idx = 0;
2798                                 Parameter p = pars.GetParameterByName (name, out idx);
2799                                 if (p != null) {
2800                                         Report.Error (136, l, "A local variable named `" + name + "' " +
2801                                                       "cannot be declared in this scope since it would " +
2802                                                       "give a different meaning to `" + name + "', which " +
2803                                                       "is already used in a `parent or current' scope to " +
2804                                                       "denote something else");
2805                                         return null;
2806                                 }
2807                         }
2808                         
2809                         vi = new VariableInfo (type, name, ID, l);
2810
2811                         variables.Add (name, vi);
2812
2813                         if (variables_initialized)
2814                                 throw new Exception ();
2815
2816                         // Console.WriteLine ("Adding {0} to {1}", name, ID);
2817                         return vi;
2818                 }
2819
2820                 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
2821                 {
2822                         if (AddVariable (type, name, pars, l) == null)
2823                                 return false;
2824                         
2825                         if (constants == null)
2826                                 constants = new Hashtable ();
2827
2828                         constants.Add (name, value);
2829                         return true;
2830                 }
2831
2832                 public Hashtable Variables {
2833                         get {
2834                                 return variables;
2835                         }
2836                 }
2837
2838                 public VariableInfo GetVariableInfo (string name)
2839                 {
2840                         if (variables != null) {
2841                                 object temp;
2842                                 temp = variables [name];
2843
2844                                 if (temp != null){
2845                                         return (VariableInfo) temp;
2846                                 }
2847                         }
2848
2849                         if (Parent != null)
2850                                 return Parent.GetVariableInfo (name);
2851
2852                         return null;
2853                 }
2854                 
2855                 public Expression GetVariableType (string name)
2856                 {
2857                         VariableInfo vi = GetVariableInfo (name);
2858
2859                         if (vi != null)
2860                                 return vi.Type;
2861
2862                         return null;
2863                 }
2864
2865                 public Expression GetConstantExpression (string name)
2866                 {
2867                         if (constants != null) {
2868                                 object temp;
2869                                 temp = constants [name];
2870                                 
2871                                 if (temp != null)
2872                                         return (Expression) temp;
2873                         }
2874                         
2875                         if (Parent != null)
2876                                 return Parent.GetConstantExpression (name);
2877
2878                         return null;
2879                 }
2880                 
2881                 /// <summary>
2882                 ///   True if the variable named @name is a constant
2883                 ///  </summary>
2884                 public bool IsConstant (string name)
2885                 {
2886                         Expression e = null;
2887                         
2888                         e = GetConstantExpression (name);
2889                         
2890                         return e != null;
2891                 }
2892                 
2893                 /// <summary>
2894                 ///   Use to fetch the statement associated with this label
2895                 /// </summary>
2896                 public Statement this [string name] {
2897                         get {
2898                                 return (Statement) labels [name];
2899                         }
2900                 }
2901
2902                 Parameters parameters = null;
2903                 public Parameters Parameters {
2904                         get {
2905                                 if (Parent != null)
2906                                         return Parent.Parameters;
2907
2908                                 return parameters;
2909                         }
2910                 }
2911
2912                 /// <returns>
2913                 ///   A list of labels that were not used within this block
2914                 /// </returns>
2915                 public string [] GetUnreferenced ()
2916                 {
2917                         // FIXME: Implement me
2918                         return null;
2919                 }
2920
2921                 public void AddStatement (Statement s)
2922                 {
2923                         statements.Add (s);
2924                         used = true;
2925                 }
2926
2927                 public bool Used {
2928                         get {
2929                                 return used;
2930                         }
2931                 }
2932
2933                 public void Use ()
2934                 {
2935                         used = true;
2936                 }
2937
2938                 bool variables_initialized = false;
2939                 int count_variables = 0, first_variable = 0;
2940
2941                 void UpdateVariableInfo (EmitContext ec)
2942                 {
2943                         DeclSpace ds = ec.DeclSpace;
2944
2945                         first_variable = 0;
2946
2947                         if (Parent != null)
2948                                 first_variable += Parent.CountVariables;
2949
2950                         count_variables = first_variable;
2951                         if (variables != null) {
2952                                 foreach (VariableInfo vi in variables.Values) {
2953                                         if (!vi.Resolve (ds)) {
2954                                                 vi.Number = -1;
2955                                                 continue;
2956                                         }
2957
2958                                         vi.Number = ++count_variables;
2959
2960                                         if (vi.StructInfo != null)
2961                                                 count_variables += vi.StructInfo.Count;
2962                                 }
2963                         }
2964
2965                         variables_initialized = true;
2966                 }
2967
2968                 //
2969                 // <returns>
2970                 //   The number of local variables in this block
2971                 // </returns>
2972                 public int CountVariables
2973                 {
2974                         get {
2975                                 if (!variables_initialized)
2976                                         throw new Exception ();
2977
2978                                 return count_variables;
2979                         }
2980                 }
2981
2982                 /// <summary>
2983                 ///   Emits the variable declarations and labels.
2984                 /// </summary>
2985                 /// <remarks>
2986                 ///   tc: is our typecontainer (to resolve type references)
2987                 ///   ig: is the code generator:
2988                 ///   toplevel: the toplevel block.  This is used for checking 
2989                 ///             that no two labels with the same name are used.
2990                 /// </remarks>
2991                 public void EmitMeta (EmitContext ec, Block toplevel)
2992                 {
2993                         DeclSpace ds = ec.DeclSpace;
2994                         ILGenerator ig = ec.ig;
2995
2996                         if (!variables_initialized)
2997                                 UpdateVariableInfo (ec);
2998
2999                         //
3000                         // Process this block variables
3001                         //
3002                         if (variables != null){
3003                                 local_builders = new Hashtable ();
3004                                 
3005                                 foreach (DictionaryEntry de in variables){
3006                                         string name = (string) de.Key;
3007                                         VariableInfo vi = (VariableInfo) de.Value;
3008
3009                                         if (vi.VariableType == null)
3010                                                 continue;
3011
3012                                         vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
3013
3014                                         if (CodeGen.SymbolWriter != null)
3015                                                 vi.LocalBuilder.SetLocalSymInfo (name);
3016
3017                                         if (constants == null)
3018                                                 continue;
3019
3020                                         Expression cv = (Expression) constants [name];
3021                                         if (cv == null)
3022                                                 continue;
3023
3024                                         Expression e = cv.Resolve (ec);
3025                                         if (e == null)
3026                                                 continue;
3027
3028                                         if (!(e is Constant)){
3029                                                 Report.Error (133, vi.Location,
3030                                                               "The expression being assigned to `" +
3031                                                               name + "' must be constant (" + e + ")");
3032                                                 continue;
3033                                         }
3034
3035                                         constants.Remove (name);
3036                                         constants.Add (name, e);
3037                                 }
3038                         }
3039
3040                         //
3041                         // Now, handle the children
3042                         //
3043                         if (children != null){
3044                                 foreach (Block b in children)
3045                                         b.EmitMeta (ec, toplevel);
3046                         }
3047                 }
3048
3049                 public void UsageWarning ()
3050                 {
3051                         string name;
3052                         
3053                         if (variables != null){
3054                                 foreach (DictionaryEntry de in variables){
3055                                         VariableInfo vi = (VariableInfo) de.Value;
3056                                         
3057                                         if (vi.Used)
3058                                                 continue;
3059                                         
3060                                         name = (string) de.Key;
3061                                                 
3062                                         if (vi.Assigned){
3063                                                 Report.Warning (
3064                                                         219, vi.Location, "The variable `" + name +
3065                                                         "' is assigned but its value is never used");
3066                                         } else {
3067                                                 Report.Warning (
3068                                                         168, vi.Location, "The variable `" +
3069                                                         name +
3070                                                         "' is declared but never used");
3071                                         } 
3072                                 }
3073                         }
3074
3075                         if (children != null)
3076                                 foreach (Block b in children)
3077                                         b.UsageWarning ();
3078                 }
3079
3080                 bool has_ret = false;
3081
3082                 public override bool Resolve (EmitContext ec)
3083                 {
3084                         Block prev_block = ec.CurrentBlock;
3085                         bool ok = true;
3086
3087                         ec.CurrentBlock = this;
3088                         ec.StartFlowBranching (this);
3089
3090                         Report.Debug (1, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
3091
3092                         if (!variables_initialized)
3093                                 UpdateVariableInfo (ec);
3094
3095                         ArrayList new_statements = new ArrayList ();
3096                         bool unreachable = false, warning_shown = false;
3097
3098                         foreach (Statement s in statements){
3099                                 if (unreachable && !(s is LabeledStatement)) {
3100                                         if (!warning_shown && !(s is EmptyStatement)) {
3101                                                 warning_shown = true;
3102                                                 Warning_DeadCodeFound (s.loc);
3103                                         }
3104
3105                                         continue;
3106                                 }
3107
3108                                 if (s.Resolve (ec) == false) {
3109                                         ok = false;
3110                                         continue;
3111                                 }
3112
3113                                 if (s is LabeledStatement)
3114                                         unreachable = false;
3115                                 else
3116                                         unreachable = ! ec.CurrentBranching.IsReachable ();
3117
3118                                 new_statements.Add (s);
3119                         }
3120
3121                         statements = new_statements;
3122
3123                         Report.Debug (1, "RESOLVE BLOCK DONE", StartLocation, ec.CurrentBranching);
3124
3125                         FlowReturns returns = ec.EndFlowBranching ();
3126                         ec.CurrentBlock = prev_block;
3127
3128                         // If we're a non-static `struct' constructor which doesn't have an
3129                         // initializer, then we must initialize all of the struct's fields.
3130                         if ((this_variable != null) && (returns != FlowReturns.EXCEPTION) &&
3131                             !this_variable.IsAssigned (ec, loc))
3132                                 ok = false;
3133
3134                         if ((labels != null) && (RootContext.WarningLevel >= 2)) {
3135                                 foreach (LabeledStatement label in labels.Values)
3136                                         if (!label.HasBeenReferenced)
3137                                                 Report.Warning (164, label.Location,
3138                                                                 "This label has not been referenced");
3139                         }
3140
3141                         if ((returns == FlowReturns.ALWAYS) ||
3142                             (returns == FlowReturns.EXCEPTION) ||
3143                             (returns == FlowReturns.UNREACHABLE))
3144                                 has_ret = true;
3145
3146                         return ok;
3147                 }
3148                 
3149                 protected override bool DoEmit (EmitContext ec)
3150                 {
3151                         Block prev_block = ec.CurrentBlock;
3152
3153                         ec.CurrentBlock = this;
3154
3155                         ec.Mark (StartLocation);
3156                         foreach (Statement s in statements)
3157                                 s.Emit (ec);
3158                         ec.Mark (EndLocation); 
3159                         
3160                         ec.CurrentBlock = prev_block;
3161                         return has_ret;
3162                 }
3163         }
3164
3165         public class SwitchLabel {
3166                 Expression label;
3167                 object converted;
3168                 public Location loc;
3169                 public Label ILLabel;
3170                 public Label ILLabelCode;
3171
3172                 //
3173                 // if expr == null, then it is the default case.
3174                 //
3175                 public SwitchLabel (Expression expr, Location l)
3176                 {
3177                         label = expr;
3178                         loc = l;
3179                 }
3180
3181                 public Expression Label {
3182                         get {
3183                                 return label;
3184                         }
3185                 }
3186
3187                 public object Converted {
3188                         get {
3189                                 return converted;
3190                         }
3191                 }
3192
3193                 //
3194                 // Resolves the expression, reduces it to a literal if possible
3195                 // and then converts it to the requested type.
3196                 //
3197                 public bool ResolveAndReduce (EmitContext ec, Type required_type)
3198                 {
3199                         ILLabel = ec.ig.DefineLabel ();
3200                         ILLabelCode = ec.ig.DefineLabel ();
3201
3202                         if (label == null)
3203                                 return true;
3204                         
3205                         Expression e = label.Resolve (ec);
3206
3207                         if (e == null)
3208                                 return false;
3209
3210                         if (!(e is Constant)){
3211                                 Report.Error (150, loc, "A constant value is expected, got: " + e);
3212                                 return false;
3213                         }
3214
3215                         if (e is StringConstant || e is NullLiteral){
3216                                 if (required_type == TypeManager.string_type){
3217                                         converted = e;
3218                                         ILLabel = ec.ig.DefineLabel ();
3219                                         return true;
3220                                 }
3221                         }
3222
3223                         converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
3224                         if (converted == null)
3225                                 return false;
3226
3227                         return true;
3228                 }
3229         }
3230
3231         public class SwitchSection {
3232                 // An array of SwitchLabels.
3233                 public readonly ArrayList Labels;
3234                 public readonly Block Block;
3235                 
3236                 public SwitchSection (ArrayList labels, Block block)
3237                 {
3238                         Labels = labels;
3239                         Block = block;
3240                 }
3241         }
3242         
3243         public class Switch : Statement {
3244                 public readonly ArrayList Sections;
3245                 public Expression Expr;
3246
3247                 /// <summary>
3248                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
3249                 /// </summary>
3250                 public Hashtable Elements;
3251
3252                 /// <summary>
3253                 ///   The governing switch type
3254                 /// </summary>
3255                 public Type SwitchType;
3256
3257                 //
3258                 // Computed
3259                 //
3260                 bool got_default;
3261                 Label default_target;
3262                 Expression new_expr;
3263
3264                 //
3265                 // The types allowed to be implicitly cast from
3266                 // on the governing type
3267                 //
3268                 static Type [] allowed_types;
3269                 
3270                 public Switch (Expression e, ArrayList sects, Location l)
3271                 {
3272                         Expr = e;
3273                         Sections = sects;
3274                         loc = l;
3275                 }
3276
3277                 public bool GotDefault {
3278                         get {
3279                                 return got_default;
3280                         }
3281                 }
3282
3283                 public Label DefaultTarget {
3284                         get {
3285                                 return default_target;
3286                         }
3287                 }
3288
3289                 //
3290                 // Determines the governing type for a switch.  The returned
3291                 // expression might be the expression from the switch, or an
3292                 // expression that includes any potential conversions to the
3293                 // integral types or to string.
3294                 //
3295                 Expression SwitchGoverningType (EmitContext ec, Type t)
3296                 {
3297                         if (t == TypeManager.int32_type ||
3298                             t == TypeManager.uint32_type ||
3299                             t == TypeManager.char_type ||
3300                             t == TypeManager.byte_type ||
3301                             t == TypeManager.sbyte_type ||
3302                             t == TypeManager.ushort_type ||
3303                             t == TypeManager.short_type ||
3304                             t == TypeManager.uint64_type ||
3305                             t == TypeManager.int64_type ||
3306                             t == TypeManager.string_type ||
3307                                 t == TypeManager.bool_type ||
3308                                 t.IsSubclassOf (TypeManager.enum_type))
3309                                 return Expr;
3310
3311                         if (allowed_types == null){
3312                                 allowed_types = new Type [] {
3313                                         TypeManager.sbyte_type,
3314                                         TypeManager.byte_type,
3315                                         TypeManager.short_type,
3316                                         TypeManager.ushort_type,
3317                                         TypeManager.int32_type,
3318                                         TypeManager.uint32_type,
3319                                         TypeManager.int64_type,
3320                                         TypeManager.uint64_type,
3321                                         TypeManager.char_type,
3322                                         TypeManager.bool_type,
3323                                         TypeManager.string_type
3324                                 };
3325                         }
3326
3327                         //
3328                         // Try to find a *user* defined implicit conversion.
3329                         //
3330                         // If there is no implicit conversion, or if there are multiple
3331                         // conversions, we have to report an error
3332                         //
3333                         Expression converted = null;
3334                         foreach (Type tt in allowed_types){
3335                                 Expression e;
3336                                 
3337                                 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
3338                                 if (e == null)
3339                                         continue;
3340
3341                                 if (converted != null){
3342                                         Report.Error (-12, loc, "More than one conversion to an integral " +
3343                                                       " type exists for type `" +
3344                                                       TypeManager.CSharpName (Expr.Type)+"'");
3345                                         return null;
3346                                 } else
3347                                         converted = e;
3348                         }
3349                         return converted;
3350                 }
3351
3352                 void error152 (string n)
3353                 {
3354                         Report.Error (
3355                                 152, "The label `" + n + ":' " +
3356                                 "is already present on this switch statement");
3357                 }
3358                 
3359                 //
3360                 // Performs the basic sanity checks on the switch statement
3361                 // (looks for duplicate keys and non-constant expressions).
3362                 //
3363                 // It also returns a hashtable with the keys that we will later
3364                 // use to compute the switch tables
3365                 //
3366                 bool CheckSwitch (EmitContext ec)
3367                 {
3368                         Type compare_type;
3369                         bool error = false;
3370                         Elements = new Hashtable ();
3371                                 
3372                         got_default = false;
3373
3374                         if (TypeManager.IsEnumType (SwitchType)){
3375                                 compare_type = TypeManager.EnumToUnderlying (SwitchType);
3376                         } else
3377                                 compare_type = SwitchType;
3378                         
3379                         foreach (SwitchSection ss in Sections){
3380                                 foreach (SwitchLabel sl in ss.Labels){
3381                                         if (!sl.ResolveAndReduce (ec, SwitchType)){
3382                                                 error = true;
3383                                                 continue;
3384                                         }
3385
3386                                         if (sl.Label == null){
3387                                                 if (got_default){
3388                                                         error152 ("default");
3389                                                         error = true;
3390                                                 }
3391                                                 got_default = true;
3392                                                 continue;
3393                                         }
3394                                         
3395                                         object key = sl.Converted;
3396
3397                                         if (key is Constant)
3398                                                 key = ((Constant) key).GetValue ();
3399
3400                                         if (key == null)
3401                                                 key = NullLiteral.Null;
3402                                         
3403                                         string lname = null;
3404                                         if (compare_type == TypeManager.uint64_type){
3405                                                 ulong v = (ulong) key;
3406
3407                                                 if (Elements.Contains (v))
3408                                                         lname = v.ToString ();
3409                                                 else
3410                                                         Elements.Add (v, sl);
3411                                         } else if (compare_type == TypeManager.int64_type){
3412                                                 long v = (long) key;
3413
3414                                                 if (Elements.Contains (v))
3415                                                         lname = v.ToString ();
3416                                                 else
3417                                                         Elements.Add (v, sl);
3418                                         } else if (compare_type == TypeManager.uint32_type){
3419                                                 uint v = (uint) key;
3420
3421                                                 if (Elements.Contains (v))
3422                                                         lname = v.ToString ();
3423                                                 else
3424                                                         Elements.Add (v, sl);
3425                                         } else if (compare_type == TypeManager.char_type){
3426                                                 char v = (char) key;
3427                                                 
3428                                                 if (Elements.Contains (v))
3429                                                         lname = v.ToString ();
3430                                                 else
3431                                                         Elements.Add (v, sl);
3432                                         } else if (compare_type == TypeManager.byte_type){
3433                                                 byte v = (byte) key;
3434                                                 
3435                                                 if (Elements.Contains (v))
3436                                                         lname = v.ToString ();
3437                                                 else
3438                                                         Elements.Add (v, sl);
3439                                         } else if (compare_type == TypeManager.sbyte_type){
3440                                                 sbyte v = (sbyte) key;
3441                                                 
3442                                                 if (Elements.Contains (v))
3443                                                         lname = v.ToString ();
3444                                                 else
3445                                                         Elements.Add (v, sl);
3446                                         } else if (compare_type == TypeManager.short_type){
3447                                                 short v = (short) key;
3448                                                 
3449                                                 if (Elements.Contains (v))
3450                                                         lname = v.ToString ();
3451                                                 else
3452                                                         Elements.Add (v, sl);
3453                                         } else if (compare_type == TypeManager.ushort_type){
3454                                                 ushort v = (ushort) key;
3455                                                 
3456                                                 if (Elements.Contains (v))
3457                                                         lname = v.ToString ();
3458                                                 else
3459                                                         Elements.Add (v, sl);
3460                                         } else if (compare_type == TypeManager.string_type){
3461                                                 if (key is NullLiteral){
3462                                                         if (Elements.Contains (NullLiteral.Null))
3463                                                                 lname = "null";
3464                                                         else
3465                                                                 Elements.Add (NullLiteral.Null, null);
3466                                                 } else {
3467                                                         string s = (string) key;
3468
3469                                                         if (Elements.Contains (s))
3470                                                                 lname = s;
3471                                                         else
3472                                                                 Elements.Add (s, sl);
3473                                                 }
3474                                         } else if (compare_type == TypeManager.int32_type) {
3475                                                 int v = (int) key;
3476
3477                                                 if (Elements.Contains (v))
3478                                                         lname = v.ToString ();
3479                                                 else
3480                                                         Elements.Add (v, sl);
3481                                         } else if (compare_type == TypeManager.bool_type) {
3482                                                 bool v = (bool) key;
3483
3484                                                 if (Elements.Contains (v))
3485                                                         lname = v.ToString ();
3486                                                 else
3487                                                         Elements.Add (v, sl);
3488                                         }
3489                                         else
3490                                         {
3491                                                 throw new Exception ("Unknown switch type!" +
3492                                                                      SwitchType + " " + compare_type);
3493                                         }
3494
3495                                         if (lname != null){
3496                                                 error152 ("case + " + lname);
3497                                                 error = true;
3498                                         }
3499                                 }
3500                         }
3501                         if (error)
3502                                 return false;
3503                         
3504                         return true;
3505                 }
3506
3507                 void EmitObjectInteger (ILGenerator ig, object k)
3508                 {
3509                         if (k is int)
3510                                 IntConstant.EmitInt (ig, (int) k);
3511                         else if (k is Constant) {
3512                                 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3513                         } 
3514                         else if (k is uint)
3515                                 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3516                         else if (k is long)
3517                         {
3518                                 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3519                                 {
3520                                         IntConstant.EmitInt (ig, (int) (long) k);
3521                                         ig.Emit (OpCodes.Conv_I8);
3522                                 }
3523                                 else
3524                                         LongConstant.EmitLong (ig, (long) k);
3525                         }
3526                         else if (k is ulong)
3527                         {
3528                                 if ((ulong) k < (1L<<32))
3529                                 {
3530                                         IntConstant.EmitInt (ig, (int) (long) k);
3531                                         ig.Emit (OpCodes.Conv_U8);
3532                                 }
3533                                 else
3534                                 {
3535                                         LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
3536                                 }
3537                         }
3538                         else if (k is char)
3539                                 IntConstant.EmitInt (ig, (int) ((char) k));
3540                         else if (k is sbyte)
3541                                 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3542                         else if (k is byte)
3543                                 IntConstant.EmitInt (ig, (int) ((byte) k));
3544                         else if (k is short)
3545                                 IntConstant.EmitInt (ig, (int) ((short) k));
3546                         else if (k is ushort)
3547                                 IntConstant.EmitInt (ig, (int) ((ushort) k));
3548                         else if (k is bool)
3549                                 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3550                         else
3551                                 throw new Exception ("Unhandled case");
3552                 }
3553                 
3554                 // structure used to hold blocks of keys while calculating table switch
3555                 class KeyBlock : IComparable
3556                 {
3557                         public KeyBlock (long _nFirst)
3558                         {
3559                                 nFirst = nLast = _nFirst;
3560                         }
3561                         public long nFirst;
3562                         public long nLast;
3563                         public ArrayList rgKeys = null;
3564                         public int Length
3565                         {
3566                                 get { return (int) (nLast - nFirst + 1); }
3567                         }
3568                         public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
3569                         {
3570                                 return kbLast.nLast - kbFirst.nFirst + 1;
3571                         }
3572                         public int CompareTo (object obj)
3573                         {
3574                                 KeyBlock kb = (KeyBlock) obj;
3575                                 int nLength = Length;
3576                                 int nLengthOther = kb.Length;
3577                                 if (nLengthOther == nLength)
3578                                         return (int) (kb.nFirst - nFirst);
3579                                 return nLength - nLengthOther;
3580                         }
3581                 }
3582
3583                 /// <summary>
3584                 /// This method emits code for a lookup-based switch statement (non-string)
3585                 /// Basically it groups the cases into blocks that are at least half full,
3586                 /// and then spits out individual lookup opcodes for each block.
3587                 /// It emits the longest blocks first, and short blocks are just
3588                 /// handled with direct compares.
3589                 /// </summary>
3590                 /// <param name="ec"></param>
3591                 /// <param name="val"></param>
3592                 /// <returns></returns>
3593                 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
3594                 {
3595                         int cElements = Elements.Count;
3596                         object [] rgKeys = new object [cElements];
3597                         Elements.Keys.CopyTo (rgKeys, 0);
3598                         Array.Sort (rgKeys);
3599
3600                         // initialize the block list with one element per key
3601                         ArrayList rgKeyBlocks = new ArrayList ();
3602                         foreach (object key in rgKeys)
3603                                 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
3604
3605                         KeyBlock kbCurr;
3606                         // iteratively merge the blocks while they are at least half full
3607                         // there's probably a really cool way to do this with a tree...
3608                         while (rgKeyBlocks.Count > 1)
3609                         {
3610                                 ArrayList rgKeyBlocksNew = new ArrayList ();
3611                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
3612                                 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
3613                                 {
3614                                         KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
3615                                         if ((kbCurr.Length + kb.Length) * 2 >=  KeyBlock.TotalLength (kbCurr, kb))
3616                                         {
3617                                                 // merge blocks
3618                                                 kbCurr.nLast = kb.nLast;
3619                                         }
3620                                         else
3621                                         {
3622                                                 // start a new block
3623                                                 rgKeyBlocksNew.Add (kbCurr);
3624                                                 kbCurr = kb;
3625                                         }
3626                                 }
3627                                 rgKeyBlocksNew.Add (kbCurr);
3628                                 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
3629                                         break;
3630                                 rgKeyBlocks = rgKeyBlocksNew;
3631                         }
3632
3633                         // initialize the key lists
3634                         foreach (KeyBlock kb in rgKeyBlocks)
3635                                 kb.rgKeys = new ArrayList ();
3636
3637                         // fill the key lists
3638                         int iBlockCurr = 0;
3639                         if (rgKeyBlocks.Count > 0) {
3640                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
3641                                 foreach (object key in rgKeys)
3642                                 {
3643                                         bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
3644                                         if (fNextBlock)
3645                                                 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
3646                                         kbCurr.rgKeys.Add (key);
3647                                 }
3648                         }
3649
3650                         // sort the blocks so we can tackle the largest ones first
3651                         rgKeyBlocks.Sort ();
3652
3653                         // okay now we can start...
3654                         ILGenerator ig = ec.ig;
3655                         Label lblEnd = ig.DefineLabel ();       // at the end ;-)
3656                         Label lblDefault = ig.DefineLabel ();
3657
3658                         Type typeKeys = null;
3659                         if (rgKeys.Length > 0)
3660                                 typeKeys = rgKeys [0].GetType ();       // used for conversions
3661
3662                         for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
3663                         {
3664                                 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
3665                                 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3666                                 if (kb.Length <= 2)
3667                                 {
3668                                         foreach (object key in kb.rgKeys)
3669                                         {
3670                                                 ig.Emit (OpCodes.Ldloc, val);
3671                                                 EmitObjectInteger (ig, key);
3672                                                 SwitchLabel sl = (SwitchLabel) Elements [key];
3673                                                 ig.Emit (OpCodes.Beq, sl.ILLabel);
3674                                         }
3675                                 }
3676                                 else
3677                                 {
3678                                         // TODO: if all the keys in the block are the same and there are
3679                                         //       no gaps/defaults then just use a range-check.
3680                                         if (SwitchType == TypeManager.int64_type ||
3681                                                 SwitchType == TypeManager.uint64_type)
3682                                         {
3683                                                 // TODO: optimize constant/I4 cases
3684
3685                                                 // check block range (could be > 2^31)
3686                                                 ig.Emit (OpCodes.Ldloc, val);
3687                                                 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3688                                                 ig.Emit (OpCodes.Blt, lblDefault);
3689                                                 ig.Emit (OpCodes.Ldloc, val);
3690                                                 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3691                                                 ig.Emit (OpCodes.Bgt, lblDefault);
3692
3693                                                 // normalize range
3694                                                 ig.Emit (OpCodes.Ldloc, val);
3695                                                 if (kb.nFirst != 0)
3696                                                 {
3697                                                         EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
3698                                                         ig.Emit (OpCodes.Sub);
3699                                                 }
3700                                                 ig.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
3701                                         }
3702                                         else
3703                                         {
3704                                                 // normalize range
3705                                                 ig.Emit (OpCodes.Ldloc, val);
3706                                                 int nFirst = (int) kb.nFirst;
3707                                                 if (nFirst > 0)
3708                                                 {
3709                                                         IntConstant.EmitInt (ig, nFirst);
3710                                                         ig.Emit (OpCodes.Sub);
3711                                                 }
3712                                                 else if (nFirst < 0)
3713                                                 {
3714                                                         IntConstant.EmitInt (ig, -nFirst);
3715                                                         ig.Emit (OpCodes.Add);
3716                                                 }
3717                                         }
3718
3719                                         // first, build the list of labels for the switch
3720                                         int iKey = 0;
3721                                         int cJumps = kb.Length;
3722                                         Label [] rgLabels = new Label [cJumps];
3723                                         for (int iJump = 0; iJump < cJumps; iJump++)
3724                                         {
3725                                                 object key = kb.rgKeys [iKey];
3726                                                 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
3727                                                 {
3728                                                         SwitchLabel sl = (SwitchLabel) Elements [key];
3729                                                         rgLabels [iJump] = sl.ILLabel;
3730                                                         iKey++;
3731                                                 }
3732                                                 else
3733                                                         rgLabels [iJump] = lblDefault;
3734                                         }
3735                                         // emit the switch opcode
3736                                         ig.Emit (OpCodes.Switch, rgLabels);
3737                                 }
3738
3739                                 // mark the default for this block
3740                                 if (iBlock != 0)
3741                                         ig.MarkLabel (lblDefault);
3742                         }
3743
3744                         // TODO: find the default case and emit it here,
3745                         //       to prevent having to do the following jump.
3746                         //       make sure to mark other labels in the default section
3747
3748                         // the last default just goes to the end
3749                         ig.Emit (OpCodes.Br, lblDefault);
3750
3751                         // now emit the code for the sections
3752                         bool fFoundDefault = false;
3753                         bool fAllReturn = true;
3754                         foreach (SwitchSection ss in Sections)
3755                         {
3756                                 foreach (SwitchLabel sl in ss.Labels)
3757                                 {
3758                                         ig.MarkLabel (sl.ILLabel);
3759                                         ig.MarkLabel (sl.ILLabelCode);
3760                                         if (sl.Label == null)
3761                                         {
3762                                                 ig.MarkLabel (lblDefault);
3763                                                 fFoundDefault = true;
3764                                         }
3765                                 }
3766                                 bool returns = ss.Block.Emit (ec);
3767                                 fAllReturn &= returns;
3768                                 //ig.Emit (OpCodes.Br, lblEnd);
3769                         }
3770                         
3771                         if (!fFoundDefault) {
3772                                 ig.MarkLabel (lblDefault);
3773                                 fAllReturn = false;
3774                         }
3775                         ig.MarkLabel (lblEnd);
3776
3777                         return fAllReturn;
3778                 }
3779                 //
3780                 // This simple emit switch works, but does not take advantage of the
3781                 // `switch' opcode. 
3782                 // TODO: remove non-string logic from here
3783                 // TODO: binary search strings?
3784                 //
3785                 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3786                 {
3787                         ILGenerator ig = ec.ig;
3788                         Label end_of_switch = ig.DefineLabel ();
3789                         Label next_test = ig.DefineLabel ();
3790                         Label null_target = ig.DefineLabel ();
3791                         bool default_found = false;
3792                         bool first_test = true;
3793                         bool pending_goto_end = false;
3794                         bool all_return = true;
3795                         bool is_string = false;
3796                         bool null_found;
3797                         
3798                         //
3799                         // Special processing for strings: we cant compare
3800                         // against null.
3801                         //
3802                         if (SwitchType == TypeManager.string_type){
3803                                 ig.Emit (OpCodes.Ldloc, val);
3804                                 is_string = true;
3805                                 
3806                                 if (Elements.Contains (NullLiteral.Null)){
3807                                         ig.Emit (OpCodes.Brfalse, null_target);
3808                                 } else
3809                                         ig.Emit (OpCodes.Brfalse, default_target);
3810
3811                                 ig.Emit (OpCodes.Ldloc, val);
3812                                 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
3813                                 ig.Emit (OpCodes.Stloc, val);
3814                         }
3815                         
3816                         foreach (SwitchSection ss in Sections){
3817                                 Label sec_begin = ig.DefineLabel ();
3818
3819                                 if (pending_goto_end)
3820                                         ig.Emit (OpCodes.Br, end_of_switch);
3821
3822                                 int label_count = ss.Labels.Count;
3823                                 null_found = false;
3824                                 foreach (SwitchLabel sl in ss.Labels){
3825                                         ig.MarkLabel (sl.ILLabel);
3826                                         
3827                                         if (!first_test){
3828                                                 ig.MarkLabel (next_test);
3829                                                 next_test = ig.DefineLabel ();
3830                                         }
3831                                         //
3832                                         // If we are the default target
3833                                         //
3834                                         if (sl.Label == null){
3835                                                 ig.MarkLabel (default_target);
3836                                                 default_found = true;
3837                                         } else {
3838                                                 object lit = sl.Converted;
3839
3840                                                 if (lit is NullLiteral){
3841                                                         null_found = true;
3842                                                         if (label_count == 1)
3843                                                                 ig.Emit (OpCodes.Br, next_test);
3844                                                         continue;
3845                                                                               
3846                                                 }
3847                                                 if (is_string){
3848                                                         StringConstant str = (StringConstant) lit;
3849
3850                                                         ig.Emit (OpCodes.Ldloc, val);
3851                                                         ig.Emit (OpCodes.Ldstr, str.Value);
3852                                                         if (label_count == 1)
3853                                                                 ig.Emit (OpCodes.Bne_Un, next_test);
3854                                                         else
3855                                                                 ig.Emit (OpCodes.Beq, sec_begin);
3856                                                 } else {
3857                                                         ig.Emit (OpCodes.Ldloc, val);
3858                                                         EmitObjectInteger (ig, lit);
3859                                                         ig.Emit (OpCodes.Ceq);
3860                                                         if (label_count == 1)
3861                                                                 ig.Emit (OpCodes.Brfalse, next_test);
3862                                                         else
3863                                                                 ig.Emit (OpCodes.Brtrue, sec_begin);
3864                                                 }
3865                                         }
3866                                 }
3867                                 if (label_count != 1)
3868                                         ig.Emit (OpCodes.Br, next_test);
3869                                 
3870                                 if (null_found)
3871                                         ig.MarkLabel (null_target);
3872                                 ig.MarkLabel (sec_begin);
3873                                 foreach (SwitchLabel sl in ss.Labels)
3874                                         ig.MarkLabel (sl.ILLabelCode);
3875
3876                                 bool returns = ss.Block.Emit (ec);
3877                                 if (returns)
3878                                         pending_goto_end = false;
3879                                 else {
3880                                         all_return = false;
3881                                         pending_goto_end = true;
3882                                 }
3883                                 first_test = false;
3884                         }
3885                         if (!default_found){
3886                                 ig.MarkLabel (default_target);
3887                                 all_return = false;
3888                         }
3889                         ig.MarkLabel (next_test);
3890                         ig.MarkLabel (end_of_switch);
3891                         
3892                         return all_return;
3893                 }
3894
3895                 public override bool Resolve (EmitContext ec)
3896                 {
3897                         Expr = Expr.Resolve (ec);
3898                         if (Expr == null)
3899                                 return false;
3900
3901                         new_expr = SwitchGoverningType (ec, Expr.Type);
3902                         if (new_expr == null){
3903                                 Report.Error (151, loc, "An integer type or string was expected for switch");
3904                                 return false;
3905                         }
3906
3907                         // Validate switch.
3908                         SwitchType = new_expr.Type;
3909
3910                         if (!CheckSwitch (ec))
3911                                 return false;
3912
3913                         Switch old_switch = ec.Switch;
3914                         ec.Switch = this;
3915                         ec.Switch.SwitchType = SwitchType;
3916
3917                         ec.StartFlowBranching (FlowBranchingType.SWITCH, loc);
3918
3919                         bool first = true;
3920                         foreach (SwitchSection ss in Sections){
3921                                 if (!first)
3922                                         ec.CurrentBranching.CreateSibling ();
3923                                 else
3924                                         first = false;
3925
3926                                 if (ss.Block.Resolve (ec) != true)
3927                                         return false;
3928                         }
3929
3930
3931                         if (!got_default)
3932                                 ec.CurrentBranching.CreateSibling ();
3933
3934                         ec.EndFlowBranching ();
3935                         ec.Switch = old_switch;
3936
3937                         return true;
3938                 }
3939                 
3940                 protected override bool DoEmit (EmitContext ec)
3941                 {
3942                         // Store variable for comparission purposes
3943                         LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
3944                         new_expr.Emit (ec);
3945                         ec.ig.Emit (OpCodes.Stloc, value);
3946
3947                         ILGenerator ig = ec.ig;
3948
3949                         default_target = ig.DefineLabel ();
3950
3951                         //
3952                         // Setup the codegen context
3953                         //
3954                         Label old_end = ec.LoopEnd;
3955                         Switch old_switch = ec.Switch;
3956                         
3957                         ec.LoopEnd = ig.DefineLabel ();
3958                         ec.Switch = this;
3959
3960                         // Emit Code.
3961                         bool all_return;
3962                         if (SwitchType == TypeManager.string_type)
3963                                 all_return = SimpleSwitchEmit (ec, value);
3964                         else
3965                                 all_return = TableSwitchEmit (ec, value);
3966
3967                         // Restore context state. 
3968                         ig.MarkLabel (ec.LoopEnd);
3969
3970                         //
3971                         // Restore the previous context
3972                         //
3973                         ec.LoopEnd = old_end;
3974                         ec.Switch = old_switch;
3975                         
3976                         return all_return;
3977                 }
3978         }
3979
3980         public class Lock : Statement {
3981                 Expression expr;
3982                 Statement Statement;
3983                         
3984                 public Lock (Expression expr, Statement stmt, Location l)
3985                 {
3986                         this.expr = expr;
3987                         Statement = stmt;
3988                         loc = l;
3989                 }
3990
3991                 public override bool Resolve (EmitContext ec)
3992                 {
3993                         expr = expr.Resolve (ec);
3994                         return Statement.Resolve (ec) && expr != null;
3995                 }
3996                 
3997                 protected override bool DoEmit (EmitContext ec)
3998                 {
3999                         Type type = expr.Type;
4000                         bool val;
4001                         
4002                         if (type.IsValueType){
4003                                 Report.Error (185, loc, "lock statement requires the expression to be " +
4004                                               " a reference type (type is: `" +
4005                                               TypeManager.CSharpName (type) + "'");
4006                                 return false;
4007                         }
4008
4009                         ILGenerator ig = ec.ig;
4010                         LocalBuilder temp = ig.DeclareLocal (type);
4011                                 
4012                         expr.Emit (ec);
4013                         ig.Emit (OpCodes.Dup);
4014                         ig.Emit (OpCodes.Stloc, temp);
4015                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4016
4017                         // try
4018                         Label end = ig.BeginExceptionBlock ();
4019                         bool old_in_try = ec.InTry;
4020                         ec.InTry = true;
4021                         Label finish = ig.DefineLabel ();
4022                         val = Statement.Emit (ec);
4023                         ec.InTry = old_in_try;
4024                         // ig.Emit (OpCodes.Leave, finish);
4025
4026                         ig.MarkLabel (finish);
4027                         
4028                         // finally
4029                         ig.BeginFinallyBlock ();
4030                         ig.Emit (OpCodes.Ldloc, temp);
4031                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4032                         ig.EndExceptionBlock ();
4033                         
4034                         return val;
4035                 }
4036         }
4037
4038         public class Unchecked : Statement {
4039                 public readonly Block Block;
4040                 
4041                 public Unchecked (Block b)
4042                 {
4043                         Block = b;
4044                 }
4045
4046                 public override bool Resolve (EmitContext ec)
4047                 {
4048                         return Block.Resolve (ec);
4049                 }
4050                 
4051                 protected override bool DoEmit (EmitContext ec)
4052                 {
4053                         bool previous_state = ec.CheckState;
4054                         bool previous_state_const = ec.ConstantCheckState;
4055                         bool val;
4056                         
4057                         ec.CheckState = false;
4058                         ec.ConstantCheckState = false;
4059                         val = Block.Emit (ec);
4060                         ec.CheckState = previous_state;
4061                         ec.ConstantCheckState = previous_state_const;
4062
4063                         return val;
4064                 }
4065         }
4066
4067         public class Checked : Statement {
4068                 public readonly Block Block;
4069                 
4070                 public Checked (Block b)
4071                 {
4072                         Block = b;
4073                 }
4074
4075                 public override bool Resolve (EmitContext ec)
4076                 {
4077                         bool previous_state = ec.CheckState;
4078                         bool previous_state_const = ec.ConstantCheckState;
4079                         
4080                         ec.CheckState = true;
4081                         ec.ConstantCheckState = true;
4082                         bool ret = Block.Resolve (ec);
4083                         ec.CheckState = previous_state;
4084                         ec.ConstantCheckState = previous_state_const;
4085
4086                         return ret;
4087                 }
4088
4089                 protected override bool DoEmit (EmitContext ec)
4090                 {
4091                         bool previous_state = ec.CheckState;
4092                         bool previous_state_const = ec.ConstantCheckState;
4093                         bool val;
4094                         
4095                         ec.CheckState = true;
4096                         ec.ConstantCheckState = true;
4097                         val = Block.Emit (ec);
4098                         ec.CheckState = previous_state;
4099                         ec.ConstantCheckState = previous_state_const;
4100
4101                         return val;
4102                 }
4103         }
4104
4105         public class Unsafe : Statement {
4106                 public readonly Block Block;
4107
4108                 public Unsafe (Block b)
4109                 {
4110                         Block = b;
4111                 }
4112
4113                 public override bool Resolve (EmitContext ec)
4114                 {
4115                         bool previous_state = ec.InUnsafe;
4116                         bool val;
4117                         
4118                         ec.InUnsafe = true;
4119                         val = Block.Resolve (ec);
4120                         ec.InUnsafe = previous_state;
4121
4122                         return val;
4123                 }
4124                 
4125                 protected override bool DoEmit (EmitContext ec)
4126                 {
4127                         bool previous_state = ec.InUnsafe;
4128                         bool val;
4129                         
4130                         ec.InUnsafe = true;
4131                         val = Block.Emit (ec);
4132                         ec.InUnsafe = previous_state;
4133
4134                         return val;
4135                 }
4136         }
4137
4138         // 
4139         // Fixed statement
4140         //
4141         public class Fixed : Statement {
4142                 Expression type;
4143                 ArrayList declarators;
4144                 Statement statement;
4145                 Type expr_type;
4146                 FixedData[] data;
4147
4148                 struct FixedData {
4149                         public bool is_object;
4150                         public VariableInfo vi;
4151                         public Expression expr;
4152                         public Expression converted;
4153                 }                       
4154
4155                 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4156                 {
4157                         this.type = type;
4158                         declarators = decls;
4159                         statement = stmt;
4160                         loc = l;
4161                 }
4162
4163                 public override bool Resolve (EmitContext ec)
4164                 {
4165                         expr_type = ec.DeclSpace.ResolveType (type, false, loc);
4166                         if (expr_type == null)
4167                                 return false;
4168
4169                         data = new FixedData [declarators.Count];
4170
4171                         if (!expr_type.IsPointer){
4172                                 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
4173                                 return false;
4174                         }
4175                         
4176                         int i = 0;
4177                         foreach (Pair p in declarators){
4178                                 VariableInfo vi = (VariableInfo) p.First;
4179                                 Expression e = (Expression) p.Second;
4180
4181                                 vi.Number = -1;
4182
4183                                 //
4184                                 // The rules for the possible declarators are pretty wise,
4185                                 // but the production on the grammar is more concise.
4186                                 //
4187                                 // So we have to enforce these rules here.
4188                                 //
4189                                 // We do not resolve before doing the case 1 test,
4190                                 // because the grammar is explicit in that the token &
4191                                 // is present, so we need to test for this particular case.
4192                                 //
4193
4194                                 //
4195                                 // Case 1: & object.
4196                                 //
4197                                 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4198                                         Expression child = ((Unary) e).Expr;
4199
4200                                         vi.MakePinned ();
4201                                         if (child is ParameterReference || child is LocalVariableReference){
4202                                                 Report.Error (
4203                                                         213, loc, 
4204                                                         "No need to use fixed statement for parameters or " +
4205                                                         "local variable declarations (address is already " +
4206                                                         "fixed)");
4207                                                 return false;
4208                                         }
4209                                         
4210                                         e = e.Resolve (ec);
4211                                         if (e == null)
4212                                                 return false;
4213
4214                                         child = ((Unary) e).Expr;
4215                                         
4216                                         if (!TypeManager.VerifyUnManaged (child.Type, loc))
4217                                                 return false;
4218
4219                                         data [i].is_object = true;
4220                                         data [i].expr = e;
4221                                         data [i].converted = null;
4222                                         data [i].vi = vi;
4223                                         i++;
4224
4225                                         continue;
4226                                 }
4227
4228                                 e = e.Resolve (ec);
4229                                 if (e == null)
4230                                         return false;
4231
4232                                 //
4233                                 // Case 2: Array
4234                                 //
4235                                 if (e.Type.IsArray){
4236                                         Type array_type = e.Type.GetElementType ();
4237                                         
4238                                         vi.MakePinned ();
4239                                         //
4240                                         // Provided that array_type is unmanaged,
4241                                         //
4242                                         if (!TypeManager.VerifyUnManaged (array_type, loc))
4243                                                 return false;
4244
4245                                         //
4246                                         // and T* is implicitly convertible to the
4247                                         // pointer type given in the fixed statement.
4248                                         //
4249                                         ArrayPtr array_ptr = new ArrayPtr (e, loc);
4250                                         
4251                                         Expression converted = Expression.ConvertImplicitRequired (
4252                                                 ec, array_ptr, vi.VariableType, loc);
4253                                         if (converted == null)
4254                                                 return false;
4255
4256                                         data [i].is_object = false;
4257                                         data [i].expr = e;
4258                                         data [i].converted = converted;
4259                                         data [i].vi = vi;
4260                                         i++;
4261
4262                                         continue;
4263                                 }
4264
4265                                 //
4266                                 // Case 3: string
4267                                 //
4268                                 if (e.Type == TypeManager.string_type){
4269                                         data [i].is_object = false;
4270                                         data [i].expr = e;
4271                                         data [i].converted = null;
4272                                         data [i].vi = vi;
4273                                         i++;
4274                                 }
4275                         }
4276
4277                         return statement.Resolve (ec);
4278                 }
4279                 
4280                 protected override bool DoEmit (EmitContext ec)
4281                 {
4282                         ILGenerator ig = ec.ig;
4283
4284                         bool is_ret = false;
4285                         LocalBuilder [] clear_list = new LocalBuilder [data.Length];
4286                         
4287                         for (int i = 0; i < data.Length; i++) {
4288                                 VariableInfo vi = data [i].vi;
4289
4290                                 //
4291                                 // Case 1: & object.
4292                                 //
4293                                 if (data [i].is_object) {
4294                                         //
4295                                         // Store pointer in pinned location
4296                                         //
4297                                         data [i].expr.Emit (ec);
4298                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4299                                         clear_list [i] = vi.LocalBuilder;
4300                                         continue;
4301                                 }
4302
4303                                 //
4304                                 // Case 2: Array
4305                                 //
4306                                 if (data [i].expr.Type.IsArray){
4307                                         //
4308                                         // Store pointer in pinned location
4309                                         //
4310                                         data [i].converted.Emit (ec);
4311                                         
4312                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4313                                         clear_list [i] = vi.LocalBuilder;
4314                                         continue;
4315                                 }
4316
4317                                 //
4318                                 // Case 3: string
4319                                 //
4320                                 if (data [i].expr.Type == TypeManager.string_type){
4321                                         LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
4322                                         TypeManager.MakePinned (pinned_string);
4323                                         clear_list [i] = pinned_string;
4324                                         
4325                                         data [i].expr.Emit (ec);
4326                                         ig.Emit (OpCodes.Stloc, pinned_string);
4327
4328                                         Expression sptr = new StringPtr (pinned_string, loc);
4329                                         Expression converted = Expression.ConvertImplicitRequired (
4330                                                 ec, sptr, vi.VariableType, loc);
4331                                         
4332                                         if (converted == null)
4333                                                 continue;
4334
4335                                         converted.Emit (ec);
4336                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4337                                 }
4338                         }
4339
4340                         is_ret = statement.Emit (ec);
4341
4342                         if (is_ret)
4343                                 return is_ret;
4344                         //
4345                         // Clear the pinned variable
4346                         //
4347                         for (int i = 0; i < data.Length; i++) {
4348                                 VariableInfo vi = data [i].vi;
4349
4350                                 if (data [i].is_object || data [i].expr.Type.IsArray) {
4351                                         ig.Emit (OpCodes.Ldc_I4_0);
4352                                         ig.Emit (OpCodes.Conv_U);
4353                                         ig.Emit (OpCodes.Stloc, clear_list [i]);
4354                                 } else if (data [i].expr.Type == TypeManager.string_type){
4355                                         ig.Emit (OpCodes.Ldnull);
4356                                         ig.Emit (OpCodes.Stloc, clear_list [i]);
4357                                 }
4358                         }
4359
4360                         return is_ret;
4361                 }
4362         }
4363         
4364         public class Catch {
4365                 public readonly string Name;
4366                 public readonly Block  Block;
4367                 public readonly Location Location;
4368
4369                 Expression type_expr;
4370                 Type type;
4371                 
4372                 public Catch (Expression type, string name, Block block, Location l)
4373                 {
4374                         type_expr = type;
4375                         Name = name;
4376                         Block = block;
4377                         Location = l;
4378                 }
4379
4380                 public Type CatchType {
4381                         get {
4382                                 return type;
4383                         }
4384                 }
4385
4386                 public bool IsGeneral {
4387                         get {
4388                                 return type_expr == null;
4389                         }
4390                 }
4391
4392                 public bool Resolve (EmitContext ec)
4393                 {
4394                         if (type_expr != null) {
4395                                 type = ec.DeclSpace.ResolveType (type_expr, false, Location);
4396                                 if (type == null)
4397                                         return false;
4398
4399                                 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
4400                                         Report.Error (155, Location,
4401                                                       "The type caught or thrown must be derived " +
4402                                                       "from System.Exception");
4403                                         return false;
4404                                 }
4405                         } else
4406                                 type = null;
4407
4408                         if (!Block.Resolve (ec))
4409                                 return false;
4410
4411                         return true;
4412                 }
4413         }
4414
4415         public class Try : Statement {
4416                 public readonly Block Fini, Block;
4417                 public readonly ArrayList Specific;
4418                 public readonly Catch General;
4419                 
4420                 //
4421                 // specific, general and fini might all be null.
4422                 //
4423                 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
4424                 {
4425                         if (specific == null && general == null){
4426                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
4427                         }
4428                         
4429                         this.Block = block;
4430                         this.Specific = specific;
4431                         this.General = general;
4432                         this.Fini = fini;
4433                         loc = l;
4434                 }
4435
4436                 public override bool Resolve (EmitContext ec)
4437                 {
4438                         bool ok = true;
4439                         
4440                         ec.StartFlowBranching (FlowBranchingType.EXCEPTION, Block.StartLocation);
4441
4442                         Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
4443
4444                         bool old_in_try = ec.InTry;
4445                         ec.InTry = true;
4446
4447                         if (!Block.Resolve (ec))
4448                                 ok = false;
4449
4450                         ec.InTry = old_in_try;
4451
4452                         FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
4453
4454                         Report.Debug (1, "START OF CATCH BLOCKS", vector);
4455
4456                         foreach (Catch c in Specific){
4457                                 ec.CurrentBranching.CreateSibling ();
4458                                 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
4459
4460                                 if (c.Name != null) {
4461                                         VariableInfo vi = c.Block.GetVariableInfo (c.Name);
4462                                         if (vi == null)
4463                                                 throw new Exception ();
4464
4465                                         vi.Number = -1;
4466                                 }
4467
4468                                 bool old_in_catch = ec.InCatch;
4469                                 ec.InCatch = true;
4470
4471                                 if (!c.Resolve (ec))
4472                                         ok = false;
4473
4474                                 ec.InCatch = old_in_catch;
4475
4476                                 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
4477
4478                                 if (!current.AlwaysReturns && !current.AlwaysBreaks)
4479                                         vector.AndLocals (current);
4480                                 else
4481                                         vector.Or (current);
4482                         }
4483
4484                         Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
4485
4486                         if (General != null){
4487                                 ec.CurrentBranching.CreateSibling ();
4488                                 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
4489
4490                                 bool old_in_catch = ec.InCatch;
4491                                 ec.InCatch = true;
4492
4493                                 if (!General.Resolve (ec))
4494                                         ok = false;
4495
4496                                 ec.InCatch = old_in_catch;
4497
4498                                 FlowBranching.UsageVector current = ec.CurrentBranching.CurrentUsageVector;
4499
4500                                 if (!current.AlwaysReturns && !current.AlwaysBreaks)
4501                                         vector.AndLocals (current);
4502                                 else    
4503                                         vector.Or (current);
4504                         }
4505
4506                         Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
4507
4508                         if (Fini != null) {
4509                                 ec.CurrentBranching.CreateSiblingForFinally ();
4510                                 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
4511
4512                                 bool old_in_finally = ec.InFinally;
4513                                 ec.InFinally = true;
4514
4515                                 if (!Fini.Resolve (ec))
4516                                         ok = false;
4517
4518                                 ec.InFinally = old_in_finally;
4519                         }
4520
4521                         FlowReturns returns = ec.EndFlowBranching ();
4522
4523                         FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
4524
4525                         Report.Debug (1, "END OF FINALLY", ec.CurrentBranching, returns, vector, f_vector);
4526
4527                         if ((returns == FlowReturns.SOMETIMES) || (returns == FlowReturns.ALWAYS)) {
4528                                 ec.CurrentBranching.CheckOutParameters (f_vector.Parameters, loc);
4529                         }
4530
4531                         ec.CurrentBranching.CurrentUsageVector.Or (vector);
4532
4533                         Report.Debug (1, "END OF TRY", ec.CurrentBranching);
4534
4535                         return ok;
4536                 }
4537                 
4538                 protected override bool DoEmit (EmitContext ec)
4539                 {
4540                         ILGenerator ig = ec.ig;
4541                         Label end;
4542                         Label finish = ig.DefineLabel ();;
4543                         bool returns;
4544
4545                         ec.TryCatchLevel++;
4546                         end = ig.BeginExceptionBlock ();
4547                         bool old_in_try = ec.InTry;
4548                         ec.InTry = true;
4549                         returns = Block.Emit (ec);
4550                         ec.InTry = old_in_try;
4551
4552                         //
4553                         // System.Reflection.Emit provides this automatically:
4554                         // ig.Emit (OpCodes.Leave, finish);
4555
4556                         bool old_in_catch = ec.InCatch;
4557                         ec.InCatch = true;
4558                         DeclSpace ds = ec.DeclSpace;
4559
4560                         foreach (Catch c in Specific){
4561                                 VariableInfo vi;
4562                                 
4563                                 ig.BeginCatchBlock (c.CatchType);
4564
4565                                 if (c.Name != null){
4566                                         vi = c.Block.GetVariableInfo (c.Name);
4567                                         if (vi == null)
4568                                                 throw new Exception ("Variable does not exist in this block");
4569
4570                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
4571                                 } else
4572                                         ig.Emit (OpCodes.Pop);
4573                                 
4574                                 if (!c.Block.Emit (ec))
4575                                         returns = false;
4576                         }
4577
4578                         if (General != null){
4579                                 ig.BeginCatchBlock (TypeManager.object_type);
4580                                 ig.Emit (OpCodes.Pop);
4581                                 if (!General.Block.Emit (ec))
4582                                         returns = false;
4583                         }
4584                         ec.InCatch = old_in_catch;
4585
4586                         ig.MarkLabel (finish);
4587                         if (Fini != null){
4588                                 ig.BeginFinallyBlock ();
4589                                 bool old_in_finally = ec.InFinally;
4590                                 ec.InFinally = true;
4591                                 Fini.Emit (ec);
4592                                 ec.InFinally = old_in_finally;
4593                         }
4594                         
4595                         ig.EndExceptionBlock ();
4596                         ec.TryCatchLevel--;
4597
4598                         if (!returns || ec.InTry || ec.InCatch)
4599                                 return returns;
4600
4601                         // Unfortunately, System.Reflection.Emit automatically emits a leave
4602                         // to the end of the finally block.  This is a problem if `returns'
4603                         // is true since we may jump to a point after the end of the method.
4604                         // As a workaround, emit an explicit ret here.
4605
4606                         if (ec.ReturnType != null)
4607                                 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4608                         ec.ig.Emit (OpCodes.Ret);
4609
4610                         return true;
4611                 }
4612         }
4613
4614         public class Using : Statement {
4615                 object expression_or_block;
4616                 Statement Statement;
4617                 ArrayList var_list;
4618                 Expression expr;
4619                 Type expr_type;
4620                 Expression conv;
4621                 Expression [] converted_vars;
4622                 ExpressionStatement [] assign;
4623                 
4624                 public Using (object expression_or_block, Statement stmt, Location l)
4625                 {
4626                         this.expression_or_block = expression_or_block;
4627                         Statement = stmt;
4628                         loc = l;
4629                 }
4630
4631                 //
4632                 // Resolves for the case of using using a local variable declaration.
4633                 //
4634                 bool ResolveLocalVariableDecls (EmitContext ec)
4635                 {
4636                         bool need_conv = false;
4637                         expr_type = ec.DeclSpace.ResolveType (expr, false, loc);
4638                         int i = 0;
4639
4640                         if (expr_type == null)
4641                                 return false;
4642
4643                         //
4644                         // The type must be an IDisposable or an implicit conversion
4645                         // must exist.
4646                         //
4647                         converted_vars = new Expression [var_list.Count];
4648                         assign = new ExpressionStatement [var_list.Count];
4649                         if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4650                                 foreach (DictionaryEntry e in var_list){
4651                                         Expression var = (Expression) e.Key;
4652
4653                                         var = var.ResolveLValue (ec, new EmptyExpression ());
4654                                         if (var == null)
4655                                                 return false;
4656                                         
4657                                         converted_vars [i] = Expression.ConvertImplicitRequired (
4658                                                 ec, var, TypeManager.idisposable_type, loc);
4659
4660                                         if (converted_vars [i] == null)
4661                                                 return false;
4662                                         i++;
4663                                 }
4664                                 need_conv = true;
4665                         }
4666
4667                         i = 0;
4668                         foreach (DictionaryEntry e in var_list){
4669                                 LocalVariableReference var = (LocalVariableReference) e.Key;
4670                                 Expression new_expr = (Expression) e.Value;
4671                                 Expression a;
4672
4673                                 a = new Assign (var, new_expr, loc);
4674                                 a = a.Resolve (ec);
4675                                 if (a == null)
4676                                         return false;
4677
4678                                 if (!need_conv)
4679                                         converted_vars [i] = var;
4680                                 assign [i] = (ExpressionStatement) a;
4681                                 i++;
4682                         }
4683
4684                         return true;
4685                 }
4686
4687                 bool ResolveExpression (EmitContext ec)
4688                 {
4689                         if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
4690                                 conv = Expression.ConvertImplicitRequired (
4691                                         ec, expr, TypeManager.idisposable_type, loc);
4692
4693                                 if (conv == null)
4694                                         return false;
4695                         }
4696
4697                         return true;
4698                 }
4699                 
4700                 //
4701                 // Emits the code for the case of using using a local variable declaration.
4702                 //
4703                 bool EmitLocalVariableDecls (EmitContext ec)
4704                 {
4705                         ILGenerator ig = ec.ig;
4706                         int i = 0;
4707
4708                         bool old_in_try = ec.InTry;
4709                         ec.InTry = true;
4710                         for (i = 0; i < assign.Length; i++) {
4711                                 assign [i].EmitStatement (ec);
4712                                 
4713                                 ig.BeginExceptionBlock ();
4714                         }
4715                         Statement.Emit (ec);
4716                         ec.InTry = old_in_try;
4717
4718                         bool old_in_finally = ec.InFinally;
4719                         ec.InFinally = true;
4720                         var_list.Reverse ();
4721                         foreach (DictionaryEntry e in var_list){
4722                                 LocalVariableReference var = (LocalVariableReference) e.Key;
4723                                 Label skip = ig.DefineLabel ();
4724                                 i--;
4725                                 
4726                                 ig.BeginFinallyBlock ();
4727                                 
4728                                 var.Emit (ec);
4729                                 ig.Emit (OpCodes.Brfalse, skip);
4730                                 converted_vars [i].Emit (ec);
4731                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4732                                 ig.MarkLabel (skip);
4733                                 ig.EndExceptionBlock ();
4734                         }
4735                         ec.InFinally = old_in_finally;
4736
4737                         return false;
4738                 }
4739
4740                 bool EmitExpression (EmitContext ec)
4741                 {
4742                         //
4743                         // Make a copy of the expression and operate on that.
4744                         //
4745                         ILGenerator ig = ec.ig;
4746                         LocalBuilder local_copy = ig.DeclareLocal (expr_type);
4747                         if (conv != null)
4748                                 conv.Emit (ec);
4749                         else
4750                                 expr.Emit (ec);
4751                         ig.Emit (OpCodes.Stloc, local_copy);
4752
4753                         bool old_in_try = ec.InTry;
4754                         ec.InTry = true;
4755                         ig.BeginExceptionBlock ();
4756                         Statement.Emit (ec);
4757                         ec.InTry = old_in_try;
4758                         
4759                         Label skip = ig.DefineLabel ();
4760                         bool old_in_finally = ec.InFinally;
4761                         ig.BeginFinallyBlock ();
4762                         ig.Emit (OpCodes.Ldloc, local_copy);
4763                         ig.Emit (OpCodes.Brfalse, skip);
4764                         ig.Emit (OpCodes.Ldloc, local_copy);
4765                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4766                         ig.MarkLabel (skip);
4767                         ec.InFinally = old_in_finally;
4768                         ig.EndExceptionBlock ();
4769
4770                         return false;
4771                 }
4772                 
4773                 public override bool Resolve (EmitContext ec)
4774                 {
4775                         if (expression_or_block is DictionaryEntry){
4776                                 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4777                                 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4778
4779                                 if (!ResolveLocalVariableDecls (ec))
4780                                         return false;
4781
4782                         } else if (expression_or_block is Expression){
4783                                 expr = (Expression) expression_or_block;
4784
4785                                 expr = expr.Resolve (ec);
4786                                 if (expr == null)
4787                                         return false;
4788
4789                                 expr_type = expr.Type;
4790
4791                                 if (!ResolveExpression (ec))
4792                                         return false;
4793                         }                       
4794
4795                         return Statement.Resolve (ec);
4796                 }
4797                 
4798                 protected override bool DoEmit (EmitContext ec)
4799                 {
4800                         if (expression_or_block is DictionaryEntry)
4801                                 return EmitLocalVariableDecls (ec);
4802                         else if (expression_or_block is Expression)
4803                                 return EmitExpression (ec);
4804
4805                         return false;
4806                 }
4807         }
4808
4809         /// <summary>
4810         ///   Implementation of the foreach C# statement
4811         /// </summary>
4812         public class Foreach : Statement {
4813                 Expression type;
4814                 LocalVariableReference variable;
4815                 Expression expr;
4816                 Statement statement;
4817                 ForeachHelperMethods hm;
4818                 Expression empty, conv;
4819                 Type array_type, element_type;
4820                 Type var_type;
4821                 
4822                 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4823                                 Statement stmt, Location l)
4824                 {
4825                         this.type = type;
4826                         this.variable = var;
4827                         this.expr = expr;
4828                         statement = stmt;
4829                         loc = l;
4830                 }
4831                 
4832                 public override bool Resolve (EmitContext ec)
4833                 {
4834                         expr = expr.Resolve (ec);
4835                         if (expr == null)
4836                                 return false;
4837
4838                         var_type = ec.DeclSpace.ResolveType (type, false, loc);
4839                         if (var_type == null)
4840                                 return false;
4841                         
4842                         //
4843                         // We need an instance variable.  Not sure this is the best
4844                         // way of doing this.
4845                         //
4846                         // FIXME: When we implement propertyaccess, will those turn
4847                         // out to return values in ExprClass?  I think they should.
4848                         //
4849                         if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4850                               expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4851                                 error1579 (expr.Type);
4852                                 return false;
4853                         }
4854
4855                         if (expr.Type.IsArray) {
4856                                 array_type = expr.Type;
4857                                 element_type = array_type.GetElementType ();
4858
4859                                 empty = new EmptyExpression (element_type);
4860                         } else {
4861                                 hm = ProbeCollectionType (ec, expr.Type);
4862                                 if (hm == null){
4863                                         error1579 (expr.Type);
4864                                         return false;
4865                                 }
4866
4867                                 array_type = expr.Type;
4868                                 element_type = hm.element_type;
4869
4870                                 empty = new EmptyExpression (hm.element_type);
4871                         }
4872
4873                         ec.StartFlowBranching (FlowBranchingType.LOOP_BLOCK, loc);
4874                         ec.CurrentBranching.CreateSibling ();
4875
4876                         //
4877                         //
4878                         // FIXME: maybe we can apply the same trick we do in the
4879                         // array handling to avoid creating empty and conv in some cases.
4880                         //
4881                         // Although it is not as important in this case, as the type
4882                         // will not likely be object (what the enumerator will return).
4883                         //
4884                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
4885                         if (conv == null)
4886                                 return false;
4887
4888                         if (variable.ResolveLValue (ec, empty) == null)
4889                                 return false;
4890
4891                         if (!statement.Resolve (ec))
4892                                 return false;
4893
4894                         FlowReturns returns = ec.EndFlowBranching ();
4895
4896                         return true;
4897                 }
4898                 
4899                 //
4900                 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4901                 //
4902                 static MethodInfo FetchMethodMoveNext (Type t)
4903                 {
4904                         MemberList move_next_list;
4905                         
4906                         move_next_list = TypeContainer.FindMembers (
4907                                 t, MemberTypes.Method,
4908                                 BindingFlags.Public | BindingFlags.Instance,
4909                                 Type.FilterName, "MoveNext");
4910                         if (move_next_list.Count == 0)
4911                                 return null;
4912
4913                         foreach (MemberInfo m in move_next_list){
4914                                 MethodInfo mi = (MethodInfo) m;
4915                                 Type [] args;
4916                                 
4917                                 args = TypeManager.GetArgumentTypes (mi);
4918                                 if (args != null && args.Length == 0){
4919                                         if (mi.ReturnType == TypeManager.bool_type)
4920                                                 return mi;
4921                                 }
4922                         }
4923                         return null;
4924                 }
4925                 
4926                 //
4927                 // Retrieves a `public T get_Current ()' method from the Type `t'
4928                 //
4929                 static MethodInfo FetchMethodGetCurrent (Type t)
4930                 {
4931                         MemberList move_next_list;
4932                         
4933                         move_next_list = TypeContainer.FindMembers (
4934                                 t, MemberTypes.Method,
4935                                 BindingFlags.Public | BindingFlags.Instance,
4936                                 Type.FilterName, "get_Current");
4937                         if (move_next_list.Count == 0)
4938                                 return null;
4939
4940                         foreach (MemberInfo m in move_next_list){
4941                                 MethodInfo mi = (MethodInfo) m;
4942                                 Type [] args;
4943
4944                                 args = TypeManager.GetArgumentTypes (mi);
4945                                 if (args != null && args.Length == 0)
4946                                         return mi;
4947                         }
4948                         return null;
4949                 }
4950
4951                 // 
4952                 // This struct records the helper methods used by the Foreach construct
4953                 //
4954                 class ForeachHelperMethods {
4955                         public EmitContext ec;
4956                         public MethodInfo get_enumerator;
4957                         public MethodInfo move_next;
4958                         public MethodInfo get_current;
4959                         public Type element_type;
4960                         public Type enumerator_type;
4961                         public bool is_disposable;
4962
4963                         public ForeachHelperMethods (EmitContext ec)
4964                         {
4965                                 this.ec = ec;
4966                                 this.element_type = TypeManager.object_type;
4967                                 this.enumerator_type = TypeManager.ienumerator_type;
4968                                 this.is_disposable = true;
4969                         }
4970                 }
4971                 
4972                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
4973                 {
4974                         if (m == null)
4975                                 return false;
4976                         
4977                         if (!(m is MethodInfo))
4978                                 return false;
4979                         
4980                         if (m.Name != "GetEnumerator")
4981                                 return false;
4982
4983                         MethodInfo mi = (MethodInfo) m;
4984                         Type [] args = TypeManager.GetArgumentTypes (mi);
4985                         if (args != null){
4986                                 if (args.Length != 0)
4987                                         return false;
4988                         }
4989                         ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
4990                         EmitContext ec = hm.ec;
4991
4992                         //
4993                         // Check whether GetEnumerator is accessible to us
4994                         //
4995                         MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
4996
4997                         Type declaring = mi.DeclaringType;
4998                         if (prot == MethodAttributes.Private){
4999                                 if (declaring != ec.ContainerType)
5000                                         return false;
5001                         } else if (prot == MethodAttributes.FamANDAssem){
5002                                 // If from a different assembly, false
5003                                 if (!(mi is MethodBuilder))
5004                                         return false;
5005                                 //
5006                                 // Are we being invoked from the same class, or from a derived method?
5007                                 //
5008                                 if (ec.ContainerType != declaring){
5009                                         if (!ec.ContainerType.IsSubclassOf (declaring))
5010                                                 return false;
5011                                 }
5012                         } else if (prot == MethodAttributes.FamORAssem){
5013                                 if (!(mi is MethodBuilder ||
5014                                       ec.ContainerType == declaring ||
5015                                       ec.ContainerType.IsSubclassOf (declaring)))
5016                                         return false;
5017                         } if (prot == MethodAttributes.Family){
5018                                 if (!(ec.ContainerType == declaring ||
5019                                       ec.ContainerType.IsSubclassOf (declaring)))
5020                                         return false;
5021                         }
5022
5023                         //
5024                         // Ok, we can access it, now make sure that we can do something
5025                         // with this `GetEnumerator'
5026                         //
5027
5028                         if (mi.ReturnType == TypeManager.ienumerator_type ||
5029                             TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType) ||
5030                             (!RootContext.StdLib && TypeManager.ImplementsInterface (mi.ReturnType, TypeManager.ienumerator_type))) {
5031                                 hm.move_next = TypeManager.bool_movenext_void;
5032                                 hm.get_current = TypeManager.object_getcurrent_void;
5033                                 return true;
5034                         }
5035
5036                         //
5037                         // Ok, so they dont return an IEnumerable, we will have to
5038                         // find if they support the GetEnumerator pattern.
5039                         //
5040                         Type return_type = mi.ReturnType;
5041
5042                         hm.move_next = FetchMethodMoveNext (return_type);
5043                         if (hm.move_next == null)
5044                                 return false;
5045                         hm.get_current = FetchMethodGetCurrent (return_type);
5046                         if (hm.get_current == null)
5047                                 return false;
5048
5049                         hm.element_type = hm.get_current.ReturnType;
5050                         hm.enumerator_type = return_type;
5051                         hm.is_disposable = TypeManager.ImplementsInterface (
5052                                 hm.enumerator_type, TypeManager.idisposable_type);
5053
5054                         return true;
5055                 }
5056                 
5057                 /// <summary>
5058                 ///   This filter is used to find the GetEnumerator method
5059                 ///   on which IEnumerator operates
5060                 /// </summary>
5061                 static MemberFilter FilterEnumerator;
5062                 
5063                 static Foreach ()
5064                 {
5065                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
5066                 }
5067
5068                 void error1579 (Type t)
5069                 {
5070                         Report.Error (1579, loc,
5071                                       "foreach statement cannot operate on variables of type `" +
5072                                       t.FullName + "' because that class does not provide a " +
5073                                       " GetEnumerator method or it is inaccessible");
5074                 }
5075
5076                 static bool TryType (Type t, ForeachHelperMethods hm)
5077                 {
5078                         MemberList mi;
5079                         
5080                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
5081                                                         BindingFlags.Public | BindingFlags.NonPublic |
5082                                                         BindingFlags.Instance,
5083                                                         FilterEnumerator, hm);
5084
5085                         if (mi.Count == 0)
5086                                 return false;
5087
5088                         hm.get_enumerator = (MethodInfo) mi [0];
5089                         return true;    
5090                 }
5091                 
5092                 //
5093                 // Looks for a usable GetEnumerator in the Type, and if found returns
5094                 // the three methods that participate: GetEnumerator, MoveNext and get_Current
5095                 //
5096                 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
5097                 {
5098                         ForeachHelperMethods hm = new ForeachHelperMethods (ec);
5099
5100                         if (TryType (t, hm))
5101                                 return hm;
5102
5103                         //
5104                         // Now try to find the method in the interfaces
5105                         //
5106                         while (t != null){
5107                                 Type [] ifaces = t.GetInterfaces ();
5108
5109                                 foreach (Type i in ifaces){
5110                                         if (TryType (i, hm))
5111                                                 return hm;
5112                                 }
5113                                 
5114                                 //
5115                                 // Since TypeBuilder.GetInterfaces only returns the interface
5116                                 // types for this type, we have to keep looping, but once
5117                                 // we hit a non-TypeBuilder (ie, a Type), then we know we are
5118                                 // done, because it returns all the types
5119                                 //
5120                                 if ((t is TypeBuilder))
5121                                         t = t.BaseType;
5122                                 else
5123                                         break;
5124                         } 
5125
5126                         return null;
5127                 }
5128
5129                 //
5130                 // FIXME: possible optimization.
5131                 // We might be able to avoid creating `empty' if the type is the sam
5132                 //
5133                 bool EmitCollectionForeach (EmitContext ec)
5134                 {
5135                         ILGenerator ig = ec.ig;
5136                         LocalBuilder enumerator, disposable;
5137
5138                         enumerator = ig.DeclareLocal (hm.enumerator_type);
5139                         if (hm.is_disposable)
5140                                 disposable = ig.DeclareLocal (TypeManager.idisposable_type);
5141                         else
5142                                 disposable = null;
5143                         
5144                         //
5145                         // Instantiate the enumerator
5146                         //
5147                         if (expr.Type.IsValueType){
5148                                 if (expr is IMemoryLocation){
5149                                         IMemoryLocation ml = (IMemoryLocation) expr;
5150
5151                                         ml.AddressOf (ec, AddressOp.Load);
5152                                 } else
5153                                         throw new Exception ("Expr " + expr + " of type " + expr.Type +
5154                                                              " does not implement IMemoryLocation");
5155                                 ig.Emit (OpCodes.Call, hm.get_enumerator);
5156                         } else {
5157                                 expr.Emit (ec);
5158                                 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
5159                         }
5160                         ig.Emit (OpCodes.Stloc, enumerator);
5161
5162                         //
5163                         // Protect the code in a try/finalize block, so that
5164                         // if the beast implement IDisposable, we get rid of it
5165                         //
5166                         Label l;
5167                         bool old_in_try = ec.InTry;
5168
5169                         if (hm.is_disposable) {
5170                                 l = ig.BeginExceptionBlock ();
5171                                 ec.InTry = true;
5172                         }
5173                         
5174                         Label end_try = ig.DefineLabel ();
5175                         
5176                         ig.MarkLabel (ec.LoopBegin);
5177                         ig.Emit (OpCodes.Ldloc, enumerator);
5178                         ig.Emit (OpCodes.Callvirt, hm.move_next);
5179                         ig.Emit (OpCodes.Brfalse, end_try);
5180                         ig.Emit (OpCodes.Ldloc, enumerator);
5181                         ig.Emit (OpCodes.Callvirt, hm.get_current);
5182                         variable.EmitAssign (ec, conv);
5183                         statement.Emit (ec);
5184                         ig.Emit (OpCodes.Br, ec.LoopBegin);
5185                         ig.MarkLabel (end_try);
5186                         ec.InTry = old_in_try;
5187                         
5188                         // The runtime provides this for us.
5189                         // ig.Emit (OpCodes.Leave, end);
5190
5191                         //
5192                         // Now the finally block
5193                         //
5194                         if (hm.is_disposable) {
5195                                 Label end_finally = ig.DefineLabel ();
5196                                 bool old_in_finally = ec.InFinally;
5197                                 ec.InFinally = true;
5198                                 ig.BeginFinallyBlock ();
5199                         
5200                                 ig.Emit (OpCodes.Ldloc, enumerator);
5201                                 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5202                                 ig.Emit (OpCodes.Stloc, disposable);
5203                                 ig.Emit (OpCodes.Ldloc, disposable);
5204                                 ig.Emit (OpCodes.Brfalse, end_finally);
5205                                 ig.Emit (OpCodes.Ldloc, disposable);
5206                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5207                                 ig.MarkLabel (end_finally);
5208                                 ec.InFinally = old_in_finally;
5209
5210                                 // The runtime generates this anyways.
5211                                 // ig.Emit (OpCodes.Endfinally);
5212
5213                                 ig.EndExceptionBlock ();
5214                         }
5215
5216                         ig.MarkLabel (ec.LoopEnd);
5217                         return false;
5218                 }
5219
5220                 //
5221                 // FIXME: possible optimization.
5222                 // We might be able to avoid creating `empty' if the type is the sam
5223                 //
5224                 bool EmitArrayForeach (EmitContext ec)
5225                 {
5226                         int rank = array_type.GetArrayRank ();
5227                         ILGenerator ig = ec.ig;
5228
5229                         LocalBuilder copy = ig.DeclareLocal (array_type);
5230                         
5231                         //
5232                         // Make our copy of the array
5233                         //
5234                         expr.Emit (ec);
5235                         ig.Emit (OpCodes.Stloc, copy);
5236                         
5237                         if (rank == 1){
5238                                 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
5239
5240                                 Label loop, test;
5241                                 
5242                                 ig.Emit (OpCodes.Ldc_I4_0);
5243                                 ig.Emit (OpCodes.Stloc, counter);
5244                                 test = ig.DefineLabel ();
5245                                 ig.Emit (OpCodes.Br, test);
5246
5247                                 loop = ig.DefineLabel ();
5248                                 ig.MarkLabel (loop);
5249
5250                                 ig.Emit (OpCodes.Ldloc, copy);
5251                                 ig.Emit (OpCodes.Ldloc, counter);
5252                                 ArrayAccess.EmitLoadOpcode (ig, var_type);
5253
5254                                 variable.EmitAssign (ec, conv);
5255
5256                                 statement.Emit (ec);
5257
5258                                 ig.MarkLabel (ec.LoopBegin);
5259                                 ig.Emit (OpCodes.Ldloc, counter);
5260                                 ig.Emit (OpCodes.Ldc_I4_1);
5261                                 ig.Emit (OpCodes.Add);
5262                                 ig.Emit (OpCodes.Stloc, counter);
5263
5264                                 ig.MarkLabel (test);
5265                                 ig.Emit (OpCodes.Ldloc, counter);
5266                                 ig.Emit (OpCodes.Ldloc, copy);
5267                                 ig.Emit (OpCodes.Ldlen);
5268                                 ig.Emit (OpCodes.Conv_I4);
5269                                 ig.Emit (OpCodes.Blt, loop);
5270                         } else {
5271                                 LocalBuilder [] dim_len   = new LocalBuilder [rank];
5272                                 LocalBuilder [] dim_count = new LocalBuilder [rank];
5273                                 Label [] loop = new Label [rank];
5274                                 Label [] test = new Label [rank];
5275                                 int dim;
5276                                 
5277                                 for (dim = 0; dim < rank; dim++){
5278                                         dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
5279                                         dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
5280                                         test [dim] = ig.DefineLabel ();
5281                                         loop [dim] = ig.DefineLabel ();
5282                                 }
5283                                         
5284                                 for (dim = 0; dim < rank; dim++){
5285                                         ig.Emit (OpCodes.Ldloc, copy);
5286                                         IntLiteral.EmitInt (ig, dim);
5287                                         ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
5288                                         ig.Emit (OpCodes.Stloc, dim_len [dim]);
5289                                 }
5290
5291                                 for (dim = 0; dim < rank; dim++){
5292                                         ig.Emit (OpCodes.Ldc_I4_0);
5293                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
5294                                         ig.Emit (OpCodes.Br, test [dim]);
5295                                         ig.MarkLabel (loop [dim]);
5296                                 }
5297
5298                                 ig.Emit (OpCodes.Ldloc, copy);
5299                                 for (dim = 0; dim < rank; dim++)
5300                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5301
5302                                 //
5303                                 // FIXME: Maybe we can cache the computation of `get'?
5304                                 //
5305                                 Type [] args = new Type [rank];
5306                                 MethodInfo get;
5307
5308                                 for (int i = 0; i < rank; i++)
5309                                         args [i] = TypeManager.int32_type;
5310
5311                                 ModuleBuilder mb = CodeGen.ModuleBuilder;
5312                                 get = mb.GetArrayMethod (
5313                                         array_type, "Get",
5314                                         CallingConventions.HasThis| CallingConventions.Standard,
5315                                         var_type, args);
5316                                 ig.Emit (OpCodes.Call, get);
5317                                 variable.EmitAssign (ec, conv);
5318                                 statement.Emit (ec);
5319                                 ig.MarkLabel (ec.LoopBegin);
5320                                 for (dim = rank - 1; dim >= 0; dim--){
5321                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5322                                         ig.Emit (OpCodes.Ldc_I4_1);
5323                                         ig.Emit (OpCodes.Add);
5324                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
5325
5326                                         ig.MarkLabel (test [dim]);
5327                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
5328                                         ig.Emit (OpCodes.Ldloc, dim_len [dim]);
5329                                         ig.Emit (OpCodes.Blt, loop [dim]);
5330                                 }
5331                         }
5332                         ig.MarkLabel (ec.LoopEnd);
5333                         
5334                         return false;
5335                 }
5336                 
5337                 protected override bool DoEmit (EmitContext ec)
5338                 {
5339                         bool ret_val;
5340                         
5341                         ILGenerator ig = ec.ig;
5342                         
5343                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5344                         bool old_inloop = ec.InLoop;
5345                         int old_loop_begin_try_catch_level = ec.LoopBeginTryCatchLevel;
5346                         ec.LoopBegin = ig.DefineLabel ();
5347                         ec.LoopEnd = ig.DefineLabel ();
5348                         ec.InLoop = true;
5349                         ec.LoopBeginTryCatchLevel = ec.TryCatchLevel;
5350                         
5351                         if (hm != null)
5352                                 ret_val = EmitCollectionForeach (ec);
5353                         else
5354                                 ret_val = EmitArrayForeach (ec);
5355                         
5356                         ec.LoopBegin = old_begin;
5357                         ec.LoopEnd = old_end;
5358                         ec.InLoop = old_inloop;
5359                         ec.LoopBeginTryCatchLevel = old_loop_begin_try_catch_level;
5360
5361                         return ret_val;
5362                 }
5363         }
5364 }