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