Revert last patch
[mono.git] / mcs / mcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 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
21                 /// <summary>
22                 ///   Return value indicates whether all code paths emitted return.
23                 /// </summary>
24                 public abstract bool Emit (EmitContext ec);
25
26                 /// <remarks>
27                 ///    Emits a bool expression.
28                 /// </remarks>
29                 public static bool EmitBoolExpression (EmitContext ec, Expression e, Label l, bool isTrue)
30                 {
31                         e = e.Resolve (ec);
32
33                         if (e == null)
34                                 return false;
35
36                         if (e.Type != TypeManager.bool_type)
37                                 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
38                                                                 new Location (-1));
39
40                         if (e == null){
41                                 Report.Error (
42                                         31, "Can not convert the expression to a boolean");
43                                 return false;
44                         }
45
46                         bool invert = false;
47                         if (e is Unary){
48                                 Unary u = (Unary) e;
49                                 
50                                 if (u.Oper == Unary.Operator.LogicalNot){
51                                         invert = true;
52
53                                         u.EmitLogicalNot (ec);
54                                 }
55                         } 
56
57                         if (!invert)
58                                 e.Emit (ec);
59
60                         if (isTrue){
61                                 if (invert)
62                                         ec.ig.Emit (OpCodes.Brfalse, l);
63                                 else
64                                         ec.ig.Emit (OpCodes.Brtrue, l);
65                         } else {
66                                 if (invert)
67                                         ec.ig.Emit (OpCodes.Brtrue, l);
68                                 else
69                                         ec.ig.Emit (OpCodes.Brfalse, l);
70                         }
71                         
72                         return true;
73                 }
74
75         }
76
77         public class EmptyStatement : Statement {
78                 public override bool Emit (EmitContext ec)
79                 {
80                         return false;
81                 }
82         }
83         
84         public class If : Statement {
85                 public readonly Expression  Expr;
86                 public readonly Statement   TrueStatement;
87                 public readonly Statement   FalseStatement;
88                 
89                 public If (Expression expr, Statement trueStatement)
90                 {
91                         Expr = expr;
92                         TrueStatement = trueStatement;
93                 }
94
95                 public If (Expression expr,
96                            Statement trueStatement,
97                            Statement falseStatement)
98                 {
99                         Expr = expr;
100                         TrueStatement = trueStatement;
101                         FalseStatement = falseStatement;
102                 }
103
104                 public override bool Emit (EmitContext ec)
105                 {
106                         ILGenerator ig = ec.ig;
107                         Label false_target = ig.DefineLabel ();
108                         Label end;
109                         bool is_true_ret, is_false_ret;
110                         
111                         if (!EmitBoolExpression (ec, Expr, false_target, false))
112                                 return false;
113                         
114                         is_true_ret = TrueStatement.Emit (ec);
115                         is_false_ret = is_true_ret;
116
117                         if (FalseStatement != null){
118                                 bool branch_emitted = false;
119                                 
120                                 end = ig.DefineLabel ();
121                                 if (!is_true_ret){
122                                         ig.Emit (OpCodes.Br, end);
123                                         branch_emitted = true;
124                                 }
125                         
126                                 ig.MarkLabel (false_target);
127                                 is_false_ret = FalseStatement.Emit (ec);
128
129                                 if (branch_emitted)
130                                         ig.MarkLabel (end);
131                         } else {
132                                 ig.MarkLabel (false_target);
133                                 is_false_ret = false;
134                         }
135
136                         return is_true_ret && is_false_ret;
137                 }
138         }
139
140         public class Do : Statement {
141                 public readonly Expression Expr;
142                 public readonly Statement  EmbeddedStatement;
143                 
144                 public Do (Statement statement, Expression boolExpr)
145                 {
146                         Expr = boolExpr;
147                         EmbeddedStatement = statement;
148                 }
149
150                 public override bool Emit (EmitContext ec)
151                 {
152                         ILGenerator ig = ec.ig;
153                         Label loop = ig.DefineLabel ();
154                         Label old_begin = ec.LoopBegin;
155                         Label old_end = ec.LoopEnd;
156                         bool  old_inloop = ec.InLoop;
157                         
158                         ec.LoopBegin = ig.DefineLabel ();
159                         ec.LoopEnd = ig.DefineLabel ();
160                         ec.InLoop = true;
161                                 
162                         ig.MarkLabel (loop);
163                         EmbeddedStatement.Emit (ec);
164                         ig.MarkLabel (ec.LoopBegin);
165                         EmitBoolExpression (ec, Expr, loop, true);
166                         ig.MarkLabel (ec.LoopEnd);
167
168                         ec.LoopBegin = old_begin;
169                         ec.LoopEnd = old_end;
170                         ec.InLoop = old_inloop;
171                         
172                         return false;
173                 }
174         }
175
176         public class While : Statement {
177                 public readonly Expression Expr;
178                 public readonly Statement Statement;
179                 
180                 public While (Expression boolExpr, Statement statement)
181                 {
182                         Expr = boolExpr;
183                         Statement = statement;
184                 }
185
186                 public override bool Emit (EmitContext ec)
187                 {
188                         ILGenerator ig = ec.ig;
189                         Label old_begin = ec.LoopBegin;
190                         Label old_end = ec.LoopEnd;
191                         bool old_inloop = ec.InLoop;
192                         
193                         ec.LoopBegin = ig.DefineLabel ();
194                         ec.LoopEnd = ig.DefineLabel ();
195                         ec.InLoop = true;
196                         
197                         ig.MarkLabel (ec.LoopBegin);
198                         EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
199                         Statement.Emit (ec);
200                         ig.Emit (OpCodes.Br, ec.LoopBegin);
201                         ig.MarkLabel (ec.LoopEnd);
202
203                         ec.LoopBegin = old_begin;
204                         ec.LoopEnd = old_end;
205                         ec.InLoop = old_inloop;
206                         
207                         return false;
208                 }
209         }
210
211         public class For : Statement {
212                 public readonly Statement InitStatement;
213                 public readonly Expression Test;
214                 public readonly Statement Increment;
215                 public readonly Statement Statement;
216                 
217                 public For (Statement initStatement,
218                             Expression test,
219                             Statement increment,
220                             Statement statement)
221                 {
222                         InitStatement = initStatement;
223                         Test = test;
224                         Increment = increment;
225                         Statement = statement;
226                 }
227
228                 public override bool Emit (EmitContext ec)
229                 {
230                         ILGenerator ig = ec.ig;
231                         Label old_begin = ec.LoopBegin;
232                         Label old_end = ec.LoopEnd;
233                         bool old_inloop = ec.InLoop;
234                         Label loop = ig.DefineLabel ();
235
236                         if (InitStatement != null)
237                                 if (! (InitStatement is EmptyStatement))
238                                         InitStatement.Emit (ec);
239
240                         ec.LoopBegin = ig.DefineLabel ();
241                         ec.LoopEnd = ig.DefineLabel ();
242                         ec.InLoop = true;
243
244                         ig.MarkLabel (loop);
245
246                         //
247                         // If test is null, there is no test, and we are just
248                         // an infinite loop
249                         //
250                         if (Test != null)
251                                 EmitBoolExpression (ec, Test, ec.LoopEnd, false);
252                 
253                         Statement.Emit (ec);
254                         ig.MarkLabel (ec.LoopBegin);
255                         if (!(Increment is EmptyStatement))
256                                 Increment.Emit (ec);
257                         ig.Emit (OpCodes.Br, loop);
258                         ig.MarkLabel (ec.LoopEnd);
259
260                         ec.LoopBegin = old_begin;
261                         ec.LoopEnd = old_end;
262                         ec.InLoop = old_inloop;
263                         return false;
264                 }
265         }
266         
267         public class StatementExpression : Statement {
268                 public readonly ExpressionStatement Expr;
269                 
270                 public StatementExpression (ExpressionStatement expr)
271                 {
272                         Expr = expr;
273                 }
274
275                 public override bool Emit (EmitContext ec)
276                 {
277                         ILGenerator ig = ec.ig;
278                         Expression ne;
279                         
280                         ne = Expr.Resolve (ec);
281                         if (ne != null){
282                                 if (ne is ExpressionStatement)
283                                         ((ExpressionStatement) ne).EmitStatement (ec);
284                                 else {
285                                         ne.Emit (ec);
286                                         ig.Emit (OpCodes.Pop);
287                                 }
288                         }
289
290                         return false;
291                 }
292
293                 public override string ToString ()
294                 {
295                         return "StatementExpression (" + Expr + ")";
296                 }
297         }
298
299         /// <summary>
300         ///   Implements the return statement
301         /// </summary>
302         public class Return : Statement {
303                 public Expression Expr;
304                 public readonly Location loc;
305                 
306                 public Return (Expression expr, Location l)
307                 {
308                         Expr = expr;
309                         loc = l;
310                 }
311
312                 public override bool Emit (EmitContext ec)
313                 {
314                         if (ec.InFinally){
315                                 Report.Error (157,loc,"Control can not leave the body of the finally block");
316                                 return false;
317                         }
318                         
319                         if (ec.ReturnType == null){
320                                 if (Expr != null){
321                                         Report.Error (127, loc, "Return with a value not allowed here");
322                                         return false;
323                                 }
324                         } else {
325                                 if (Expr == null){
326                                         Report.Error (126, loc, "An object of type `" +
327                                                       TypeManager.CSharpName (ec.ReturnType) + "' is " +
328                                                       "expected for the return statement");
329                                         return false;
330                                 }
331
332                                 Expr = Expr.Resolve (ec);
333                                 if (Expr == null)
334                                         return false;
335
336                                 if (Expr.Type != ec.ReturnType)
337                                         Expr = Expression.ConvertImplicitRequired (
338                                                 ec, Expr, ec.ReturnType, loc);
339
340                                 if (Expr == null)
341                                         return false;
342
343                                 Expr.Emit (ec);
344
345                                 if (ec.InTry || ec.InCatch)
346                                         ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
347                         }
348
349                         if (ec.InTry || ec.InCatch){
350                                 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
351                                 return false; 
352                         } else {
353                                 ec.ig.Emit (OpCodes.Ret);
354                                 return true;
355                         }
356                 }
357         }
358
359         public class Goto : Statement {
360                 string target;
361                 Location loc;
362                 Block block;
363                 
364                 public Goto (Block parent_block, string label, Location l)
365                 {
366                         block = parent_block;
367                         loc = l;
368                         target = label;
369                 }
370
371                 public string Target {
372                         get {
373                                 return target;
374                         }
375                 }
376
377                 public override bool Emit (EmitContext ec)
378                 {
379                         LabeledStatement label = block.LookupLabel (target);
380
381                         if (label == null){
382                                 //
383                                 // Maybe we should catch this before?
384                                 //
385                                 Report.Error (
386                                         159, loc,
387                                         "No such label `" + target + "' in this scope");
388                                 return false;
389                         }
390                         Label l = label.LabelTarget (ec);
391                         ec.ig.Emit (OpCodes.Br, l);
392                         
393                         return false;
394                 }
395         }
396
397         public class LabeledStatement : Statement {
398                 string label_name;
399                 bool defined;
400                 Label label;
401                 
402                 public LabeledStatement (string label_name)
403                 {
404                         this.label_name = label_name;
405                 }
406
407                 public Label LabelTarget (EmitContext ec)
408                 {
409                         if (defined)
410                                 return label;
411                         label = ec.ig.DefineLabel ();
412                         defined = true;
413
414                         return label;
415                 }
416                 
417                 public override bool Emit (EmitContext ec)
418                 {
419                         LabelTarget (ec);
420                         ec.ig.MarkLabel (label);
421
422                         return false;
423                 }
424         }
425         
426
427         /// <summary>
428         ///   `goto default' statement
429         /// </summary>
430         public class GotoDefault : Statement {
431                 Location loc;
432                 
433                 public GotoDefault (Location l)
434                 {
435                         loc = l;
436                 }
437
438                 public override bool Emit (EmitContext ec)
439                 {
440                         if (ec.Switch == null){
441                                 Report.Error (153, loc, "goto default is only valid in a switch statement");
442                                 return false;
443                         }
444
445                         if (!ec.Switch.GotDefault){
446                                 Report.Error (159, loc, "No default target on switch statement");
447                                 return false;
448                         }
449                         ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
450                         return false;
451                 }
452         }
453
454         /// <summary>
455         ///   `goto case' statement
456         /// </summary>
457         public class GotoCase : Statement {
458                 Location loc;
459                 Expression expr;
460                 
461                 public GotoCase (Expression e, Location l)
462                 {
463                         expr = e;
464                         loc = l;
465                 }
466
467                 public override bool Emit (EmitContext ec)
468                 {
469                         if (ec.Switch == null){
470                                 Report.Error (153, loc, "goto case is only valid in a switch statement");
471                                 return false;
472                         }
473
474                         expr = expr.Resolve (ec);
475                         if (expr == null)
476                                 return false;
477
478                         if (!(expr is Constant)){
479                                 Report.Error (159, loc, "Target expression for goto case is not constant");
480                                 return false;
481                         }
482
483                         object val = Expression.ConvertIntLiteral (
484                                 (Constant) expr, ec.Switch.SwitchType, loc);
485
486                         if (val == null)
487                                 return false;
488                                         
489                         SwitchLabel sl = (SwitchLabel) ec.Switch.Elements [val];
490
491                         if (sl == null){
492                                 Report.Error (
493                                         159, loc,
494                                         "No such label 'case " + val + "': for the goto case");
495                         }
496
497                         ec.ig.Emit (OpCodes.Br, sl.ILLabel);
498                         return false;
499                 }
500         }
501         
502         public class Throw : Statement {
503                 public readonly Expression Expr;
504                 Location loc;
505                 
506                 public Throw (Expression expr, Location l)
507                 {
508                         Expr = expr;
509                         loc = l;
510                 }
511
512                 public override bool Emit (EmitContext ec)
513                 {
514                         if (Expr == null){
515                                 if (ec.InCatch)
516                                         ec.ig.Emit (OpCodes.Rethrow);
517                                 else {
518                                         Report.Error (
519                                                 156, loc,
520                                                 "A throw statement with no argument is only " +
521                                                 "allowed in a catch clause");
522                                 }
523                                 return false;
524                         }
525                         
526                         Expression e = Expr.Resolve (ec);
527
528                         if (e == null)
529                                 return false;
530                         
531                         e.Emit (ec);
532
533                         ec.ig.Emit (OpCodes.Throw);
534
535                         return true;
536                 }
537         }
538
539         public class Break : Statement {
540                 Location loc;
541                 
542                 public Break (Location l)
543                 {
544                         loc = l;
545                 }
546
547                 public override bool Emit (EmitContext ec)
548                 {
549                         ILGenerator ig = ec.ig;
550
551                         if (ec.InLoop == false && ec.Switch == null){
552                                 Report.Error (139, loc, "No enclosing loop or switch to continue to");
553                                 return false;
554                         }
555                         
556                         ig.Emit (OpCodes.Br, ec.LoopEnd);
557                         return false;
558                 }
559         }
560
561         public class Continue : Statement {
562                 Location loc;
563                 
564                 public Continue (Location l)
565                 {
566                         loc = l;
567                 }
568
569                 public override bool Emit (EmitContext ec)
570                 {
571                         Label begin = ec.LoopBegin;
572                         
573                         if (!ec.InLoop){
574                                 Report.Error (139, loc, "No enclosing loop to continue to");
575                                 return false;
576                         } 
577
578                         //
579                         // UGH: Non trivial.  This Br might cross a try/catch boundary
580                         // How can we tell?
581                         //
582                         // while () {
583                         //   try { ... } catch { continue; }
584                         // }
585                         //
586                         // From:
587                         // try {} catch { while () { continue; }}
588                         //
589                         ec.ig.Emit (OpCodes.Br, begin);
590                         return false;
591                 }
592         }
593         
594         public class VariableInfo {
595                 public readonly string Type;
596                 public LocalBuilder LocalBuilder;
597                 public Type VariableType;
598                 public readonly Location Location;
599                 
600                 int  idx;
601                 public bool Used;
602                 public bool Assigned;
603                 public bool ReadOnly;
604                 
605                 public VariableInfo (string type, Location l)
606                 {
607                         Type = type;
608                         LocalBuilder = null;
609                         idx = -1;
610                         Location = l;
611                 }
612
613                 public int Idx {
614                         get {
615                                 if (idx == -1)
616                                         throw new Exception ("Unassigned idx for variable");
617                                 
618                                 return idx;
619                         }
620
621                         set {
622                                 idx = value;
623                         }
624                 }
625
626         }
627                 
628         /// <summary>
629         ///   Block represents a C# block.
630         /// </summary>
631         ///
632         /// <remarks>
633         ///   This class is used in a number of places: either to represent
634         ///   explicit blocks that the programmer places or implicit blocks.
635         ///
636         ///   Implicit blocks are used as labels or to introduce variable
637         ///   declarations.
638         /// </remarks>
639         public class Block : Statement {
640                 public readonly Block  Parent;
641                 public readonly bool   Implicit;
642
643                 //
644                 // The statements in this block
645                 //
646                 StatementCollection statements;
647
648                 //
649                 // An array of Blocks.  We keep track of children just
650                 // to generate the local variable declarations.
651                 //
652                 // Statements and child statements are handled through the
653                 // statements.
654                 //
655                 ArrayList children;
656                 
657                 //
658                 // Labels.  (label, block) pairs.
659                 //
660                 Hashtable labels;
661
662                 //
663                 // Keeps track of (name, type) pairs
664                 //
665                 Hashtable variables;
666
667                 //
668                 // Keeps track of constants
669                 Hashtable constants;
670
671                 //
672                 // Maps variable names to ILGenerator.LocalBuilders
673                 //
674                 Hashtable local_builders;
675
676                 bool used = false;
677
678                 static int id;
679
680                 int this_id;
681                 
682                 public Block (Block parent)
683                 {
684                         if (parent != null)
685                                 parent.AddChild (this);
686                         
687                         this.Parent = parent;
688                         this.Implicit = false;
689
690                         this_id = id++;
691                 }
692
693                 public Block (Block parent, bool implicit_block)
694                 {
695                         if (parent != null)
696                                 parent.AddChild (this);
697                         
698                         this.Parent = parent;
699                         this.Implicit = true;
700                         this_id = id++;
701                 }
702
703                 public int ID {
704                         get {
705                                 return this_id;
706                         }
707                 }
708                 
709                 void AddChild (Block b)
710                 {
711                         if (children == null)
712                                 children = new ArrayList ();
713                         
714                         children.Add (b);
715                 }
716
717                 /// <summary>
718                 ///   Adds a label to the current block. 
719                 /// </summary>
720                 ///
721                 /// <returns>
722                 ///   false if the name already exists in this block. true
723                 ///   otherwise.
724                 /// </returns>
725                 ///
726                 public bool AddLabel (string name, LabeledStatement target)
727                 {
728                         if (labels == null)
729                                 labels = new Hashtable ();
730                         if (labels.Contains (name))
731                                 return false;
732                         
733                         labels.Add (name, target);
734                         return true;
735                 }
736
737                 public LabeledStatement LookupLabel (string name)
738                 {
739                         if (labels != null){
740                                 if (labels.Contains (name))
741                                         return ((LabeledStatement) labels [name]);
742                         }
743
744                         if (Parent != null)
745                                 return Parent.LookupLabel (name);
746
747                         return null;
748                 }
749                 
750                 public bool AddVariable (string type, string name, Parameters pars, Location l)
751                 {
752                         if (variables == null)
753                                 variables = new Hashtable ();
754
755                         if (GetVariableType (name) != null)
756                                 return false;
757
758                         if (pars != null) {
759                                 int idx = 0;
760                                 Parameter p = pars.GetParameterByName (name, out idx);
761                                 if (p != null) 
762                                         return false;
763                         }
764                         
765                         VariableInfo vi = new VariableInfo (type, l);
766
767                         variables.Add (name, vi);
768
769                         return true;
770                 }
771
772                 public bool AddConstant (string type, string name, Expression value, Parameters pars, Location l)
773                 {
774                         if (!AddVariable (type, name, pars, l))
775                                 return false;
776                         
777                         if (constants == null)
778                                 constants = new Hashtable ();
779
780                         constants.Add (name, value);
781
782                         return true;
783                 }
784
785                 public Hashtable Variables {
786                         get {
787                                 return variables;
788                         }
789                 }
790
791                 public VariableInfo GetVariableInfo (string name)
792                 {
793                         if (variables != null) {
794                                 object temp;
795                                 temp = variables [name];
796
797                                 if (temp != null){
798                                         return (VariableInfo) temp;
799                                 }
800                         }
801
802                         if (Parent != null)
803                                 return Parent.GetVariableInfo (name);
804
805                         return null;
806                 }
807                 
808                 public string GetVariableType (string name)
809                 {
810                         VariableInfo vi = GetVariableInfo (name);
811
812                         if (vi != null)
813                                 return vi.Type;
814
815                         return null;
816                 }
817
818                 public Expression GetConstantExpression (string name)
819                 {
820                         if (constants != null) {
821                                 object temp;
822                                 temp = constants [name];
823                                 
824                                 if (temp != null)
825                                         return (Expression) temp;
826                         }
827                         
828                         if (Parent != null)
829                                 return Parent.GetConstantExpression (name);
830
831                         return null;
832                 }
833                 
834                 /// <summary>
835                 ///   True if the variable named @name has been defined
836                 ///   in this block
837                 /// </summary>
838                 public bool IsVariableDefined (string name)
839                 {
840                         if (variables != null) {
841                                 if (variables.Contains (name))
842                                         return true;
843                         }
844                         
845                         if (Parent != null)
846                                 return Parent.IsVariableDefined (name);
847
848                         return false;
849                 }
850
851                 /// <summary>
852                 ///   True if the variable named @name is a constant
853                 ///  </summary>
854                 public bool IsConstant (string name)
855                 {
856                         Expression e = null;
857                         
858                         e = GetConstantExpression (name);
859                         
860                         return e != null;
861                 }
862                 
863                 /// <summary>
864                 ///   Use to fetch the statement associated with this label
865                 /// </summary>
866                 public Statement this [string name] {
867                         get {
868                                 return (Statement) labels [name];
869                         }
870                 }
871
872                 /// <returns>
873                 ///   A list of labels that were not used within this block
874                 /// </returns>
875                 public string [] GetUnreferenced ()
876                 {
877                         // FIXME: Implement me
878                         return null;
879                 }
880
881                 public StatementCollection Statements {
882                         get {
883                                 if (statements == null)
884                                         statements = new StatementCollection ();
885
886                                 return statements;
887                         }
888                 }
889
890                 public void AddStatement (Statement s)
891                 {
892                         if (statements == null)
893                                 statements = new StatementCollection ();
894
895                         statements.Add (s);
896                         used = true;
897                 }
898
899                 public bool Used {
900                         get {
901                                 return used;
902                         }
903                 }
904
905                 public void Use ()
906                 {
907                         used = true;
908                 }
909                 
910                 /// <summary>
911                 ///   Emits the variable declarations and labels.
912                 /// </summary>
913                 /// <remarks>
914                 ///   tc: is our typecontainer (to resolve type references)
915                 ///   ig: is the code generator:
916                 ///   toplevel: the toplevel block.  This is used for checking 
917                 ///             that no two labels with the same name are used.
918                 /// </remarks>
919                 public int EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
920                 {
921                         //
922                         // Process this block variables
923                         //
924                         if (variables != null){
925                                 local_builders = new Hashtable ();
926                                 
927                                 foreach (DictionaryEntry de in variables){
928                                         string name = (string) de.Key;
929                                         VariableInfo vi = (VariableInfo) de.Value;
930                                         Type t;
931                                         
932                                         t = RootContext.LookupType (tc, vi.Type, false, vi.Location);
933                                         if (t == null)
934                                                 continue;
935
936                                         vi.VariableType = t;
937                                         vi.LocalBuilder = ig.DeclareLocal (t);
938                                         vi.Idx = count++;
939                                 }
940                         }
941
942                         //
943                         // Now, handle the children
944                         //
945                         if (children != null){
946                                 foreach (Block b in children)
947                                         count = b.EmitMeta (tc, ig, toplevel, count);
948                         }
949
950                         return count;
951                 }
952
953                 public void UsageWarning ()
954                 {
955                         string name;
956                         
957                         if (variables != null){
958                                 foreach (DictionaryEntry de in variables){
959                                         VariableInfo vi = (VariableInfo) de.Value;
960                                         
961                                         if (vi.Used)
962                                                 continue;
963                                         
964                                         name = (string) de.Key;
965                                                 
966                                         if (vi.Assigned){
967                                                 Report.Warning (
968                                                         219, vi.Location, "The variable `" + name +
969                                                         "' is assigned but its value is never used");
970                                         } else {
971                                                 Report.Warning (
972                                                         168, vi.Location, "The variable `" +
973                                                         name +
974                                                         "' is declared but never used");
975                                         } 
976                                 }
977                         }
978
979                         if (children != null)
980                                 foreach (Block b in children)
981                                         b.UsageWarning ();
982                 }
983
984 //              static int count;
985                 
986                 public override bool Emit (EmitContext ec)
987                 {
988                         bool is_ret = false;
989                         Block prev_block = ec.CurrentBlock;
990
991 //                      count++;
992                         ec.CurrentBlock = this;
993 //                      if (count == 40)
994 //                              throw new Exception ();
995                         foreach (Statement s in Statements)
996                                 is_ret = s.Emit (ec);
997 //                      count--;
998                         
999                         ec.CurrentBlock = prev_block;
1000                         return is_ret;
1001                 }
1002         }
1003
1004         public class SwitchLabel {
1005                 Expression label;
1006                 object converted;
1007                 public Location loc;
1008                 public Label ILLabel;
1009                 
1010                 //
1011                 // if expr == null, then it is the default case.
1012                 //
1013                 public SwitchLabel (Expression expr, Location l)
1014                 {
1015                         label = expr;
1016                         loc = l;
1017                 }
1018
1019                 public Expression Label {
1020                         get {
1021                                 return label;
1022                         }
1023                 }
1024
1025                 public object Converted {
1026                         get {
1027                                 return converted;
1028                         }
1029                 }
1030                 
1031                 //
1032                 // Resolves the expression, reduces it to a literal if possible
1033                 // and then converts it to the requested type.
1034                 //
1035                 public bool ResolveAndReduce (EmitContext ec, Type required_type)
1036                 {
1037                         ILLabel = ec.ig.DefineLabel ();
1038
1039                         if (label == null)
1040                                 return true;
1041                         
1042                         Expression e = label.Resolve (ec);
1043
1044                         if (e == null)
1045                                 return false;
1046
1047                         if (!(e is Constant)){
1048                                 Console.WriteLine ("Value is: " + label);
1049                                 Report.Error (150, loc, "A constant value is expected");
1050                                 return false;
1051                         }
1052
1053                         if (e is StringConstant || e is NullLiteral){
1054                                 if (required_type == TypeManager.string_type){
1055                                         converted = label;
1056                                         ILLabel = ec.ig.DefineLabel ();
1057                                         return true;
1058                                 }
1059                         }
1060
1061                         converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
1062                         if (converted == null)
1063                                 return false;
1064
1065                         return true;
1066                 }
1067         }
1068
1069         public class SwitchSection {
1070                 // An array of SwitchLabels.
1071                 public readonly ArrayList Labels;
1072                 public readonly Block Block;
1073                 
1074                 public SwitchSection (ArrayList labels, Block block)
1075                 {
1076                         Labels = labels;
1077                         Block = block;
1078                 }
1079         }
1080         
1081         public class Switch : Statement {
1082                 public readonly ArrayList Sections;
1083                 public Expression Expr;
1084
1085                 /// <summary>
1086                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
1087                 /// </summary>
1088                 public Hashtable Elements;
1089
1090                 /// <summary>
1091                 ///   The governing switch type
1092                 /// </summary>
1093                 public Type SwitchType;
1094
1095                 //
1096                 // Computed
1097                 //
1098                 bool got_default;
1099                 Label default_target;
1100                 Location loc;
1101                 
1102                 //
1103                 // The types allowed to be implicitly cast from
1104                 // on the governing type
1105                 //
1106                 static Type [] allowed_types;
1107                 
1108                 public Switch (Expression e, ArrayList sects, Location l)
1109                 {
1110                         Expr = e;
1111                         Sections = sects;
1112                         loc = l;
1113                 }
1114
1115                 public bool GotDefault {
1116                         get {
1117                                 return got_default;
1118                         }
1119                 }
1120
1121                 public Label DefaultTarget {
1122                         get {
1123                                 return default_target;
1124                         }
1125                 }
1126
1127                 //
1128                 // Determines the governing type for a switch.  The returned
1129                 // expression might be the expression from the switch, or an
1130                 // expression that includes any potential conversions to the
1131                 // integral types or to string.
1132                 //
1133                 Expression SwitchGoverningType (EmitContext ec, Type t)
1134                 {
1135                         if (t == TypeManager.int32_type ||
1136                             t == TypeManager.uint32_type ||
1137                             t == TypeManager.char_type ||
1138                             t == TypeManager.byte_type ||
1139                             t == TypeManager.sbyte_type ||
1140                             t == TypeManager.ushort_type ||
1141                             t == TypeManager.short_type ||
1142                             t == TypeManager.uint64_type ||
1143                             t == TypeManager.int64_type ||
1144                             t == TypeManager.string_type ||
1145                             t.IsSubclassOf (TypeManager.enum_type))
1146                                 return Expr;
1147
1148                         if (allowed_types == null){
1149                                 allowed_types = new Type [] {
1150                                         TypeManager.sbyte_type,
1151                                         TypeManager.byte_type,
1152                                         TypeManager.short_type,
1153                                         TypeManager.ushort_type,
1154                                         TypeManager.int32_type,
1155                                         TypeManager.uint32_type,
1156                                         TypeManager.int64_type,
1157                                         TypeManager.uint64_type,
1158                                         TypeManager.char_type,
1159                                         TypeManager.string_type
1160                                 };
1161                         }
1162
1163                         //
1164                         // Try to find a *user* defined implicit conversion.
1165                         //
1166                         // If there is no implicit conversion, or if there are multiple
1167                         // conversions, we have to report an error
1168                         //
1169                         Expression converted = null;
1170                         foreach (Type tt in allowed_types){
1171                                 Expression e;
1172                                 
1173                                 e = Expression.ImplicitUserConversion (ec, Expr, tt, loc);
1174                                 if (e == null)
1175                                         continue;
1176
1177                                 if (converted != null){
1178                                         Report.Error (-12, loc, "More than one conversion to an integral " +
1179                                                       " type exists for type `" +
1180                                                       TypeManager.CSharpName (Expr.Type)+"'");
1181                                         return null;
1182                                 }
1183                         }
1184                         return converted;
1185                 }
1186
1187                 void error152 (string n)
1188                 {
1189                         Report.Error (
1190                                 152, "The label `" + n + ":' " +
1191                                 "is already present on this switch statement");
1192                 }
1193                 
1194                 //
1195                 // Performs the basic sanity checks on the switch statement
1196                 // (looks for duplicate keys and non-constant expressions).
1197                 //
1198                 // It also returns a hashtable with the keys that we will later
1199                 // use to compute the switch tables
1200                 //
1201                 bool CheckSwitch (EmitContext ec)
1202                 {
1203                         Type compare_type;
1204                         bool error = false;
1205                         Elements = new Hashtable ();
1206                                 
1207                         got_default = false;
1208
1209                         if (TypeManager.IsEnumType (SwitchType)){
1210                                 compare_type = TypeManager.EnumToUnderlying (SwitchType);
1211                         } else
1212                                 compare_type = SwitchType;
1213                         
1214                         foreach (SwitchSection ss in Sections){
1215                                 foreach (SwitchLabel sl in ss.Labels){
1216                                         if (!sl.ResolveAndReduce (ec, SwitchType)){
1217                                                 error = true;
1218                                                 continue;
1219                                         }
1220
1221                                         if (sl.Label == null){
1222                                                 if (got_default){
1223                                                         error152 ("default");
1224                                                         error = true;
1225                                                 }
1226                                                 got_default = true;
1227                                                 continue;
1228                                         }
1229                                         
1230                                         object key = sl.Converted;
1231
1232                                         if (key is Constant)
1233                                                 key = ((Constant) key).GetValue ();
1234
1235                                         if (key == null)
1236                                                 key = NullLiteral.Null;
1237                                         
1238                                         string lname = null;
1239                                         if (compare_type == TypeManager.uint64_type){
1240                                                 ulong v = (ulong) key;
1241
1242                                                 if (Elements.Contains (v))
1243                                                         lname = v.ToString ();
1244                                                 else
1245                                                         Elements.Add (v, sl);
1246                                         } else if (compare_type == TypeManager.int64_type){
1247                                                 long v = (long) key;
1248
1249                                                 if (Elements.Contains (v))
1250                                                         lname = v.ToString ();
1251                                                 else
1252                                                         Elements.Add (v, sl);
1253                                         } else if (compare_type == TypeManager.uint32_type){
1254                                                 uint v = (uint) key;
1255
1256                                                 if (Elements.Contains (v))
1257                                                         lname = v.ToString ();
1258                                                 else
1259                                                         Elements.Add (v, sl);
1260                                         } else if (compare_type == TypeManager.char_type){
1261                                                 char v = (char) key;
1262                                                 
1263                                                 if (Elements.Contains (v))
1264                                                         lname = v.ToString ();
1265                                                 else
1266                                                         Elements.Add (v, sl);
1267                                         } else if (compare_type == TypeManager.byte_type){
1268                                                 byte v = (byte) key;
1269                                                 
1270                                                 if (Elements.Contains (v))
1271                                                         lname = v.ToString ();
1272                                                 else
1273                                                         Elements.Add (v, sl);
1274                                         } else if (compare_type == TypeManager.sbyte_type){
1275                                                 sbyte v = (sbyte) key;
1276                                                 
1277                                                 if (Elements.Contains (v))
1278                                                         lname = v.ToString ();
1279                                                 else
1280                                                         Elements.Add (v, sl);
1281                                         } else if (compare_type == TypeManager.short_type){
1282                                                 short v = (short) key;
1283                                                 
1284                                                 if (Elements.Contains (v))
1285                                                         lname = v.ToString ();
1286                                                 else
1287                                                         Elements.Add (v, sl);
1288                                         } else if (compare_type == TypeManager.ushort_type){
1289                                                 ushort v = (ushort) key;
1290                                                 
1291                                                 if (Elements.Contains (v))
1292                                                         lname = v.ToString ();
1293                                                 else
1294                                                         Elements.Add (v, sl);
1295                                         } else if (compare_type == TypeManager.string_type){
1296                                                 if (key is NullLiteral){
1297                                                         if (Elements.Contains (NullLiteral.Null))
1298                                                                 lname = "null";
1299                                                         else
1300                                                                 Elements.Add (NullLiteral.Null, null);
1301                                                 } else {
1302                                                         string s = (string) key;
1303
1304                                                         if (Elements.Contains (s))
1305                                                                 lname = s;
1306                                                         else
1307                                                                 Elements.Add (s, sl);
1308                                                 }
1309                                         } else if (compare_type == TypeManager.int32_type) {
1310                                                 int v = (int) key;
1311
1312                                                 if (Elements.Contains (v))
1313                                                         lname = v.ToString ();
1314                                                 else
1315                                                         Elements.Add (v, sl);
1316                                         } else {
1317                                                 throw new Exception ("Unknown switch type!" +
1318                                                                      SwitchType + " " + compare_type);
1319                                         }
1320
1321                                         if (lname != null){
1322                                                 error152 ("case + " + lname);
1323                                                 error = true;
1324                                         }
1325                                 }
1326                         }
1327                         if (error)
1328                                 return false;
1329                         
1330                         return true;
1331                 }
1332
1333                 void EmitObjectInteger (ILGenerator ig, object k)
1334                 {
1335                         if (k is int)
1336                                 IntConstant.EmitInt (ig, (int) k);
1337                         else if (k is Constant){
1338                                 EmitObjectInteger (ig, ((Constant) k).GetValue ());
1339                         } else if (k is uint)
1340                                 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
1341                         else if (k is long)
1342                                 LongConstant.EmitLong (ig, (long) k);
1343                         else if (k is ulong)
1344                                 LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
1345                         else if (k is char)
1346                                 IntConstant.EmitInt (ig, (int) ((char) k));
1347                         else if (k is sbyte)
1348                                 IntConstant.EmitInt (ig, (int) ((sbyte) k));
1349                         else if (k is byte)
1350                                 IntConstant.EmitInt (ig, (int) ((byte) k));
1351                         else 
1352                                 throw new Exception ("Unhandled case");
1353                 }
1354                 
1355                 //
1356                 // This simple emit switch works, but does not take advantage of the
1357                 // `switch' opcode.  The swithc opcode uses a jump table that we are not
1358                 // computing at this point
1359                 //
1360                 bool SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
1361                 {
1362                         ILGenerator ig = ec.ig;
1363                         Label end_of_switch = ig.DefineLabel ();
1364                         Label next_test = ig.DefineLabel ();
1365                         Label null_target = ig.DefineLabel ();
1366                         bool default_found = false;
1367                         bool first_test = true;
1368                         bool pending_goto_end = false;
1369                         bool all_return = true;
1370                         bool is_string = false;
1371                         bool null_found;
1372                         
1373                         //
1374                         // Special processing for strings: we cant compare
1375                         // against null.
1376                         //
1377                         if (SwitchType == TypeManager.string_type){
1378                                 ig.Emit (OpCodes.Ldloc, val);
1379                                 is_string = true;
1380                                 
1381                                 if (Elements.Contains (NullLiteral.Null)){
1382                                         ig.Emit (OpCodes.Brfalse, null_target);
1383                                 } else
1384                                         ig.Emit (OpCodes.Brfalse, default_target);
1385
1386                                 ig.Emit (OpCodes.Ldloc, val);
1387                                 ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
1388                                 ig.Emit (OpCodes.Stloc, val);
1389                         }
1390                         
1391                         foreach (SwitchSection ss in Sections){
1392                                 Label sec_begin = ig.DefineLabel ();
1393
1394                                 if (pending_goto_end)
1395                                         ig.Emit (OpCodes.Br, end_of_switch);
1396
1397                                 int label_count = ss.Labels.Count;
1398                                 null_found = false;
1399                                 foreach (SwitchLabel sl in ss.Labels){
1400                                         ig.MarkLabel (sl.ILLabel);
1401                                         
1402                                         if (!first_test){
1403                                                 ig.MarkLabel (next_test);
1404                                                 next_test = ig.DefineLabel ();
1405                                         }
1406                                         //
1407                                         // If we are the default target
1408                                         //
1409                                         if (sl.Label == null){
1410                                                 ig.MarkLabel (default_target);
1411                                                 default_found = true;
1412                                         } else {
1413                                                 object lit = sl.Converted;
1414
1415                                                 if (lit is NullLiteral){
1416                                                         null_found = true;
1417                                                         if (label_count == 1)
1418                                                                 ig.Emit (OpCodes.Br, next_test);
1419                                                         continue;
1420                                                                               
1421                                                 }
1422                                                 if (is_string){
1423                                                         StringConstant str = (StringConstant) lit;
1424
1425                                                         ig.Emit (OpCodes.Ldloc, val);
1426                                                         ig.Emit (OpCodes.Ldstr, str.Value);
1427                                                         if (label_count == 1)
1428                                                                 ig.Emit (OpCodes.Bne_Un, next_test);
1429                                                         else
1430                                                                 ig.Emit (OpCodes.Beq, sec_begin);
1431                                                 } else {
1432                                                         ig.Emit (OpCodes.Ldloc, val);
1433                                                         EmitObjectInteger (ig, lit);
1434                                                         ig.Emit (OpCodes.Ceq);
1435                                                         if (label_count == 1)
1436                                                                 ig.Emit (OpCodes.Brfalse, next_test);
1437                                                         else
1438                                                                 ig.Emit (OpCodes.Brtrue, sec_begin);
1439                                                 }
1440                                         }
1441                                 }
1442                                 if (label_count != 1)
1443                                         ig.Emit (OpCodes.Br, next_test);
1444                                 
1445                                 if (null_found)
1446                                         ig.MarkLabel (null_target);
1447                                 ig.MarkLabel (sec_begin);
1448                                 if (ss.Block.Emit (ec))
1449                                         pending_goto_end = false;
1450                                 else {
1451                                         all_return = false;
1452                                         pending_goto_end = true;
1453                                 }
1454                                 first_test = false;
1455                         }
1456                         if (!default_found)
1457                                 ig.MarkLabel (default_target);
1458                         ig.MarkLabel (next_test);
1459                         ig.MarkLabel (end_of_switch);
1460                         
1461                         return all_return;
1462                 }
1463                 
1464                 public override bool Emit (EmitContext ec)
1465                 {
1466                         Expr = Expr.Resolve (ec);
1467                         if (Expr == null)
1468                                 return false;
1469
1470                         Expression new_expr = SwitchGoverningType (ec, Expr.Type);
1471                         if (new_expr == null){
1472                                 Report.Error (151, loc, "An integer type or string was expected for switch");
1473                                 return false;
1474                         }
1475
1476                         // Validate switch.
1477                         SwitchType = new_expr.Type;
1478
1479                         if (!CheckSwitch (ec))
1480                                 return false;
1481
1482                         // Store variable for comparission purposes
1483                         LocalBuilder value = ec.ig.DeclareLocal (SwitchType);
1484                         new_expr.Emit (ec);
1485                         ec.ig.Emit (OpCodes.Stloc, value);
1486
1487                         ILGenerator ig = ec.ig;
1488
1489                         default_target = ig.DefineLabel ();
1490
1491                         //
1492                         // Setup the codegen context
1493                         //
1494                         Label old_end = ec.LoopEnd;
1495                         Switch old_switch = ec.Switch;
1496                         
1497                         ec.LoopEnd = ig.DefineLabel ();
1498                         ec.Switch = this;
1499
1500                         // Emit Code.
1501                         bool all_return =  SimpleSwitchEmit (ec, value);
1502
1503                         // Restore context state. 
1504                         ig.MarkLabel (ec.LoopEnd);
1505
1506                         //
1507                         // FIXME: I am emitting a nop, because the switch performs
1508                         // no analysis on whether something ever reaches the end
1509                         //
1510                         // try: b (int a) { switch (a) { default: return 0; }  }
1511                         ig.Emit (OpCodes.Nop);
1512
1513                         //
1514                         // Restore the previous context
1515                         //
1516                         ec.LoopEnd = old_end;
1517                         ec.Switch = old_switch;
1518                         
1519                         //
1520                         // Because we have a nop at the end
1521                         //
1522                         return false;
1523                 }
1524         }
1525
1526         public class Lock : Statement {
1527                 public readonly Expression Expr;
1528                 public readonly Statement Statement;
1529                 Location loc;
1530                         
1531                 public Lock (Expression expr, Statement stmt, Location l)
1532                 {
1533                         Expr = expr;
1534                         Statement = stmt;
1535                         loc = l;
1536                 }
1537
1538                 public override bool Emit (EmitContext ec)
1539                 {
1540                         Expression e = Expr.Resolve (ec);
1541                         if (e == null)
1542                                 return false;
1543
1544                         Type type = e.Type;
1545                         
1546                         if (type.IsValueType){
1547                                 Report.Error (185, loc, "lock statement requires the expression to be " +
1548                                               " a reference type (type is: `" +
1549                                               TypeManager.CSharpName (type) + "'");
1550                                 return false;
1551                         }
1552
1553                         ILGenerator ig = ec.ig;
1554                         LocalBuilder temp = ig.DeclareLocal (type);
1555                                 
1556                         e.Emit (ec);
1557                         ig.Emit (OpCodes.Dup);
1558                         ig.Emit (OpCodes.Stloc, temp);
1559                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
1560
1561                         // try
1562                         Label end = ig.BeginExceptionBlock ();
1563                         bool old_in_try = ec.InTry;
1564                         ec.InTry = true;
1565                         Label finish = ig.DefineLabel ();
1566                         Statement.Emit (ec);
1567                         ec.InTry = old_in_try;
1568                         // ig.Emit (OpCodes.Leave, finish);
1569
1570                         ig.MarkLabel (finish);
1571                         
1572                         // finally
1573                         ig.BeginFinallyBlock ();
1574                         ig.Emit (OpCodes.Ldloc, temp);
1575                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
1576                         ig.EndExceptionBlock ();
1577                         
1578                         return false;
1579                 }
1580         }
1581
1582         public class Unchecked : Statement {
1583                 public readonly Block Block;
1584                 
1585                 public Unchecked (Block b)
1586                 {
1587                         Block = b;
1588                 }
1589
1590                 public override bool Emit (EmitContext ec)
1591                 {
1592                         bool previous_state = ec.CheckState;
1593                         bool val;
1594                         
1595                         ec.CheckState = false;
1596                         val = Block.Emit (ec);
1597                         ec.CheckState = previous_state;
1598
1599                         return val;
1600                 }
1601         }
1602
1603         public class Checked : Statement {
1604                 public readonly Block Block;
1605                 
1606                 public Checked (Block b)
1607                 {
1608                         Block = b;
1609                 }
1610
1611                 public override bool Emit (EmitContext ec)
1612                 {
1613                         bool previous_state = ec.CheckState;
1614                         bool val;
1615                         
1616                         ec.CheckState = true;
1617                         val = Block.Emit (ec);
1618                         ec.CheckState = previous_state;
1619
1620                         return val;
1621                 }
1622         }
1623
1624         public class Unsafe : Statement {
1625                 public readonly Block Block;
1626
1627                 public Unsafe (Block b)
1628                 {
1629                         Block = b;
1630                 }
1631
1632                 public override bool Emit (EmitContext ec)
1633                 {
1634                         bool previous_state = ec.InUnsafe;
1635                         bool val;
1636                         
1637                         ec.InUnsafe = true;
1638                         val = Block.Emit (ec);
1639                         ec.InUnsafe = previous_state;
1640
1641                         return val;
1642                 }
1643         }
1644
1645         public class Unsafe : Statement {
1646                 public readonly Block Block;
1647
1648                 public Unsafe (Block b)
1649                 {
1650                         Block = b;
1651                 }
1652
1653                 public override bool Emit (EmitContext ec)
1654                 {
1655                         bool previous_state = ec.InUnsafe;
1656                         bool val;
1657                         
1658                         ec.InUnsafe = true;
1659                         val = Block.Emit (ec);
1660                         ec.InUnsafe = previous_state;
1661
1662                         return val;
1663                 }
1664         }
1665
1666         public class Catch {
1667                 public readonly string Type;
1668                 public readonly string Name;
1669                 public readonly Block  Block;
1670                 public readonly Location Location;
1671                 
1672                 public Catch (string type, string name, Block block, Location l)
1673                 {
1674                         Type = type;
1675                         Name = name;
1676                         Block = block;
1677                         Location = l;
1678                 }
1679         }
1680
1681         public class Try : Statement {
1682                 public readonly Block Fini, Block;
1683                 public readonly ArrayList Specific;
1684                 public readonly Catch General;
1685                 
1686                 //
1687                 // specific, general and fini might all be null.
1688                 //
1689                 public Try (Block block, ArrayList specific, Catch general, Block fini)
1690                 {
1691                         if (specific == null && general == null){
1692                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
1693                         }
1694                         
1695                         this.Block = block;
1696                         this.Specific = specific;
1697                         this.General = general;
1698                         this.Fini = fini;
1699                 }
1700
1701                 public override bool Emit (EmitContext ec)
1702                 {
1703                         ILGenerator ig = ec.ig;
1704                         Label end;
1705                         Label finish = ig.DefineLabel ();;
1706                         bool returns;
1707                         
1708                         end = ig.BeginExceptionBlock ();
1709                         bool old_in_try = ec.InTry;
1710                         ec.InTry = true;
1711                         returns = Block.Emit (ec);
1712                         ec.InTry = old_in_try;
1713
1714                         //
1715                         // System.Reflection.Emit provides this automatically:
1716                         // ig.Emit (OpCodes.Leave, finish);
1717
1718                         bool old_in_catch = ec.InCatch;
1719                         ec.InCatch = true;
1720                         DeclSpace ds = ec.TypeContainer;
1721                         
1722                         foreach (Catch c in Specific){
1723                                 Type catch_type = RootContext.LookupType (ds, c.Type, false, c.Location);
1724                                 VariableInfo vi;
1725                                 
1726                                 if (catch_type == null)
1727                                         return false;
1728
1729                                 ig.BeginCatchBlock (catch_type);
1730
1731                                 if (c.Name != null){
1732                                         vi = c.Block.GetVariableInfo (c.Name);
1733                                         if (vi == null){
1734                                                 Console.WriteLine ("This should not happen! variable does not exist in this block");
1735                                                 Environment.Exit (0);
1736                                         }
1737                                 
1738                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
1739                                 } else
1740                                         ig.Emit (OpCodes.Pop);
1741                                 
1742                                 c.Block.Emit (ec);
1743                         }
1744
1745                         if (General != null){
1746                                 ig.BeginCatchBlock (TypeManager.object_type);
1747                                 ig.Emit (OpCodes.Pop);
1748                                 General.Block.Emit (ec);
1749                         }
1750                         ec.InCatch = old_in_catch;
1751
1752                         ig.MarkLabel (finish);
1753                         if (Fini != null){
1754                                 ig.BeginFinallyBlock ();
1755                                 bool old_in_finally = ec.InFinally;
1756                                 ec.InFinally = true;
1757                                 Fini.Emit (ec);
1758                                 ec.InFinally = old_in_finally;
1759                         }
1760                         
1761                         ig.EndExceptionBlock ();
1762
1763                         //
1764                         // FIXME: Is this correct?
1765                         // Replace with `returns' and check test-18, maybe we can
1766                         // perform an optimization here.
1767                         //
1768                         return false;
1769                 }
1770         }
1771
1772         //
1773         // FIXME: We still do not support the expression variant of the using
1774         // statement.
1775         //
1776         public class Using : Statement {
1777                 object expression_or_block;
1778                 Statement Statement;
1779                 Location loc;
1780                 
1781                 public Using (object expression_or_block, Statement stmt, Location l)
1782                 {
1783                         this.expression_or_block = expression_or_block;
1784                         Statement = stmt;
1785                         loc = l;
1786                 }
1787
1788                 //
1789                 // Emits the code for the case of using using a local variable declaration.
1790                 //
1791                 bool EmitLocalVariableDecls (EmitContext ec, string type_name, ArrayList var_list)
1792                 {
1793                         ILGenerator ig = ec.ig;
1794                         Expression [] converted_vars;
1795                         bool need_conv = false;
1796                         Type type = RootContext.LookupType (ec.TypeContainer, type_name, false, loc);
1797                         int i = 0;
1798
1799                         if (type == null)
1800                                 return false;
1801                         
1802                         //
1803                         // The type must be an IDisposable or an implicit conversion
1804                         // must exist.
1805                         //
1806                         converted_vars = new Expression [var_list.Count];
1807                         if (!TypeManager.ImplementsInterface (type, TypeManager.idisposable_type)){
1808                                 foreach (DictionaryEntry e in var_list){
1809                                         Expression var = (Expression) e.Key;
1810
1811                                         var = var.Resolve (ec);
1812                                         if (var == null)
1813                                                 return false;
1814                                         
1815                                         converted_vars [i] = Expression.ConvertImplicit (
1816                                                 ec, var, TypeManager.idisposable_type, loc);
1817
1818                                         if (converted_vars [i] == null)
1819                                                 return false;
1820                                         i++;
1821                                 }
1822                                 need_conv = true;
1823                         }
1824                         
1825                         i = 0;
1826                         bool old_in_try = ec.InTry;
1827                         ec.InTry = true;
1828                         foreach (DictionaryEntry e in var_list){
1829                                 LocalVariableReference var = (LocalVariableReference) e.Key;
1830                                 Expression expr = (Expression) e.Value;
1831                                 Expression a;
1832
1833                                 a = new Assign (var, expr, loc);
1834                                 a.Resolve (ec);
1835                                 if (!need_conv)
1836                                         converted_vars [i] = var;
1837                                 i++;
1838                                 if (a == null)
1839                                         continue;
1840                                 ((ExpressionStatement) a).EmitStatement (ec);
1841                                 
1842                                 ig.BeginExceptionBlock ();
1843
1844                         }
1845                         Statement.Emit (ec);
1846                         ec.InTry = old_in_try;
1847
1848                         bool old_in_finally = ec.InFinally;
1849                         ec.InFinally = true;
1850                         var_list.Reverse ();
1851                         foreach (DictionaryEntry e in var_list){
1852                                 LocalVariableReference var = (LocalVariableReference) e.Key;
1853                                 Label skip = ig.DefineLabel ();
1854                                 i--;
1855                                 
1856                                 ig.BeginFinallyBlock ();
1857                                 
1858                                 var.Emit (ec);
1859                                 ig.Emit (OpCodes.Brfalse, skip);
1860                                 converted_vars [i].Emit (ec);
1861                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1862                                 ig.MarkLabel (skip);
1863                                 ig.EndExceptionBlock ();
1864                         }
1865                         ec.InFinally = old_in_finally;
1866
1867                         return false;
1868                 }
1869
1870                 bool EmitExpression (EmitContext ec, Expression expr)
1871                 {
1872                         Type expr_type = expr.Type;
1873                         Expression conv = null;
1874                         
1875                         if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
1876                                 conv = Expression.ConvertImplicit (
1877                                         ec, expr, TypeManager.idisposable_type, loc);
1878
1879                                 if (conv == null)
1880                                         return false;
1881                         }
1882
1883                         //
1884                         // Make a copy of the expression and operate on that.
1885                         //
1886                         ILGenerator ig = ec.ig;
1887                         LocalBuilder local_copy = ig.DeclareLocal (expr_type);
1888                         if (conv != null)
1889                                 conv.Emit (ec);
1890                         else
1891                                 expr.Emit (ec);
1892                         ig.Emit (OpCodes.Stloc, local_copy);
1893
1894                         bool old_in_try = ec.InTry;
1895                         ec.InTry = true;
1896                         ig.BeginExceptionBlock ();
1897                         Statement.Emit (ec);
1898                         ec.InTry = old_in_try;
1899                         
1900                         Label skip = ig.DefineLabel ();
1901                         bool old_in_finally = ec.InFinally;
1902                         ig.BeginFinallyBlock ();
1903                         ig.Emit (OpCodes.Ldloc, local_copy);
1904                         ig.Emit (OpCodes.Brfalse, skip);
1905                         ig.Emit (OpCodes.Ldloc, local_copy);
1906                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1907                         ig.MarkLabel (skip);
1908                         ec.InFinally = old_in_finally;
1909                         ig.EndExceptionBlock ();
1910
1911                         return false;
1912                 }
1913                 
1914                 public override bool Emit (EmitContext ec)
1915                 {
1916                         if (expression_or_block is DictionaryEntry){
1917                                 string t = (string) ((DictionaryEntry) expression_or_block).Key;
1918                                 ArrayList var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
1919
1920                                 return EmitLocalVariableDecls (ec, t, var_list);
1921                         } if (expression_or_block is Expression){
1922                                 Expression e = (Expression) expression_or_block;
1923
1924                                 e = e.Resolve (ec);
1925                                 if (e == null)
1926                                         return false;
1927
1928                                 return EmitExpression (ec, e);
1929                         }
1930                         return false;
1931                 }
1932         }
1933
1934         /// <summary>
1935         ///   Implementation of the foreach C# statement
1936         /// </summary>
1937         public class Foreach : Statement {
1938                 string type;
1939                 LocalVariableReference variable;
1940                 Expression expr;
1941                 Statement statement;
1942                 Location loc;
1943                 
1944                 public Foreach (string type, LocalVariableReference var, Expression expr,
1945                                 Statement stmt, Location l)
1946                 {
1947                         this.type = type;
1948                         this.variable = var;
1949                         this.expr = expr;
1950                         statement = stmt;
1951                         loc = l;
1952                 }
1953
1954                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1955                 {
1956                         if (m == null)
1957                                 return false;
1958                         
1959                         if (!(m is MethodInfo))
1960                                 return false;
1961                         
1962                         if (m.Name != "GetEnumerator")
1963                                 return false;
1964                         
1965                         MethodInfo mi = (MethodInfo) m;
1966
1967                         if (mi.ReturnType != TypeManager.ienumerator_type){
1968                                 if (!TypeManager.ienumerator_type.IsAssignableFrom (mi.ReturnType))
1969                                         return false;
1970                         }
1971                         
1972                         Type [] args = TypeManager.GetArgumentTypes (mi);
1973                         if (args == null)
1974                                 return true;
1975                         
1976                         if (args.Length == 0)
1977                                 return true;
1978                         
1979                         return false;
1980                 }
1981                 
1982                 /// <summary>
1983                 ///   This filter is used to find the GetEnumerator method
1984                 ///   on which IEnumerator operates
1985                 /// </summary>
1986                 static MemberFilter FilterEnumerator;
1987                 
1988                 static Foreach ()
1989                 {
1990                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1991                 }
1992
1993                 void error1579 (Type t)
1994                 {
1995                         Report.Error (1579, loc,
1996                                       "foreach statement cannot operate on variables of type `" +
1997                                       t.FullName + "' because that class does not provide a " +
1998                                       " GetEnumerator method or it is inaccessible");
1999                 }
2000
2001                 MethodInfo ProbeCollectionType (Type t)
2002                 {
2003                         MemberInfo [] mi;
2004
2005                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
2006                                                         BindingFlags.Public | BindingFlags.Instance,
2007                                                         FilterEnumerator, null);
2008
2009                         if (mi == null){
2010                                 error1579 (t);
2011                                 return null;
2012                         }
2013
2014                         if (mi.Length == 0){
2015                                 error1579 (t);
2016                                 return null;
2017                         }
2018
2019                         return (MethodInfo) mi [0];
2020                 }
2021
2022                 //
2023                 // FIXME: possible optimization.
2024                 // We might be able to avoid creating `empty' if the type is the sam
2025                 //
2026                 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
2027                 {
2028                         ILGenerator ig = ec.ig;
2029                         LocalBuilder enumerator, disposable;
2030                         Expression empty = new EmptyExpression ();
2031                         Expression conv;
2032
2033                         //
2034                         // FIXME: maybe we can apply the same trick we do in the
2035                         // array handling to avoid creating empty and conv in some cases.
2036                         //
2037                         // Although it is not as important in this case, as the type
2038                         // will not likely be object (what the enumerator will return).
2039                         //
2040                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2041                         if (conv == null)
2042                                 return false;
2043                         
2044                         enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
2045                         disposable = ig.DeclareLocal (TypeManager.idisposable_type);
2046                         
2047                         //
2048                         // Instantiate the enumerator
2049
2050                         if (expr.Type.IsValueType){
2051                                 if (expr is IMemoryLocation){
2052                                         IMemoryLocation ml = (IMemoryLocation) expr;
2053
2054                                         ml.AddressOf (ec);
2055                                 } else
2056                                         throw new Exception ("Expr " + expr + " of type " + expr.Type +
2057                                                              " does not implement IMemoryLocation");
2058                                 ig.Emit (OpCodes.Call, get_enum);
2059                         } else {
2060                                 expr.Emit (ec);
2061                                 ig.Emit (OpCodes.Callvirt, get_enum);
2062                         }
2063                         ig.Emit (OpCodes.Stloc, enumerator);
2064
2065                         //
2066                         // Protect the code in a try/finalize block, so that
2067                         // if the beast implement IDisposable, we get rid of it
2068                         //
2069                         Label l = ig.BeginExceptionBlock ();
2070                         bool old_in_try = ec.InTry;
2071                         ec.InTry = true;
2072                         
2073                         Label end_try = ig.DefineLabel ();
2074                         
2075                         ig.MarkLabel (ec.LoopBegin);
2076                         ig.Emit (OpCodes.Ldloc, enumerator);
2077                         ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
2078                         ig.Emit (OpCodes.Brfalse, end_try);
2079                         ig.Emit (OpCodes.Ldloc, enumerator);
2080                         ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
2081                         variable.EmitAssign (ec, conv);
2082                         statement.Emit (ec);
2083                         ig.Emit (OpCodes.Br, ec.LoopBegin);
2084                         ig.MarkLabel (end_try);
2085                         ec.InTry = old_in_try;
2086                         
2087                         // The runtime provides this for us.
2088                         // ig.Emit (OpCodes.Leave, end);
2089
2090                         //
2091                         // Now the finally block
2092                         //
2093                         Label end_finally = ig.DefineLabel ();
2094                         bool old_in_finally = ec.InFinally;
2095                         ec.InFinally = true;
2096                         ig.BeginFinallyBlock ();
2097                         
2098                         ig.Emit (OpCodes.Ldloc, enumerator);
2099                         ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
2100                         ig.Emit (OpCodes.Stloc, disposable);
2101                         ig.Emit (OpCodes.Ldloc, disposable);
2102                         ig.Emit (OpCodes.Brfalse, end_finally);
2103                         ig.Emit (OpCodes.Ldloc, disposable);
2104                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
2105                         ig.MarkLabel (end_finally);
2106                         ec.InFinally = old_in_finally;
2107
2108                         // The runtime generates this anyways.
2109                         // ig.Emit (OpCodes.Endfinally);
2110
2111                         ig.EndExceptionBlock ();
2112
2113                         ig.MarkLabel (ec.LoopEnd);
2114                         return false;
2115                 }
2116
2117                 //
2118                 // FIXME: possible optimization.
2119                 // We might be able to avoid creating `empty' if the type is the sam
2120                 //
2121                 bool EmitArrayForeach (EmitContext ec, Type var_type)
2122                 {
2123                         Type array_type = expr.Type;
2124                         Type element_type = array_type.GetElementType ();
2125                         Expression conv = null;
2126                         Expression empty = new EmptyExpression (element_type);
2127                         
2128                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
2129                         if (conv == null)
2130                                 return false;
2131
2132                         int rank = array_type.GetArrayRank ();
2133                         ILGenerator ig = ec.ig;
2134
2135                         LocalBuilder copy = ig.DeclareLocal (array_type);
2136                         
2137                         //
2138                         // Make our copy of the array
2139                         //
2140                         expr.Emit (ec);
2141                         ig.Emit (OpCodes.Stloc, copy);
2142                         
2143                         if (rank == 1){
2144                                 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
2145
2146                                 Label loop, test;
2147                                 
2148                                 ig.Emit (OpCodes.Ldc_I4_0);
2149                                 ig.Emit (OpCodes.Stloc, counter);
2150                                 test = ig.DefineLabel ();
2151                                 ig.Emit (OpCodes.Br, test);
2152
2153                                 loop = ig.DefineLabel ();
2154                                 ig.MarkLabel (loop);
2155
2156                                 ig.Emit (OpCodes.Ldloc, copy);
2157                                 ig.Emit (OpCodes.Ldloc, counter);
2158                                 ArrayAccess.EmitLoadOpcode (ig, var_type);
2159
2160                                 variable.EmitAssign (ec, conv);
2161
2162                                 statement.Emit (ec);
2163
2164                                 ig.MarkLabel (ec.LoopBegin);
2165                                 ig.Emit (OpCodes.Ldloc, counter);
2166                                 ig.Emit (OpCodes.Ldc_I4_1);
2167                                 ig.Emit (OpCodes.Add);
2168                                 ig.Emit (OpCodes.Stloc, counter);
2169
2170                                 ig.MarkLabel (test);
2171                                 ig.Emit (OpCodes.Ldloc, counter);
2172                                 ig.Emit (OpCodes.Ldloc, copy);
2173                                 ig.Emit (OpCodes.Ldlen);
2174                                 ig.Emit (OpCodes.Conv_I4);
2175                                 ig.Emit (OpCodes.Blt, loop);
2176                         } else {
2177                                 LocalBuilder [] dim_len   = new LocalBuilder [rank];
2178                                 LocalBuilder [] dim_count = new LocalBuilder [rank];
2179                                 Label [] loop = new Label [rank];
2180                                 Label [] test = new Label [rank];
2181                                 int dim;
2182                                 
2183                                 for (dim = 0; dim < rank; dim++){
2184                                         dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
2185                                         dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
2186                                         test [dim] = ig.DefineLabel ();
2187                                         loop [dim] = ig.DefineLabel ();
2188                                 }
2189                                         
2190                                 for (dim = 0; dim < rank; dim++){
2191                                         ig.Emit (OpCodes.Ldloc, copy);
2192                                         IntLiteral.EmitInt (ig, dim);
2193                                         ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
2194                                         ig.Emit (OpCodes.Stloc, dim_len [dim]);
2195                                 }
2196
2197                                 for (dim = 0; dim < rank; dim++){
2198                                         ig.Emit (OpCodes.Ldc_I4_0);
2199                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
2200                                         ig.Emit (OpCodes.Br, test [dim]);
2201                                         ig.MarkLabel (loop [dim]);
2202                                 }
2203
2204                                 ig.Emit (OpCodes.Ldloc, copy);
2205                                 for (dim = 0; dim < rank; dim++)
2206                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2207
2208                                 //
2209                                 // FIXME: Maybe we can cache the computation of `get'?
2210                                 //
2211                                 Type [] args = new Type [rank];
2212                                 MethodInfo get;
2213
2214                                 for (int i = 0; i < rank; i++)
2215                                         args [i] = TypeManager.int32_type;
2216
2217                                 ModuleBuilder mb = RootContext.ModuleBuilder;
2218                                 get = mb.GetArrayMethod (
2219                                         array_type, "Get",
2220                                         CallingConventions.HasThis| CallingConventions.Standard,
2221                                         var_type, args);
2222                                 ig.Emit (OpCodes.Call, get);
2223                                 variable.EmitAssign (ec, conv);
2224                                 statement.Emit (ec);
2225                                 ig.MarkLabel (ec.LoopBegin);
2226                                 for (dim = rank - 1; dim >= 0; dim--){
2227                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2228                                         ig.Emit (OpCodes.Ldc_I4_1);
2229                                         ig.Emit (OpCodes.Add);
2230                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
2231
2232                                         ig.MarkLabel (test [dim]);
2233                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
2234                                         ig.Emit (OpCodes.Ldloc, dim_len [dim]);
2235                                         ig.Emit (OpCodes.Blt, loop [dim]);
2236                                 }
2237                         }
2238                         ig.MarkLabel (ec.LoopEnd);
2239                         
2240                         return false;
2241                 }
2242                 
2243                 public override bool Emit (EmitContext ec)
2244                 {
2245                         Type var_type;
2246                         bool ret_val;
2247                         
2248                         expr = expr.Resolve (ec);
2249                         if (expr == null)
2250                                 return false;
2251
2252                         var_type = RootContext.LookupType (ec.TypeContainer, type, false, loc);
2253                         if (var_type == null)
2254                                 return false;
2255                         
2256                         //
2257                         // We need an instance variable.  Not sure this is the best
2258                         // way of doing this.
2259                         //
2260                         // FIXME: When we implement propertyaccess, will those turn
2261                         // out to return values in ExprClass?  I think they should.
2262                         //
2263                         if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
2264                               expr.eclass == ExprClass.PropertyAccess)){
2265                                 error1579 (expr.Type);
2266                                 return false;
2267                         }
2268
2269                         ILGenerator ig = ec.ig;
2270                         
2271                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
2272                         bool old_inloop = ec.InLoop;
2273                         ec.LoopBegin = ig.DefineLabel ();
2274                         ec.LoopEnd = ig.DefineLabel ();
2275                         ec.InLoop = true;
2276                         
2277                         if (expr.Type.IsArray)
2278                                 ret_val = EmitArrayForeach (ec, var_type);
2279                         else {
2280                                 MethodInfo get_enum;
2281                                 
2282                                 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
2283                                         return false;
2284
2285                                 ret_val = EmitCollectionForeach (ec, var_type, get_enum);
2286                         }
2287                         
2288                         ec.LoopBegin = old_begin;
2289                         ec.LoopEnd = old_end;
2290                         ec.InLoop = old_inloop;
2291
2292                         return ret_val;
2293                 }
2294         }
2295 }
2296