2001-11-05 Miguel de Icaza <miguel@ximian.com>
[mono.git] / mcs / mcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) 2001 Ximian, Inc.
8 //
9
10 using System;
11 using System.Reflection;
12 using System.Reflection.Emit;
13
14 namespace CIR {
15
16         using System.Collections;
17         
18         public abstract class Statement {
19
20                 //
21                 // Return value indicates whether the last instruction
22                 // was a return instruction
23                 //
24                 public abstract bool Emit (EmitContext ec);
25
26                 // <remarks>
27                 //    Emits a bool expression.  Generates a jump to the `t' label if true
28                 //    if defined, or to `f' if defined.
29                 //
30                 //    t and f can not be both non-null
31                 // </remarks>
32                 public static bool EmitBoolExpression (EmitContext ec, Expression e, Label l, bool isTrue)
33                 {
34                         e = e.Resolve (ec);
35
36                         if (e == null)
37                                 return false;
38
39                         if (e.Type != TypeManager.bool_type)
40                                 e = Expression.ConvertImplicit (ec, e, TypeManager.bool_type,
41                                                                 new Location (-1));
42
43                         if (e == null){
44                                 Report.Error (
45                                         31, "Can not convert the expression to a boolean");
46                                 return false;
47                         }
48
49                         bool invert = false;
50                         if (e is Unary){
51                                 Unary u = (Unary) e;
52                                 
53                                 if (u.Oper == Unary.Operator.LogicalNot){
54                                         invert = true;
55
56                                         u.EmitLogicalNot (ec);
57                                 }
58                         } 
59
60                         if (!invert)
61                                 e.Emit (ec);
62
63                         if (isTrue){
64                                 if (invert)
65                                         ec.ig.Emit (OpCodes.Brfalse, l);
66                                 else
67                                         ec.ig.Emit (OpCodes.Brtrue, l);
68                         } else {
69                                 if (invert)
70                                         ec.ig.Emit (OpCodes.Brtrue, l);
71                                 else
72                                         ec.ig.Emit (OpCodes.Brfalse, l);
73                         }
74                         
75                         return true;
76                 }
77
78         }
79
80         public class EmptyStatement : Statement {
81                 public override bool Emit (EmitContext ec)
82                 {
83                         return false;
84                 }
85         }
86         
87         public class If : Statement {
88                 public readonly Expression  Expr;
89                 public readonly Statement   TrueStatement;
90                 public readonly Statement   FalseStatement;
91                 
92                 public If (Expression expr, Statement trueStatement)
93                 {
94                         Expr = expr;
95                         TrueStatement = trueStatement;
96                 }
97
98                 public If (Expression expr,
99                            Statement trueStatement,
100                            Statement falseStatement)
101                 {
102                         Expr = expr;
103                         TrueStatement = trueStatement;
104                         FalseStatement = falseStatement;
105                 }
106
107                 public override bool Emit (EmitContext ec)
108                 {
109                         ILGenerator ig = ec.ig;
110                         Label false_target = ig.DefineLabel ();
111                         Label end;
112                         bool is_ret;
113                         
114                         if (!EmitBoolExpression (ec, Expr, false_target, false))
115                                 return false;
116                         
117                         is_ret = TrueStatement.Emit (ec);
118
119                         if (FalseStatement != null){
120                                 bool branch_emitted = false;
121                                 
122                                 end = ig.DefineLabel ();
123                                 if (!is_ret){
124                                         ig.Emit (OpCodes.Br, end);
125                                         branch_emitted = true;
126                                 }
127                         
128                                 ig.MarkLabel (false_target);
129                                 is_ret = FalseStatement.Emit (ec);
130
131                                 if (branch_emitted)
132                                         ig.MarkLabel (end);
133                         } else
134                                 ig.MarkLabel (false_target);
135
136                         return is_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 is EmptyStatement))
237                                 InitStatement.Emit (ec);
238
239                         ec.LoopBegin = ig.DefineLabel ();
240                         ec.LoopEnd = ig.DefineLabel ();
241                         ec.InLoop = true;
242
243                         ig.MarkLabel (loop);
244                         EmitBoolExpression (ec, Test, ec.LoopEnd, false);
245                         Statement.Emit (ec);
246                         ig.MarkLabel (ec.LoopBegin);
247                         if (!(Increment is EmptyStatement))
248                                 Increment.Emit (ec);
249                         ig.Emit (OpCodes.Br, loop);
250                         ig.MarkLabel (ec.LoopEnd);
251
252                         ec.LoopBegin = old_begin;
253                         ec.LoopEnd = old_end;
254                         ec.InLoop = old_inloop;
255                         return false;
256                 }
257         }
258         
259         public class StatementExpression : Statement {
260                 public readonly ExpressionStatement Expr;
261                 
262                 public StatementExpression (ExpressionStatement expr)
263                 {
264                         Expr = expr;
265                 }
266
267                 public override bool Emit (EmitContext ec)
268                 {
269                         ILGenerator ig = ec.ig;
270                         Expression ne;
271                         
272                         ne = Expr.Resolve (ec);
273                         if (ne != null){
274                                 if (ne is ExpressionStatement)
275                                         ((ExpressionStatement) ne).EmitStatement (ec);
276                                 else {
277                                         ne.Emit (ec);
278                                         ig.Emit (OpCodes.Pop);
279                                 }
280                         }
281
282                         return false;
283                 }
284         }
285
286         public class Return : Statement {
287                 public Expression Expr;
288                 public readonly Location loc;
289                 
290                 public Return (Expression expr, Location l)
291                 {
292                         Expr = expr;
293                         loc = l;
294                 }
295
296                 public override bool Emit (EmitContext ec)
297                 {
298                         if (ec.ReturnType == null){
299                                 if (Expr != null){
300                                         Report.Error (127, loc, "Return with a value not allowed here");
301                                         return false;
302                                 }
303                         } else {
304                                 if (Expr == null){
305                                         Report.Error (126, loc, "An object of type `" +
306                                                       TypeManager.CSharpName (ec.ReturnType) + "' is " +
307                                                       "expected for the return statement");
308                                         return false;
309                                 }
310
311                                 Expr = Expr.Resolve (ec);
312                                 if (Expr == null)
313                                         return false;
314
315                                 if (Expr.Type != ec.ReturnType)
316                                         Expr = Expression.ConvertImplicitRequired (
317                                                 ec, Expr, ec.ReturnType, loc);
318
319                                 if (Expr == null)
320                                         return false;
321
322                                 Expr.Emit (ec);
323                         }
324
325                         ec.ig.Emit (OpCodes.Ret);
326
327                         return true; 
328                 }
329         }
330
331         public class Goto : Statement {
332                 string target;
333                 Location loc;
334                         
335                 public Goto (string label, Location l)
336                 {
337                         loc = l;
338                         target = label;
339                 }
340
341                 public string Target {
342                         get {
343                                 return target;
344                         }
345                 }
346
347                 public override bool Emit (EmitContext ec)
348                 {
349                         Console.WriteLine ("Attempting to goto to: " + target);
350                         
351                         return false;
352                 }
353         }
354
355         public class Throw : Statement {
356                 public readonly Expression Expr;
357                 
358                 public Throw (Expression expr)
359                 {
360                         Expr = expr;
361                 }
362
363                 public override bool Emit (EmitContext ec)
364                 {
365                         Expression e = Expr.Resolve (ec);
366
367                         if (e == null)
368                                 return false;
369
370                         e.Emit (ec);
371                         ec.ig.Emit (OpCodes.Throw);
372
373                         return false;
374                 }
375         }
376
377         public class Break : Statement {
378                 Location loc;
379                 
380                 public Break (Location l)
381                 {
382                         loc = l;
383                 }
384
385                 public override bool Emit (EmitContext ec)
386                 {
387                         ILGenerator ig = ec.ig;
388
389                         if (!ec.InLoop){
390                                 Report.Error (139, loc, "No enclosing loop to continue to");
391                                 return false;
392                         }
393                         
394                         ig.Emit (OpCodes.Br, ec.LoopEnd);
395                         return false;
396                 }
397         }
398
399         public class Continue : Statement {
400                 Location loc;
401                 
402                 public Continue (Location l)
403                 {
404                         loc = l;
405                 }
406
407                 public override bool Emit (EmitContext ec)
408                 {
409                         Label begin = ec.LoopBegin;
410                         
411                         if (!ec.InLoop){
412                                 Report.Error (139, loc, "No enclosing loop to continue to");
413                                 return false;
414                         } 
415
416                         ec.ig.Emit (OpCodes.Br, begin);
417                         return false;
418                 }
419         }
420         
421         public class VariableInfo {
422                 public readonly string Type;
423                 public LocalBuilder LocalBuilder;
424                 public Type VariableType;
425                 public readonly Location Location;
426                 
427                 int  idx;
428                 public bool Used;
429                 public bool Assigned; 
430                 
431                 public VariableInfo (string type, Location l)
432                 {
433                         Type = type;
434                         LocalBuilder = null;
435                         idx = -1;
436                         Location = l;
437                 }
438
439                 public int Idx {
440                         get {
441                                 if (idx == -1)
442                                         throw new Exception ("Unassigned idx for variable");
443                                 
444                                 return idx;
445                         }
446
447                         set {
448                                 idx = value;
449                         }
450                 }
451
452         }
453                 
454         // <summary>
455         //   Used for Label management
456         // </summary>
457         //
458         public class Block : Statement {
459                 public readonly Block  Parent;
460                 public readonly bool   Implicit;
461                 public readonly string Label;
462
463                 //
464                 // The statements in this block
465                 //
466                 StatementCollection statements;
467
468                 //
469                 // An array of Blocks.  We keep track of children just
470                 // to generate the local variable declarations.
471                 //
472                 // Statements and child statements are handled through the
473                 // statements.
474                 //
475                 ArrayList children;
476                 
477                 //
478                 // Labels.  (label, block) pairs.
479                 //
480                 Hashtable labels;
481
482                 //
483                 // Keeps track of (name, type) pairs
484                 //
485                 Hashtable variables;
486
487                 //
488                 // Maps variable names to ILGenerator.LocalBuilders
489                 //
490                 Hashtable local_builders;
491
492                 bool used = false;
493
494                 public Block (Block parent)
495                 {
496                         if (parent != null)
497                                 parent.AddChild (this);
498                         
499                         this.Parent = parent;
500                         this.Implicit = false;
501                 }
502
503                 public Block (Block parent, bool implicit_block)
504                 {
505                         if (parent != null)
506                                 parent.AddChild (this);
507                         
508                         this.Parent = parent;
509                         this.Implicit = true;
510                 }
511
512                 public Block (Block parent, string labeled)
513                 {
514                         if (parent != null)
515                                 parent.AddChild (this);
516                         
517                         this.Parent = parent;
518                         this.Implicit = true;
519                         Label = labeled;
520                 }
521
522                 public void AddChild (Block b)
523                 {
524                         if (children == null)
525                                 children = new ArrayList ();
526                         
527                         children.Add (b);
528                 }
529
530                 // <summary>
531                 //   Adds a label to the current block. 
532                 // </summary>
533                 //
534                 // <returns>
535                 //   false if the name already exists in this block. true
536                 //   otherwise.
537                 // </returns>
538                 //
539                 public bool AddLabel (string name, Block block)
540                 {
541                         if (labels == null)
542                                 labels = new Hashtable ();
543                         if (labels.Contains (name))
544                                 return false;
545                         
546                         labels.Add (name, block);
547                         return true;
548                 }
549
550                 public bool AddVariable (string type, string name, Location l)
551                 {
552                         if (variables == null)
553                                 variables = new Hashtable ();
554
555                         if (GetVariableType (name) != null)
556                                 return false;
557
558                         VariableInfo vi = new VariableInfo (type, l);
559                         
560                         variables.Add (name, vi);
561                         return true;
562                 }
563
564                 public Hashtable Variables {
565                         get {
566                                 return variables;
567                         }
568                 }
569
570                 public VariableInfo GetVariableInfo (string name)
571                 {
572                         if (variables != null) {
573                                 object temp;
574                                 temp = variables [name];
575
576                                 if (temp != null)
577                                         return (VariableInfo) temp;
578                         }
579
580                         if (Parent != null){
581                                 return Parent.GetVariableInfo (name);
582                         }
583
584                         return null;
585                 }
586                 
587                 public string GetVariableType (string name)
588                 {
589                         VariableInfo vi = GetVariableInfo (name);
590
591                         if (vi != null)
592                                 return vi.Type;
593
594                         return null;
595                 }
596
597                 // <summary>
598                 //   True if the variable named @name has been defined
599                 //   in this block
600                 // </summary>
601                 public bool IsVariableDefined (string name)
602                 {
603                         return GetVariableType (name) != null;
604                 }
605
606                 // <summary>
607                 //   Use to fetch the statement associated with this label
608                 // </summary>
609                 public Statement this [string name] {
610                         get {
611                                 return (Statement) labels [name];
612                         }
613                 }
614
615                 // <returns>
616                 //   A list of labels that were not used within this block
617                 // </returns>
618                 public string [] GetUnreferenced ()
619                 {
620                         // FIXME: Implement me
621                         return null;
622                 }
623
624                 public StatementCollection Statements {
625                         get {
626                                 if (statements == null)
627                                         statements = new StatementCollection ();
628
629                                 return statements;
630                         }
631                 }
632
633                 public void AddStatement (Statement s)
634                 {
635                         if (statements == null)
636                                 statements = new StatementCollection ();
637
638                         statements.Add (s);
639                         used = true;
640                 }
641
642                 public bool Used {
643                         get {
644                                 return used;
645                         }
646                 }
647
648                 public void Use ()
649                 {
650                         used = true;
651                 }
652                 
653                 // <summary>
654                 //   Creates a compiler-internal identifier, this is
655                 //   used to create temporary variables that should not
656                 //   be seen by the application
657                 // </summary
658                 int internal_id_serial;
659                 public string MakeInternalID () {
660                         string ret = internal_id_serial.ToString ();
661
662                         internal_id_serial++;
663                         return "0_" + ret;
664                 }
665
666                 // <summary>
667                 //   Emits the variable declarations and labels.
668                 // </summary>
669                 //
670                 // tc: is our typecontainer (to resolve type references)
671                 // ig: is the code generator:
672                 // toplevel: the toplevel block.  This is used for checking 
673                 //           that no two labels with the same name are used.
674                 //
675                 public void EmitMeta (TypeContainer tc, ILGenerator ig, Block toplevel, int count)
676                 {
677                         //
678                         // Process this block variables
679                         //
680                         if (variables != null){
681                                 local_builders = new Hashtable ();
682                                 
683                                 foreach (DictionaryEntry de in variables){
684                                         string name = (string) de.Key;
685                                         VariableInfo vi = (VariableInfo) de.Value;
686                                         Type t;
687                                         
688                                         t = tc.LookupType (vi.Type, false);
689                                         if (t == null)
690                                                 continue;
691
692                                         vi.VariableType = t;
693                                         vi.LocalBuilder = ig.DeclareLocal (t);
694                                         vi.Idx = count++;
695                                 }
696                         }
697
698                         //
699                         // Now, handle the children
700                         //
701                         if (children != null){
702                                 foreach (Block b in children)
703                                         b.EmitMeta (tc, ig, toplevel, count);
704                         }
705                 }
706
707                 public void UsageWarning ()
708                 {
709                         string name;
710                         
711                         if (variables != null){
712                                 foreach (DictionaryEntry de in variables){
713                                         VariableInfo vi = (VariableInfo) de.Value;
714                                         
715                                         if (vi.Used)
716                                                 continue;
717                                         
718                                         name = (string) de.Key;
719                                                 
720                                         if (vi.Assigned){
721                                                 Report.Warning (
722                                                         219, vi.Location, "The variable `" + name +
723                                                         "' is assigned but its value is never used");
724                                         } else {
725                                                 Report.Warning (
726                                                         168, vi.Location, "The variable `" +
727                                                         name +
728                                                         "' is declared but never used");
729                                         } 
730                                 }
731                         }
732
733                         if (children != null)
734                                 foreach (Block b in children)
735                                         b.UsageWarning ();
736                 }
737
738                 public override bool Emit (EmitContext ec)
739                 {
740                         bool is_ret = false;
741                         Block prev_block = ec.CurrentBlock;
742                         
743                         ec.CurrentBlock = this;
744                         foreach (Statement s in Statements)
745                                 is_ret = s.Emit (ec);
746
747                         ec.CurrentBlock = prev_block;
748                         return is_ret;
749                 }
750         }
751
752         public class SwitchLabel {
753                 Expression label;
754
755                 //
756                 // if expr == null, then it is the default case.
757                 //
758                 public SwitchLabel (Expression expr)
759                 {
760                         label = expr;
761                 }
762                 
763                 public Expression Label {
764                         get {
765                                 return label;
766                         }
767                 }
768         }
769
770         public class SwitchSection {
771                 // An array of SwitchLabels.
772                 ArrayList labels;
773                 Block block;
774                 
775                 public SwitchSection (ArrayList labels, Block block)
776                 {
777                         this.labels = labels;
778                         this.block = block;
779                 }
780
781                 public Block Block {
782                         get {
783                                 return block;
784                         }
785                 }
786
787                 public ArrayList Labels {
788                         get {
789                                 return labels;
790                         }
791                 }
792         }
793         
794         public class Switch : Statement {
795                 ArrayList sections;
796                 Expression expr;
797                 
798                 public Switch (Expression expr, ArrayList sections)
799                 {
800                         this.expr = expr;
801                         this.sections = sections;
802                 }
803
804                 public Expression Expr {
805                         get {
806                                 return expr;
807                         }
808                 }
809
810                 public ArrayList Sections {
811                         get {
812                                 return sections;
813                         }
814                 }
815
816                 public override bool Emit (EmitContext ec)
817                 {
818                         throw new Exception ("Unimplemented");
819                 }
820         }
821
822         public class Lock : Statement {
823                 public readonly Expression Expr;
824                 public readonly Statement Statement;
825                 Location loc;
826                         
827                 public Lock (Expression expr, Statement stmt, Location l)
828                 {
829                         Expr = expr;
830                         Statement = stmt;
831                         loc = l;
832                 }
833
834                 public override bool Emit (EmitContext ec)
835                 {
836                         Expression e = Expr.Resolve (ec);
837                         if (e == null)
838                                 return false;
839
840                         Type type = e.Type;
841                         
842                         if (type.IsValueType){
843                                 Report.Error (185, loc, "lock statement requires the expression to be " +
844                                               " a reference type (type is: `" +
845                                               TypeManager.CSharpName (type) + "'");
846                                 return false;
847                         }
848
849                         LocalBuilder temp = ec.GetTemporaryStorage (type);
850                         ILGenerator ig = ec.ig;
851                                 
852                         e.Emit (ec);
853                         ig.Emit (OpCodes.Dup);
854                         ig.Emit (OpCodes.Stloc, temp);
855                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
856
857                         // try
858                         Label end = ig.BeginExceptionBlock ();
859                         Label finish = ig.DefineLabel ();
860                         Statement.Emit (ec);
861                         // ig.Emit (OpCodes.Leave, finish);
862
863                         ig.MarkLabel (finish);
864                         
865                         // finally
866                         ig.BeginFinallyBlock ();
867                         ig.Emit (OpCodes.Ldloc, temp);
868                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
869                         ig.EndExceptionBlock ();
870                         
871                         return false;
872                 }
873         }
874
875         public class Unchecked : Statement {
876                 public readonly Block Block;
877                 
878                 public Unchecked (Block b)
879                 {
880                         Block = b;
881                 }
882
883                 public override bool Emit (EmitContext ec)
884                 {
885                         bool previous_state = ec.CheckState;
886                         bool val;
887                         
888                         ec.CheckState = false;
889                         val = Block.Emit (ec);
890                         ec.CheckState = previous_state;
891
892                         return val;
893                 }
894         }
895
896         public class Checked : Statement {
897                 public readonly Block Block;
898                 
899                 public Checked (Block b)
900                 {
901                         Block = b;
902                 }
903
904                 public override bool Emit (EmitContext ec)
905                 {
906                         bool previous_state = ec.CheckState;
907                         bool val;
908                         
909                         ec.CheckState = true;
910                         val = Block.Emit (ec);
911                         ec.CheckState = previous_state;
912
913                         return val;
914                 }
915         }
916
917         public class Catch {
918                 public readonly string Type;
919                 public readonly string Name;
920                 public readonly Block  Block;
921                 
922                 public Catch (string type, string name, Block block)
923                 {
924                         Type = type;
925                         Name = name;
926                         Block = block;
927                 }
928         }
929
930         public class Try : Statement {
931                 public readonly Block Fini, Block;
932                 public readonly ArrayList Specific;
933                 public readonly Catch General;
934                 
935                 //
936                 // specific, general and fini might all be null.
937                 //
938                 public Try (Block block, ArrayList specific, Catch general, Block fini)
939                 {
940                         if (specific == null && general == null){
941                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
942                         }
943                         
944                         this.Block = block;
945                         this.Specific = specific;
946                         this.General = general;
947                         this.Fini = fini;
948                 }
949
950                 public override bool Emit (EmitContext ec)
951                 {
952                         ILGenerator ig = ec.ig;
953                         Label end;
954                         Label finish = ig.DefineLabel ();;
955
956                         end = ig.BeginExceptionBlock ();
957                         Block.Emit (ec);
958                         ig.Emit (OpCodes.Leave, finish);
959                         
960                         foreach (Catch c in Specific){
961                                 Type catch_type = ec.TypeContainer.LookupType (c.Type, false);
962                                 VariableInfo vi;
963                                 
964                                 if (catch_type == null)
965                                         return false;
966
967                                 ig.BeginCatchBlock (catch_type);
968
969                                 if (c.Name != null){
970                                         vi = c.Block.GetVariableInfo (c.Name);
971                                         if (vi == null){
972                                                 Console.WriteLine ("This should not happen! variable does not exist in this block");
973                                                 Environment.Exit (0);
974                                         }
975                                 
976                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
977                                 } else
978                                         ig.Emit (OpCodes.Pop);
979                                 
980                                 c.Block.Emit (ec);
981                         }
982
983                         if (General != null){
984                                 ig.BeginCatchBlock (TypeManager.object_type);
985                                 ig.Emit (OpCodes.Pop);
986                         }
987
988                         ig.MarkLabel (finish);
989                         if (Fini != null){
990                                 ig.BeginFinallyBlock ();
991                                 Fini.Emit (ec);
992                         }
993                         
994                         ig.EndExceptionBlock ();
995
996                         return false;
997                 }
998         }
999
1000         public class Using : Statement {
1001                 object expression_or_block;
1002                 Statement Statement;
1003                 Location loc;
1004                 
1005                 public Using (object expression_or_block, Statement stmt, Location l)
1006                 {
1007                         this.expression_or_block = expression_or_block;
1008                         Statement = stmt;
1009                         loc = l;
1010                 }
1011
1012                 public override bool Emit (EmitContext ec)
1013                 {
1014                         //
1015                         // Expressions are simple. 
1016                         // The problem is with blocks, blocks might contain
1017                         // more than one variable, ie like this:
1018                         //
1019                         // using (a = new X (), b = new Y ()) stmt;
1020                         //
1021                         // which is turned into:
1022                         // using (a = new X ()) using (b = new Y ()) stmt;
1023                         //
1024                         // The trick is that the block will contain a bunch
1025                         // of potential Assign expressions
1026                         //
1027                         //
1028                         // We need to signal an error if a variable lacks
1029                         // an assignment. (210).
1030                         //
1031                         // This is one solution.  Another is to set a flag
1032                         // when we get the USING token, and have declare_local_variables
1033                         // do something *different* that we can better cope with
1034                         //
1035                         throw new Exception ("Implement me!");
1036                 }
1037         }
1038         
1039         public class Foreach : Statement {
1040                 public readonly string Type;
1041                 public readonly LocalVariableReference Variable;
1042                 public readonly Expression Expr;
1043                 public readonly Statement Statement;
1044                 public readonly Location Location;
1045                 
1046                 public Foreach (string type, LocalVariableReference var, Expression expr,
1047                                 Statement stmt, Location l)
1048                 {
1049                         Type = type;
1050                         Variable = var;
1051                         Expr = expr;
1052                         Statement = stmt;
1053                         Location = l;
1054                 }
1055
1056                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
1057                 {
1058                         if (m == null)
1059                                 return false;
1060                         
1061                         if (!(m is MethodInfo))
1062                                 return false;
1063                         
1064                         if (m.Name != "GetEnumerator")
1065                                 return false;
1066                         
1067                         MethodInfo mi = (MethodInfo) m;
1068                         
1069                         if (mi.ReturnType != TypeManager.ienumerator_type)
1070                                 return false;
1071                         
1072                         Type [] args = TypeManager.GetArgumentTypes (mi);
1073                         if (args == null)
1074                                 return true;
1075                         
1076                         if (args.Length == 0)
1077                                 return true;
1078                         
1079                         return false;
1080                 }
1081                 
1082                 // <summary>
1083                 //   This filter is used to find the GetEnumerator method
1084                 //   on which IEnumerator operates
1085                 // </summary>
1086                 static MemberFilter FilterEnumerator;
1087                 
1088                 static Foreach ()
1089                 {
1090                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
1091                 }
1092
1093                 void error1579 (Type t)
1094                 {
1095                         Report.Error (1579, Location,
1096                                       "foreach statement cannot operate on variables of type `" +
1097                                       t.FullName + "' because that class does not provide a " +
1098                                       " GetEnumerator method or it is inaccessible");
1099                 }
1100
1101                 MethodInfo ProbeCollectionType (Type t)
1102                 {
1103                         MemberInfo [] mi;
1104
1105                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
1106                                                         BindingFlags.Public,
1107                                                         FilterEnumerator, null);
1108
1109                         if (mi == null){
1110                                 error1579 (t);
1111                                 return null;
1112                         }
1113
1114                         if (mi.Length == 0){
1115                                 error1579 (t);
1116                                 return null;
1117                         }
1118
1119                         return (MethodInfo) mi [0];
1120                 }
1121                 
1122                 public override bool Emit (EmitContext ec)
1123                 {
1124                         ILGenerator ig = ec.ig;
1125                         Expression e = Expr;
1126                         MethodInfo get_enum;
1127                         LocalBuilder enumerator, disposable;
1128                         Type var_type;
1129                         
1130                         e = e.Resolve (ec);
1131                         if (e == null)
1132                                 return false;
1133
1134                         var_type = ec.TypeContainer.LookupType (Type, false);
1135                         if (var_type == null)
1136                                 return false;
1137                         
1138                         //
1139                         // We need an instance variable.  Not sure this is the best
1140                         // way of doing this.
1141                         //
1142                         // FIXME: When we implement propertyaccess, will those turn
1143                         // out to return values in ExprClass?  I think they should.
1144                         //
1145                         if (!(e.ExprClass == ExprClass.Variable || e.ExprClass == ExprClass.Value)){
1146                                 error1579 (e.Type);
1147                                 return false;
1148                         }
1149                         
1150                         if ((get_enum = ProbeCollectionType (e.Type)) == null)
1151                                 return false;
1152
1153                         Expression empty = new EmptyExpression ();
1154                         Expression conv;
1155
1156                         conv = Expression.ConvertExplicit (ec, empty, var_type, Location);
1157                         if (conv == null)
1158                                 return false;
1159                         
1160                         enumerator = ig.DeclareLocal (TypeManager.ienumerator_type);
1161                         disposable = ig.DeclareLocal (TypeManager.idisposable_type);
1162                         
1163                         //
1164                         // Instantiate the enumerator
1165
1166                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
1167                         Label end_try = ig.DefineLabel ();
1168                         bool old_inloop = ec.InLoop;
1169                         ec.LoopBegin = ig.DefineLabel ();
1170                         ec.LoopEnd = ig.DefineLabel ();
1171                         ec.InLoop = true;
1172                         
1173                         //
1174                         // FIXME: This code does not work for cases like:
1175                         // foreach (int a in ValueTypeVariable){
1176                         // }
1177                         //
1178                         // The code should emit an ldarga instruction
1179                         // for the ValueTypeVariable rather than a ldarg
1180                         //
1181                         if (e.Type.IsValueType){
1182                                 ig.Emit (OpCodes.Call, get_enum);
1183                         } else {
1184                                 e.Emit (ec);
1185                                 ig.Emit (OpCodes.Callvirt, get_enum);
1186                         }
1187                         ig.Emit (OpCodes.Stloc, enumerator);
1188
1189                         //
1190                         // Protect the code in a try/finalize block, so that
1191                         // if the beast implement IDisposable, we get rid of it
1192                         //
1193                         Label l = ig.BeginExceptionBlock ();
1194                         ig.MarkLabel (ec.LoopBegin);
1195                         ig.Emit (OpCodes.Ldloc, enumerator);
1196                         ig.Emit (OpCodes.Callvirt, TypeManager.bool_movenext_void);
1197                         ig.Emit (OpCodes.Brfalse, end_try);
1198                         ig.Emit (OpCodes.Ldloc, enumerator);
1199                         ig.Emit (OpCodes.Callvirt, TypeManager.object_getcurrent_void);
1200                         conv.Emit (ec);
1201                         Variable.Store (ec);
1202                         Statement.Emit (ec);
1203                         ig.Emit (OpCodes.Br, ec.LoopBegin);
1204                         ig.MarkLabel (end_try);
1205
1206                         // The runtime provides this for us.
1207                         // ig.Emit (OpCodes.Leave, end);
1208
1209                         //
1210                         // Now the finally block
1211                         //
1212                         Label end_finally = ig.DefineLabel ();
1213                         
1214                         ig.BeginFinallyBlock ();
1215                         ig.Emit (OpCodes.Ldloc, enumerator);
1216                         ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
1217                         ig.Emit (OpCodes.Stloc, disposable);
1218                         ig.Emit (OpCodes.Ldloc, disposable);
1219                         ig.Emit (OpCodes.Brfalse, end_finally);
1220                         ig.Emit (OpCodes.Ldloc, disposable);
1221                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
1222                         ig.MarkLabel (end_finally);
1223
1224                         // The runtime generates this anyways.
1225                         // ig.Emit (OpCodes.Endfinally);
1226
1227                         ig.EndExceptionBlock ();
1228
1229                         ig.MarkLabel (ec.LoopEnd);
1230                         
1231                         ec.LoopBegin = old_begin;
1232                         ec.LoopEnd = old_end;
1233                         ec.InLoop = old_inloop;
1234
1235                         return false;
1236                 }
1237
1238         }
1239 }
1240