2002-07-22 Peter Williams <peterw@ximian.com>
[mono.git] / mcs / mbas / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001, 2002 Ximian, Inc.
8 //
9
10 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
13 using System.Diagnostics;
14
15 namespace Mono.CSharp {
16
17         using System.Collections;
18         
19         public abstract class Statement {
20                 public Location loc;
21                 
22                 ///
23                 /// Resolves the statement, true means that all sub-statements
24                 /// did resolve ok.
25                 //
26                 public virtual bool Resolve (EmitContext ec)
27                 {
28                         return true;
29                 }
30                 
31                 /// <summary>
32                 ///   Return value indicates whether all code paths emitted return.
33                 /// </summary>
34                 public abstract bool Emit (EmitContext ec);
35                 
36                 public static Expression ResolveBoolean (EmitContext ec, Expression e, Location loc)
37                 {
38                         e = e.Resolve (ec);
39                         if (e == null)
40                                 return null;
41                         
42                         if (e.Type != TypeManager.bool_type){
43                                 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
44                                                                 new Location (-1));
45                         }
46
47                         if (e == null){
48                                 Report.Error (
49                                         31, loc, "Can not convert the expression to a boolean");
50                         }
51
52                         if (CodeGen.SymbolWriter != null)
53                                 ec.Mark (loc);
54
55                         return e;
56                 }
57                 
58                 /// <remarks>
59                 ///    Encapsulates the emission of a boolean test and jumping to a
60                 ///    destination.
61                 ///
62                 ///    This will emit the bool expression in `bool_expr' and if
63                 ///    `target_is_for_true' is true, then the code will generate a 
64                 ///    brtrue to the target.   Otherwise a brfalse. 
65                 /// </remarks>
66                 public static void EmitBoolExpression (EmitContext ec, Expression bool_expr,
67                                                        Label target, bool target_is_for_true)
68                 {
69                         ILGenerator ig = ec.ig;
70                         
71                         bool invert = false;
72                         if (bool_expr is Unary){
73                                 Unary u = (Unary) bool_expr;
74                                 
75                                 if (u.Oper == Unary.Operator.LogicalNot){
76                                         invert = true;
77
78                                         u.EmitLogicalNot (ec);
79                                 }
80                         } else if (bool_expr is Binary){
81                                 Binary b = (Binary) bool_expr;
82
83                                 if (b.EmitBranchable (ec, target, target_is_for_true))
84                                         return;
85                         }
86
87                         if (!invert)
88                                 bool_expr.Emit (ec);
89
90                         if (target_is_for_true){
91                                 if (invert)
92                                         ig.Emit (OpCodes.Brfalse, target);
93                                 else
94                                         ig.Emit (OpCodes.Brtrue, target);
95                         } else {
96                                 if (invert)
97                                         ig.Emit (OpCodes.Brtrue, target);
98                                 else
99                                         ig.Emit (OpCodes.Brfalse, target);
100                         }
101                 }
102
103                 public static void Warning_DeadCodeFound (Location loc)
104                 {
105                         Report.Warning (162, loc, "Unreachable code detected");
106                 }
107         }
108
109         public class EmptyStatement : Statement {
110                 public override bool Resolve (EmitContext ec)
111                 {
112                         return true;
113                 }
114                 
115                 public override bool Emit (EmitContext ec)
116                 {
117                         return false;
118                 }
119         }
120         
121         public class If : Statement {
122                 Expression expr;
123                 public Statement TrueStatement;
124                 public Statement FalseStatement;
125                 
126                 public If (Expression expr, Statement trueStatement, Location l)
127                 {
128                         this.expr = expr;
129                         TrueStatement = trueStatement;
130                         loc = l;
131                 }
132
133                 public If (Expression expr,
134                            Statement trueStatement,
135                            Statement falseStatement,
136                            Location l)
137                 {
138                         this.expr = expr;
139                         TrueStatement = trueStatement;
140                         FalseStatement = falseStatement;
141                         loc = l;
142                 }
143
144                 public override bool Resolve (EmitContext ec)
145                 {
146                         expr = ResolveBoolean (ec, expr, loc);
147                         if (expr == null){
148                                 return false;
149                         }
150                         
151                         if (TrueStatement.Resolve (ec)){
152                                 if (FalseStatement != null){
153                                         if (FalseStatement.Resolve (ec))
154                                                 return true;
155                                         
156                                         return false;
157                                 }
158                                 return true;
159                         }
160                         return false;
161                 }
162                 
163                 public override bool Emit (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                 
220                 public Do (Statement statement, Expression boolExpr, Location l)
221                 {
222                         expr = boolExpr;
223                         EmbeddedStatement = statement;
224                         loc = l;
225                 }
226
227                 public override bool Resolve (EmitContext ec)
228                 {
229                         expr = ResolveBoolean (ec, expr, loc);
230                         if (expr == null)
231                                 return false;
232                         
233                         return EmbeddedStatement.Resolve (ec);
234                 }
235                 
236                 public override bool Emit (EmitContext ec)
237                 {
238                         ILGenerator ig = ec.ig;
239                         Label loop = ig.DefineLabel ();
240                         Label old_begin = ec.LoopBegin;
241                         Label old_end = ec.LoopEnd;
242                         bool  old_inloop = ec.InLoop;
243                         bool old_breaks = ec.Breaks;
244                         
245                         ec.LoopBegin = ig.DefineLabel ();
246                         ec.LoopEnd = ig.DefineLabel ();
247                         ec.InLoop = true;
248                                 
249                         ig.MarkLabel (loop);
250                         ec.Breaks = false;
251                         EmbeddedStatement.Emit (ec);
252                         bool breaks = ec.Breaks;
253                         ig.MarkLabel (ec.LoopBegin);
254
255                         //
256                         // Dead code elimination
257                         //
258                         if (expr is BoolConstant){
259                                 bool res = ((BoolConstant) expr).Value;
260
261                                 if (res)
262                                         ec.ig.Emit (OpCodes.Br, loop); 
263                         } else
264                                 EmitBoolExpression (ec, expr, loop, true);
265                         
266                         ig.MarkLabel (ec.LoopEnd);
267
268                         ec.LoopBegin = old_begin;
269                         ec.LoopEnd = old_end;
270                         ec.InLoop = old_inloop;
271                         ec.Breaks = old_breaks;
272
273                         //
274                         // Inform whether we are infinite or not
275                         //
276                         if (expr is BoolConstant){
277                                 BoolConstant bc = (BoolConstant) expr;
278
279                                 if (bc.Value == true)
280                                         return breaks == false;
281                         }
282                         
283                         return false;
284                 }
285         }
286
287         public class While : Statement {
288                 public Expression expr;
289                 public readonly Statement Statement;
290                 
291                 public While (Expression boolExpr, Statement statement, Location l)
292                 {
293                         this.expr = boolExpr;
294                         Statement = statement;
295                         loc = l;
296                 }
297
298                 public override bool Resolve (EmitContext ec)
299                 {
300                         expr = ResolveBoolean (ec, expr, loc);
301                         if (expr == null)
302                                 return false;
303                         
304                         return Statement.Resolve (ec);
305                 }
306                 
307                 public override bool Emit (EmitContext ec)
308                 {
309                         ILGenerator ig = ec.ig;
310                         Label old_begin = ec.LoopBegin;
311                         Label old_end = ec.LoopEnd;
312                         bool old_inloop = ec.InLoop;
313                         bool old_breaks = ec.Breaks;
314                         Label while_loop = ig.DefineLabel ();
315                         bool ret;
316                         
317                         ec.LoopBegin = ig.DefineLabel ();
318                         ec.LoopEnd = ig.DefineLabel ();
319                         ec.InLoop = true;
320
321                         ig.Emit (OpCodes.Br, ec.LoopBegin);
322                         ig.MarkLabel (while_loop);
323
324                         //
325                         // Inform whether we are infinite or not
326                         //
327                         if (expr is BoolConstant){
328                                 BoolConstant bc = (BoolConstant) expr;
329
330                                 ig.MarkLabel (ec.LoopBegin);
331                                 if (bc.Value == false){
332                                         Warning_DeadCodeFound (Statement.loc);
333                                         ret = false;
334                                 } else {
335                                         bool breaks;
336                                         
337                                         ec.Breaks = false;
338                                         Statement.Emit (ec);
339                                         breaks = ec.Breaks;
340                                         ig.Emit (OpCodes.Br, ec.LoopBegin);
341                                         
342                                         //
343                                         // Inform that we are infinite (ie, `we return'), only
344                                         // if we do not `break' inside the code.
345                                         //
346                                         ret = breaks == false;
347                                 }
348                                 ig.MarkLabel (ec.LoopEnd);
349                         } else {
350                                 Statement.Emit (ec);
351                         
352                                 ig.MarkLabel (ec.LoopBegin);
353
354                                 EmitBoolExpression (ec, expr, while_loop, true);
355                                 ig.MarkLabel (ec.LoopEnd);
356
357                                 ret = false;
358                         }       
359
360                         ec.LoopBegin = old_begin;
361                         ec.LoopEnd = old_end;
362                         ec.InLoop = old_inloop;
363                         ec.Breaks = old_breaks;
364
365                         return ret;
366                 }
367         }
368
369         public class For : Statement {
370                 Expression Test;
371                 readonly Statement InitStatement;
372                 readonly Statement Increment;
373                 readonly Statement Statement;
374                 
375                 public For (Statement initStatement,
376                             Expression test,
377                             Statement increment,
378                             Statement statement,
379                             Location l)
380                 {
381                         InitStatement = initStatement;
382                         Test = test;
383                         Increment = increment;
384                         Statement = statement;
385                         loc = l;
386                 }
387
388                 public override bool Resolve (EmitContext ec)
389                 {
390                         bool ok = true;
391
392                         if (Test != null){
393                                 Test = ResolveBoolean (ec, Test, loc);
394                                 if (Test == null)
395                                         ok = false;
396                         }
397
398                         if (InitStatement != null){
399                                 if (!InitStatement.Resolve (ec))
400                                         ok = false;
401                         }
402
403                         if (Increment != null){
404                                 if (!Increment.Resolve (ec))
405                                         ok = false;
406                         }
407                         
408                         return Statement.Resolve (ec) && ok;
409                 }
410                 
411                 public override bool Emit (EmitContext ec)
412                 {
413                         ILGenerator ig = ec.ig;
414                         Label old_begin = ec.LoopBegin;
415                         Label old_end = ec.LoopEnd;
416                         bool old_inloop = ec.InLoop;
417                         bool old_breaks = ec.Breaks;
418                         Label loop = ig.DefineLabel ();
419                         Label test = ig.DefineLabel ();
420                         
421                         if (InitStatement != null)
422                                 if (! (InitStatement is EmptyStatement))
423                                         InitStatement.Emit (ec);
424
425                         ec.LoopBegin = ig.DefineLabel ();
426                         ec.LoopEnd = ig.DefineLabel ();
427                         ec.InLoop = true;
428
429                         ig.Emit (OpCodes.Br, test);
430                         ig.MarkLabel (loop);
431                         ec.Breaks = false;
432                         Statement.Emit (ec);
433                         bool breaks = ec.Breaks;
434
435                         ig.MarkLabel (ec.LoopBegin);
436                         if (!(Increment is EmptyStatement))
437                                 Increment.Emit (ec);
438
439                         ig.MarkLabel (test);
440                         //
441                         // If test is null, there is no test, and we are just
442                         // an infinite loop
443                         //
444                         if (Test != null)
445                                 EmitBoolExpression (ec, Test, loop, true);
446                         else
447                                 ig.Emit (OpCodes.Br, loop);
448                         ig.MarkLabel (ec.LoopEnd);
449
450                         ec.LoopBegin = old_begin;
451                         ec.LoopEnd = old_end;
452                         ec.InLoop = old_inloop;
453                         ec.Breaks = old_breaks;
454                         
455                         //
456                         // Inform whether we are infinite or not
457                         //
458                         if (Test != null){
459                                 if (Test is BoolConstant){
460                                         BoolConstant bc = (BoolConstant) Test;
461
462                                         if (bc.Value)
463                                                 return breaks == false;
464                                 }
465                                 return false;
466                         } else
467                                 return true;
468                 }
469         }
470         
471         public class StatementExpression : Statement {
472                 Expression expr;
473                 
474                 public StatementExpression (ExpressionStatement expr, Location l)
475                 {
476                         this.expr = expr;
477                         loc = l;
478                 }
479
480                 public override bool Resolve (EmitContext ec)
481                 {
482                         expr = (Expression) expr.Resolve (ec);
483                         return expr != null;
484                 }
485                 
486                 public override bool Emit (EmitContext ec)
487                 {
488                         ILGenerator ig = ec.ig;
489                         
490                         if (expr is ExpressionStatement)
491                                 ((ExpressionStatement) expr).EmitStatement (ec);
492                         else {
493                                 expr.Emit (ec);
494                                 ig.Emit (OpCodes.Pop);
495                         }
496
497                         return false;
498                 }
499
500                 public override string ToString ()
501                 {
502                         return "StatementExpression (" + expr + ")";
503                 }
504         }
505
506         /// <summary>
507         ///   Implements the return statement
508         /// </summary>
509         public class Return : Statement {
510                 public Expression Expr;
511                 
512                 public Return (Expression expr, Location l)
513                 {
514                         Expr = expr;
515                         loc = l;
516                 }
517
518                 public override bool Resolve (EmitContext ec)
519                 {
520                         if (Expr != null){
521                                 Expr = Expr.Resolve (ec);
522                                 if (Expr == null)
523                                         return false;
524                         }
525                         return true;
526                 }
527                 
528                 public override bool Emit (EmitContext ec)
529                 {
530                         if (ec.InFinally){
531                                 Report.Error (157,loc,"Control can not leave the body of the finally block");
532                                 return false;
533                         }
534                         
535                         if (ec.ReturnType == null){
536                                 if (Expr != null){
537                                         Report.Error (127, loc, "Return with a value not allowed here");
538                                         return false;
539                                 }
540                         } else {
541                                 if (Expr == null){
542                                         Report.Error (126, loc, "An object of type `" +
543                                                       TypeManager.CSharpName (ec.ReturnType) + "' is " +
544                                                       "expected for the return statement");
545                                         return false;
546                                 }
547
548                                 if (Expr.Type != ec.ReturnType)
549                                         Expr = Expression.ConvertImplicitRequired (
550                                                 ec, Expr, ec.ReturnType, loc);
551
552                                 if (Expr == null)
553                                         return false;
554
555                                 Expr.Emit (ec);
556
557                                 if (ec.InTry || ec.InCatch)
558                                         ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
559                         }
560
561                         if (ec.InTry || ec.InCatch)
562                                 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
563                         else
564                                 ec.ig.Emit (OpCodes.Ret);
565
566                         return true; 
567                 }
568         }
569
570         public class Goto : Statement {
571                 string target;
572                 Block block;
573                 
574                 public override bool Resolve (EmitContext ec)
575                 {
576                         return true;
577                 }
578                 
579                 public Goto (Block parent_block, string label, Location l)
580                 {
581                         block = parent_block;
582                         loc = l;
583                         target = label;
584                 }
585
586                 public string Target {
587                         get {
588                                 return target;
589                         }
590                 }
591
592                 public override bool Emit (EmitContext ec)
593                 {
594                         LabeledStatement label = block.LookupLabel (target);
595
596                         if (label == null){
597                                 //
598                                 // Maybe we should catch this before?
599                                 //
600                                 Report.Error (
601                                         159, loc,
602                                         "No such label `" + target + "' in this scope");
603                                 return false;
604                         }
605                         Label l = label.LabelTarget (ec);
606                         ec.ig.Emit (OpCodes.Br, l);
607                         
608                         return false;
609                 }
610         }
611
612         public class LabeledStatement : Statement {
613                 string label_name;
614                 bool defined;
615                 Label label;
616                 
617                 public LabeledStatement (string label_name)
618                 {
619                         this.label_name = label_name;
620                 }
621
622                 public Label LabelTarget (EmitContext ec)
623                 {
624                         if (defined)
625                                 return label;
626                         label = ec.ig.DefineLabel ();
627                         defined = true;
628
629                         return label;
630                 }
631
632                 public override bool Emit (EmitContext ec)
633                 {
634                         LabelTarget (ec);
635                         ec.ig.MarkLabel (label);
636
637                         return false;
638                 }
639         }
640         
641
642         /// <summary>
643         ///   `goto default' statement
644         /// </summary>
645         public class GotoDefault : Statement {
646                 
647                 public GotoDefault (Location l)
648                 {
649                         loc = l;
650                 }
651
652                 public override bool Emit (EmitContext ec)
653                 {
654                         if (ec.Switch == null){
655                                 Report.Error (153, loc, "goto default is only valid in a switch statement");
656                                 return false;
657                         }
658
659                         if (!ec.Switch.GotDefault){
660                                 Report.Error (159, loc, "No default target on switch statement");
661                                 return false;
662                         }
663                         ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
664                         return false;
665                 }
666         }
667
668         /// <summary>
669         ///   `goto case' statement
670         /// </summary>
671         public class GotoCase : Statement {
672                 Expression expr;
673                 
674                 public GotoCase (Expression e, Location l)
675                 {
676                         expr = e;
677                         loc = l;
678                 }
679
680                 public override bool Emit (EmitContext ec)
681                 {
682                         if (ec.Switch == null){
683                                 Report.Error (153, loc, "goto case is only valid in a switch statement");
684                                 return false;
685                         }
686
687                         expr = expr.Resolve (ec);
688                         if (expr == null)
689                                 return false;
690
691                         if (!(expr is Constant)){
692                                 Report.Error (159, loc, "Target expression for goto case is not constant");
693                                 return false;
694                         }
695
696                         object val = Expression.ConvertIntLiteral (
697                                 (Constant) expr, ec.Switch.SwitchType, loc);
698
699                         if (val == null)
700                                 return false;
701                                         
702                         SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
703
704                         if (sl == null){
705                                 Report.Error (
706                                         159, loc,
707                                         "No such label 'case " + val + "': for the goto case");
708                         }
709
710                         ec.ig.Emit (OpCodes.Br, sl.ILLabelCode);
711                         return true;
712                 }
713         }
714         
715         public class Throw : Statement {
716                 Expression expr;
717                 
718                 public Throw (Expression expr, Location l)
719                 {
720                         this.expr = expr;
721                         loc = l;
722                 }
723
724                 public override bool Resolve (EmitContext ec)
725                 {
726                         if (expr != null){
727                                 expr = expr.Resolve (ec);
728                                 if (expr == null)
729                                         return false;
730                         }
731                         return true;
732                 }
733                         
734                 public override bool Emit (EmitContext ec)
735                 {
736                         if (expr == null){
737                                 if (ec.InCatch)
738                                         ec.ig.Emit (OpCodes.Rethrow);
739                                 else {
740                                         Report.Error (
741                                                 156, loc,
742                                                 "A throw statement with no argument is only " +
743                                                 "allowed in a catch clause");
744                                 }
745                                 return false;
746                         }
747                         
748                         expr.Emit (ec);
749
750                         ec.ig.Emit (OpCodes.Throw);
751
752                         return true;
753                 }
754         }
755
756         public class Break : Statement {
757                 
758                 public Break (Location l)
759                 {
760                         loc = l;
761                 }
762
763                 public override bool Emit (EmitContext ec)
764                 {
765                         ILGenerator ig = ec.ig;
766
767                         if (ec.InLoop == false && ec.Switch == null){
768                                 Report.Error (139, loc, "No enclosing loop or switch to continue to");
769                                 return false;
770                         }
771
772                         ec.Breaks = true;
773                         if (ec.InTry || ec.InCatch)
774                                 ig.Emit (OpCodes.Leave, ec.LoopEnd);
775                         else
776                                 ig.Emit (OpCodes.Br, ec.LoopEnd);
777
778                         return false;
779                 }
780         }
781
782         public class Continue : Statement {
783                 
784                 public Continue (Location l)
785                 {
786                         loc = l;
787                 }
788
789                 public override bool Emit (EmitContext ec)
790                 {
791                         Label begin = ec.LoopBegin;
792                         
793                         if (!ec.InLoop){
794                                 Report.Error (139, loc, "No enclosing loop to continue to");
795                                 return false;
796                         } 
797
798                         //
799                         // UGH: Non trivial.  This Br might cross a try/catch boundary
800                         // How can we tell?
801                         //
802                         // while () {
803                         //   try { ... } catch { continue; }
804                         // }
805                         //
806                         // From:
807                         // try {} catch { while () { continue; }}
808                         //
809                         ec.ig.Emit (OpCodes.Br, begin);
810                         return false;
811                 }
812         }
813         
814         public class VariableInfo {
815                 public readonly string Type;
816                 public LocalBuilder LocalBuilder;
817                 public Type VariableType;
818                 public readonly Location Location;
819                 
820                 public bool Used;
821                 public bool Assigned;
822                 public bool ReadOnly;
823                 
824                 public VariableInfo (string type, Location l)
825                 {
826                         Type = type;
827                         LocalBuilder = null;
828                         Location = l;
829                 }
830
831                 public void MakePinned ()
832                 {
833                         TypeManager.MakePinned (LocalBuilder);
834                 }                               
835         }
836                 
837         /// <summary>
838         ///   Block represents a C# block.
839         /// </summary>
840         ///
841         /// <remarks>
842         ///   This class is used in a number of places: either to represent
843         ///   explicit blocks that the programmer places or implicit blocks.
844         ///
845         ///   Implicit blocks are used as labels or to introduce variable
846         ///   declarations.
847         /// </remarks>
848         public class Block : Statement {
849                 public readonly Block     Parent;
850                 public readonly bool      Implicit;
851                 public readonly Location  StartLocation;
852                 public Location           EndLocation;
853
854                 //
855                 // The statements in this block
856                 //
857                 ArrayList statements;
858
859                 //
860                 // An array of Blocks.  We keep track of children just
861                 // to generate the local variable declarations.
862                 //
863                 // Statements and child statements are handled through the
864                 // statements.
865                 //
866                 ArrayList children;
867                 
868                 //
869                 // Labels.  (label, block) pairs.
870                 //
871                 Hashtable labels;
872
873                 //
874                 // Keeps track of (name, type) pairs
875                 //
876                 Hashtable variables;
877
878                 //
879                 // Keeps track of constants
880                 Hashtable constants;
881
882                 //
883                 // Maps variable names to ILGenerator.LocalBuilders
884                 //
885                 Hashtable local_builders;
886
887                 bool used = false;
888
889                 static int id;
890
891                 int this_id;
892                 
893                 public Block (Block parent)
894                         : this (parent, false, Location.Null, Location.Null)
895                 { }
896
897                 public Block (Block parent, bool implicit_block)
898                         : this (parent, implicit_block, Location.Null, Location.Null)
899                 { }
900
901                 public Block (Block parent, Location start, Location end)
902                         : this (parent, false, start, end)
903                 { }
904
905                 public Block (Block parent, bool implicit_block, Location start, Location end)
906                 {
907                         if (parent != null)
908                                 parent.AddChild (this);
909                         
910                         this.Parent = parent;
911                         this.Implicit = implicit_block;
912                         this.StartLocation = start;
913                         this.EndLocation = end;
914                         this.loc = start;
915                         this_id = id++;
916                         statements = new ArrayList ();
917                 }
918
919                 public int ID {
920                         get {
921                                 return this_id;
922                         }
923                 }
924                 
925                 void AddChild (Block b)
926                 {
927                         if (children == null)
928                                 children = new ArrayList ();
929                         
930                         children.Add (b);
931                 }
932
933                 public void SetEndLocation (Location loc)
934                 {
935                         EndLocation = loc;
936                 }
937
938                 /// <summary>
939                 ///   Adds a label to the current block. 
940                 /// </summary>
941                 ///
942                 /// <returns>
943                 ///   false if the name already exists in this block. true
944                 ///   otherwise.
945                 /// </returns>
946                 ///
947                 public bool AddLabel (string name, LabeledStatement target)
948                 {
949                         if (labels == null)
950                                 labels = new Hashtable ();
951                         if (labels.Contains (name))
952                                 return false;
953                         
954                         labels.Add (name, target);
955                         return true;
956                 }
957
958                 public LabeledStatement LookupLabel (string name)
959                 {
960                         if (labels != null){
961                                 if (labels.Contains (name))
962                                         return ((LabeledStatement) labels [name]);
963                         }
964
965                         if (Parent != null)
966                                 return Parent.LookupLabel (name);
967
968                         return null;
969                 }
970
971                 public VariableInfo AddVariable (string type, string name, Parameters pars, Location l)
972                 {
973                         if (variables == null)
974                                 variables = new Hashtable ();
975
976                         if (GetVariableType (name) != null)
977                                 return null;
978
979                         if (pars != null) {
980                                 int idx = 0;
981                                 Parameter p = pars.GetParameterByName (name, out idx);
982                                 if (p != null) 
983                                         return null;
984                         }
985                         
986                         VariableInfo vi = new VariableInfo (type, l);
987
988                         variables.Add (name, vi);
989
990                         // Console.WriteLine ("Adding {0} to {1}", name, ID);
991                         return vi;
992                 }
993
994                 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
995                 {
996                         if (AddVariable (type, name, pars, l) == null)
997                                 return false;
998                         
999                         if (constants == null)
1000                                 constants = new Hashtable ();
1001
1002                         constants.Add (name, value);
1003                         return true;
1004                 }
1005
1006                 public Hashtable Variables {
1007                         get {
1008                                 return variables;
1009                         }
1010                 }
1011
1012                 public VariableInfo GetVariableInfo (string name)
1013                 {
1014                         if (variables != null) {
1015                                 object temp;
1016                                 temp = variables [name];
1017
1018                                 if (temp != null){
1019                                         return (VariableInfo) temp;
1020                                 }
1021                         }
1022
1023                         if (Parent != null)
1024                                 return Parent.GetVariableInfo (name);
1025
1026                         return null;
1027                 }
1028                 
1029                 public string GetVariableType (string name)
1030                 {
1031                         VariableInfo vi = GetVariableInfo (name);
1032
1033                         if (vi != null)
1034                                 return vi.Type;
1035
1036                         return null;
1037                 }
1038
1039                 public Expression GetConstantExpression (string name)
1040                 {
1041                         if (constants != null) {
1042                                 object temp;
1043                                 temp = constants [name];
1044                                 
1045                                 if (temp != null)
1046                                         return (Expression) temp;
1047                         }
1048                         
1049                         if (Parent != null)
1050                                 return Parent.GetConstantExpression (name);
1051
1052                         return null;
1053                 }
1054                 
1055                 /// <summary>
1056                 ///   True if the variable named @name has been defined
1057                 ///   in this block
1058                 /// </summary>
1059                 public bool IsVariableDefined (string name)
1060                 {
1061                         // Console.WriteLine ("Looking up {0} in {1}", name, ID);
1062                         if (variables != null) {
1063                                 if (variables.Contains (name))
1064                                         return true;
1065                         }
1066                         
1067                         if (Parent != null)
1068                                 return Parent.IsVariableDefined (name);
1069
1070                         return false;
1071                 }
1072
1073                 /// <summary>
1074                 ///   True if the variable named @name is a constant
1075                 ///  </summary>
1076                 public bool IsConstant (string name)
1077                 {
1078                         Expression e = null;
1079                         
1080                         e = GetConstantExpression (name);
1081                         
1082                         return e != null;
1083                 }
1084                 
1085                 /// <summary>
1086                 ///   Use to fetch the statement associated with this label
1087                 /// </summary>
1088                 public Statement this [string name] {
1089                         get {
1090                                 return (Statement) labels [name];
1091                         }
1092                 }
1093
1094                 /// <returns>
1095                 ///   A list of labels that were not used within this block
1096                 /// </returns>
1097                 public string [] GetUnreferenced ()
1098                 {
1099                         // FIXME: Implement me
1100                         return null;
1101                 }
1102
1103                 public void AddStatement (Statement s)
1104                 {
1105                         statements.Add (s);
1106                         used = true;
1107                 }
1108
1109                 public bool Used {
1110                         get {
1111                                 return used;
1112                         }
1113                 }
1114
1115                 public void Use ()
1116                 {
1117                         used = true;
1118                 }
1119                 
1120                 /// <summary>
1121                 ///   Emits the variable declarations and labels.
1122                 /// </summary>
1123                 /// <remarks>
1124                 ///   tc: is our typecontainer (to resolve type references)
1125                 ///   ig: is the code generator:
1126                 ///   toplevel: the toplevel block.  This is used for checking 
1127                 ///             that no two labels with the same name are used.
1128                 /// </remarks>
1129                 public void EmitMeta (EmitContext ec, Block toplevel)
1130                 {
1131                         DeclSpace ds = ec.DeclSpace;
1132                         ILGenerator ig = ec.ig;
1133                                 
1134                         //
1135                         // Process this block variables
1136                         //
1137                         if (variables != null){
1138                                 local_builders = new Hashtable ();
1139                                 
1140                                 foreach (DictionaryEntry de in variables){
1141                                         string name = (string) de.Key;
1142                                         VariableInfo vi = (VariableInfo) de.Value;
1143                                         Type t;
1144
1145                                         t = RootContext.LookupType (ds, vi.Type, false, vi.Location);
1146                                         if (t == null)
1147                                                 continue;
1148
1149                                         vi.VariableType = t;
1150                                         vi.LocalBuilder = ig.DeclareLocal (t);
1151
1152                                         if (CodeGen.SymbolWriter != null)
1153                                                 vi.LocalBuilder.SetLocalSymInfo (name);
1154
1155                                         if (constants == null)
1156                                                 continue;
1157
1158                                         Expression cv = (Expression) constants [name];
1159                                         if (cv == null)
1160                                                 continue;
1161
1162                                         Expression e = cv.Resolve (ec);
1163                                         if (e == null)
1164                                                 continue;
1165
1166                                         if (!(e is Constant)){
1167                                                 Report.Error (133, vi.Location,
1168                                                               "The expression being assigned to `" +
1169                                                               name + "' must be constant (" + e + ")");
1170                                                 continue;
1171                                         }
1172
1173                                         constants.Remove (name);
1174                                         constants.Add (name, e);
1175                                 }
1176                         }
1177
1178                         //
1179                         // Now, handle the children
1180                         //
1181                         if (children != null){
1182                                 foreach (Block b in children)
1183                                         b.EmitMeta (ec, toplevel);
1184                         }
1185                 }
1186
1187                 public void UsageWarning ()
1188                 {
1189                         string name;
1190                         
1191                         if (variables != null){
1192                                 foreach (DictionaryEntry de in variables){
1193                                         VariableInfo vi = (VariableInfo) de.Value;
1194                                         
1195                                         if (vi.Used)
1196                                                 continue;
1197                                         
1198                                         name = (string) de.Key;
1199                                                 
1200                                         if (vi.Assigned){
1201                                                 Report.Warning (
1202                                                         219, vi.Location, "The variable `" + name +
1203                                                         "' is assigned but its value is never used");
1204                                         } else {
1205                                                 Report.Warning (
1206                                                         168, vi.Location, "The variable `" +
1207                                                         name +
1208                                                         "' is declared but never used");
1209                                         } 
1210                                 }
1211                         }
1212
1213                         if (children != null)
1214                                 foreach (Block b in children)
1215                                         b.UsageWarning ();
1216                 }
1217
1218                 public override bool Resolve (EmitContext ec)
1219                 {
1220                         Block prev_block = ec.CurrentBlock;
1221
1222                         ec.CurrentBlock = this;
1223                         foreach (Statement s in statements){
1224                                 if (s.Resolve (ec) == false){
1225                                         ec.CurrentBlock = prev_block;
1226                                         return false;
1227                                 }
1228                         }
1229
1230                         ec.CurrentBlock = prev_block;
1231                         return true;
1232                 }
1233                 
1234                 public override bool Emit (EmitContext ec)
1235                 {
1236                         bool is_ret = false;
1237                         Block prev_block = ec.CurrentBlock;
1238                         
1239                         ec.CurrentBlock = this;
1240
1241                         if (CodeGen.SymbolWriter != null) {
1242                                 ec.Mark (StartLocation);
1243                                 
1244                                 foreach (Statement s in statements) {
1245                                         ec.Mark (s.loc);
1246                                         
1247                                         is_ret = s.Emit (ec);
1248                                 }
1249
1250                                 ec.Mark (EndLocation); 
1251                         } else {
1252                                 foreach (Statement s in statements)
1253                                         is_ret = s.Emit (ec);
1254                         }
1255                         
1256                         ec.CurrentBlock = prev_block;
1257                         return is_ret;
1258                 }
1259         }
1260
1261         public class SwitchLabel {
1262                 Expression label;
1263                 object converted;
1264                 public Location loc;
1265                 public Label ILLabel;
1266                 public Label ILLabelCode;
1267                 
1268                 //
1269                 // if expr == null, then it is the default case.
1270                 //
1271                 public SwitchLabel (Expression expr, Location l)
1272                 {
1273                         label = expr;
1274                         loc = l;
1275                 }
1276
1277                 public Expression Label {
1278                         get {
1279                                 return label;
1280                         }
1281                 }
1282
1283                 public object Converted {
1284                         get {
1285                                 return converted;
1286                         }
1287                 }
1288                 
1289                 //
1290                 // Resolves the expression, reduces it to a literal if possible
1291                 // and then converts it to the requested type.
1292                 //
1293                 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1294                 {
1295                         ILLabel = ec.ig.DefineLabel ();
1296                         ILLabelCode = ec.ig.DefineLabel ();
1297
1298                         if (label == null)
1299                                 return true;
1300                         
1301                         Expression e = label.Resolve (ec);
1302
1303                         if (e == null)
1304                                 return false;
1305
1306                         if (!(e is Constant)){
1307                                 Console.WriteLine ("Value is: " + label);
1308                                 Report.Error (150, loc, "A constant value is expected");
1309                                 return false;
1310                         }
1311
1312                         if (e is StringConstant || e is NullLiteral){
1313                                 if (required_type == TypeManager.string_type){
1314                                         converted = label;
1315                                         ILLabel = ec.ig.DefineLabel ();
1316                                         return true;
1317                                 }
1318                         }
1319
1320                         converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1321                         if (converted == null)
1322                                 return false;
1323
1324                         return true;
1325                 }
1326         }
1327
1328         public class SwitchSection {
1329                 // An array of SwitchLabels.
1330                 public readonly ArrayList Labels;
1331                 public readonly Block Block;
1332                 
1333                 public SwitchSection (ArrayList labels, Block block)
1334                 {
1335                         Labels = labels;
1336                         Block = block;
1337                 }
1338         }
1339         
1340         public class Switch : Statement {
1341                 public readonly ArrayList Sections;
1342                 public Expression Expr;
1343
1344                 /// <summary>
1345                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
1346                 /// </summary>
1347                 public Hashtable Elements;
1348
1349                 /// <summary>
1350                 ///   The governing switch type
1351                 /// </summary>
1352                 public Type SwitchType;
1353
1354                 //
1355                 // Computed
1356                 //
1357                 bool got_default;
1358                 Label default_target;
1359                 
1360                 //
1361                 // The types allowed to be implicitly cast from
1362                 // on the governing type
1363                 //
1364                 static Type [] allowed_types;
1365                 
1366                 public Switch (Expression e, ArrayList sects, Location l)
1367                 {
1368                         Expr = e;
1369                         Sections = sects;
1370                         loc = l;
1371                 }
1372
1373                 public bool GotDefault {
1374                         get {
1375                                 return got_default;
1376                         }
1377                 }
1378
1379                 public Label DefaultTarget {
1380                         get {
1381                                 return default_target;
1382                         }
1383                 }
1384
1385                 //
1386                 // Determines the governing type for a switch.  The returned
1387                 // expression might be the expression from the switch, or an
1388                 // expression that includes any potential conversions to the
1389                 // integral types or to string.
1390                 //
1391                 Expression SwitchGoverningType (EmitContext ec, Type t)
1392                 {
1393                         if (t == TypeManager.int32_type ||
1394                             t == TypeManager.uint32_type ||
1395                             t == TypeManager.char_type ||
1396                             t == TypeManager.byte_type ||
1397                             t == TypeManager.sbyte_type ||
1398                             t == TypeManager.ushort_type ||
1399                             t == TypeManager.short_type ||
1400                             t == TypeManager.uint64_type ||
1401                             t == TypeManager.int64_type ||
1402                             t == TypeManager.string_type ||
1403                                 t == TypeManager.bool_type ||
1404                                 t.IsSubclassOf (TypeManager.enum_type))
1405                                 return Expr;
1406
1407                         if (allowed_types == null){
1408                                 allowed_types = new Type [] {
1409                                         TypeManager.sbyte_type,
1410                                         TypeManager.byte_type,
1411                                         TypeManager.short_type,
1412                                         TypeManager.ushort_type,
1413                                         TypeManager.int32_type,
1414                                         TypeManager.uint32_type,
1415                                         TypeManager.int64_type,
1416                                         TypeManager.uint64_type,
1417                                         TypeManager.char_type,
1418                                         TypeManager.bool_type,
1419                                         TypeManager.string_type
1420                                 };
1421                         }
1422
1423                         //
1424                         // Try to find a *user* defined implicit conversion.
1425                         //
1426                         // If there is no implicit conversion, or if there are multiple
1427                         // conversions, we have to report an error
1428                         //
1429                         Expression converted = null;
1430                         foreach (Type tt in allowed_types){
1431                                 Expression e;
1432                                 
1433                                 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1434                                 if (e == null)
1435                                         continue;
1436
1437                                 if (converted != null){
1438                                         Report.Error (-12, loc, "More than one conversion to an integral " +
1439                                                       " type exists for type `" +
1440                                                       TypeManager.CSharpName (Expr.Type)+"'");
1441                                         return null;
1442                                 } else
1443                                         converted = e;
1444                         }
1445                         return converted;
1446                 }
1447
1448                 void error152 (string n)
1449                 {
1450                         Report.Error (
1451                                 152, "The label `" + n + ":' " +
1452                                 "is already present on this switch statement");
1453                 }
1454                 
1455                 //
1456                 // Performs the basic sanity checks on the switch statement
1457                 // (looks for duplicate keys and non-constant expressions).
1458                 //
1459                 // It also returns a hashtable with the keys that we will later
1460                 // use to compute the switch tables
1461                 //
1462                 bool CheckSwitch (EmitContext ec)
1463                 {
1464                         Type compare_type;
1465                         bool error = false;
1466                         Elements = new Hashtable ();
1467                                 
1468                         got_default = false;
1469
1470                         if (TypeManager.IsEnumType (SwitchType)){
1471                                 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1472                         } else
1473                                 compare_type = SwitchType;
1474                         
1475                         foreach (SwitchSection ss in Sections){
1476                                 foreach (SwitchLabel sl in ss.Labels){
1477                                         if (!sl.ResolveAndReduce (ec, SwitchType)){
1478                                                 error = true;
1479                                                 continue;
1480                                         }
1481
1482                                         if (sl.Label == null){
1483                                                 if (got_default){
1484                                                         error152 ("default");
1485                                                         error = true;
1486                                                 }
1487                                                 got_default = true;
1488                                                 continue;
1489                                         }
1490                                         
1491                                         object key = sl.Converted;
1492
1493                                         if (key is Constant)
1494                                                 key = ((Constant) key).GetValue ();
1495
1496                                         if (key == null)
1497                                                 key = NullLiteral.Null;
1498                                         
1499                                         string lname = null;
1500                                         if (compare_type == TypeManager.uint64_type){
1501                                                 ulong v = (ulong) key;
1502
1503                                                 if (Elements.Contains (v))
1504                                                         lname = v.ToString ();
1505                                                 else
1506                                                         Elements.Add (v, sl);
1507                                         } else if (compare_type == TypeManager.int64_type){
1508                                                 long v = (long) key;
1509
1510                                                 if (Elements.Contains (v))
1511                                                         lname = v.ToString ();
1512                                                 else
1513                                                         Elements.Add (v, sl);
1514                                         } else if (compare_type == TypeManager.uint32_type){
1515                                                 uint v = (uint) key;
1516
1517                                                 if (Elements.Contains (v))
1518                                                         lname = v.ToString ();
1519                                                 else
1520                                                         Elements.Add (v, sl);
1521                                         } else if (compare_type == TypeManager.char_type){
1522                                                 char v = (char) key;
1523                                                 
1524                                                 if (Elements.Contains (v))
1525                                                         lname = v.ToString ();
1526                                                 else
1527                                                         Elements.Add (v, sl);
1528                                         } else if (compare_type == TypeManager.byte_type){
1529                                                 byte v = (byte) key;
1530                                                 
1531                                                 if (Elements.Contains (v))
1532                                                         lname = v.ToString ();
1533                                                 else
1534                                                         Elements.Add (v, sl);
1535                                         } else if (compare_type == TypeManager.sbyte_type){
1536                                                 sbyte v = (sbyte) key;
1537                                                 
1538                                                 if (Elements.Contains (v))
1539                                                         lname = v.ToString ();
1540                                                 else
1541                                                         Elements.Add (v, sl);
1542                                         } else if (compare_type == TypeManager.short_type){
1543                                                 short v = (short) key;
1544                                                 
1545                                                 if (Elements.Contains (v))
1546                                                         lname = v.ToString ();
1547                                                 else
1548                                                         Elements.Add (v, sl);
1549                                         } else if (compare_type == TypeManager.ushort_type){
1550                                                 ushort v = (ushort) key;
1551                                                 
1552                                                 if (Elements.Contains (v))
1553                                                         lname = v.ToString ();
1554                                                 else
1555                                                         Elements.Add (v, sl);
1556                                         } else if (compare_type == TypeManager.string_type){
1557                                                 if (key is NullLiteral){
1558                                                         if (Elements.Contains (NullLiteral.Null))
1559                                                                 lname = "null";
1560                                                         else
1561                                                                 Elements.Add (NullLiteral.Null, null);
1562                                                 } else {
1563                                                         string s = (string) key;
1564
1565                                                         if (Elements.Contains (s))
1566                                                                 lname = s;
1567                                                         else
1568                                                                 Elements.Add (s, sl);
1569                                                 }
1570                                         } else if (compare_type == TypeManager.int32_type) {
1571                                                 int v = (int) key;
1572
1573                                                 if (Elements.Contains (v))
1574                                                         lname = v.ToString ();
1575                                                 else
1576                                                         Elements.Add (v, sl);
1577                                         } else if (compare_type == TypeManager.bool_type) {
1578                                                 bool v = (bool) key;
1579
1580                                                 if (Elements.Contains (v))
1581                                                         lname = v.ToString ();
1582                                                 else
1583                                                         Elements.Add (v, sl);
1584                                         }
1585                                         else
1586                                         {
1587                                                 throw new Exception ("Unknown switch type!" +
1588                                                                      SwitchType + " " + compare_type);
1589                                         }
1590
1591                                         if (lname != null){
1592                                                 error152 ("case + " + lname);
1593                                                 error = true;
1594                                         }
1595                                 }
1596                         }
1597                         if (error)
1598                                 return false;
1599                         
1600                         return true;
1601                 }
1602
1603                 void EmitObjectInteger (ILGenerator ig, object k)
1604                 {
1605                         if (k is int)
1606                                 IntConstant.EmitInt (ig, (int) k);
1607                         else if (k is Constant) {
1608                                 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1609                         } 
1610                         else if (k is uint)
1611                                 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1612                         else if (k is long)
1613                         {
1614                                 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
1615                                 {
1616                                         IntConstant.EmitInt (ig, (int) (long) k);
1617                                         ig.Emit (OpCodes.Conv_I8);
1618                                 }
1619                                 else
1620                                         LongConstant.EmitLong (ig, (long) k);
1621                         }
1622                         else if (k is ulong)
1623                         {
1624                                 if ((ulong) k < (1L<<32))
1625                                 {
1626                                         IntConstant.EmitInt (ig, (int) (long) k);
1627                                         ig.Emit (OpCodes.Conv_U8);
1628                                 }
1629                                 else
1630                                 {
1631                                         LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1632                                 }
1633                         }
1634                         else if (k is char)
1635                                 IntConstant.EmitInt (ig, (int) ((char) k));
1636                         else if (k is sbyte)
1637                                 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1638                         else if (k is byte)
1639                                 IntConstant.EmitInt (ig, (int) ((byte) k));
1640                         else if (k is short)
1641                                 IntConstant.EmitInt (ig, (int) ((short) k));
1642                         else if (k is ushort)
1643                                 IntConstant.EmitInt (ig, (int) ((ushort) k));
1644                         else if (k is bool)
1645                                 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
1646                         else
1647                                 throw new Exception ("Unhandled case");
1648                 }
1649                 
1650                 // structure used to hold blocks of keys while calculating table switch
1651                 class KeyBlock : IComparable
1652                 {
1653                         public KeyBlock (long _nFirst)
1654                         {
1655                                 nFirst = nLast = _nFirst;
1656                         }
1657                         public long nFirst;
1658                         public long nLast;
1659                         public ArrayList rgKeys = null;
1660                         public int Length
1661                         {
1662                                 get { return (int) (nLast - nFirst + 1); }
1663                         }
1664                         public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
1665                         {
1666                                 return kbLast.nLast - kbFirst.nFirst + 1;
1667                         }
1668                         public int CompareTo (object obj)
1669                         {
1670                                 KeyBlock kb = (KeyBlock) obj;
1671                                 int nLength = Length;
1672                                 int nLengthOther = kb.Length;
1673                                 if (nLengthOther == nLength)
1674                                         return (int) (kb.nFirst - nFirst);
1675                                 return nLength - nLengthOther;
1676                         }
1677                 }
1678
1679                 /// <summary>
1680                 /// This method emits code for a lookup-based switch statement (non-string)
1681                 /// Basically it groups the cases into blocks that are at least half full,
1682                 /// and then spits out individual lookup opcodes for each block.
1683                 /// It emits the longest blocks first, and short blocks are just
1684                 /// handled with direct compares.
1685                 /// </summary>
1686                 /// <param name="ec"></param>
1687                 /// <param name="val"></param>
1688                 /// <returns></returns>
1689                 bool TableSwitchEmit (EmitContext ec, LocalBuilder val)
1690                 {
1691                         int cElements = Elements.Count;
1692                         object [] rgKeys = new object [cElements];
1693                         Elements.Keys.CopyTo (rgKeys, 0);
1694                         Array.Sort (rgKeys);
1695
1696                         // initialize the block list with one element per key
1697                         ArrayList rgKeyBlocks = new ArrayList ();
1698                         foreach (object key in rgKeys)
1699                                 rgKeyBlocks.Add (new KeyBlock (Convert.ToInt64 (key)));
1700
1701                         KeyBlock kbCurr;
1702                         // iteratively merge the blocks while they are at least half full
1703                         // there's probably a really cool way to do this with a tree...
1704                         while (rgKeyBlocks.Count > 1)
1705                         {
1706                                 ArrayList rgKeyBlocksNew = new ArrayList ();
1707                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
1708                                 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
1709                                 {
1710                                         KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
1711                                         if ((kbCurr.Length + kb.Length) * 2 >=  KeyBlock.TotalLength (kbCurr, kb))
1712                                         {
1713                                                 // merge blocks
1714                                                 kbCurr.nLast = kb.nLast;
1715                                         }
1716                                         else
1717                                         {
1718                                                 // start a new block
1719                                                 rgKeyBlocksNew.Add (kbCurr);
1720                                                 kbCurr = kb;
1721                                         }
1722                                 }
1723                                 rgKeyBlocksNew.Add (kbCurr);
1724                                 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
1725                                         break;
1726                                 rgKeyBlocks = rgKeyBlocksNew;
1727                         }
1728
1729                         // initialize the key lists
1730                         foreach (KeyBlock kb in rgKeyBlocks)
1731                                 kb.rgKeys = new ArrayList ();
1732
1733                         // fill the key lists
1734                         int iBlockCurr = 0;
1735                         if (rgKeyBlocks.Count > 0) {
1736                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
1737                                 foreach (object key in rgKeys)
1738                                 {
1739                                         bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast : Convert.ToInt64 (key) > kbCurr.nLast;
1740                                         if (fNextBlock)
1741                                                 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
1742                                         kbCurr.rgKeys.Add (key);
1743                                 }
1744                         }
1745
1746                         // sort the blocks so we can tackle the largest ones first
1747                         rgKeyBlocks.Sort ();
1748
1749                         // okay now we can start...
1750                         ILGenerator ig = ec.ig;
1751                         Label lblEnd = ig.DefineLabel ();       // at the end ;-)
1752                         Label lblDefault = ig.DefineLabel ();
1753
1754                         Type typeKeys = null;
1755                         if (rgKeys.Length > 0)
1756                                 typeKeys = rgKeys [0].GetType ();       // used for conversions
1757
1758                         for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
1759                         {
1760                                 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
1761                                 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
1762                                 if (kb.Length <= 2)
1763                                 {
1764                                         foreach (object key in kb.rgKeys)
1765                                         {
1766                                                 ig.Emit (OpCodes.Ldloc, val);
1767                                                 EmitObjectInteger (ig, key);
1768                                                 SwitchLabel sl = (SwitchLabel) Elements [key];
1769                                                 ig.Emit (OpCodes.Beq, sl.ILLabel);
1770                                         }
1771                                 }
1772                                 else
1773                                 {
1774                                         // TODO: if all the keys in the block are the same and there are
1775                                         //       no gaps/defaults then just use a range-check.
1776                                         if (SwitchType == TypeManager.int64_type ||
1777                                                 SwitchType == TypeManager.uint64_type)
1778                                         {
1779                                                 // TODO: optimize constant/I4 cases
1780
1781                                                 // check block range (could be > 2^31)
1782                                                 ig.Emit (OpCodes.Ldloc, val);
1783                                                 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1784                                                 ig.Emit (OpCodes.Blt, lblDefault);
1785                                                 ig.Emit (OpCodes.Ldloc, val);
1786                                                 EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1787                                                 ig.Emit (OpCodes.Bgt, lblDefault);
1788
1789                                                 // normalize range
1790                                                 ig.Emit (OpCodes.Ldloc, val);
1791                                                 if (kb.nFirst != 0)
1792                                                 {
1793                                                         EmitObjectInteger (ig, Convert.ChangeType (kb.nFirst, typeKeys));
1794                                                         ig.Emit (OpCodes.Sub);
1795                                                 }
1796                                                 ig.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
1797                                         }
1798                                         else
1799                                         {
1800                                                 // normalize range
1801                                                 ig.Emit (OpCodes.Ldloc, val);
1802                                                 int nFirst = (int) kb.nFirst;
1803                                                 if (nFirst > 0)
1804                                                 {
1805                                                         IntConstant.EmitInt (ig, nFirst);
1806                                                         ig.Emit (OpCodes.Sub);
1807                                                 }
1808                                                 else if (nFirst < 0)
1809                                                 {
1810                                                         IntConstant.EmitInt (ig, -nFirst);
1811                                                         ig.Emit (OpCodes.Add);
1812                                                 }
1813                                         }
1814
1815                                         // first, build the list of labels for the switch
1816                                         int iKey = 0;
1817                                         int cJumps = kb.Length;
1818                                         Label [] rgLabels = new Label [cJumps];
1819                                         for (int iJump = 0; iJump < cJumps; iJump++)
1820                                         {
1821                                                 object key = kb.rgKeys [iKey];
1822                                                 if (Convert.ToInt64 (key) == kb.nFirst + iJump)
1823                                                 {
1824                                                         SwitchLabel sl = (SwitchLabel) Elements [key];
1825                                                         rgLabels [iJump] = sl.ILLabel;
1826                                                         iKey++;
1827                                                 }
1828                                                 else
1829                                                         rgLabels [iJump] = lblDefault;
1830                                         }
1831                                         // emit the switch opcode
1832                                         ig.Emit (OpCodes.Switch, rgLabels);
1833                                 }
1834
1835                                 // mark the default for this block
1836                                 if (iBlock != 0)
1837                                         ig.MarkLabel (lblDefault);
1838                         }
1839
1840                         // TODO: find the default case and emit it here,
1841                         //       to prevent having to do the following jump.
1842                         //       make sure to mark other labels in the default section
1843
1844                         // the last default just goes to the end
1845                         ig.Emit (OpCodes.Br, lblDefault);
1846
1847                         // now emit the code for the sections
1848                         bool fFoundDefault = false;
1849                         bool fAllReturn = true;
1850                         foreach (SwitchSection ss in Sections)
1851                         {
1852                                 foreach (SwitchLabel sl in ss.Labels)
1853                                 {
1854                                         ig.MarkLabel (sl.ILLabel);
1855                                         ig.MarkLabel (sl.ILLabelCode);
1856                                         if (sl.Label == null)
1857                                         {
1858                                                 ig.MarkLabel (lblDefault);
1859                                                 fFoundDefault = true;
1860                                         }
1861                                 }
1862                                 fAllReturn &= ss.Block.Emit (ec);
1863                                 //ig.Emit (OpCodes.Br, lblEnd);
1864                         }
1865                         
1866                         if (!fFoundDefault)
1867                                 ig.MarkLabel (lblDefault);
1868                         ig.MarkLabel (lblEnd);
1869
1870                         return fAllReturn;
1871                 }
1872                 //
1873                 // This simple emit switch works, but does not take advantage of the
1874                 // `switch' opcode. 
1875                 // TODO: remove non-string logic from here
1876                 // TODO: binary search strings?
1877                 //
1878                 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1879                 {
1880                         ILGenerator ig = ec.ig;
1881                         Label end_of_switch = ig.DefineLabel ();
1882                         Label next_test = ig.DefineLabel ();
1883                         Label null_target = ig.DefineLabel ();
1884                         bool default_found = false;
1885                         bool first_test = true;
1886                         bool pending_goto_end = false;
1887                         bool all_return = true;
1888                         bool is_string = false;
1889                         bool null_found;
1890                         
1891                         //
1892                         // Special processing for strings: we cant compare
1893                         // against null.
1894                         //
1895                         if (SwitchType == TypeManager.string_type){
1896                                 ig.Emit (OpCodes.Ldloc, val);
1897                                 is_string = true;
1898                                 
1899                                 if (Elements.Contains (NullLiteral.Null)){
1900                                         ig.Emit (OpCodes.Brfalse, null_target);
1901                                 } else
1902                                         ig.Emit (OpCodes.Brfalse, default_target);
1903
1904                                 ig.Emit (OpCodes.Ldloc, val);
1905                                 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1906                                 ig.Emit (OpCodes.Stloc, val);
1907                         }
1908
1909                         SwitchSection last_section;
1910                         last_section = (SwitchSection) Sections [Sections.Count-1];
1911                         
1912                         foreach (SwitchSection ss in Sections){
1913                                 Label sec_begin = ig.DefineLabel ();
1914
1915                                 if (pending_goto_end)
1916                                         ig.Emit (OpCodes.Br, end_of_switch);
1917
1918                                 int label_count = ss.Labels.Count;
1919                                 null_found = false;
1920                                 foreach (SwitchLabel sl in ss.Labels){
1921                                         ig.MarkLabel (sl.ILLabel);
1922                                         
1923                                         if (!first_test){
1924                                                 ig.MarkLabel (next_test);
1925                                                 next_test = ig.DefineLabel ();
1926                                         }
1927                                         //
1928                                         // If we are the default target
1929                                         //
1930                                         if (sl.Label == null){
1931                                                 ig.MarkLabel (default_target);
1932                                                 default_found = true;
1933                                         } else {
1934                                                 object lit = sl.Converted;
1935
1936                                                 if (lit is NullLiteral){
1937                                                         null_found = true;
1938                                                         if (label_count == 1)
1939                                                                 ig.Emit (OpCodes.Br, next_test);
1940                                                         continue;
1941                                                                               
1942                                                 }
1943                                                 if (is_string){
1944                                                         StringConstant str = (StringConstant) lit;
1945
1946                                                         ig.Emit (OpCodes.Ldloc, val);
1947                                                         ig.Emit (OpCodes.Ldstr, str.Value);
1948                                                         if (label_count == 1)
1949                                                                 ig.Emit (OpCodes.Bne_Un, next_test);
1950                                                         else
1951                                                                 ig.Emit (OpCodes.Beq, sec_begin);
1952                                                 } else {
1953                                                         ig.Emit (OpCodes.Ldloc, val);
1954                                                         EmitObjectInteger (ig, lit);
1955                                                         ig.Emit (OpCodes.Ceq);
1956                                                         if (label_count == 1)
1957                                                                 ig.Emit (OpCodes.Brfalse, next_test);
1958                                                         else
1959                                                                 ig.Emit (OpCodes.Brtrue, sec_begin);
1960                                                 }
1961                                         }
1962                                 }
1963                                 if (label_count != 1 && ss != last_section)
1964                                         ig.Emit (OpCodes.Br, next_test);
1965                                 
1966                                 if (null_found)
1967                                         ig.MarkLabel (null_target);
1968                                 ig.MarkLabel (sec_begin);
1969                                 foreach (SwitchLabel sl in ss.Labels)
1970                                         ig.MarkLabel (sl.ILLabelCode);
1971                                 if (ss.Block.Emit (ec))
1972                                         pending_goto_end = false;
1973                                 else {
1974                                         all_return = false;
1975                                         pending_goto_end = true;
1976                                 }
1977                                 first_test = false;
1978                         }
1979                         if (!default_found){
1980                                 ig.MarkLabel (default_target);
1981                                 all_return = false;
1982                         }
1983                         ig.MarkLabel (next_test);
1984                         ig.MarkLabel (end_of_switch);
1985                         
1986                         return all_return;
1987                 }
1988
1989                 public override bool Resolve (EmitContext ec)
1990                 {
1991                         foreach (SwitchSection ss in Sections){
1992                                 if (ss.Block.Resolve (ec) != true)
1993                                         return false;
1994                         }
1995
1996                         return true;
1997                 }
1998                 
1999                 public override bool Emit (EmitContext ec)
2000                 {
2001                         Expr = Expr.Resolve (ec);
2002                         if (Expr == null)
2003                                 return false;
2004
2005                         Expression new_expr = SwitchGoverningType (ec, Expr.Type);
2006                         if (new_expr == null){
2007                                 Report.Error (151, loc, "An integer type or string was expected for switch");
2008                                 return false;
2009                         }
2010
2011                         // Validate switch.
2012                         SwitchType = new_expr.Type;
2013
2014                         if (!CheckSwitch (ec))
2015                                 return false;
2016
2017                         // Store variable for comparission purposes
2018                         LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
2019                         new_expr.Emit (ec);
2020                         ec.ig.Emit (OpCodes.Stloc, value);
2021
2022                         ILGenerator ig = ec.ig;
2023
2024                         default_target = ig.DefineLabel ();
2025
2026                         //
2027                         // Setup the codegen context
2028                         //
2029                         Label old_end = ec.LoopEnd;
2030                         Switch old_switch = ec.Switch;
2031                         
2032                         ec.LoopEnd = ig.DefineLabel ();
2033                         ec.Switch = this;
2034
2035                         // Emit Code.
2036                         bool all_return;
2037                         if (SwitchType == TypeManager.string_type)
2038                                 all_return = SimpleSwitchEmit (ec, value);
2039                         else
2040                                 all_return = TableSwitchEmit (ec, value);
2041
2042                         // Restore context state. 
2043                         ig.MarkLabel (ec.LoopEnd);
2044
2045                         //
2046                         // Restore the previous context
2047                         //
2048                         ec.LoopEnd = old_end;
2049                         ec.Switch = old_switch;
2050                         
2051                         return all_return;
2052                 }
2053         }
2054
2055         public class Lock : Statement {
2056                 Expression expr;
2057                 Statement Statement;
2058                         
2059                 public Lock (Expression expr, Statement stmt, Location l)
2060                 {
2061                         this.expr = expr;
2062                         Statement = stmt;
2063                         loc = l;
2064                 }
2065
2066                 public override bool Resolve (EmitContext ec)
2067                 {
2068                         expr = expr.Resolve (ec);
2069                         return Statement.Resolve (ec) && expr != null;
2070                 }
2071                 
2072                 public override bool Emit (EmitContext ec)
2073                 {
2074                         Type type = expr.Type;
2075                         bool val;
2076                         
2077                         if (type.IsValueType){
2078                                 Report.Error (185, loc, "lock statement requires the expression to be " +
2079                                               " a reference type (type is: `" +
2080                                               TypeManager.CSharpName (type) + "'");
2081                                 return false;
2082                         }
2083
2084                         ILGenerator ig = ec.ig;
2085                         LocalBuilder temp = ig.DeclareLocal (type);
2086                                 
2087                         expr.Emit (ec);
2088                         ig.Emit (OpCodes.Dup);
2089                         ig.Emit (OpCodes.Stloc, temp);
2090                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
2091
2092                         // try
2093                         Label end = ig.BeginExceptionBlock ();
2094                         bool old_in_try = ec.InTry;
2095                         ec.InTry = true;
2096                         Label finish = ig.DefineLabel ();
2097                         val = Statement.Emit (ec);
2098                         ec.InTry = old_in_try;
2099                         // ig.Emit (OpCodes.Leave, finish);
2100
2101                         ig.MarkLabel (finish);
2102                         
2103                         // finally
2104                         ig.BeginFinallyBlock ();
2105                         ig.Emit (OpCodes.Ldloc, temp);
2106                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
2107                         ig.EndExceptionBlock ();
2108                         
2109                         return val;
2110                 }
2111         }
2112
2113         public class Unchecked : Statement {
2114                 public readonly Block Block;
2115                 
2116                 public Unchecked (Block b)
2117                 {
2118                         Block = b;
2119                 }
2120
2121                 public override bool Resolve (EmitContext ec)
2122                 {
2123                         return Block.Resolve (ec);
2124                 }
2125                 
2126                 public override bool Emit (EmitContext ec)
2127                 {
2128                         bool previous_state = ec.CheckState;
2129                         bool previous_state_const = ec.ConstantCheckState;
2130                         bool val;
2131                         
2132                         ec.CheckState = false;
2133                         ec.ConstantCheckState = false;
2134                         val = Block.Emit (ec);
2135                         ec.CheckState = previous_state;
2136                         ec.ConstantCheckState = previous_state_const;
2137
2138                         return val;
2139                 }
2140         }
2141
2142         public class Checked : Statement {
2143                 public readonly Block Block;
2144                 
2145                 public Checked (Block b)
2146                 {
2147                         Block = b;
2148                 }
2149
2150                 public override bool Resolve (EmitContext ec)
2151                 {
2152                         bool previous_state = ec.CheckState;
2153                         bool previous_state_const = ec.ConstantCheckState;
2154                         
2155                         ec.CheckState = true;
2156                         ec.ConstantCheckState = true;
2157                         bool ret = Block.Resolve (ec);
2158                         ec.CheckState = previous_state;
2159                         ec.ConstantCheckState = previous_state_const;
2160
2161                         return ret;
2162                 }
2163
2164                 public override bool Emit (EmitContext ec)
2165                 {
2166                         bool previous_state = ec.CheckState;
2167                         bool previous_state_const = ec.ConstantCheckState;
2168                         bool val;
2169                         
2170                         ec.CheckState = true;
2171                         ec.ConstantCheckState = true;
2172                         val = Block.Emit (ec);
2173                         ec.CheckState = previous_state;
2174                         ec.ConstantCheckState = previous_state_const;
2175
2176                         return val;
2177                 }
2178         }
2179
2180         public class Unsafe : Statement {
2181                 public readonly Block Block;
2182
2183                 public Unsafe (Block b)
2184                 {
2185                         Block = b;
2186                 }
2187
2188                 public override bool Resolve (EmitContext ec)
2189                 {
2190                         bool previous_state = ec.InUnsafe;
2191                         bool val;
2192                         
2193                         ec.InUnsafe = true;
2194                         val = Block.Resolve (ec);
2195                         ec.InUnsafe = previous_state;
2196
2197                         return val;
2198                 }
2199                 
2200                 public override bool Emit (EmitContext ec)
2201                 {
2202                         bool previous_state = ec.InUnsafe;
2203                         bool val;
2204                         
2205                         ec.InUnsafe = true;
2206                         val = Block.Emit (ec);
2207                         ec.InUnsafe = previous_state;
2208
2209                         return val;
2210                 }
2211         }
2212
2213         // 
2214         // Fixed statement
2215         //
2216         public class Fixed : Statement {
2217                 string    type;
2218                 ArrayList declarators;
2219                 Statement statement;
2220
2221                 public Fixed (string type, ArrayList decls, Statement stmt, Location l)
2222                 {
2223                         this.type = type;
2224                         declarators = decls;
2225                         statement = stmt;
2226                         loc = l;
2227                 }
2228
2229                 public override bool Resolve (EmitContext ec)
2230                 {
2231                         return statement.Resolve (ec);
2232                 }
2233                 
2234                 public override bool Emit (EmitContext ec)
2235                 {
2236                         ILGenerator ig = ec.ig;
2237                         Type t;
2238                         
2239                         t = RootContext.LookupType (ec.DeclSpace, type, false, loc);
2240                         if (t == null)
2241                                 return false;
2242
2243                         bool is_ret = false;
2244
2245                         foreach (Pair p in declarators){
2246                                 VariableInfo vi = (VariableInfo) p.First;
2247                                 Expression e = (Expression) p.Second;
2248
2249                                 //
2250                                 // The rules for the possible declarators are pretty wise,
2251                                 // but the production on the grammar is more concise.
2252                                 //
2253                                 // So we have to enforce these rules here.
2254                                 //
2255                                 // We do not resolve before doing the case 1 test,
2256                                 // because the grammar is explicit in that the token &
2257                                 // is present, so we need to test for this particular case.
2258                                 //
2259
2260                                 //
2261                                 // Case 1: & object.
2262                                 //
2263                                 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
2264                                         Expression child = ((Unary) e).Expr;
2265
2266                                         vi.MakePinned ();
2267                                         if (child is ParameterReference || child is LocalVariableReference){
2268                                                 Report.Error (
2269                                                         213, loc, 
2270                                                         "No need to use fixed statement for parameters or " +
2271                                                         "local variable declarations (address is already " +
2272                                                         "fixed)");
2273                                                 continue;
2274                                         }
2275                                         
2276                                         e = e.Resolve (ec);
2277                                         if (e == null)
2278                                                 continue;
2279
2280                                         child = ((Unary) e).Expr;
2281                                         
2282                                         if (!TypeManager.VerifyUnManaged (child.Type, loc))
2283                                                 continue;
2284
2285                                         //
2286                                         // Store pointer in pinned location
2287                                         //
2288                                         e.Emit (ec);
2289                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2290
2291                                         is_ret = statement.Emit (ec);
2292
2293                                         // Clear the pinned variable.
2294                                         ig.Emit (OpCodes.Ldc_I4_0);
2295                                         ig.Emit (OpCodes.Conv_U);
2296                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2297
2298                                         continue;
2299                                 }
2300
2301                                 e = e.Resolve (ec);
2302                                 if (e == null)
2303                                         continue;
2304
2305                                 //
2306                                 // Case 2: Array
2307                                 //
2308                                 if (e.Type.IsArray){
2309                                         Type array_type = e.Type.GetElementType ();
2310                                         
2311                                         vi.MakePinned ();
2312                                         //
2313                                         // Provided that array_type is unmanaged,
2314                                         //
2315                                         if (!TypeManager.VerifyUnManaged (array_type, loc))
2316                                                 continue;
2317
2318                                         //
2319                                         // and T* is implicitly convertible to the
2320                                         // pointer type given in the fixed statement.
2321                                         //
2322                                         ArrayPtr array_ptr = new ArrayPtr (e);
2323                                         
2324                                         Expression converted = Expression.ConvertImplicitRequired (
2325                                                 ec, array_ptr, vi.VariableType, loc);
2326                                         if (converted == null)
2327                                                 continue;
2328
2329                                         //
2330                                         // Store pointer in pinned location
2331                                         //
2332                                         converted.Emit (ec);
2333                                         
2334                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2335
2336                                         is_ret = statement.Emit (ec);
2337                                         
2338                                         // Clear the pinned variable.
2339                                         ig.Emit (OpCodes.Ldc_I4_0);
2340                                         ig.Emit (OpCodes.Conv_U);
2341                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2342
2343                                         continue;
2344                                 }
2345
2346                                 //
2347                                 // Case 3: string
2348                                 //
2349                                 if (e.Type == TypeManager.string_type){
2350                                         LocalBuilder pinned_string = ig.DeclareLocal (TypeManager.string_type);
2351                                         TypeManager.MakePinned (pinned_string);
2352                                         
2353                                         e.Emit (ec);
2354                                         ig.Emit (OpCodes.Stloc, pinned_string);
2355
2356                                         Expression sptr = new StringPtr (pinned_string);
2357                                         Expression converted = Expression.ConvertImplicitRequired (
2358                                                 ec, sptr, vi.VariableType, loc);
2359                                         
2360                                         if (converted == null)
2361                                                 continue;
2362
2363                                         converted.Emit (ec);
2364                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2365                                         
2366                                         is_ret = statement.Emit (ec);
2367
2368                                         // Clear the pinned variable
2369                                         ig.Emit (OpCodes.Ldnull);
2370                                         ig.Emit (OpCodes.Stloc, pinned_string);
2371                                 }
2372                         }
2373
2374                         return is_ret;
2375                 }
2376         }
2377         
2378         public class Catch {
2379                 public readonly string Type;
2380                 public readonly string Name;
2381                 public readonly Block  Block;
2382                 public readonly Location Location;
2383                 
2384                 public Catch (string type, string name, Block block, Location l)
2385                 {
2386                         Type = type;
2387                         Name = name;
2388                         Block = block;
2389                         Location = l;
2390                 }
2391         }
2392
2393         public class Try : Statement {
2394                 public readonly Block Fini, Block;
2395                 public readonly ArrayList Specific;
2396                 public readonly Catch General;
2397                 
2398                 //
2399                 // specific, general and fini might all be null.
2400                 //
2401                 public Try (Block block, ArrayList specific, Catch general, Block fini)
2402                 {
2403                         if (specific == null && general == null){
2404                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
2405                         }
2406                         
2407                         this.Block = block;
2408                         this.Specific = specific;
2409                         this.General = general;
2410                         this.Fini = fini;
2411                 }
2412
2413                 public override bool Resolve (EmitContext ec)
2414                 {
2415                         bool ok = true;
2416                         
2417                         if (General != null)
2418                                 if (!General.Block.Resolve (ec))
2419                                         ok = false;
2420
2421                         foreach (Catch c in Specific){
2422                                 if (!c.Block.Resolve (ec))
2423                                         ok = false;
2424                         }
2425
2426                         if (!Block.Resolve (ec))
2427                                 ok = false;
2428
2429                         if (Fini != null)
2430                                 if (!Fini.Resolve (ec))
2431                                         ok = false;
2432                         
2433                         return ok;
2434                 }
2435                 
2436                 public override bool Emit (EmitContext ec)
2437                 {
2438                         ILGenerator ig = ec.ig;
2439                         Label end;
2440                         Label finish = ig.DefineLabel ();;
2441                         bool returns;
2442                         
2443                         end = ig.BeginExceptionBlock ();
2444                         bool old_in_try = ec.InTry;
2445                         ec.InTry = true;
2446                         returns = Block.Emit (ec);
2447                         ec.InTry = old_in_try;
2448
2449                         //
2450                         // System.Reflection.Emit provides this automatically:
2451                         // ig.Emit (OpCodes.Leave, finish);
2452
2453                         bool old_in_catch = ec.InCatch;
2454                         ec.InCatch = true;
2455                         DeclSpace ds = ec.DeclSpace;
2456
2457                         foreach (Catch c in Specific){
2458                                 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
2459                                 VariableInfo vi;
2460                                 
2461                                 if (catch_type == null)
2462                                         return false;
2463
2464                                 ig.BeginCatchBlock (catch_type);
2465
2466                                 if (c.Name != null){
2467                                         vi = c.Block.GetVariableInfo (c.Name);
2468                                         if (vi == null){
2469                                                 Console.WriteLine ("This should not happen! variable does not exist in this block");
2470                                                 Environment.Exit (0);
2471                                         }
2472                                 
2473                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
2474                                 } else
2475                                         ig.Emit (OpCodes.Pop);
2476                                 
2477                                 if (!c.Block.Emit (ec))
2478                                         returns = false;
2479                         }
2480
2481                         if (General != null){
2482                                 ig.BeginCatchBlock (TypeManager.object_type);
2483                                 ig.Emit (OpCodes.Pop);
2484                                 if (!General.Block.Emit (ec))
2485                                         returns = false;
2486                         }
2487                         ec.InCatch = old_in_catch;
2488
2489                         ig.MarkLabel (finish);
2490                         if (Fini != null){
2491                                 ig.BeginFinallyBlock ();
2492                                 bool old_in_finally = ec.InFinally;
2493                                 ec.InFinally = true;
2494                                 Fini.Emit (ec);
2495                                 ec.InFinally = old_in_finally;
2496                         }
2497                         
2498                         ig.EndExceptionBlock ();
2499
2500                         //
2501                         // FIXME: Is this correct?
2502                         // Replace with `returns' and check test-18, maybe we can
2503                         // perform an optimization here.
2504                         //
2505                         return returns;
2506                 }
2507         }
2508
2509         //
2510         // FIXME: We still do not support the expression variant of the using
2511         // statement.
2512         //
2513         public class Using : Statement {
2514                 object expression_or_block;
2515                 Statement Statement;
2516                 
2517                 public Using (object expression_or_block, Statement stmt, Location l)
2518                 {
2519                         this.expression_or_block = expression_or_block;
2520                         Statement = stmt;
2521                         loc = l;
2522                 }
2523
2524                 //
2525                 // Emits the code for the case of using using a local variable declaration.
2526                 //
2527                 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
2528                 {
2529                         ILGenerator ig = ec.ig;
2530                         Expression [] converted_vars;
2531                         bool need_conv = false;
2532                         Type type = RootContext.LookupType (ec.DeclSpace, type_name, false, loc);
2533                         int i = 0;
2534
2535                         if (type == null)
2536                                 return false;
2537                         
2538                         //
2539                         // The type must be an IDisposable or an implicit conversion
2540                         // must exist.
2541                         //
2542                         converted_vars = new Expression [var_list.Count];
2543                         if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
2544                                 foreach (DictionaryEntry e in var_list){
2545                                         Expression var = (Expression) e.Key;
2546
2547                                         var = var.Resolve (ec);
2548                                         if (var == null)
2549                                                 return false;
2550                                         
2551                                         converted_vars [i] = Expression.ConvertImplicit (
2552                                                 ec, var, TypeManager.idisposable_type, loc);
2553
2554                                         if (converted_vars [i] == null)
2555                                                 return false;
2556                                         i++;
2557                                 }
2558                                 need_conv = true;
2559                         }
2560                         
2561                         i = 0;
2562                         bool old_in_try = ec.InTry;
2563                         ec.InTry = true;
2564                         bool error = false;
2565                         foreach (DictionaryEntry e in var_list){
2566                                 LocalVariableReference var = (LocalVariableReference) e.Key;
2567                                 Expression expr = (Expression) e.Value;
2568                                 Expression a;
2569
2570                                 a = new Assign (var, expr, loc);
2571                                 a = a.Resolve (ec);
2572                                 if (!need_conv)
2573                                         converted_vars [i] = var;
2574                                 i++;
2575                                 if (a == null){
2576                                         error = true;
2577                                         continue;
2578                                 }
2579                                 ((ExpressionStatement) a).EmitStatement (ec);
2580                                 
2581                                 ig.BeginExceptionBlock ();
2582
2583                         }
2584                         if (error)
2585                                 return false;
2586                         Statement.Emit (ec);
2587                         ec.InTry = old_in_try;
2588
2589                         bool old_in_finally = ec.InFinally;
2590                         ec.InFinally = true;
2591                         var_list.Reverse ();
2592                         foreach (DictionaryEntry e in var_list){
2593                                 LocalVariableReference var = (LocalVariableReference) e.Key;
2594                                 Label skip = ig.DefineLabel ();
2595                                 i--;
2596                                 
2597                                 ig.BeginFinallyBlock ();
2598                                 
2599                                 var.Emit (ec);
2600                                 ig.Emit (OpCodes.Brfalse, skip);
2601                                 converted_vars [i].Emit (ec);
2602                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2603                                 ig.MarkLabel (skip);
2604                                 ig.EndExceptionBlock ();
2605                         }
2606                         ec.InFinally = old_in_finally;
2607
2608                         return false;
2609                 }
2610
2611                 bool EmitExpression (EmitContext ec, Expression expr)
2612                 {
2613                         Type expr_type = expr.Type;
2614                         Expression conv = null;
2615                         
2616                         if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
2617                                 conv = Expression.ConvertImplicit (
2618                                         ec, expr, TypeManager.idisposable_type, loc);
2619
2620                                 if (conv == null)
2621                                         return false;
2622                         }
2623
2624                         //
2625                         // Make a copy of the expression and operate on that.
2626                         //
2627                         ILGenerator ig = ec.ig;
2628                         LocalBuilder local_copy = ig.DeclareLocal (expr_type);
2629                         if (conv != null)
2630                                 conv.Emit (ec);
2631                         else
2632                                 expr.Emit (ec);
2633                         ig.Emit (OpCodes.Stloc, local_copy);
2634
2635                         bool old_in_try = ec.InTry;
2636                         ec.InTry = true;
2637                         ig.BeginExceptionBlock ();
2638                         Statement.Emit (ec);
2639                         ec.InTry = old_in_try;
2640                         
2641                         Label skip = ig.DefineLabel ();
2642                         bool old_in_finally = ec.InFinally;
2643                         ig.BeginFinallyBlock ();
2644                         ig.Emit (OpCodes.Ldloc, local_copy);
2645                         ig.Emit (OpCodes.Brfalse, skip);
2646                         ig.Emit (OpCodes.Ldloc, local_copy);
2647                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2648                         ig.MarkLabel (skip);
2649                         ec.InFinally = old_in_finally;
2650                         ig.EndExceptionBlock ();
2651
2652                         return false;
2653                 }
2654                 
2655                 public override bool Resolve (EmitContext ec)
2656                 {
2657                         return Statement.Resolve (ec);
2658                 }
2659                 
2660                 public override bool Emit (EmitContext ec)
2661                 {
2662                         if (expression_or_block is DictionaryEntry){
2663                                 string t = (string) ((DictionaryEntry) expression_or_block).Key;
2664                                 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
2665
2666                                 return EmitLocalVariableDecls (ec, t, var_list);
2667                         } if (expression_or_block is Expression){
2668                                 Expression e = (Expression) expression_or_block;
2669
2670                                 e = e.Resolve (ec);
2671                                 if (e == null)
2672                                         return false;
2673
2674                                 return EmitExpression (ec, e);
2675                         }
2676                         return false;
2677                 }
2678         }
2679
2680         /// <summary>
2681         ///   Implementation of the foreach C# statement
2682         /// </summary>
2683         public class Foreach : Statement {
2684                 string type;
2685                 LocalVariableReference variable;
2686                 Expression expr;
2687                 Statement statement;
2688                 
2689                 public Foreach (string type, LocalVariableReference var, Expression expr,
2690                                 Statement stmt, Location l)
2691                 {
2692                         this.type = type;
2693                         this.variable = var;
2694                         this.expr = expr;
2695                         statement = stmt;
2696                         loc = l;
2697                 }
2698                 
2699                 public override bool Resolve (EmitContext ec)
2700                 {
2701                         expr = expr.Resolve (ec);
2702                         return statement.Resolve (ec) && expr != null;
2703                 }
2704                 
2705                 //
2706                 // Retrieves a `public bool MoveNext ()' method from the Type `t'
2707                 //
2708                 static MethodInfo FetchMethodMoveNext (Type t)
2709                 {
2710                         MemberInfo [] move_next_list;
2711                         
2712                         move_next_list = TypeContainer.FindMembers (
2713                                 t, MemberTypes.Method,
2714                                 BindingFlags.Public | BindingFlags.Instance,
2715                                 Type.FilterName, "MoveNext");
2716                         if (move_next_list == null || move_next_list.Length == 0)
2717                                 return null;
2718
2719                         foreach (MemberInfo m in move_next_list){
2720                                 MethodInfo mi = (MethodInfo) m;
2721                                 Type [] args;
2722                                 
2723                                 args = TypeManager.GetArgumentTypes (mi);
2724                                 if (args != null && args.Length == 0){
2725                                         if (mi.ReturnType == TypeManager.bool_type)
2726                                                 return mi;
2727                                 }
2728                         }
2729                         return null;
2730                 }
2731                 
2732                 //
2733                 // Retrieves a `public T get_Current ()' method from the Type `t'
2734                 //
2735                 static MethodInfo FetchMethodGetCurrent (Type t)
2736                 {
2737                         MemberInfo [] move_next_list;
2738                         
2739                         move_next_list = TypeContainer.FindMembers (
2740                                 t, MemberTypes.Method,
2741                                 BindingFlags.Public | BindingFlags.Instance,
2742                                 Type.FilterName, "get_Current");
2743                         if (move_next_list == null || move_next_list.Length == 0)
2744                                 return null;
2745
2746                         foreach (MemberInfo m in move_next_list){
2747                                 MethodInfo mi = (MethodInfo) m;
2748                                 Type [] args;
2749                                 
2750                                 args = TypeManager.GetArgumentTypes (mi);
2751                                 if (args != null && args.Length == 0)
2752                                         return mi;
2753                         }
2754                         return null;
2755                 }
2756
2757                 // 
2758                 // This struct records the helper methods used by the Foreach construct
2759                 //
2760                 class ForeachHelperMethods {
2761                         public EmitContext ec;
2762                         public MethodInfo get_enumerator;
2763                         public MethodInfo move_next;
2764                         public MethodInfo get_current;
2765
2766                         public ForeachHelperMethods (EmitContext ec)
2767                         {
2768                                 this.ec = ec;
2769                         }
2770                 }
2771                 
2772                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
2773                 {
2774                         if (m == null)
2775                                 return false;
2776                         
2777                         if (!(m is MethodInfo))
2778                                 return false;
2779                         
2780                         if (m.Name != "GetEnumerator")
2781                                 return false;
2782
2783                         MethodInfo mi = (MethodInfo) m;
2784                         Type [] args = TypeManager.GetArgumentTypes (mi);
2785                         if (args != null){
2786                                 if (args.Length != 0)
2787                                         return false;
2788                         }
2789                         ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
2790                         EmitContext ec = hm.ec;
2791                         
2792                         //
2793                         // Check whether GetEnumerator is accessible to us
2794                         //
2795                         MethodAttributes prot = mi.Attributes & MethodAttributes.MemberAccessMask;
2796
2797                         Type declaring = mi.DeclaringType;
2798                         if (prot == MethodAttributes.Private){
2799                                 if (declaring != ec.ContainerType)
2800                                         return false;
2801                         } else if (prot == MethodAttributes.FamANDAssem){
2802                                 // If from a different assembly, false
2803                                 if (!(mi is MethodBuilder))
2804                                         return false;
2805                                 //
2806                                 // Are we being invoked from the same class, or from a derived method?
2807                                 //
2808                                 if (ec.ContainerType != declaring){
2809                                         if (!ec.ContainerType.IsSubclassOf (declaring))
2810                                                 return false;
2811                                 }
2812                         } else if (prot == MethodAttributes.FamORAssem){
2813                                 if (!(mi is MethodBuilder ||
2814                                       ec.ContainerType == declaring ||
2815                                       ec.ContainerType.IsSubclassOf (declaring)))
2816                                         return false;
2817                         } if (prot == MethodAttributes.Family){
2818                                 if (!(ec.ContainerType == declaring ||
2819                                       ec.ContainerType.IsSubclassOf (declaring)))
2820                                         return false;
2821                         }
2822
2823                         //
2824                         // Ok, we can access it, now make sure that we can do something
2825                         // with this `GetEnumerator'
2826                         //
2827                         if (mi.ReturnType == TypeManager.ienumerator_type || 
2828                             TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType)){
2829                                 hm.move_next = TypeManager.bool_movenext_void;
2830                                 hm.get_current = TypeManager.object_getcurrent_void;
2831                                 return true;
2832                         }
2833
2834                         //
2835                         // Ok, so they dont return an IEnumerable, we will have to
2836                         // find if they support the GetEnumerator pattern.
2837                         //
2838                         Type return_type = mi.ReturnType;
2839
2840                         hm.move_next = FetchMethodMoveNext (return_type);
2841                         if (hm.move_next == null)
2842                                 return false;
2843                         hm.get_current = FetchMethodGetCurrent (return_type);
2844                         if (hm.get_current == null)
2845                                 return false;
2846
2847                         return true;
2848                 }
2849                 
2850                 /// <summary>
2851                 ///   This filter is used to find the GetEnumerator method
2852                 ///   on which IEnumerator operates
2853                 /// </summary>
2854                 static MemberFilter FilterEnumerator;
2855                 
2856                 static Foreach ()
2857                 {
2858                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
2859                 }
2860
2861                 void error1579 (Type t)
2862                 {
2863                         Report.Error (1579, loc,
2864                                       "foreach statement cannot operate on variables of type `" +
2865                                       t.FullName + "' because that class does not provide a " +
2866                                       " GetEnumerator method or it is inaccessible");
2867                 }
2868
2869                 static bool TryType (Type t, ForeachHelperMethods hm)
2870                 {
2871                         MemberInfo [] mi;
2872                         
2873                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2874                                                         BindingFlags.Public | BindingFlags.NonPublic |
2875                                                         BindingFlags.Instance,
2876                                                         FilterEnumerator, hm);
2877
2878                         if (mi == null || mi.Length == 0)
2879                                 return false;
2880
2881                         hm.get_enumerator = (MethodInfo) mi [0];
2882                         return true;    
2883                 }
2884                 
2885                 //
2886                 // Looks for a usable GetEnumerator in the Type, and if found returns
2887                 // the three methods that participate: GetEnumerator, MoveNext and get_Current
2888                 //
2889                 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
2890                 {
2891                         ForeachHelperMethods hm = new ForeachHelperMethods (ec);
2892
2893                         if (TryType (t, hm))
2894                                 return hm;
2895
2896                         //
2897                         // Now try to find the method in the interfaces
2898                         //
2899                         while (t != null){
2900                                 Type [] ifaces = t.GetInterfaces ();
2901
2902                                 foreach (Type i in ifaces){
2903                                         if (TryType (i, hm))
2904                                                 return hm;
2905                                 }
2906                                 
2907                                 //
2908                                 // Since TypeBuilder.GetInterfaces only returns the interface
2909                                 // types for this type, we have to keep looping, but once
2910                                 // we hit a non-TypeBuilder (ie, a Type), then we know we are
2911                                 // done, because it returns all the types
2912                                 //
2913                                 if ((t is TypeBuilder))
2914                                         t = t.BaseType;
2915                                 else
2916                                         break;
2917                         } 
2918
2919                         return null;
2920                 }
2921
2922                 //
2923                 // FIXME: possible optimization.
2924                 // We might be able to avoid creating `empty' if the type is the sam
2925                 //
2926                 bool EmitCollectionForeach (EmitContext ec, Type var_type, ForeachHelperMethods hm)
2927                 {
2928                         ILGenerator ig = ec.ig;
2929                         LocalBuilder enumerator, disposable;
2930                         Expression empty = new EmptyExpression ();
2931                         Expression conv;
2932
2933                         //
2934                         // FIXME: maybe we can apply the same trick we do in the
2935                         // array handling to avoid creating empty and conv in some cases.
2936                         //
2937                         // Although it is not as important in this case, as the type
2938                         // will not likely be object (what the enumerator will return).
2939                         //
2940                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2941                         if (conv == null)
2942                                 return false;
2943                         
2944                         enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2945                         disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2946                         
2947                         //
2948                         // Instantiate the enumerator
2949                         //
2950                         if (expr.Type.IsValueType){
2951                                 if (expr is IMemoryLocation){
2952                                         IMemoryLocation ml = (IMemoryLocation) expr;
2953
2954                                         ml.AddressOf (ec, AddressOp.Load);
2955                                 } else
2956                                         throw new Exception ("Expr " + expr + " of type " + expr.Type +
2957                                                              " does not implement IMemoryLocation");
2958                                 ig.Emit (OpCodes.Call, hm.get_enumerator);
2959                         } else {
2960                                 expr.Emit (ec);
2961                                 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
2962                         }
2963                         ig.Emit (OpCodes.Stloc, enumerator);
2964
2965                         //
2966                         // Protect the code in a try/finalize block, so that
2967                         // if the beast implement IDisposable, we get rid of it
2968                         //
2969                         Label l = ig.BeginExceptionBlock ();
2970                         bool old_in_try = ec.InTry;
2971                         ec.InTry = true;
2972                         
2973                         Label end_try = ig.DefineLabel ();
2974                         
2975                         ig.MarkLabel (ec.LoopBegin);
2976                         ig.Emit (OpCodes.Ldloc, enumerator);
2977                         ig.Emit (OpCodes.Callvirt, hm.move_next);
2978                         ig.Emit (OpCodes.Brfalse, end_try);
2979                         ig.Emit (OpCodes.Ldloc, enumerator);
2980                         ig.Emit (OpCodes.Callvirt, hm.get_current);
2981                         variable.EmitAssign (ec, conv);
2982                         statement.Emit (ec);
2983                         ig.Emit (OpCodes.Br, ec.LoopBegin);
2984                         ig.MarkLabel (end_try);
2985                         ec.InTry = old_in_try;
2986                         
2987                         // The runtime provides this for us.
2988                         // ig.Emit (OpCodes.Leave, end);
2989
2990                         //
2991                         // Now the finally block
2992                         //
2993                         Label end_finally = ig.DefineLabel ();
2994                         bool old_in_finally = ec.InFinally;
2995                         ec.InFinally = true;
2996                         ig.BeginFinallyBlock ();
2997                         
2998                         ig.Emit (OpCodes.Ldloc, enumerator);
2999                         ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
3000                         ig.Emit (OpCodes.Stloc, disposable);
3001                         ig.Emit (OpCodes.Ldloc, disposable);
3002                         ig.Emit (OpCodes.Brfalse, end_finally);
3003                         ig.Emit (OpCodes.Ldloc, disposable);
3004                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3005                         ig.MarkLabel (end_finally);
3006                         ec.InFinally = old_in_finally;
3007
3008                         // The runtime generates this anyways.
3009                         // ig.Emit (OpCodes.Endfinally);
3010
3011                         ig.EndExceptionBlock ();
3012
3013                         ig.MarkLabel (ec.LoopEnd);
3014                         return false;
3015                 }
3016
3017                 //
3018                 // FIXME: possible optimization.
3019                 // We might be able to avoid creating `empty' if the type is the sam
3020                 //
3021                 bool EmitArrayForeach (EmitContext ec, Type var_type)
3022                 {
3023                         Type array_type = expr.Type;
3024                         Type element_type = array_type.GetElementType ();
3025                         Expression conv = null;
3026                         Expression empty = new EmptyExpression (element_type);
3027                         
3028                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
3029                         if (conv == null)
3030                                 return false;
3031
3032                         int rank = array_type.GetArrayRank ();
3033                         ILGenerator ig = ec.ig;
3034
3035                         LocalBuilder copy = ig.DeclareLocal (array_type);
3036                         
3037                         //
3038                         // Make our copy of the array
3039                         //
3040                         expr.Emit (ec);
3041                         ig.Emit (OpCodes.Stloc, copy);
3042                         
3043                         if (rank == 1){
3044                                 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
3045
3046                                 Label loop, test;
3047                                 
3048                                 ig.Emit (OpCodes.Ldc_I4_0);
3049                                 ig.Emit (OpCodes.Stloc, counter);
3050                                 test = ig.DefineLabel ();
3051                                 ig.Emit (OpCodes.Br, test);
3052
3053                                 loop = ig.DefineLabel ();
3054                                 ig.MarkLabel (loop);
3055
3056                                 ig.Emit (OpCodes.Ldloc, copy);
3057                                 ig.Emit (OpCodes.Ldloc, counter);
3058                                 ArrayAccess.EmitLoadOpcode (ig, var_type);
3059
3060                                 variable.EmitAssign (ec, conv);
3061
3062                                 statement.Emit (ec);
3063
3064                                 ig.MarkLabel (ec.LoopBegin);
3065                                 ig.Emit (OpCodes.Ldloc, counter);
3066                                 ig.Emit (OpCodes.Ldc_I4_1);
3067                                 ig.Emit (OpCodes.Add);
3068                                 ig.Emit (OpCodes.Stloc, counter);
3069
3070                                 ig.MarkLabel (test);
3071                                 ig.Emit (OpCodes.Ldloc, counter);
3072                                 ig.Emit (OpCodes.Ldloc, copy);
3073                                 ig.Emit (OpCodes.Ldlen);
3074                                 ig.Emit (OpCodes.Conv_I4);
3075                                 ig.Emit (OpCodes.Blt, loop);
3076                         } else {
3077                                 LocalBuilder [] dim_len   = new LocalBuilder [rank];
3078                                 LocalBuilder [] dim_count = new LocalBuilder [rank];
3079                                 Label [] loop = new Label [rank];
3080                                 Label [] test = new Label [rank];
3081                                 int dim;
3082                                 
3083                                 for (dim = 0; dim < rank; dim++){
3084                                         dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
3085                                         dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
3086                                         test [dim] = ig.DefineLabel ();
3087                                         loop [dim] = ig.DefineLabel ();
3088                                 }
3089                                         
3090                                 for (dim = 0; dim < rank; dim++){
3091                                         ig.Emit (OpCodes.Ldloc, copy);
3092                                         IntLiteral.EmitInt (ig, dim);
3093                                         ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
3094                                         ig.Emit (OpCodes.Stloc, dim_len [dim]);
3095                                 }
3096
3097                                 for (dim = 0; dim < rank; dim++){
3098                                         ig.Emit (OpCodes.Ldc_I4_0);
3099                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
3100                                         ig.Emit (OpCodes.Br, test [dim]);
3101                                         ig.MarkLabel (loop [dim]);
3102                                 }
3103
3104                                 ig.Emit (OpCodes.Ldloc, copy);
3105                                 for (dim = 0; dim < rank; dim++)
3106                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3107
3108                                 //
3109                                 // FIXME: Maybe we can cache the computation of `get'?
3110                                 //
3111                                 Type [] args = new Type [rank];
3112                                 MethodInfo get;
3113
3114                                 for (int i = 0; i < rank; i++)
3115                                         args [i] = TypeManager.int32_type;
3116
3117                                 ModuleBuilder mb = CodeGen.ModuleBuilder;
3118                                 get = mb.GetArrayMethod (
3119                                         array_type, "Get",
3120                                         CallingConventions.HasThis| CallingConventions.Standard,
3121                                         var_type, args);
3122                                 ig.Emit (OpCodes.Call, get);
3123                                 variable.EmitAssign (ec, conv);
3124                                 statement.Emit (ec);
3125                                 ig.MarkLabel (ec.LoopBegin);
3126                                 for (dim = rank - 1; dim >= 0; dim--){
3127                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3128                                         ig.Emit (OpCodes.Ldc_I4_1);
3129                                         ig.Emit (OpCodes.Add);
3130                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
3131
3132                                         ig.MarkLabel (test [dim]);
3133                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
3134                                         ig.Emit (OpCodes.Ldloc, dim_len [dim]);
3135                                         ig.Emit (OpCodes.Blt, loop [dim]);
3136                                 }
3137                         }
3138                         ig.MarkLabel (ec.LoopEnd);
3139                         
3140                         return false;
3141                 }
3142                 
3143                 public override bool Emit (EmitContext ec)
3144                 {
3145                         Type var_type;
3146                         bool ret_val;
3147                         
3148                         var_type = RootContext.LookupType (ec.DeclSpace, type, false, loc);
3149                         if (var_type == null)
3150                                 return false;
3151                         
3152                         //
3153                         // We need an instance variable.  Not sure this is the best
3154                         // way of doing this.
3155                         //
3156                         // FIXME: When we implement propertyaccess, will those turn
3157                         // out to return values in ExprClass?  I think they should.
3158                         //
3159                         if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
3160                               expr.eclass == ExprClass.PropertyAccess)){
3161                                 error1579 (expr.Type);
3162                                 return false;
3163                         }
3164
3165                         ILGenerator ig = ec.ig;
3166                         
3167                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
3168                         bool old_inloop = ec.InLoop;
3169                         ec.LoopBegin = ig.DefineLabel ();
3170                         ec.LoopEnd = ig.DefineLabel ();
3171                         ec.InLoop = true;
3172                         
3173                         if (expr.Type.IsArray)
3174                                 ret_val = EmitArrayForeach (ec, var_type);
3175                         else {
3176                                 ForeachHelperMethods hm;
3177                                 
3178                                 hm = ProbeCollectionType (ec, expr.Type);
3179                                 if (hm == null){
3180                                         error1579 (expr.Type);
3181                                         return false;
3182                                 }
3183
3184                                 ret_val = EmitCollectionForeach (ec, var_type, hm);
3185                         }
3186                         
3187                         ec.LoopBegin = old_begin;
3188                         ec.LoopEnd = old_end;
3189                         ec.InLoop = old_inloop;
3190
3191                         return ret_val;
3192                 }
3193         }
3194 }
3195