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