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