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