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