2001-11-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
14 namespace Mono.CSharp {
15
16         using System.Collections;
17         
18         public abstract class Statement {
19
20                 /// <summary>
21                 ///   Return value indicates whether the last instruction
22                 ///   was a return instruction
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_ret;
110                         
111                         if (!EmitBoolExpression (ec, Expr, false_target, false))
112                                 return false;
113                         
114                         is_ret = TrueStatement.Emit (ec);
115
116                         if (FalseStatement != null){
117                                 bool branch_emitted = false;
118                                 
119                                 end = ig.DefineLabel ();
120                                 if (!is_ret){
121                                         ig.Emit (OpCodes.Br, end);
122                                         branch_emitted = true;
123                                 }
124                         
125                                 ig.MarkLabel (false_target);
126                                 is_ret = FalseStatement.Emit (ec);
127
128                                 if (branch_emitted)
129                                         ig.MarkLabel (end);
130                         } else
131                                 ig.MarkLabel (false_target);
132
133                         return is_ret;
134                 }
135         }
136
137         public class Do : Statement {
138                 public readonly Expression Expr;
139                 public readonly Statement  EmbeddedStatement;
140                 
141                 public Do (Statement statement, Expression boolExpr)
142                 {
143                         Expr = boolExpr;
144                         EmbeddedStatement = statement;
145                 }
146
147                 public override bool Emit (EmitContext ec)
148                 {
149                         ILGenerator ig = ec.ig;
150                         Label loop = ig.DefineLabel ();
151                         Label old_begin = ec.LoopBegin;
152                         Label old_end = ec.LoopEnd;
153                         bool  old_inloop = ec.InLoop;
154                         
155                         ec.LoopBegin = ig.DefineLabel ();
156                         ec.LoopEnd = ig.DefineLabel ();
157                         ec.InLoop = true;
158                                 
159                         ig.MarkLabel (loop);
160                         EmbeddedStatement.Emit (ec);
161                         ig.MarkLabel (ec.LoopBegin);
162                         EmitBoolExpression (ec, Expr, loop, true);
163                         ig.MarkLabel (ec.LoopEnd);
164
165                         ec.LoopBegin = old_begin;
166                         ec.LoopEnd = old_end;
167                         ec.InLoop = old_inloop;
168                         
169                         return false;
170                 }
171         }
172
173         public class While : Statement {
174                 public readonly Expression Expr;
175                 public readonly Statement Statement;
176                 
177                 public While (Expression boolExpr, Statement statement)
178                 {
179                         Expr = boolExpr;
180                         Statement = statement;
181                 }
182
183                 public override bool Emit (EmitContext ec)
184                 {
185                         ILGenerator ig = ec.ig;
186                         Label old_begin = ec.LoopBegin;
187                         Label old_end = ec.LoopEnd;
188                         bool old_inloop = ec.InLoop;
189                         
190                         ec.LoopBegin = ig.DefineLabel ();
191                         ec.LoopEnd = ig.DefineLabel ();
192                         ec.InLoop = true;
193                         
194                         ig.MarkLabel (ec.LoopBegin);
195                         EmitBoolExpression (ec, Expr, ec.LoopEnd, false);
196                         Statement.Emit (ec);
197                         ig.Emit (OpCodes.Br, ec.LoopBegin);
198                         ig.MarkLabel (ec.LoopEnd);
199
200                         ec.LoopBegin = old_begin;
201                         ec.LoopEnd = old_end;
202                         ec.InLoop = old_inloop;
203                         
204                         return false;
205                 }
206         }
207
208         public class For : Statement {
209                 public readonly Statement InitStatement;
210                 public readonly Expression Test;
211                 public readonly Statement Increment;
212                 public readonly Statement Statement;
213                 
214                 public For (Statement initStatement,
215                             Expression test,
216                             Statement increment,
217                             Statement statement)
218                 {
219                         InitStatement = initStatement;
220                         Test = test;
221                         Increment = increment;
222                         Statement = statement;
223                 }
224
225                 public override bool Emit (EmitContext ec)
226                 {
227                         ILGenerator ig = ec.ig;
228                         Label old_begin = ec.LoopBegin;
229                         Label old_end = ec.LoopEnd;
230                         bool old_inloop = ec.InLoop;
231                         Label loop = ig.DefineLabel ();
232
233                         if (InitStatement != null)
234                                 if (! (InitStatement is EmptyStatement))
235                                         InitStatement.Emit (ec);
236
237                         ec.LoopBegin = ig.DefineLabel ();
238                         ec.LoopEnd = ig.DefineLabel ();
239                         ec.InLoop = true;
240
241                         ig.MarkLabel (loop);
242                         EmitBoolExpression (ec, Test, ec.LoopEnd, false);
243                         Statement.Emit (ec);
244                         ig.MarkLabel (ec.LoopBegin);
245                         if (!(Increment is EmptyStatement))
246                                 Increment.Emit (ec);
247                         ig.Emit (OpCodes.Br, loop);
248                         ig.MarkLabel (ec.LoopEnd);
249
250                         ec.LoopBegin = old_begin;
251                         ec.LoopEnd = old_end;
252                         ec.InLoop = old_inloop;
253                         return false;
254                 }
255         }
256         
257         public class StatementExpression : Statement {
258                 public readonly ExpressionStatement Expr;
259                 
260                 public StatementExpression (ExpressionStatement expr)
261                 {
262                         Expr = expr;
263                 }
264
265                 public override bool Emit (EmitContext ec)
266                 {
267                         ILGenerator ig = ec.ig;
268                         Expression ne;
269                         
270                         ne = Expr.Resolve (ec);
271                         if (ne != null){
272                                 if (ne is ExpressionStatement)
273                                         ((ExpressionStatement) ne).EmitStatement (ec);
274                                 else {
275                                         ne.Emit (ec);
276                                         ig.Emit (OpCodes.Pop);
277                                 }
278                         }
279
280                         return false;
281                 }
282
283                 public override string ToString ()
284                 {
285                         return "StatementExpression (" + Expr + ")";
286                 }
287         }
288
289         public class Return : Statement {
290                 public Expression Expr;
291                 public readonly Location loc;
292                 
293                 public Return (Expression expr, Location l)
294                 {
295                         Expr = expr;
296                         loc = l;
297                 }
298
299                 public override bool Emit (EmitContext ec)
300                 {
301                         if (ec.ReturnType == null){
302                                 if (Expr != null){
303                                         Report.Error (127, loc, "Return with a value not allowed here");
304                                         return false;
305                                 }
306                         } else {
307                                 if (Expr == null){
308                                         Report.Error (126, loc, "An object of type `" +
309                                                       TypeManager.CSharpName (ec.ReturnType) + "' is " +
310                                                       "expected for the return statement");
311                                         return false;
312                                 }
313
314                                 Expr = Expr.Resolve (ec);
315                                 if (Expr == null)
316                                         return false;
317
318                                 if (Expr.Type != ec.ReturnType)
319                                         Expr = Expression.ConvertImplicitRequired (
320                                                 ec, Expr, ec.ReturnType, loc);
321
322                                 if (Expr == null)
323                                         return false;
324
325                                 Expr.Emit (ec);
326                         }
327
328                         ec.ig.Emit (OpCodes.Ret);
329
330                         return true; 
331                 }
332         }
333
334         public class Goto : Statement {
335                 string target;
336                 Location loc;
337                         
338                 public Goto (string label, Location l)
339                 {
340                         loc = l;
341                         target = label;
342                 }
343
344                 public string Target {
345                         get {
346                                 return target;
347                         }
348                 }
349
350                 public override bool Emit (EmitContext ec)
351                 {
352                         Console.WriteLine ("Attempting to goto to: " + target);
353                         
354                         return false;
355                 }
356         }
357
358         public class Throw : Statement {
359                 public readonly Expression Expr;
360                 
361                 public Throw (Expression expr)
362                 {
363                         Expr = expr;
364                 }
365
366                 public override bool Emit (EmitContext ec)
367                 {
368                         Expression e = Expr.Resolve (ec);
369
370                         if (e == null)
371                                 return false;
372
373                         e.Emit (ec);
374                         ec.ig.Emit (OpCodes.Throw);
375
376                         return false;
377                 }
378         }
379
380         public class Break : Statement {
381                 Location loc;
382                 
383                 public Break (Location l)
384                 {
385                         loc = l;
386                 }
387
388                 public override bool Emit (EmitContext ec)
389                 {
390                         ILGenerator ig = ec.ig;
391
392                         if (!ec.InLoop){
393                                 Report.Error (139, loc, "No enclosing loop to continue to");
394                                 return false;
395                         }
396                         
397                         ig.Emit (OpCodes.Br, ec.LoopEnd);
398                         return false;
399                 }
400         }
401
402         public class Continue : Statement {
403                 Location loc;
404                 
405                 public Continue (Location l)
406                 {
407                         loc = l;
408                 }
409
410                 public override bool Emit (EmitContext ec)
411                 {
412                         Label begin = ec.LoopBegin;
413                         
414                         if (!ec.InLoop){
415                                 Report.Error (139, loc, "No enclosing loop to continue to");
416                                 return false;
417                         } 
418
419                         ec.ig.Emit (OpCodes.Br, begin);
420                         return false;
421                 }
422         }
423         
424         public class VariableInfo {
425                 public readonly string Type;
426                 public LocalBuilder LocalBuilder;
427                 public Type VariableType;
428                 public readonly Location Location;
429                 
430                 int  idx;
431                 public bool Used;
432                 public bool Assigned; 
433                 
434                 public VariableInfo (string type, Location l)
435                 {
436                         Type = type;
437                         LocalBuilder = null;
438                         idx = -1;
439                         Location = l;
440                 }
441
442                 public int Idx {
443                         get {
444                                 if (idx == -1)
445                                         throw new Exception ("Unassigned idx for variable");
446                                 
447                                 return idx;
448                         }
449
450                         set {
451                                 idx = value;
452                         }
453                 }
454
455         }
456                 
457         /// <summary>
458         ///   Block represents a C# block.
459         /// </summary>
460         ///
461         /// <remarks>
462         ///   This class is used in a number of places: either to represent
463         ///   explicit blocks that the programmer places or implicit blocks.
464         ///
465         ///   Implicit blocks are used as labels or to introduce variable
466         ///   declarations.
467         /// </remarks>
468         public class Block : Statement {
469                 public readonly Block  Parent;
470                 public readonly bool   Implicit;
471                 public readonly string Label;
472
473                 //
474                 // The statements in this block
475                 //
476                 StatementCollection statements;
477
478                 //
479                 // An array of Blocks.  We keep track of children just
480                 // to generate the local variable declarations.
481                 //
482                 // Statements and child statements are handled through the
483                 // statements.
484                 //
485                 ArrayList children;
486                 
487                 //
488                 // Labels.  (label, block) pairs.
489                 //
490                 Hashtable labels;
491
492                 //
493                 // Keeps track of (name, type) pairs
494                 //
495                 Hashtable variables;
496
497                 //
498                 // Maps variable names to ILGenerator.LocalBuilders
499                 //
500                 Hashtable local_builders;
501
502                 bool used = false;
503
504                 static int id;
505
506                 int this_id;
507                 
508                 public Block (Block parent)
509                 {
510                         if (parent != null)
511                                 parent.AddChild (this);
512                         
513                         this.Parent = parent;
514                         this.Implicit = false;
515
516                         this_id = id++;
517                 }
518
519                 public Block (Block parent, bool implicit_block)
520                 {
521                         if (parent != null)
522                                 parent.AddChild (this);
523                         
524                         this.Parent = parent;
525                         this.Implicit = true;
526                         this_id = id++;
527                 }
528
529                 public Block (Block parent, string labeled)
530                 {
531                         if (parent != null)
532                                 parent.AddChild (this);
533                         
534                         this.Parent = parent;
535                         this.Implicit = true;
536                         Label = labeled;
537                         this_id = id++;
538                 }
539
540                 public int ID {
541                         get {
542                                 return this_id;
543                         }
544                 }
545                 
546                 void AddChild (Block b)
547                 {
548                         if (children == null)
549                                 children = new ArrayList ();
550                         
551                         children.Add (b);
552                 }
553
554                 /// <summary>
555                 ///   Adds a label to the current block. 
556                 /// </summary>
557                 ///
558                 /// <returns>
559                 ///   false if the name already exists in this block. true
560                 ///   otherwise.
561                 /// </returns>
562                 ///
563                 public bool AddLabel (string name, Block block)
564                 {
565                         if (labels == null)
566                                 labels = new Hashtable ();
567                         if (labels.Contains (name))
568                                 return false;
569                         
570                         labels.Add (name, block);
571                         return true;
572                 }
573
574                 public bool AddVariable (string type, string name, Location l)
575                 {
576                         if (variables == null)
577                                 variables = new Hashtable ();
578
579                         if (GetVariableType (name) != null)
580                                 return false;
581
582                         VariableInfo vi = new VariableInfo (type, l);
583
584                         variables.Add (name, vi);
585
586                         return true;
587                 }
588
589                 public Hashtable Variables {
590                         get {
591                                 return variables;
592                         }
593                 }
594
595                 public VariableInfo GetVariableInfo (string name)
596                 {
597                         if (variables != null) {
598                                 object temp;
599                                 temp = variables [name];
600
601                                 if (temp != null){
602                                         return (VariableInfo) temp;
603                                 }
604                         }
605
606                         if (Parent != null)
607                                 return Parent.GetVariableInfo (name);
608
609                         return null;
610                 }
611                 
612                 public string GetVariableType (string name)
613                 {
614                         VariableInfo vi = GetVariableInfo (name);
615
616                         if (vi != null)
617                                 return vi.Type;
618
619                         return null;
620                 }
621
622                 /// <summary>
623                 ///   True if the variable named @name has been defined
624                 ///   in this block
625                 /// </summary>
626                 public bool IsVariableDefined (string name)
627                 {
628                         return GetVariableType (name) != null;
629                 }
630
631                 /// <summary>
632                 ///   Use to fetch the statement associated with this label
633                 /// </summary>
634                 public Statement this [string name] {
635                         get {
636                                 return (Statement) labels [name];
637                         }
638                 }
639
640                 /// <returns>
641                 ///   A list of labels that were not used within this block
642                 /// </returns>
643                 public string [] GetUnreferenced ()
644                 {
645                         // FIXME: Implement me
646                         return null;
647                 }
648
649                 public StatementCollection Statements {
650                         get {
651                                 if (statements == null)
652                                         statements = new StatementCollection ();
653
654                                 return statements;
655                         }
656                 }
657
658                 public void AddStatement (Statement s)
659                 {
660                         if (statements == null)
661                                 statements = new StatementCollection ();
662
663                         statements.Add (s);
664                         used = true;
665                 }
666
667                 public bool Used {
668                         get {
669                                 return used;
670                         }
671                 }
672
673                 public void Use ()
674                 {
675                         used = true;
676                 }
677                 
678                 /// <summary>
679                 ///   Emits the variable declarations and labels.
680                 /// </summary>
681                 /// <remarks>
682                 ///   tc: is our typecontainer (to resolve type references)
683                 ///   ig: is the code generator:
684                 ///   toplevel: the toplevel block.  This is used for checking 
685                 ///             that no two labels with the same name are used.
686                 /// </remarks>
687                 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
688                 {
689                         //
690                         // Process this block variables
691                         //
692                         if (variables != null){
693                                 local_builders = new Hashtable ();
694                                 
695                                 foreach (DictionaryEntry de in variables){
696                                         string name = (string) de.Key;
697                                         VariableInfo vi = (VariableInfo) de.Value;
698                                         Type t;
699                                         
700                                         t = tc.LookupType (vi.Type, false);
701                                         if (t == null)
702                                                 continue;
703
704                                         vi.VariableType = t;
705                                         vi.LocalBuilder = ig.DeclareLocal (t);
706                                         vi.Idx = count++;
707                                 }
708                         }
709
710                         //
711                         // Now, handle the children
712                         //
713                         if (children != null){
714                                 foreach (Block b in children)
715                                         b.EmitMeta (tc, ig, toplevel, count);
716                         }
717                 }
718
719                 public void UsageWarning ()
720                 {
721                         string name;
722                         
723                         if (variables != null){
724                                 foreach (DictionaryEntry de in variables){
725                                         VariableInfo vi = (VariableInfo) de.Value;
726                                         
727                                         if (vi.Used)
728                                                 continue;
729                                         
730                                         name = (string) de.Key;
731                                                 
732                                         if (vi.Assigned){
733                                                 Report.Warning (
734                                                         219, vi.Location, "The variable `" + name +
735                                                         "' is assigned but its value is never used");
736                                         } else {
737                                                 Report.Warning (
738                                                         168, vi.Location, "The variable `" +
739                                                         name +
740                                                         "' is declared but never used");
741                                         } 
742                                 }
743                         }
744
745                         if (children != null)
746                                 foreach (Block b in children)
747                                         b.UsageWarning ();
748                 }
749
750                 public override bool Emit (EmitContext ec)
751                 {
752                         bool is_ret = false;
753                         Block prev_block = ec.CurrentBlock;
754
755                         ec.CurrentBlock = this;
756                         foreach (Statement s in Statements)
757                                 is_ret = s.Emit (ec);
758
759                         ec.CurrentBlock = prev_block;
760                         return is_ret;
761                 }
762         }
763
764         public class SwitchLabel {
765                 Expression label;
766
767                 //
768                 // if expr == null, then it is the default case.
769                 //
770                 public SwitchLabel (Expression expr)
771                 {
772                         label = expr;
773                 }
774                 
775                 public Expression Label {
776                         get {
777                                 return label;
778                         }
779                 }
780         }
781
782         public class SwitchSection {
783                 // An array of SwitchLabels.
784                 ArrayList labels;
785                 Block block;
786                 
787                 public SwitchSection (ArrayList labels, Block block)
788                 {
789                         this.labels = labels;
790                         this.block = block;
791                 }
792
793                 public Block Block {
794                         get {
795                                 return block;
796                         }
797                 }
798
799                 public ArrayList Labels {
800                         get {
801                                 return labels;
802                         }
803                 }
804         }
805         
806         public class Switch : Statement {
807                 ArrayList sections;
808                 Expression expr;
809                 
810                 public Switch (Expression expr, ArrayList sections)
811                 {
812                         this.expr = expr;
813                         this.sections = sections;
814                 }
815
816                 public Expression Expr {
817                         get {
818                                 return expr;
819                         }
820                 }
821
822                 public ArrayList Sections {
823                         get {
824                                 return sections;
825                         }
826                 }
827
828                 public override bool Emit (EmitContext ec)
829                 {
830                         throw new Exception ("Unimplemented");
831                 }
832         }
833
834         public class Lock : Statement {
835                 public readonly Expression Expr;
836                 public readonly Statement Statement;
837                 Location loc;
838                         
839                 public Lock (Expression expr, Statement stmt, Location l)
840                 {
841                         Expr = expr;
842                         Statement = stmt;
843                         loc = l;
844                 }
845
846                 public override bool Emit (EmitContext ec)
847                 {
848                         Expression e = Expr.Resolve (ec);
849                         if (e == null)
850                                 return false;
851
852                         Type type = e.Type;
853                         
854                         if (type.IsValueType){
855                                 Report.Error (185, loc, "lock statement requires the expression to be " +
856                                               " a reference type (type is: `" +
857                                               TypeManager.CSharpName (type) + "'");
858                                 return false;
859                         }
860
861                         ILGenerator ig = ec.ig;
862                         LocalBuilder temp = ig.DeclareLocal (type);
863                                 
864                         e.Emit (ec);
865                         ig.Emit (OpCodes.Dup);
866                         ig.Emit (OpCodes.Stloc, temp);
867                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
868
869                         // try
870                         Label end = ig.BeginExceptionBlock ();
871                         Label finish = ig.DefineLabel ();
872                         Statement.Emit (ec);
873                         // ig.Emit (OpCodes.Leave, finish);
874
875                         ig.MarkLabel (finish);
876                         
877                         // finally
878                         ig.BeginFinallyBlock ();
879                         ig.Emit (OpCodes.Ldloc, temp);
880                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
881                         ig.EndExceptionBlock ();
882                         
883                         return false;
884                 }
885         }
886
887         public class Unchecked : Statement {
888                 public readonly Block Block;
889                 
890                 public Unchecked (Block b)
891                 {
892                         Block = b;
893                 }
894
895                 public override bool Emit (EmitContext ec)
896                 {
897                         bool previous_state = ec.CheckState;
898                         bool val;
899                         
900                         ec.CheckState = false;
901                         val = Block.Emit (ec);
902                         ec.CheckState = previous_state;
903
904                         return val;
905                 }
906         }
907
908         public class Checked : Statement {
909                 public readonly Block Block;
910                 
911                 public Checked (Block b)
912                 {
913                         Block = b;
914                 }
915
916                 public override bool Emit (EmitContext ec)
917                 {
918                         bool previous_state = ec.CheckState;
919                         bool val;
920                         
921                         ec.CheckState = true;
922                         val = Block.Emit (ec);
923                         ec.CheckState = previous_state;
924
925                         return val;
926                 }
927         }
928
929         public class Catch {
930                 public readonly string Type;
931                 public readonly string Name;
932                 public readonly Block  Block;
933                 
934                 public Catch (string type, string name, Block block)
935                 {
936                         Type = type;
937                         Name = name;
938                         Block = block;
939                 }
940         }
941
942         public class Try : Statement {
943                 public readonly Block Fini, Block;
944                 public readonly ArrayList Specific;
945                 public readonly Catch General;
946                 
947                 //
948                 // specific, general and fini might all be null.
949                 //
950                 public Try (Block block, ArrayList specific, Catch general, Block fini)
951                 {
952                         if (specific == null && general == null){
953                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
954                         }
955                         
956                         this.Block = block;
957                         this.Specific = specific;
958                         this.General = general;
959                         this.Fini = fini;
960                 }
961
962                 public override bool Emit (EmitContext ec)
963                 {
964                         ILGenerator ig = ec.ig;
965                         Label end;
966                         Label finish = ig.DefineLabel ();;
967
968                         end = ig.BeginExceptionBlock ();
969                         Block.Emit (ec);
970                         ig.Emit (OpCodes.Leave, finish);
971                         
972                         foreach (Catch c in Specific){
973                                 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
974                                 VariableInfo vi;
975                                 
976                                 if (catch_type == null)
977                                         return false;
978
979                                 ig.BeginCatchBlock (catch_type);
980
981                                 if (c.Name != null){
982                                         vi = c.Block.GetVariableInfo (c.Name);
983                                         if (vi == null){
984                                                 Console.WriteLine ("This should not happen! variable does not exist in this block");
985                                                 Environment.Exit (0);
986                                         }
987                                 
988                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
989                                 } else
990                                         ig.Emit (OpCodes.Pop);
991                                 
992                                 c.Block.Emit (ec);
993                         }
994
995                         if (General != null){
996                                 ig.BeginCatchBlock (TypeManager.object_type);
997                                 ig.Emit (OpCodes.Pop);
998                         }
999
1000                         ig.MarkLabel (finish);
1001                         if (Fini != null){
1002                                 ig.BeginFinallyBlock ();
1003                                 Fini.Emit (ec);
1004                         }
1005                         
1006                         ig.EndExceptionBlock ();
1007
1008                         return false;
1009                 }
1010         }
1011
1012         public class Using : Statement {
1013                 object expression_or_block;
1014                 Statement Statement;
1015                 Location loc;
1016                 
1017                 public Using (object expression_or_block, Statement stmt, Location l)
1018                 {
1019                         this.expression_or_block = expression_or_block;
1020                         Statement = stmt;
1021                         loc = l;
1022                 }
1023
1024                 public override bool Emit (EmitContext ec)
1025                 {
1026                         //
1027                         // Expressions are simple. 
1028                         // The problem is with blocks, blocks might contain
1029                         // more than one variable, ie like this:
1030                         //
1031                         // using (a = new X (), b = new Y ()) stmt;
1032                         //
1033                         // which is turned into:
1034                         // using (a = new X ()) using (b = new Y ()) stmt;
1035                         //
1036                         // The trick is that the block will contain a bunch
1037                         // of potential Assign expressions
1038                         //
1039                         //
1040                         // We need to signal an error if a variable lacks
1041                         // an assignment. (210).
1042                         //
1043                         // This is one solution.  Another is to set a flag
1044                         // when we get the USING token, and have declare_local_variables
1045                         // do something *different* that we can better cope with
1046                         //
1047                         throw new Exception ("Implement me!");
1048                 }
1049         }
1050         
1051         public class Foreach : Statement {
1052                 string type;
1053                 LocalVariableReference variable;
1054                 Expression expr;
1055                 Statement statement;
1056                 Location loc;
1057                 
1058                 public Foreach (string type, LocalVariableReference var, Expression expr,
1059                                 Statement stmt, Location l)
1060                 {
1061                         this.type = type;
1062                         this.variable = var;
1063                         this.expr = expr;
1064                         statement = stmt;
1065                         loc = l;
1066                 }
1067
1068                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1069                 {
1070                         if (m == null)
1071                                 return false;
1072                         
1073                         if (!(m is MethodInfo))
1074                                 return false;
1075                         
1076                         if (m.Name != "GetEnumerator")
1077                                 return false;
1078                         
1079                         MethodInfo mi = (MethodInfo) m;
1080                         
1081                         if (mi.ReturnType != TypeManager.ienumerator_type)
1082                                 return false;
1083                         
1084                         Type [] args = TypeManager.GetArgumentTypes (mi);
1085                         if (args == null)
1086                                 return true;
1087                         
1088                         if (args.Length == 0)
1089                                 return true;
1090                         
1091                         return false;
1092                 }
1093                 
1094                 /// <summary>
1095                 ///   This filter is used to find the GetEnumerator method
1096                 ///   on which IEnumerator operates
1097                 /// </summary>
1098                 static MemberFilter FilterEnumerator;
1099                 
1100                 static Foreach ()
1101                 {
1102                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1103                 }
1104
1105                 void error1579 (Type t)
1106                 {
1107                         Report.Error (1579, loc,
1108                                       "foreach statement cannot operate on variables of type `" +
1109                                       t.FullName + "' because that class does not provide a " +
1110                                       " GetEnumerator method or it is inaccessible");
1111                 }
1112
1113                 MethodInfo ProbeCollectionType (Type t)
1114                 {
1115                         MemberInfo [] mi;
1116
1117                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1118                                                         BindingFlags.Public | BindingFlags.Instance,
1119                                                         FilterEnumerator, null);
1120
1121                         if (mi == null){
1122                                 error1579 (t);
1123                                 return null;
1124                         }
1125
1126                         if (mi.Length == 0){
1127                                 error1579 (t);
1128                                 return null;
1129                         }
1130
1131                         return (MethodInfo) mi [0];
1132                 }
1133
1134                 //
1135                 // FIXME: possible optimization.
1136                 // We might be able to avoid creating `empty' if the type is the sam
1137                 //
1138                 bool EmitCollectionForeach (EmitContext ec, Type var_type, MethodInfo get_enum)
1139                 {
1140                         ILGenerator ig = ec.ig;
1141                         LocalBuilder enumerator, disposable;
1142                         Expression empty = new EmptyExpression ();
1143                         Expression conv;
1144
1145                         //
1146                         // FIXME: maybe we can apply the same trick we do in the
1147                         // array handling to avoid creating empty and conv in some cases.
1148                         //
1149                         // Although it is not as important in this case, as the type
1150                         // will not likely be object (what the enumerator will return).
1151                         //
1152                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
1153                         if (conv == null)
1154                                 return false;
1155                         
1156                         enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1157                         disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1158                         
1159                         //
1160                         // Instantiate the enumerator
1161
1162                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1163                         Label end_try = ig.DefineLabel ();
1164                         bool old_inloop = ec.InLoop;
1165                         ec.LoopBegin = ig.DefineLabel ();
1166                         ec.LoopEnd = ig.DefineLabel ();
1167                         ec.InLoop = true;
1168                         
1169                         //
1170                         // FIXME: This code does not work for cases like:
1171                         // foreach (int a in ValueTypeVariable){
1172                         // }
1173                         //
1174                         // The code should emit an ldarga instruction
1175                         // for the ValueTypeVariable rather than a ldarg
1176                         //
1177                         if (expr.Type.IsValueType){
1178                                 ig.Emit (OpCodes.Call, get_enum);
1179                         } else {
1180                                 expr.Emit (ec);
1181                                 ig.Emit (OpCodes.Callvirt, get_enum);
1182                         }
1183                         ig.Emit (OpCodes.Stloc, enumerator);
1184
1185                         //
1186                         // Protect the code in a try/finalize block, so that
1187                         // if the beast implement IDisposable, we get rid of it
1188                         //
1189                         Label l = ig.BeginExceptionBlock ();
1190                         ig.MarkLabel (ec.LoopBegin);
1191                         ig.Emit (OpCodes.Ldloc, enumerator);
1192                         ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1193                         ig.Emit (OpCodes.Brfalse, end_try);
1194                         ig.Emit (OpCodes.Ldloc, enumerator);
1195                         ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1196                         variable.EmitAssign (ec, conv);
1197                         statement.Emit (ec);
1198                         ig.Emit (OpCodes.Br, ec.LoopBegin);
1199                         ig.MarkLabel (end_try);
1200
1201                         // The runtime provides this for us.
1202                         // ig.Emit (OpCodes.Leave, end);
1203
1204                         //
1205                         // Now the finally block
1206                         //
1207                         Label end_finally = ig.DefineLabel ();
1208                         
1209                         ig.BeginFinallyBlock ();
1210                         ig.Emit (OpCodes.Ldloc, enumerator);
1211                         ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1212                         ig.Emit (OpCodes.Stloc, disposable);
1213                         ig.Emit (OpCodes.Ldloc, disposable);
1214                         ig.Emit (OpCodes.Brfalse, end_finally);
1215                         ig.Emit (OpCodes.Ldloc, disposable);
1216                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1217                         ig.MarkLabel (end_finally);
1218
1219                         // The runtime generates this anyways.
1220                         // ig.Emit (OpCodes.Endfinally);
1221
1222                         ig.EndExceptionBlock ();
1223
1224                         ig.MarkLabel (ec.LoopEnd);
1225                         
1226                         ec.LoopBegin = old_begin;
1227                         ec.LoopEnd = old_end;
1228                         ec.InLoop = old_inloop;
1229
1230                         return false;
1231                 }
1232
1233                 //
1234                 // FIXME: possible optimization.
1235                 // We might be able to avoid creating `empty' if the type is the sam
1236                 //
1237                 bool EmitArrayForeach (EmitContext ec, Type var_type)
1238                 {
1239                         Type array_type = expr.Type;
1240                         Type element_type = array_type.GetElementType ();
1241                         Expression conv = null;
1242                         Expression empty = new EmptyExpression (var_type);
1243                         
1244                         conv = Expression.ConvertExplicit (ec, empty, var_type, loc);
1245                         if (conv == null)
1246                                         return false;
1247
1248                         int rank = array_type.GetArrayRank ();
1249                         ILGenerator ig = ec.ig;
1250
1251                         LocalBuilder copy = ig.DeclareLocal (array_type);
1252                         
1253                         //
1254                         // Make our copy of the array
1255                         //
1256                         expr.Emit (ec);
1257                         ig.Emit (OpCodes.Stloc, copy);
1258                         
1259                         if (rank == 1){
1260                                 LocalBuilder counter = ig.DeclareLocal (TypeManager.int32_type);
1261
1262                                 Label loop, test;
1263                                 
1264                                 ig.Emit (OpCodes.Ldc_I4_0);
1265                                 ig.Emit (OpCodes.Stloc, counter);
1266                                 test = ig.DefineLabel ();
1267                                 ig.Emit (OpCodes.Br, test);
1268
1269                                 loop = ig.DefineLabel ();
1270                                 ig.MarkLabel (loop);
1271
1272                                 ig.Emit (OpCodes.Ldloc, copy);
1273                                 ig.Emit (OpCodes.Ldloc, counter);
1274                                 ArrayAccess.EmitLoadOpcode (ig, var_type);
1275
1276                                 variable.EmitAssign (ec, conv);
1277
1278                                 statement.Emit (ec);
1279
1280                                 ig.Emit (OpCodes.Ldloc, counter);
1281                                 ig.Emit (OpCodes.Ldc_I4_1);
1282                                 ig.Emit (OpCodes.Add);
1283                                 ig.Emit (OpCodes.Stloc, counter);
1284
1285                                 ig.MarkLabel (test);
1286                                 ig.Emit (OpCodes.Ldloc, counter);
1287                                 ig.Emit (OpCodes.Ldloc, copy);
1288                                 ig.Emit (OpCodes.Ldlen);
1289                                 ig.Emit (OpCodes.Conv_I4);
1290                                 ig.Emit (OpCodes.Blt, loop);
1291                         } else {
1292                                 LocalBuilder [] dim_len   = new LocalBuilder [rank];
1293                                 LocalBuilder [] dim_count = new LocalBuilder [rank];
1294                                 Label [] loop = new Label [rank];
1295                                 Label [] test = new Label [rank];
1296                                 int dim;
1297                                 
1298                                 for (dim = 0; dim < rank; dim++){
1299                                         dim_len [dim] = ig.DeclareLocal (TypeManager.int32_type);
1300                                         dim_count [dim] = ig.DeclareLocal (TypeManager.int32_type);
1301                                         test [dim] = ig.DefineLabel ();
1302                                         loop [dim] = ig.DefineLabel ();
1303                                 }
1304                                         
1305                                 for (dim = 0; dim < rank; dim++){
1306                                         ig.Emit (OpCodes.Ldloc, copy);
1307                                         IntLiteral.EmitInt (ig, dim);
1308                                         ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
1309                                         ig.Emit (OpCodes.Stloc, dim_len [dim]);
1310                                 }
1311
1312                                 for (dim = 0; dim < rank; dim++){
1313                                         ig.Emit (OpCodes.Ldc_I4_0);
1314                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
1315                                         ig.Emit (OpCodes.Br, test [dim]);
1316                                         ig.MarkLabel (loop [dim]);
1317                                 }
1318
1319                                 ig.Emit (OpCodes.Ldloc, copy);
1320                                 for (dim = 0; dim < rank; dim++)
1321                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1322
1323                                 //
1324                                 // FIXME: Maybe we can cache the computation of `get'?
1325                                 //
1326                                 Type [] args = new Type [rank];
1327                                 MethodInfo get;
1328
1329                                 for (int i = 0; i < rank; i++)
1330                                         args [i] = TypeManager.int32_type;
1331
1332                                 ModuleBuilder mb = ec.TypeContainer.RootContext.ModuleBuilder;
1333                                 get = mb.GetArrayMethod (
1334                                         array_type, "Get",
1335                                         CallingConventions.HasThis| CallingConventions.Standard,
1336                                         var_type, args);
1337                                 ig.Emit (OpCodes.Call, get);
1338                                 variable.EmitAssign (ec, conv);
1339                                 statement.Emit (ec);
1340                                 for (dim = rank - 1; dim >= 0; dim--){
1341                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1342                                         ig.Emit (OpCodes.Ldc_I4_1);
1343                                         ig.Emit (OpCodes.Add);
1344                                         ig.Emit (OpCodes.Stloc, dim_count [dim]);
1345
1346                                         ig.MarkLabel (test [dim]);
1347                                         ig.Emit (OpCodes.Ldloc, dim_count [dim]);
1348                                         ig.Emit (OpCodes.Ldloc, dim_len [dim]);
1349                                         ig.Emit (OpCodes.Blt, loop [dim]);
1350                                 }
1351                         }
1352
1353                         return false;
1354                 }
1355                 
1356                 public override bool Emit (EmitContext ec)
1357                 {
1358                         Type var_type;
1359                         
1360                         expr = expr.Resolve (ec);
1361                         if (expr == null)
1362                                 return false;
1363
1364                         var_type = ec.TypeContainer.LookupType (type, false);
1365                         if (var_type == null)
1366                                 return false;
1367                         
1368                         //
1369                         // We need an instance variable.  Not sure this is the best
1370                         // way of doing this.
1371                         //
1372                         // FIXME: When we implement propertyaccess, will those turn
1373                         // out to return values in ExprClass?  I think they should.
1374                         //
1375                         if (!(expr.ExprClass == ExprClass.Variable || expr.ExprClass == ExprClass.Value)){
1376                                 error1579 (expr.Type);
1377                                 return false;
1378                         }
1379
1380                         if (expr.Type.IsArray)
1381                                 return EmitArrayForeach (ec, var_type);
1382                         else {
1383                                 MethodInfo get_enum;
1384                                 
1385                                 if ((get_enum = ProbeCollectionType (expr.Type)) == null)
1386                                         return false;
1387
1388                                 return EmitCollectionForeach (ec, var_type, get_enum);
1389                         }
1390
1391                 }
1392
1393         }
1394 }
1395