**** Merged r37835-r37843 from MCS ****
[mono.git] / mcs / gmcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Martin Baulig (martin@ximian.com)
7 //
8 // (C) 2001, 2002, 2003 Ximian, Inc.
9 // (C) 2003, 2004 Novell, Inc.
10 //
11
12 using System;
13 using System.Text;
14 using System.Reflection;
15 using System.Reflection.Emit;
16 using System.Diagnostics;
17
18 namespace Mono.CSharp {
19
20         using System.Collections;
21         
22         public abstract class Statement {
23                 public Location loc;
24                 
25                 /// <summary>
26                 /// Resolves the statement, true means that all sub-statements
27                 /// did resolve ok.
28                 //  </summary>
29                 public virtual bool Resolve (EmitContext ec)
30                 {
31                         return true;
32                 }
33                 
34                 /// <summary>
35                 ///   We already know that the statement is unreachable, but we still
36                 ///   need to resolve it to catch errors.
37                 /// </summary>
38                 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
39                 {
40                         //
41                         // This conflicts with csc's way of doing this, but IMHO it's
42                         // the right thing to do.
43                         //
44                         // If something is unreachable, we still check whether it's
45                         // correct.  This means that you cannot use unassigned variables
46                         // in unreachable code, for instance.
47                         //
48
49                         if (warn && (RootContext.WarningLevel >= 2))
50                                 Report.Warning (162, loc, "Unreachable code detected");
51
52                         ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
53                         bool ok = Resolve (ec);
54                         ec.KillFlowBranching ();
55
56                         return ok;
57                 }
58                 
59                 protected void CheckObsolete (Type type)
60                 {
61                         ObsoleteAttribute obsolete_attr = AttributeTester.GetObsoleteAttribute (type);
62                         if (obsolete_attr == null)
63                                 return;
64
65                         AttributeTester.Report_ObsoleteMessage (obsolete_attr, type.FullName, loc);
66                 }
67                 
68                 /// <summary>
69                 ///   Return value indicates whether all code paths emitted return.
70                 /// </summary>
71                 protected abstract void DoEmit (EmitContext ec);
72
73                 /// <summary>
74                 ///   Utility wrapper routine for Error, just to beautify the code
75                 /// </summary>
76                 public void Error (int error, string format, params object[] args)
77                 {
78                         Error (error, String.Format (format, args));
79                 }
80                 
81                 public void Error (int error, string s)
82                 {
83                         if (!Location.IsNull (loc))
84                                 Report.Error (error, loc, s);
85                                 else
86                                 Report.Error (error, s);
87                 }
88
89                 /// <summary>
90                 ///   Return value indicates whether all code paths emitted return.
91                 /// </summary>
92                 public virtual void Emit (EmitContext ec)
93                 {
94                         ec.Mark (loc, true);
95                         DoEmit (ec);
96                 }
97         }
98
99         public sealed class EmptyStatement : Statement {
100                 
101                 private EmptyStatement () {}
102                 
103                 public static readonly EmptyStatement Value = new EmptyStatement ();
104                 
105                 public override bool Resolve (EmitContext ec)
106                 {
107                         return true;
108                 }
109                 
110                 protected override void DoEmit (EmitContext ec)
111                 {
112                 }
113         }
114         
115         public class If : Statement {
116                 Expression expr;
117                 public Statement TrueStatement;
118                 public Statement FalseStatement;
119                 
120                 bool is_true_ret;
121                 
122                 public If (Expression expr, Statement trueStatement, Location l)
123                 {
124                         this.expr = expr;
125                         TrueStatement = trueStatement;
126                         loc = l;
127                 }
128
129                 public If (Expression expr,
130                            Statement trueStatement,
131                            Statement falseStatement,
132                            Location l)
133                 {
134                         this.expr = expr;
135                         TrueStatement = trueStatement;
136                         FalseStatement = falseStatement;
137                         loc = l;
138                 }
139
140                 public override bool Resolve (EmitContext ec)
141                 {
142                         Report.Debug (1, "START IF BLOCK", loc);
143
144                         expr = Expression.ResolveBoolean (ec, expr, loc);
145                         if (expr == null){
146                                 return false;
147                         }
148                         
149                         Assign ass = expr as Assign;
150                         if (ass != null && ass.Source is Constant) {
151                                 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
152                         }
153
154                         //
155                         // Dead code elimination
156                         //
157                         if (expr is BoolConstant){
158                                 bool take = ((BoolConstant) expr).Value;
159
160                                 if (take){
161                                         if (!TrueStatement.Resolve (ec))
162                                                 return false;
163
164                                         if ((FalseStatement != null) &&
165                                             !FalseStatement.ResolveUnreachable (ec, true))
166                                                 return false;
167                                         FalseStatement = null;
168                                 } else {
169                                         if (!TrueStatement.ResolveUnreachable (ec, true))
170                                                 return false;
171                                         TrueStatement = null;
172                         
173                                         if ((FalseStatement != null) &&
174                                             !FalseStatement.Resolve (ec))
175                                 return false;
176                         }
177
178                                 return true;
179                         }
180                         
181                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
182                         
183                         bool ok = TrueStatement.Resolve (ec);
184
185                         is_true_ret = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
186
187                         ec.CurrentBranching.CreateSibling ();
188
189                         if ((FalseStatement != null) && !FalseStatement.Resolve (ec))
190                                 ok = false;
191                                         
192                         ec.EndFlowBranching ();
193
194                         Report.Debug (1, "END IF BLOCK", loc);
195
196                         return ok;
197                 }
198                 
199                 protected override void DoEmit (EmitContext ec)
200                 {
201                         ILGenerator ig = ec.ig;
202                         Label false_target = ig.DefineLabel ();
203                         Label end;
204
205                         //
206                         // If we're a boolean expression, Resolve() already
207                         // eliminated dead code for us.
208                         //
209                         if (expr is BoolConstant){
210                                 bool take = ((BoolConstant) expr).Value;
211
212                                 if (take)
213                                         TrueStatement.Emit (ec);
214                                 else if (FalseStatement != null)
215                                                 FalseStatement.Emit (ec);
216
217                                                 return;
218                                         }
219                         
220                         expr.EmitBranchable (ec, false_target, false);
221
222                         TrueStatement.Emit (ec);
223
224                         if (FalseStatement != null){
225                                 bool branch_emitted = false;
226                                 
227                                 end = ig.DefineLabel ();
228                                 if (!is_true_ret){
229                                         ig.Emit (OpCodes.Br, end);
230                                         branch_emitted = true;
231                                 }
232
233                                 ig.MarkLabel (false_target);
234                                 FalseStatement.Emit (ec);
235
236                                 if (branch_emitted)
237                                         ig.MarkLabel (end);
238                         } else {
239                                 ig.MarkLabel (false_target);
240                         }
241                 }
242         }
243
244         public class Do : Statement {
245                 public Expression expr;
246                 public readonly Statement  EmbeddedStatement;
247                 bool infinite;
248                 
249                 public Do (Statement statement, Expression boolExpr, Location l)
250                 {
251                         expr = boolExpr;
252                         EmbeddedStatement = statement;
253                         loc = l;
254                 }
255
256                 public override bool Resolve (EmitContext ec)
257                 {
258                         bool ok = true;
259
260                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
261
262                         if (!EmbeddedStatement.Resolve (ec))
263                                 ok = false;
264
265                         expr = Expression.ResolveBoolean (ec, expr, loc);
266                         if (expr == null)
267                                 ok = false;
268                         else if (expr is BoolConstant){
269                                 bool res = ((BoolConstant) expr).Value;
270
271                                 if (res)
272                                         infinite = true;
273                         }
274
275                         ec.CurrentBranching.Infinite = infinite;
276                         ec.EndFlowBranching ();
277
278                         return ok;
279                 }
280                 
281                 protected override void DoEmit (EmitContext ec)
282                 {
283                         ILGenerator ig = ec.ig;
284                         Label loop = ig.DefineLabel ();
285                         Label old_begin = ec.LoopBegin;
286                         Label old_end = ec.LoopEnd;
287                         
288                         ec.LoopBegin = ig.DefineLabel ();
289                         ec.LoopEnd = ig.DefineLabel ();
290                                 
291                         ig.MarkLabel (loop);
292                         EmbeddedStatement.Emit (ec);
293                         ig.MarkLabel (ec.LoopBegin);
294
295                         //
296                         // Dead code elimination
297                         //
298                         if (expr is BoolConstant){
299                                 bool res = ((BoolConstant) expr).Value;
300
301                                 if (res)
302                                         ec.ig.Emit (OpCodes.Br, loop); 
303                         } else
304                                 expr.EmitBranchable (ec, loop, true);
305                         
306                         ig.MarkLabel (ec.LoopEnd);
307
308                         ec.LoopBegin = old_begin;
309                         ec.LoopEnd = old_end;
310                 }
311         }
312
313         public class While : Statement {
314                 public Expression expr;
315                 public readonly Statement Statement;
316                 bool infinite, empty;
317                 
318                 public While (Expression boolExpr, Statement statement, Location l)
319                 {
320                         this.expr = boolExpr;
321                         Statement = statement;
322                         loc = l;
323                 }
324
325                 public override bool Resolve (EmitContext ec)
326                 {
327                         bool ok = true;
328
329                         expr = Expression.ResolveBoolean (ec, expr, loc);
330                         if (expr == null)
331                                 return false;
332
333                         //
334                         // Inform whether we are infinite or not
335                         //
336                         if (expr is BoolConstant){
337                                 BoolConstant bc = (BoolConstant) expr;
338
339                                 if (bc.Value == false){
340                                         if (!Statement.ResolveUnreachable (ec, true))
341                                                 return false;
342                                         empty = true;
343                                         return true;
344                                 } else
345                                         infinite = true;
346                         }
347
348                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
349
350                         if (!Statement.Resolve (ec))
351                                 ok = false;
352
353                                 ec.CurrentBranching.Infinite = infinite;
354                                 ec.EndFlowBranching ();
355
356                         return ok;
357                 }
358                 
359                 protected override void DoEmit (EmitContext ec)
360                 {
361                         if (empty)
362                                 return;
363
364                         ILGenerator ig = ec.ig;
365                         Label old_begin = ec.LoopBegin;
366                         Label old_end = ec.LoopEnd;
367                         
368                         ec.LoopBegin = ig.DefineLabel ();
369                         ec.LoopEnd = ig.DefineLabel ();
370
371                         //
372                         // Inform whether we are infinite or not
373                         //
374                         if (expr is BoolConstant){
375                                 ig.MarkLabel (ec.LoopBegin);
376                                 Statement.Emit (ec);
377                                 ig.Emit (OpCodes.Br, ec.LoopBegin);
378                                         
379                                 //
380                                 // Inform that we are infinite (ie, `we return'), only
381                                 // if we do not `break' inside the code.
382                                 //
383                                 ig.MarkLabel (ec.LoopEnd);
384                         } else {
385                                 Label while_loop = ig.DefineLabel ();
386
387                                 ig.Emit (OpCodes.Br, ec.LoopBegin);
388                                 ig.MarkLabel (while_loop);
389
390                                 Statement.Emit (ec);
391                         
392                                 ig.MarkLabel (ec.LoopBegin);
393
394                                 expr.EmitBranchable (ec, while_loop, true);
395                                 
396                                 ig.MarkLabel (ec.LoopEnd);
397                         }       
398
399                         ec.LoopBegin = old_begin;
400                         ec.LoopEnd = old_end;
401                 }
402         }
403
404         public class For : Statement {
405                 Expression Test;
406                 readonly Statement InitStatement;
407                 readonly Statement Increment;
408                 readonly Statement Statement;
409                 bool infinite, empty;
410                 
411                 public For (Statement initStatement,
412                             Expression test,
413                             Statement increment,
414                             Statement statement,
415                             Location l)
416                 {
417                         InitStatement = initStatement;
418                         Test = test;
419                         Increment = increment;
420                         Statement = statement;
421                         loc = l;
422                 }
423
424                 public override bool Resolve (EmitContext ec)
425                 {
426                         bool ok = true;
427
428                         if (InitStatement != null){
429                                 if (!InitStatement.Resolve (ec))
430                                         ok = false;
431                         }
432
433                         if (Test != null){
434                                 Test = Expression.ResolveBoolean (ec, Test, loc);
435                                 if (Test == null)
436                                         ok = false;
437                                 else if (Test is BoolConstant){
438                                         BoolConstant bc = (BoolConstant) Test;
439
440                                         if (bc.Value == false){
441                                                 if (!Statement.ResolveUnreachable (ec, true))
442                                                         return false;
443                                                 if ((Increment != null) &&
444                                                     !Increment.ResolveUnreachable (ec, false))
445                                                         return false;
446                                                 empty = true;
447                                                 return true;
448                                         } else
449                                                 infinite = true;
450                                 }
451                         } else
452                                 infinite = true;
453
454                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
455                         if (!infinite)
456                                 ec.CurrentBranching.CreateSibling ();
457
458                         if (!Statement.Resolve (ec))
459                                 ok = false;
460
461                         if (Increment != null){
462                                 if (!Increment.Resolve (ec))
463                                         ok = false;
464                         }
465
466                                 ec.CurrentBranching.Infinite = infinite;
467                                 ec.EndFlowBranching ();
468
469                         return ok;
470                 }
471                 
472                 protected override void DoEmit (EmitContext ec)
473                 {
474                         if (empty)
475                                 return;
476
477                         ILGenerator ig = ec.ig;
478                         Label old_begin = ec.LoopBegin;
479                         Label old_end = ec.LoopEnd;
480                         Label loop = ig.DefineLabel ();
481                         Label test = ig.DefineLabel ();
482                         
483                         if (InitStatement != null && InitStatement != EmptyStatement.Value)
484                                         InitStatement.Emit (ec);
485
486                         ec.LoopBegin = ig.DefineLabel ();
487                         ec.LoopEnd = ig.DefineLabel ();
488
489                         ig.Emit (OpCodes.Br, test);
490                         ig.MarkLabel (loop);
491                         Statement.Emit (ec);
492
493                         ig.MarkLabel (ec.LoopBegin);
494                         if (Increment != EmptyStatement.Value)
495                                 Increment.Emit (ec);
496
497                         ig.MarkLabel (test);
498                         //
499                         // If test is null, there is no test, and we are just
500                         // an infinite loop
501                         //
502                         if (Test != null){
503                                 //
504                                 // The Resolve code already catches the case for
505                                 // Test == BoolConstant (false) so we know that
506                                 // this is true
507                                 //
508                                 if (Test is BoolConstant)
509                                         ig.Emit (OpCodes.Br, loop);
510                                 else
511                                         Test.EmitBranchable (ec, loop, true);
512                                 
513                         } else
514                                 ig.Emit (OpCodes.Br, loop);
515                         ig.MarkLabel (ec.LoopEnd);
516
517                         ec.LoopBegin = old_begin;
518                         ec.LoopEnd = old_end;
519                 }
520         }
521         
522         public class StatementExpression : Statement {
523                 ExpressionStatement expr;
524                 
525                 public StatementExpression (ExpressionStatement expr, Location l)
526                 {
527                         this.expr = expr;
528                         loc = l;
529                 }
530
531                 public override bool Resolve (EmitContext ec)
532                 {
533                         expr = expr.ResolveStatement (ec);
534                         return expr != null;
535                 }
536                 
537                 protected override void DoEmit (EmitContext ec)
538                 {
539                         expr.EmitStatement (ec);
540                 }
541
542                 public override string ToString ()
543                 {
544                         return "StatementExpression (" + expr + ")";
545                 }
546         }
547
548         /// <summary>
549         ///   Implements the return statement
550         /// </summary>
551         public class Return : Statement {
552                 public Expression Expr;
553                 
554                 public Return (Expression expr, Location l)
555                 {
556                         Expr = expr;
557                         loc = l;
558                 }
559
560                 bool in_exc;
561
562                 public override bool Resolve (EmitContext ec)
563                 {
564                         if (ec.ReturnType == null){
565                         if (Expr != null){
566                                         if (ec.CurrentAnonymousMethod != null){
567                                                 Report.Error (1662, loc, String.Format (
568                                                         "Anonymous method could not be converted to delegate " +
569                                                         "since the return value does not match the delegate value"));
570                                         }
571                                         Error (127, "Return with a value not allowed here");
572                                         return false;
573                                 }
574                         } else {
575                                 if (Expr == null){
576                                         Error (126, "An object of type `{0}' is expected " +
577                                                "for the return statement",
578                                                TypeManager.CSharpName (ec.ReturnType));
579                                         return false;
580                                 }
581
582                                 Expr = Expr.Resolve (ec);
583                                 if (Expr == null)
584                                         return false;
585
586                                 if (Expr.Type != ec.ReturnType) {
587                                         Expr = Convert.ImplicitConversionRequired (
588                                                 ec, Expr, ec.ReturnType, loc);
589                                         if (Expr == null)
590                                                 return false;
591                                 }
592                         }
593
594                         if (ec.InIterator){
595                                 Error (-206, "Return statement not allowed inside iterators");
596                                 return false;
597                         }
598                                 
599                         FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
600
601                         if (ec.CurrentBranching.InTryOrCatch (true)) {
602                                 ec.CurrentBranching.AddFinallyVector (vector);
603                                 in_exc = true;
604                         } else if (ec.CurrentBranching.InFinally (true)) {
605                                 Error (157, "Control can not leave the body of the finally block");
606                                 return false;
607                         } else
608                                 vector.CheckOutParameters (ec.CurrentBranching);
609
610                         if (in_exc)
611                                 ec.NeedReturnLabel ();
612
613                         ec.CurrentBranching.CurrentUsageVector.Return ();
614                         return true;
615                 }
616                 
617                 protected override void DoEmit (EmitContext ec)
618                 {
619                         if (Expr != null) {
620                                 Expr.Emit (ec);
621
622                                 if (in_exc)
623                                         ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
624                         }
625
626                         if (in_exc)
627                                 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
628                         else
629                                 ec.ig.Emit (OpCodes.Ret);
630                         }
631                 }
632
633         public class Goto : Statement {
634                 string target;
635                 Block block;
636                 LabeledStatement label;
637                 
638                 public override bool Resolve (EmitContext ec)
639                 {
640                         label = ec.CurrentBranching.LookupLabel (target, loc);
641                         if (label == null)
642                                 return false;
643
644                         // If this is a forward goto.
645                         if (!label.IsDefined)
646                                 label.AddUsageVector (ec.CurrentBranching.CurrentUsageVector);
647
648                         ec.CurrentBranching.CurrentUsageVector.Goto ();
649
650                         return true;
651                 }
652                 
653                 public Goto (Block parent_block, string label, Location l)
654                 {
655                         block = parent_block;
656                         loc = l;
657                         target = label;
658                 }
659
660                 public string Target {
661                         get {
662                                 return target;
663                         }
664                 }
665
666                 protected override void DoEmit (EmitContext ec)
667                 {
668                         Label l = label.LabelTarget (ec);
669                         ec.ig.Emit (OpCodes.Br, l);
670                 }
671         }
672
673         public class LabeledStatement : Statement {
674                 public readonly Location Location;
675                 bool defined;
676                 bool referenced;
677                 Label label;
678
679                 FlowBranching.UsageVector vectors;
680                 
681                 public LabeledStatement (string label_name, Location l)
682                 {
683                         this.Location = l;
684                 }
685
686                 public Label LabelTarget (EmitContext ec)
687                 {
688                         if (defined)
689                                 return label;
690                         label = ec.ig.DefineLabel ();
691                         defined = true;
692
693                         return label;
694                 }
695
696                 public bool IsDefined {
697                         get {
698                                 return defined;
699                         }
700                 }
701
702                 public bool HasBeenReferenced {
703                         get {
704                                 return referenced;
705                         }
706                 }
707
708                 public void AddUsageVector (FlowBranching.UsageVector vector)
709                 {
710                         vector = vector.Clone ();
711                         vector.Next = vectors;
712                         vectors = vector;
713                 }
714
715                 public override bool Resolve (EmitContext ec)
716                 {
717                         ec.CurrentBranching.Label (vectors);
718
719                         referenced = true;
720
721                         return true;
722                 }
723
724                 protected override void DoEmit (EmitContext ec)
725                 {
726                         LabelTarget (ec);
727                         ec.ig.MarkLabel (label);
728                 }
729         }
730         
731
732         /// <summary>
733         ///   `goto default' statement
734         /// </summary>
735         public class GotoDefault : Statement {
736                 
737                 public GotoDefault (Location l)
738                 {
739                         loc = l;
740                 }
741
742                 public override bool Resolve (EmitContext ec)
743                 {
744                         ec.CurrentBranching.CurrentUsageVector.Goto ();
745                         return true;
746                 }
747
748                 protected override void DoEmit (EmitContext ec)
749                 {
750                         if (ec.Switch == null){
751                                 Report.Error (153, loc, "goto default is only valid in a switch statement");
752                                 return;
753                         }
754
755                         if (!ec.Switch.GotDefault){
756                                 Report.Error (159, loc, "No default target on switch statement");
757                                 return;
758                         }
759                         ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
760                 }
761         }
762
763         /// <summary>
764         ///   `goto case' statement
765         /// </summary>
766         public class GotoCase : Statement {
767                 Expression expr;
768                 SwitchLabel sl;
769                 
770                 public GotoCase (Expression e, Location l)
771                 {
772                         expr = e;
773                         loc = l;
774                 }
775
776                 public override bool Resolve (EmitContext ec)
777                 {
778                         if (ec.Switch == null){
779                                 Report.Error (153, loc, "goto case is only valid in a switch statement");
780                                 return false;
781                         }
782
783                         expr = expr.Resolve (ec);
784                         if (expr == null)
785                                 return false;
786
787                         if (!(expr is Constant)){
788                                 Report.Error (159, loc, "Target expression for goto case is not constant");
789                                 return false;
790                         }
791
792                         object val = Expression.ConvertIntLiteral (
793                                 (Constant) expr, ec.Switch.SwitchType, loc);
794
795                         if (val == null)
796                                 return false;
797                                         
798                         sl = (SwitchLabel) ec.Switch.Elements [val];
799
800                         if (sl == null){
801                                 Report.Error (
802                                         159, loc,
803                                         "No such label 'case " + val + "': for the goto case");
804                                 return false;
805                         }
806
807                         ec.CurrentBranching.CurrentUsageVector.Goto ();
808                         return true;
809                 }
810
811                 protected override void DoEmit (EmitContext ec)
812                 {
813                         ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
814                 }
815         }
816         
817         public class Throw : Statement {
818                 Expression expr;
819                 
820                 public Throw (Expression expr, Location l)
821                 {
822                         this.expr = expr;
823                         loc = l;
824                 }
825
826                 public override bool Resolve (EmitContext ec)
827                 {
828                         ec.CurrentBranching.CurrentUsageVector.Throw ();
829
830                         if (expr != null){
831                                 expr = expr.Resolve (ec);
832                                 if (expr == null)
833                                         return false;
834
835                                 ExprClass eclass = expr.eclass;
836
837                                 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
838                                         eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
839                                         expr.Error_UnexpectedKind ("value, variable, property or indexer access ", loc);
840                                         return false;
841                                 }
842
843                                 Type t = expr.Type;
844                                 
845                                 if ((t != TypeManager.exception_type) &&
846                                         !t.IsSubclassOf (TypeManager.exception_type) &&
847                                         !(expr is NullLiteral)) {
848                                         Error (155,
849                                                 "The type caught or thrown must be derived " +
850                                                 "from System.Exception");
851                                         return false;
852                                 }
853                                 return true;
854                         }
855
856                         if (ec.CurrentBranching.InFinally (true)) {
857                                 Error (724, "A throw statement with no argument is only allowed in a catch clause nested inside of the innermost catch clause");
858                                 return false;
859                         }
860
861                         if (!ec.CurrentBranching.InCatch ()) {
862                                 Error (156, "A throw statement with no argument is only allowed in a catch clause");
863                                 return false;
864                         }
865                         return true;
866                 }
867                         
868                 protected override void DoEmit (EmitContext ec)
869                 {
870                         if (expr == null)
871                                         ec.ig.Emit (OpCodes.Rethrow);
872                                 else {
873                         expr.Emit (ec);
874
875                         ec.ig.Emit (OpCodes.Throw);
876                 }
877         }
878         }
879
880         public class Break : Statement {
881                 
882                 public Break (Location l)
883                 {
884                         loc = l;
885                 }
886
887                 bool crossing_exc;
888
889                 public override bool Resolve (EmitContext ec)
890                 {
891                         if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
892                                 Error (139, "No enclosing loop or switch to continue to");
893                                 return false;
894                         } else if (ec.CurrentBranching.InFinally (false)) {
895                                 Error (157, "Control can not leave the body of the finally block");
896                                 return false;
897                         } else if (ec.CurrentBranching.InTryOrCatch (false))
898                                 ec.CurrentBranching.AddFinallyVector (
899                                         ec.CurrentBranching.CurrentUsageVector);
900                         else if (ec.CurrentBranching.InLoop ())
901                                 ec.CurrentBranching.AddBreakVector (
902                                         ec.CurrentBranching.CurrentUsageVector);
903
904                         crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
905
906                         if (!crossing_exc)
907                                 ec.NeedReturnLabel ();
908
909                         ec.CurrentBranching.CurrentUsageVector.Break ();
910                         return true;
911                 }
912
913                 protected override void DoEmit (EmitContext ec)
914                 {
915                         ILGenerator ig = ec.ig;
916
917                         if (crossing_exc)
918                                 ig.Emit (OpCodes.Leave, ec.LoopEnd);
919                         else {
920                                 ig.Emit (OpCodes.Br, ec.LoopEnd);
921                 }
922         }
923         }
924
925         public class Continue : Statement {
926                 
927                 public Continue (Location l)
928                 {
929                         loc = l;
930                 }
931
932                 bool crossing_exc;
933
934                 public override bool Resolve (EmitContext ec)
935                 {
936                         if (!ec.CurrentBranching.InLoop () && !ec.CurrentBranching.InSwitch ()){
937                                 Error (139, "No enclosing loop to continue to");
938                                 return false;
939                         } else if (ec.CurrentBranching.InFinally (false)) {
940                                 Error (157, "Control can not leave the body of the finally block");
941                                 return false;
942                         } else if (ec.CurrentBranching.InTryOrCatch (false))
943                                 ec.CurrentBranching.AddFinallyVector (ec.CurrentBranching.CurrentUsageVector);
944
945                         crossing_exc = ec.CurrentBranching.BreakCrossesTryCatchBoundary ();
946
947                         ec.CurrentBranching.CurrentUsageVector.Goto ();
948                         return true;
949                 }
950
951                 protected override void DoEmit (EmitContext ec)
952                 {
953                         Label begin = ec.LoopBegin;
954                         
955                         if (crossing_exc)
956                                 ec.ig.Emit (OpCodes.Leave, begin);
957                         else
958                                 ec.ig.Emit (OpCodes.Br, begin);
959                 }
960         }
961
962         //
963         // The information about a user-perceived local variable
964         //
965         public class LocalInfo {
966                 public Expression Type;
967
968                 //
969                 // Most of the time a variable will be stored in a LocalBuilder
970                 //
971                 // But sometimes, it will be stored in a field (variables that have been
972                 // hoisted by iterators or by anonymous methods).  The context of the field will
973                 // be stored in the EmitContext
974                 //
975                 //
976                 public LocalBuilder LocalBuilder;
977                 public FieldBuilder FieldBuilder;
978
979                 public Type VariableType;
980                 public readonly string Name;
981                 public readonly Location Location;
982                 public readonly Block Block;
983
984                 public VariableInfo VariableInfo;
985
986                 enum Flags : byte {
987                         Used = 1,
988                         ReadOnly = 2,
989                         Pinned = 4,
990                         IsThis = 8,
991                         Captured = 16,
992                         AddressTaken = 32
993                 }
994
995                 Flags flags;
996
997                 public LocalInfo (Expression type, string name, Block block, Location l)
998                 {
999                         Type = type;
1000                         Name = name;
1001                         Block = block;
1002                         Location = l;
1003                 }
1004
1005                 public LocalInfo (TypeContainer tc, Block block, Location l)
1006                 {
1007                         VariableType = tc.TypeBuilder;
1008                         Block = block;
1009                         Location = l;
1010                 }
1011
1012                 public bool IsThisAssigned (EmitContext ec, Location loc)
1013                 {
1014                         if (VariableInfo == null)
1015                                 throw new Exception ();
1016
1017                         if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1018                                 return true;
1019
1020                         return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, loc);
1021                 }
1022
1023                 public bool IsAssigned (EmitContext ec)
1024                 {
1025                         if (VariableInfo == null)
1026                                 throw new Exception ();
1027
1028                         return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1029                 }
1030
1031                 public bool Resolve (EmitContext ec)
1032                 {
1033                         if (VariableType == null) {
1034                                 TypeExpr texpr = Type.ResolveAsTypeTerminal (ec);
1035                                 if (texpr == null)
1036                                         return false;
1037                                 
1038                                 VariableType = texpr.Type;
1039                         }
1040
1041                         if (VariableType == TypeManager.void_type) {
1042                                 Report.Error (1547, Location,
1043                                               "Keyword 'void' cannot be used in this context");
1044                                 return false;
1045                         }
1046
1047                         if (VariableType.IsAbstract && VariableType.IsSealed) {
1048                                 Report.Error (723, Location, "Cannot declare variable of static type '{0}'", TypeManager.CSharpName (VariableType));
1049                                 return false;
1050                         }
1051 // TODO: breaks the build
1052 //                      if (VariableType.IsPointer && !ec.InUnsafe)
1053 //                              Expression.UnsafeError (Location);
1054
1055                         return true;
1056                 }
1057
1058                 //
1059                 // Whether the variable is Fixed (because its Pinned or its a value type)
1060                 //
1061                 public bool IsFixed {
1062                         get {
1063                                 if (((flags & Flags.Pinned) != 0) || TypeManager.IsValueType (VariableType))
1064                                         return true;
1065                                 
1066                                 return false;
1067                         }
1068                 }
1069                 
1070                 public bool IsCaptured {
1071                         get {
1072                                 return (flags & Flags.Captured) != 0;
1073                         }
1074
1075                         set {
1076                                 flags |= Flags.Captured;
1077                         }
1078                 }
1079
1080                 public bool AddressTaken {
1081                         get {
1082                                 return (flags & Flags.AddressTaken) != 0;
1083                         }
1084
1085                         set {
1086                                 flags |= Flags.AddressTaken;
1087                         }
1088                 }
1089
1090                 public override string ToString ()
1091                 {
1092                         return String.Format ("LocalInfo ({0},{1},{2},{3})",
1093                                               Name, Type, VariableInfo, Location);
1094                 }
1095
1096                 public bool Used {
1097                         get {
1098                                 return (flags & Flags.Used) != 0;
1099                         }
1100                         set {
1101                                 flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used));
1102                         }
1103                 }
1104
1105                 public bool ReadOnly {
1106                         get {
1107                                 return (flags & Flags.ReadOnly) != 0;
1108                         }
1109                         set {
1110                                 flags = value ? (flags | Flags.ReadOnly) : (unchecked (flags & ~Flags.ReadOnly));
1111                         }
1112                 }
1113
1114                 //
1115                 // Whether the variable is pinned, if Pinned the variable has been 
1116                 // allocated in a pinned slot with DeclareLocal.
1117                 //
1118                 public bool Pinned {
1119                         get {
1120                                 return (flags & Flags.Pinned) != 0;
1121                         }
1122                         set {
1123                                 flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned);
1124                         }
1125                 }
1126
1127                 public bool IsThis {
1128                         get {
1129                                 return (flags & Flags.IsThis) != 0;
1130                         }
1131                         set {
1132                                 flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis);
1133                         }
1134                 }
1135         }
1136                 
1137         /// <summary>
1138         ///   Block represents a C# block.
1139         /// </summary>
1140         ///
1141         /// <remarks>
1142         ///   This class is used in a number of places: either to represent
1143         ///   explicit blocks that the programmer places or implicit blocks.
1144         ///
1145         ///   Implicit blocks are used as labels or to introduce variable
1146         ///   declarations.
1147         ///
1148         ///   Top-level blocks derive from Block, and they are called ToplevelBlock
1149         ///   they contain extra information that is not necessary on normal blocks.
1150         /// </remarks>
1151         public class Block : Statement {
1152                 public Block    Parent;
1153                 public readonly Location  StartLocation;
1154                 public Location           EndLocation = Location.Null;
1155
1156                 [Flags]
1157                 public enum Flags {
1158                         Implicit  = 1,
1159                         Unchecked = 2,
1160                         BlockUsed = 4,
1161                         VariablesInitialized = 8,
1162                         HasRet = 16,
1163                         IsDestructor = 32,
1164                         HasVarargs = 64,
1165                         IsToplevel = 128,
1166                         Unsafe = 256
1167                 }
1168                 Flags flags;
1169
1170                 public bool Implicit {
1171                         get {
1172                                 return (flags & Flags.Implicit) != 0;
1173                         }
1174                 }
1175
1176                 public bool Unchecked {
1177                         get {
1178                                 return (flags & Flags.Unchecked) != 0;
1179                         }
1180                         set {
1181                                 flags |= Flags.Unchecked;
1182                         }
1183                 }
1184
1185                 public bool Unsafe {
1186                         get {
1187                                 return (flags & Flags.Unsafe) != 0;
1188                         }
1189                         set {
1190                                 flags |= Flags.Unsafe;
1191                         }
1192                 }
1193
1194                 public bool HasVarargs {
1195                         get {
1196                                 if (Parent != null)
1197                                         return Parent.HasVarargs;
1198                                 else
1199                                         return (flags & Flags.HasVarargs) != 0;
1200                         }
1201                         set {
1202                                 flags |= Flags.HasVarargs;
1203                         }
1204                 }
1205
1206                 //
1207                 // The statements in this block
1208                 //
1209                 ArrayList statements;
1210                 int num_statements;
1211
1212                 //
1213                 // An array of Blocks.  We keep track of children just
1214                 // to generate the local variable declarations.
1215                 //
1216                 // Statements and child statements are handled through the
1217                 // statements.
1218                 //
1219                 ArrayList children;
1220                 
1221                 //
1222                 // Labels.  (label, block) pairs.
1223                 //
1224                 Hashtable labels;
1225
1226                 //
1227                 // Keeps track of (name, type) pairs
1228                 //
1229                 Hashtable variables;
1230
1231                 //
1232                 // Keeps track of constants
1233                 Hashtable constants;
1234
1235                 //
1236                 // The parameters for the block, this is only needed on the toplevel block really
1237                 // TODO: move `parameters' into ToplevelBlock
1238                 Parameters parameters;
1239                 
1240                 //
1241                 // If this is a switch section, the enclosing switch block.
1242                 //
1243                 Block switch_block;
1244
1245                 protected static int id;
1246
1247                 int this_id;
1248                 
1249                 public Block (Block parent)
1250                         : this (parent, (Flags) 0, Location.Null, Location.Null)
1251                 { }
1252
1253                 public Block (Block parent, Flags flags)
1254                         : this (parent, flags, Location.Null, Location.Null)
1255                 { }
1256
1257                 public Block (Block parent, Flags flags, Parameters parameters)
1258                         : this (parent, flags, parameters, Location.Null, Location.Null)
1259                 { }
1260
1261                 public Block (Block parent, Location start, Location end)
1262                         : this (parent, (Flags) 0, start, end)
1263                 { }
1264
1265                 public Block (Block parent, Parameters parameters, Location start, Location end)
1266                         : this (parent, (Flags) 0, parameters, start, end)
1267                 { }
1268
1269                 public Block (Block parent, Flags flags, Location start, Location end)
1270                         : this (parent, flags, Parameters.EmptyReadOnlyParameters, start, end)
1271                 { }
1272
1273                 public Block (Block parent, Flags flags, Parameters parameters,
1274                               Location start, Location end)
1275                 {
1276                         if (parent != null)
1277                                 parent.AddChild (this);
1278                         
1279                         this.Parent = parent;
1280                         this.flags = flags;
1281                         this.parameters = parameters;
1282                         this.StartLocation = start;
1283                         this.EndLocation = end;
1284                         this.loc = start;
1285                         this_id = id++;
1286                         statements = new ArrayList ();
1287
1288                         if (parent != null && Implicit) {
1289                                 if (parent.child_variable_names == null)
1290                                         parent.child_variable_names = new Hashtable();
1291                                 // share with parent
1292                                 child_variable_names = parent.child_variable_names;
1293                         }
1294                                 
1295                 }
1296
1297                 public Block CreateSwitchBlock (Location start)
1298                 {
1299                         Block new_block = new Block (this, start, start);
1300                         new_block.switch_block = this;
1301                         return new_block;
1302                 }
1303
1304                 public int ID {
1305                         get {
1306                                 return this_id;
1307                         }
1308                 }
1309
1310                 void AddChild (Block b)
1311                 {
1312                         if (children == null)
1313                                 children = new ArrayList ();
1314                         
1315                         children.Add (b);
1316                 }
1317
1318                 public void SetEndLocation (Location loc)
1319                 {
1320                         EndLocation = loc;
1321                 }
1322
1323                 /// <summary>
1324                 ///   Adds a label to the current block. 
1325                 /// </summary>
1326                 ///
1327                 /// <returns>
1328                 ///   false if the name already exists in this block. true
1329                 ///   otherwise.
1330                 /// </returns>
1331                 ///
1332                 public bool AddLabel (string name, LabeledStatement target, Location loc)
1333                 {
1334                         if (switch_block != null)
1335                                 return switch_block.AddLabel (name, target, loc);
1336
1337                         Block cur = this;
1338                         while (cur != null) {
1339                                 if (cur.DoLookupLabel (name) != null) {
1340                                         Report.Error (
1341                                                 140, loc, "The label '{0}' is a duplicate",
1342                                                 name);
1343                                         return false;
1344                                 }
1345
1346                                 if (!Implicit)
1347                                         break;
1348
1349                                 cur = cur.Parent;
1350                         }
1351
1352                         while (cur != null) {
1353                                 if (cur.DoLookupLabel (name) != null) {
1354                                         Report.Error (
1355                                                 158, loc,
1356                                                 "The label '{0}' shadows another label " +
1357                                                 "by the same name in a containing scope.",
1358                                                 name);
1359                                         return false;
1360                                 }
1361
1362                                 if (children != null) {
1363                                         foreach (Block b in children) {
1364                                                 LabeledStatement s = b.DoLookupLabel (name);
1365                                                 if (s == null)
1366                                                         continue;
1367
1368                                                 Report.Error (
1369                                                         158, s.Location,
1370                                                         "The label '{0}' shadows another " +
1371                                                         "label by the same name in a " +
1372                                                         "containing scope.",
1373                                                         name);
1374                                                 return false;
1375                                         }
1376                                 }
1377
1378
1379                                 cur = cur.Parent;
1380                         }
1381
1382                         if (labels == null)
1383                                 labels = new Hashtable ();
1384
1385                         labels.Add (name, target);
1386                         return true;
1387                 }
1388
1389                 public LabeledStatement LookupLabel (string name)
1390                 {
1391                         LabeledStatement s = DoLookupLabel (name);
1392                         if (s != null)
1393                                 return s;
1394
1395                         if (children == null)
1396                                 return null;
1397
1398                         foreach (Block child in children) {
1399                                 if (!child.Implicit)
1400                                         continue;
1401
1402                                 s = child.LookupLabel (name);
1403                                 if (s != null)
1404                                         return s;
1405                         }
1406
1407                         return null;
1408                 }
1409
1410                 LabeledStatement DoLookupLabel (string name)
1411                 {
1412                         if (switch_block != null)
1413                                 return switch_block.LookupLabel (name);
1414
1415                         if (labels != null)
1416                                 if (labels.Contains (name))
1417                                         return ((LabeledStatement) labels [name]);
1418
1419                         return null;
1420                 }
1421
1422                 LocalInfo this_variable = null;
1423
1424                 // <summary>
1425                 //   Returns the "this" instance variable of this block.
1426                 //   See AddThisVariable() for more information.
1427                 // </summary>
1428                 public LocalInfo ThisVariable {
1429                         get {
1430                                 if (this_variable != null)
1431                                         return this_variable;
1432                                 else if (Parent != null)
1433                                         return Parent.ThisVariable;
1434                                 else
1435                                         return null;
1436                         }
1437                 }
1438
1439                 Hashtable child_variable_names;
1440
1441                 // <summary>
1442                 //   Marks a variable with name @name as being used in a child block.
1443                 //   If a variable name has been used in a child block, it's illegal to
1444                 //   declare a variable with the same name in the current block.
1445                 // </summary>
1446                 public void AddChildVariableName (string name)
1447                 {
1448                         if (child_variable_names == null)
1449                                 child_variable_names = new Hashtable ();
1450
1451                         child_variable_names [name] = null;
1452                 }
1453
1454                 // <summary>
1455                 //   Checks whether a variable name has already been used in a child block.
1456                 // </summary>
1457                 public bool IsVariableNameUsedInChildBlock (string name)
1458                 {
1459                         if (child_variable_names == null)
1460                                 return false;
1461
1462                         return child_variable_names.Contains (name);
1463                 }
1464
1465                 // <summary>
1466                 //   This is used by non-static `struct' constructors which do not have an
1467                 //   initializer - in this case, the constructor must initialize all of the
1468                 //   struct's fields.  To do this, we add a "this" variable and use the flow
1469                 //   analysis code to ensure that it's been fully initialized before control
1470                 //   leaves the constructor.
1471                 // </summary>
1472                 public LocalInfo AddThisVariable (TypeContainer tc, Location l)
1473                 {
1474                         if (this_variable != null)
1475                                 return this_variable;
1476
1477                         if (variables == null)
1478                                 variables = new Hashtable ();
1479
1480                         this_variable = new LocalInfo (tc, this, l);
1481                         this_variable.Used = true;
1482                         this_variable.IsThis = true;
1483
1484                         variables.Add ("this", this_variable);
1485
1486                         return this_variable;
1487                 }
1488
1489                 public LocalInfo AddVariable (Expression type, string name, Parameters pars, Location l)
1490                 {
1491                         if (variables == null)
1492                                 variables = new Hashtable ();
1493
1494                         LocalInfo vi = GetLocalInfo (name);
1495                         if (vi != null) {
1496                                 if (vi.Block != this)
1497                                         Report.Error (136, l, "A local variable named `" + name + "' " +
1498                                                       "cannot be declared in this scope since it would " +
1499                                                       "give a different meaning to `" + name + "', which " +
1500                                                       "is already used in a `parent or current' scope to " +
1501                                                       "denote something else");
1502                                 else
1503                                         Report.Error (128, l, "A local variable `" + name + "' is already " +
1504                                                       "defined in this scope");
1505                                 return null;
1506                         }
1507
1508                         if (IsVariableNameUsedInChildBlock (name)) {
1509                                 Report.Error (136, l, "A local variable named `" + name + "' " +
1510                                               "cannot be declared in this scope since it would " +
1511                                               "give a different meaning to `" + name + "', which " +
1512                                               "is already used in a `child' scope to denote something " +
1513                                               "else");
1514                                 return null;
1515                         }
1516
1517                         if (pars != null) {
1518                                 int idx;
1519                                 Parameter p = pars.GetParameterByName (name, out idx);
1520                                 if (p != null) {
1521                                         Report.Error (136, l, "A local variable named `" + name + "' " +
1522                                                       "cannot be declared in this scope since it would " +
1523                                                       "give a different meaning to `" + name + "', which " +
1524                                                       "is already used in a `parent or current' scope to " +
1525                                                       "denote something else");
1526                                         return null;
1527                                 }
1528                         }
1529
1530                         vi = new LocalInfo (type, name, this, l);
1531
1532                         variables.Add (name, vi);
1533
1534                         // Mark 'name' as "used by a child block" in every surrounding block
1535                         Block cur = this;
1536                         while (cur != null && cur.Implicit) 
1537                                 cur = cur.Parent;
1538                         if (cur != null)
1539                                 for (Block par = cur.Parent; par != null; par = par.Parent)
1540                                         par.AddChildVariableName (name);
1541
1542                         if ((flags & Flags.VariablesInitialized) != 0)
1543                                 throw new Exception ();
1544
1545                         // Console.WriteLine ("Adding {0} to {1}", name, ID);
1546                         return vi;
1547                 }
1548
1549                 public bool AddConstant (Expression type, string name, Expression value, Parameters pars, Location l)
1550                 {
1551                         if (AddVariable (type, name, pars, l) == null)
1552                                 return false;
1553                         
1554                         if (constants == null)
1555                                 constants = new Hashtable ();
1556
1557                         constants.Add (name, value);
1558                         return true;
1559                 }
1560
1561                 public Hashtable Variables {
1562                         get {
1563                                 return variables;
1564                         }
1565                 }
1566
1567                 public LocalInfo GetLocalInfo (string name)
1568                 {
1569                         for (Block b = this; b != null; b = b.Parent) {
1570                                 if (b.variables != null) {
1571                                         LocalInfo ret = b.variables [name] as LocalInfo;
1572                                         if (ret != null)
1573                                                 return ret;
1574                                 }
1575                         }
1576                         return null;
1577                 }
1578
1579                 public Expression GetVariableType (string name)
1580                 {
1581                         LocalInfo vi = GetLocalInfo (name);
1582
1583                         if (vi != null)
1584                                 return vi.Type;
1585
1586                         return null;
1587                 }
1588
1589                 public Expression GetConstantExpression (string name)
1590                 {
1591                         for (Block b = this; b != null; b = b.Parent) {
1592                                 if (b.constants != null) {
1593                                         Expression ret = b.constants [name] as Expression;
1594                                         if (ret != null)
1595                                                 return ret;
1596                                 }
1597                         }
1598                         return null;
1599                 }
1600                 
1601                 /// <summary>
1602                 ///   True if the variable named @name is a constant
1603                 ///  </summary>
1604                 public bool IsConstant (string name)
1605                 {
1606                         Expression e = null;
1607                         
1608                         e = GetConstantExpression (name);
1609                         
1610                         return e != null;
1611                 }
1612                 
1613                 //
1614                 // Returns a `ParameterReference' for the given name, or null if there
1615                 // is no such parameter
1616                 //
1617                 public ParameterReference GetParameterReference (string name, Location loc)
1618                 {
1619                                 Block b = this;
1620
1621                         do {
1622                                 Parameters pars = b.parameters;
1623                                 
1624                                 if (pars != null){
1625                                         Parameter par;
1626                                         int idx;
1627                                         
1628                                         par = pars.GetParameterByName (name, out idx);
1629                                         if (par != null){
1630                                                 ParameterReference pr;
1631
1632                                                 pr = new ParameterReference (pars, this, idx, name, loc);
1633                                                 return pr;
1634                                         }
1635                                 }
1636                                         b = b.Parent;
1637                         } while (b != null);
1638                         return null;
1639                         }
1640
1641                 //
1642                 // Whether the parameter named `name' is local to this block, 
1643                 // or false, if the parameter belongs to an encompassing block.
1644                 //
1645                 public bool IsLocalParameter (string name)
1646                 {
1647                                 Block b = this;
1648                         int toplevel_count = 0;
1649
1650                         do {
1651                                 if (this is ToplevelBlock)
1652                                         toplevel_count++;
1653
1654                                 Parameters pars = b.parameters;
1655                                 if (pars != null){
1656                                         if (pars.GetParameterByName (name) != null)
1657                                                 return true;
1658                                         return false;
1659                                 }
1660                                 if (toplevel_count > 0)
1661                                         return false;
1662                                         b = b.Parent;
1663                         } while (b != null);
1664                         return false;
1665                         }
1666                 
1667                 //
1668                 // Whether the `name' is a parameter reference
1669                 //
1670                 public bool IsParameterReference (string name)
1671                 {
1672                         Block b = this;
1673
1674                         do {
1675                                 Parameters pars = b.parameters;
1676                                 
1677                                 if (pars != null)
1678                                         if (pars.GetParameterByName (name) != null)
1679                                                 return true;
1680                                 b = b.Parent;
1681                         } while (b != null);
1682                         return false;
1683                 }
1684
1685                 /// <returns>
1686                 ///   A list of labels that were not used within this block
1687                 /// </returns>
1688                 public string [] GetUnreferenced ()
1689                 {
1690                         // FIXME: Implement me
1691                         return null;
1692                 }
1693
1694                 public void AddStatement (Statement s)
1695                 {
1696                         statements.Add (s);
1697                         flags |= Flags.BlockUsed;
1698                 }
1699
1700                 public bool Used {
1701                         get {
1702                                 return (flags & Flags.BlockUsed) != 0;
1703                         }
1704                 }
1705
1706                 public void Use ()
1707                 {
1708                         flags |= Flags.BlockUsed;
1709                 }
1710
1711                 public bool HasRet {
1712                         get {
1713                                 return (flags & Flags.HasRet) != 0;
1714                         }
1715                 }
1716
1717                 public bool IsDestructor {
1718                         get {
1719                                 return (flags & Flags.IsDestructor) != 0;
1720                         }
1721                 }
1722
1723                 public void SetDestructor ()
1724                 {
1725                         flags |= Flags.IsDestructor;
1726                 }
1727
1728                 VariableMap param_map, local_map;
1729
1730                 public VariableMap ParameterMap {
1731                         get {
1732                                 if ((flags & Flags.VariablesInitialized) == 0)
1733                                         throw new Exception ("Variables have not been initialized yet");
1734
1735                                 return param_map;
1736                         }
1737                 }
1738
1739                 public VariableMap LocalMap {
1740                         get {
1741                                 if ((flags & Flags.VariablesInitialized) == 0)
1742                                         throw new Exception ("Variables have not been initialized yet");
1743
1744                                 return local_map;
1745                         }
1746                 }
1747
1748                 /// <summary>
1749                 ///   Emits the variable declarations and labels.
1750                 /// </summary>
1751                 /// <remarks>
1752                 ///   tc: is our typecontainer (to resolve type references)
1753                 ///   ig: is the code generator:
1754                 /// </remarks>
1755                 public void ResolveMeta (ToplevelBlock toplevel, EmitContext ec, InternalParameters ip)
1756                 {
1757                         bool old_unsafe = ec.InUnsafe;
1758
1759                         // If some parent block was unsafe, we remain unsafe even if this block
1760                         // isn't explicitly marked as such.
1761                         ec.InUnsafe |= Unsafe;
1762
1763                         //
1764                         // Compute the VariableMap's.
1765                         //
1766                         // Unfortunately, we don't know the type when adding variables with
1767                         // AddVariable(), so we need to compute this info here.
1768                         //
1769
1770                         LocalInfo[] locals;
1771                         if (variables != null) {
1772                                 foreach (LocalInfo li in variables.Values)
1773                                         li.Resolve (ec);
1774
1775                                 locals = new LocalInfo [variables.Count];
1776                                 variables.Values.CopyTo (locals, 0);
1777                         } else
1778                                 locals = new LocalInfo [0];
1779
1780                         if (Parent != null)
1781                                 local_map = new VariableMap (Parent.LocalMap, locals);
1782                         else
1783                                 local_map = new VariableMap (locals);
1784
1785                         param_map = new VariableMap (ip);
1786                         flags |= Flags.VariablesInitialized;
1787
1788                         bool old_check_state = ec.ConstantCheckState;
1789                         ec.ConstantCheckState = (flags & Flags.Unchecked) == 0;
1790                                 
1791                         //
1792                         // Process this block variables
1793                         //
1794                         if (variables != null){
1795                                 foreach (DictionaryEntry de in variables){
1796                                         string name = (string) de.Key;
1797                                         LocalInfo vi = (LocalInfo) de.Value;
1798                                         
1799                                         if (vi.VariableType == null)
1800                                                 continue;
1801
1802                                         Type variable_type = vi.VariableType;
1803
1804                                         if (variable_type.IsPointer){
1805                                                 //
1806                                                 // Am not really convinced that this test is required (Microsoft does it)
1807                                                 // but the fact is that you would not be able to use the pointer variable
1808                                                 // *anyways*
1809                                                 //
1810                                                 if (!TypeManager.VerifyUnManaged (TypeManager.GetElementType (variable_type),
1811                                                                                   vi.Location))
1812                                                         continue;
1813                                         }
1814
1815 #if false
1816                                         if (remap_locals)
1817                                                 vi.FieldBuilder = ec.MapVariable (name, vi.VariableType);
1818                                         else if (vi.Pinned)
1819                                                 //
1820                                                 // This is needed to compile on both .NET 1.x and .NET 2.x
1821                                                 // the later introduced `DeclareLocal (Type t, bool pinned)'
1822                                                 //
1823                                                 vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
1824                                         else if (!vi.IsThis)
1825                                                 vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1826 #endif
1827
1828                                         if (constants == null)
1829                                                 continue;
1830
1831                                         Expression cv = (Expression) constants [name];
1832                                         if (cv == null)
1833                                                 continue;
1834
1835                                         ec.CurrentBlock = this;
1836                                         Expression e = cv.Resolve (ec);
1837                                         if (e == null)
1838                                                 continue;
1839
1840                                         Constant ce = e as Constant;
1841                                         if (ce == null){
1842                                                 Report.Error (133, vi.Location,
1843                                                               "The expression being assigned to `" +
1844                                                               name + "' must be constant (" + e + ")");
1845                                                 continue;
1846                                         }
1847
1848                                         if (e.Type != variable_type){
1849                                                 e = Const.ChangeType (vi.Location, ce, variable_type);
1850                                                 if (e == null)
1851                                                         continue;
1852                                         }
1853
1854                                         constants.Remove (name);
1855                                         constants.Add (name, e);
1856                                 }
1857                         }
1858                         ec.ConstantCheckState = old_check_state;
1859
1860                         //
1861                         // Now, handle the children
1862                         //
1863                         if (children != null){
1864                                 foreach (Block b in children)
1865                                         b.ResolveMeta (toplevel, ec, ip);
1866                         }
1867                         ec.InUnsafe = old_unsafe;
1868                 }
1869
1870                 //
1871                 // Emits the local variable declarations for a block
1872                 //
1873                 public void EmitMeta (EmitContext ec)
1874                 {
1875                         ILGenerator ig = ec.ig;
1876                         
1877                         if (variables != null){
1878                                 bool have_captured_vars = ec.HaveCapturedVariables ();
1879                                 bool remap_locals = ec.RemapToProxy;
1880                                 
1881                                 foreach (DictionaryEntry de in variables){
1882                                         LocalInfo vi = (LocalInfo) de.Value;
1883
1884                                         if (have_captured_vars && ec.IsCaptured (vi))
1885                                                 continue;
1886
1887                                         if (remap_locals){
1888                                                 vi.FieldBuilder = ec.MapVariable (vi.Name, vi.VariableType);
1889                                         } else {
1890                                                 if (vi.Pinned)
1891                                                         //
1892                                                         // This is needed to compile on both .NET 1.x and .NET 2.x
1893                                                         // the later introduced `DeclareLocal (Type t, bool pinned)'
1894                                                         //
1895                                                         vi.LocalBuilder = TypeManager.DeclareLocalPinned (ig, vi.VariableType);
1896                                                 else if (!vi.IsThis)
1897                                                         vi.LocalBuilder = ig.DeclareLocal (vi.VariableType);
1898                                         }
1899                                 }
1900                         }
1901
1902                         if (children != null){
1903                                 foreach (Block b in children)
1904                                         b.EmitMeta (ec);
1905                         }
1906                 }
1907
1908                 void UsageWarning (FlowBranching.UsageVector vector)
1909                 {
1910                         string name;
1911                         
1912                         if ((variables != null) && (RootContext.WarningLevel >= 3)) {
1913                                 foreach (DictionaryEntry de in variables){
1914                                         LocalInfo vi = (LocalInfo) de.Value;
1915                                         
1916                                         if (vi.Used)
1917                                                 continue;
1918                                         
1919                                         name = (string) de.Key;
1920                                                 
1921                                         if (vector.IsAssigned (vi.VariableInfo)){
1922                                                 Report.Warning (219, vi.Location, "The variable '{0}' is assigned but its value is never used", name);
1923                                         } else {
1924                                                 Report.Warning (168, vi.Location, "The variable '{0}' is declared but never used", name);
1925                                         }
1926                                 }
1927                         }
1928                 }
1929
1930                 bool unreachable_shown;
1931
1932                 public override bool Resolve (EmitContext ec)
1933                 {
1934                         Block prev_block = ec.CurrentBlock;
1935                         bool ok = true;
1936
1937                         int errors = Report.Errors;
1938
1939                         ec.CurrentBlock = this;
1940                         ec.StartFlowBranching (this);
1941
1942                         Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1943
1944                         bool unreachable = false;
1945
1946                         int statement_count = statements.Count;
1947                         for (int ix = 0; ix < statement_count; ix++){
1948                                 Statement s = (Statement) statements [ix];
1949                                 
1950                                 if (unreachable && !(s is LabeledStatement)) {
1951                                         if (s == EmptyStatement.Value)
1952                                                 s.loc = EndLocation;
1953
1954                                         if (!s.ResolveUnreachable (ec, !unreachable_shown))
1955                                                 ok = false;
1956
1957                                         if (s != EmptyStatement.Value)
1958                                                 unreachable_shown = true;
1959                                         else
1960                                                 s.loc = Location.Null;
1961
1962                                         statements [ix] = EmptyStatement.Value;
1963                                         continue;
1964                                 }
1965
1966                                 if (s.Resolve (ec) == false) {
1967                                         ok = false;
1968                                         statements [ix] = EmptyStatement.Value;
1969                                         continue;
1970                                 }
1971
1972                                 num_statements = ix + 1;
1973
1974                                 if (s is LabeledStatement)
1975                                         unreachable = false;
1976                                 else
1977                                         unreachable = ec.CurrentBranching.CurrentUsageVector.Reachability.IsUnreachable;
1978                         }
1979
1980                         Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
1981                                       ec.CurrentBranching, statement_count, num_statements);
1982
1983
1984                         FlowBranching.UsageVector vector = ec.DoEndFlowBranching ();
1985
1986                         ec.CurrentBlock = prev_block;
1987
1988                         // If we're a non-static `struct' constructor which doesn't have an
1989                         // initializer, then we must initialize all of the struct's fields.
1990                         if ((this_variable != null) &&
1991                             (vector.Reachability.Throws != FlowBranching.FlowReturns.Always) &&
1992                             !this_variable.IsThisAssigned (ec, loc))
1993                                 ok = false;
1994
1995                         if ((labels != null) && (RootContext.WarningLevel >= 2)) {
1996                                 foreach (LabeledStatement label in labels.Values)
1997                                         if (!label.HasBeenReferenced)
1998                                                 Report.Warning (164, label.Location,
1999                                                                 "This label has not been referenced");
2000                         }
2001
2002                         Report.Debug (4, "RESOLVE BLOCK DONE #2", StartLocation, vector);
2003
2004                         if ((vector.Reachability.Returns == FlowBranching.FlowReturns.Always) ||
2005                             (vector.Reachability.Throws == FlowBranching.FlowReturns.Always) ||
2006                             (vector.Reachability.Reachable == FlowBranching.FlowReturns.Never))
2007                                 flags |= Flags.HasRet;
2008
2009                         if (ok && (errors == Report.Errors)) {
2010                                 if (RootContext.WarningLevel >= 3)
2011                                         UsageWarning (vector);
2012                         }
2013
2014                         return ok;
2015                 }
2016                 
2017                 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2018                 {
2019                         unreachable_shown = true;
2020                         return base.ResolveUnreachable (ec, warn);
2021                 }
2022                 
2023                 protected override void DoEmit (EmitContext ec)
2024                 {
2025                         for (int ix = 0; ix < num_statements; ix++){
2026                                 Statement s = (Statement) statements [ix];
2027
2028                                 // Check whether we are the last statement in a
2029                                 // top-level block.
2030
2031                                 if ((Parent == null) && (ix+1 == num_statements))
2032                                         ec.IsLastStatement = true;
2033                                 else
2034                                         ec.IsLastStatement = false;
2035
2036                                 s.Emit (ec);
2037                         }
2038                 }
2039
2040                 public override void Emit (EmitContext ec)
2041                 {
2042                         Block prev_block = ec.CurrentBlock;
2043
2044                         ec.CurrentBlock = this;
2045
2046                         bool emit_debug_info = (CodeGen.SymbolWriter != null);
2047                         bool is_lexical_block = !Implicit && (Parent != null);
2048
2049                         if (emit_debug_info) {
2050                                 if (is_lexical_block)
2051                                         ec.ig.BeginScope ();
2052
2053                                 if (variables != null) {
2054                                         foreach (DictionaryEntry de in variables) {
2055                                                 string name = (string) de.Key;
2056                                                 LocalInfo vi = (LocalInfo) de.Value;
2057
2058                                                 if (vi.LocalBuilder == null)
2059                                                         continue;
2060
2061                                                 ec.DefineLocalVariable (name, vi.LocalBuilder);
2062                                         }
2063                                 }
2064                         }
2065
2066                         ec.Mark (StartLocation, true);
2067                         DoEmit (ec);
2068                         ec.Mark (EndLocation, true); 
2069
2070                         if (emit_debug_info && is_lexical_block)
2071                                 ec.ig.EndScope ();
2072
2073                         ec.CurrentBlock = prev_block;
2074                 }
2075
2076                 public ToplevelBlock Toplevel {
2077                         get {
2078                                 Block b = this;
2079                                 while (b.Parent != null){
2080                                         if ((b.flags & Flags.IsToplevel) != 0)
2081                                                 break;
2082                                         b = b.Parent;
2083                                 }
2084
2085                                 return (ToplevelBlock) b;
2086                         }
2087         }
2088
2089         //
2090                 // Returns true if we ar ea child of `b'.
2091                 //
2092                 public bool IsChildOf (Block b)
2093                 {
2094                         Block current = this;
2095                         
2096                         do {
2097                                 if (current.Parent == b)
2098                                         return true;
2099                                 current = current.Parent;
2100                         } while (current != null);
2101                         return false;
2102                 }
2103         }
2104
2105         //
2106         // A toplevel block contains extra information, the split is done
2107         // only to separate information that would otherwise bloat the more
2108         // lightweight Block.
2109         //
2110         // In particular, this was introduced when the support for Anonymous
2111         // Methods was implemented. 
2112         // 
2113         public class ToplevelBlock : Block {
2114                 //
2115                 // Pointer to the host of this anonymous method, or null
2116                 // if we are the topmost block
2117                 //
2118                 public ToplevelBlock Container;
2119                 CaptureContext capture_context;
2120
2121                 Hashtable capture_contexts;
2122
2123                 static int did = 0;
2124                 
2125                         
2126                 public void RegisterCaptureContext (CaptureContext cc)
2127                 {
2128                         if (capture_contexts == null)
2129                                 capture_contexts = new Hashtable ();
2130                         capture_contexts [cc] = cc;
2131                 }
2132
2133                 public void CompleteContexts ()
2134                 {
2135                         if (capture_contexts == null)
2136                                 return;
2137
2138                         foreach (CaptureContext cc in capture_contexts.Keys){
2139                                 cc.AdjustScopes ();
2140                         }
2141                 }
2142                 
2143                 public CaptureContext ToplevelBlockCaptureContext {
2144                         get {
2145                                 return capture_context;
2146                         }
2147                 }
2148                 
2149                 //
2150                 // Parent is only used by anonymous blocks to link back to their
2151                 // parents
2152                 //
2153                 public ToplevelBlock (ToplevelBlock container, Parameters parameters, Location start) :
2154                         base (null, Flags.IsToplevel, parameters, start, Location.Null)
2155                 {
2156                         Container = container;
2157                 }
2158                 
2159                 public ToplevelBlock (Parameters parameters, Location start) :
2160                         base (null, Flags.IsToplevel, parameters, start, Location.Null)
2161                 {
2162                 }
2163
2164                 public ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2165                         base (null, flags | Flags.IsToplevel, parameters, start, Location.Null)
2166                 {
2167                 }
2168
2169                 public ToplevelBlock (Location loc) : base (null, Flags.IsToplevel, loc, loc)
2170                 {
2171                 }
2172
2173                 public void SetHaveAnonymousMethods (Location loc, AnonymousMethod host)
2174                 {
2175                         if (capture_context == null)
2176                                 capture_context = new CaptureContext (this, loc, host);
2177                 }
2178
2179                 public CaptureContext CaptureContext {
2180                         get {
2181                                 return capture_context;
2182                         }
2183                 }
2184         }
2185         
2186         public class SwitchLabel {
2187                 Expression label;
2188                 object converted;
2189                 public Location loc;
2190
2191                 Label il_label;
2192                 bool  il_label_set;
2193                 Label il_label_code;
2194                 bool  il_label_code_set;
2195
2196                 //
2197                 // if expr == null, then it is the default case.
2198                 //
2199                 public SwitchLabel (Expression expr, Location l)
2200                 {
2201                         label = expr;
2202                         loc = l;
2203                 }
2204
2205                 public Expression Label {
2206                         get {
2207                                 return label;
2208                         }
2209                 }
2210
2211                 public object Converted {
2212                         get {
2213                                 return converted;
2214                         }
2215                 }
2216
2217                 public Label GetILLabel (EmitContext ec)
2218                 {
2219                         if (!il_label_set){
2220                                 il_label = ec.ig.DefineLabel ();
2221                                 il_label_set = true;
2222                         }
2223                         return il_label;
2224                 }
2225
2226                 public Label GetILLabelCode (EmitContext ec)
2227                 {
2228                         if (!il_label_code_set){
2229                                 il_label_code = ec.ig.DefineLabel ();
2230                                 il_label_code_set = true;
2231                         }
2232                         return il_label_code;
2233                 }                               
2234                 
2235                 //
2236                 // Resolves the expression, reduces it to a literal if possible
2237                 // and then converts it to the requested type.
2238                 //
2239                 public bool ResolveAndReduce (EmitContext ec, Type required_type)
2240                 {
2241                         if (label == null)
2242                                 return true;
2243                         
2244                         Expression e = label.Resolve (ec);
2245
2246                         if (e == null)
2247                                 return false;
2248
2249                         if (!(e is Constant)){
2250                                 Report.Error (150, loc, "A constant value is expected, got: " + e);
2251                                 return false;
2252                         }
2253
2254                         if (e is StringConstant || e is NullLiteral){
2255                                 if (required_type == TypeManager.string_type){
2256                                         converted = e;
2257                                         return true;
2258                                 }
2259                         }
2260
2261                         converted = Expression.ConvertIntLiteral ((Constant) e, required_type, loc);
2262                         if (converted == null)
2263                                 return false;
2264
2265                         return true;
2266                 }
2267         }
2268
2269         public class SwitchSection {
2270                 // An array of SwitchLabels.
2271                 public readonly ArrayList Labels;
2272                 public readonly Block Block;
2273                 
2274                 public SwitchSection (ArrayList labels, Block block)
2275                 {
2276                         Labels = labels;
2277                         Block = block;
2278                 }
2279         }
2280         
2281         public class Switch : Statement {
2282                 public readonly ArrayList Sections;
2283                 public Expression Expr;
2284
2285                 /// <summary>
2286                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
2287                 /// </summary>
2288                 public Hashtable Elements;
2289
2290                 /// <summary>
2291                 ///   The governing switch type
2292                 /// </summary>
2293                 public Type SwitchType;
2294
2295                 //
2296                 // Computed
2297                 //
2298                 bool got_default;
2299                 Label default_target;
2300                 Expression new_expr;
2301                 bool is_constant;
2302                 SwitchSection constant_section;
2303
2304                 //
2305                 // The types allowed to be implicitly cast from
2306                 // on the governing type
2307                 //
2308                 static Type [] allowed_types;
2309                 
2310                 public Switch (Expression e, ArrayList sects, Location l)
2311                 {
2312                         Expr = e;
2313                         Sections = sects;
2314                         loc = l;
2315                 }
2316
2317                 public bool GotDefault {
2318                         get {
2319                                 return got_default;
2320                         }
2321                 }
2322
2323                 public Label DefaultTarget {
2324                         get {
2325                                 return default_target;
2326                         }
2327                 }
2328
2329                 //
2330                 // Determines the governing type for a switch.  The returned
2331                 // expression might be the expression from the switch, or an
2332                 // expression that includes any potential conversions to the
2333                 // integral types or to string.
2334                 //
2335                 Expression SwitchGoverningType (EmitContext ec, Type t)
2336                 {
2337                         if (t == TypeManager.int32_type ||
2338                             t == TypeManager.uint32_type ||
2339                             t == TypeManager.char_type ||
2340                             t == TypeManager.byte_type ||
2341                             t == TypeManager.sbyte_type ||
2342                             t == TypeManager.ushort_type ||
2343                             t == TypeManager.short_type ||
2344                             t == TypeManager.uint64_type ||
2345                             t == TypeManager.int64_type ||
2346                             t == TypeManager.string_type ||
2347                                 t == TypeManager.bool_type ||
2348                                 t.IsSubclassOf (TypeManager.enum_type))
2349                                 return Expr;
2350
2351                         if (allowed_types == null){
2352                                 allowed_types = new Type [] {
2353                                         TypeManager.int32_type,
2354                                         TypeManager.uint32_type,
2355                                         TypeManager.sbyte_type,
2356                                         TypeManager.byte_type,
2357                                         TypeManager.short_type,
2358                                         TypeManager.ushort_type,
2359                                         TypeManager.int64_type,
2360                                         TypeManager.uint64_type,
2361                                         TypeManager.char_type,
2362                                         TypeManager.bool_type,
2363                                         TypeManager.string_type
2364                                 };
2365                         }
2366
2367                         //
2368                         // Try to find a *user* defined implicit conversion.
2369                         //
2370                         // If there is no implicit conversion, or if there are multiple
2371                         // conversions, we have to report an error
2372                         //
2373                         Expression converted = null;
2374                         foreach (Type tt in allowed_types){
2375                                 Expression e;
2376                                 
2377                                 e = Convert.ImplicitUserConversion (ec, Expr, tt, loc);
2378                                 if (e == null)
2379                                         continue;
2380
2381                                 //
2382                                 // Ignore over-worked ImplicitUserConversions that do
2383                                 // an implicit conversion in addition to the user conversion.
2384                                 // 
2385                                 if (e is UserCast){
2386                                         UserCast ue = e as UserCast;
2387
2388                                         if (ue.Source != Expr)
2389                                                 e = null;
2390                                 }
2391                                 
2392                                 if (converted != null){
2393                                         Report.ExtraInformation (
2394                                                 loc,
2395                                                 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
2396                                                                TypeManager.CSharpName (Expr.Type)));
2397                                         return null;
2398                                 } else {
2399                                         converted = e;
2400                                 }
2401                         }
2402                         return converted;
2403                 }
2404
2405                 static string Error152 {
2406                         get {
2407                                 return "The label '{0}:' already occurs in this switch statement";
2408                         }
2409                 }
2410                 
2411                 //
2412                 // Performs the basic sanity checks on the switch statement
2413                 // (looks for duplicate keys and non-constant expressions).
2414                 //
2415                 // It also returns a hashtable with the keys that we will later
2416                 // use to compute the switch tables
2417                 //
2418                 bool CheckSwitch (EmitContext ec)
2419                 {
2420                         Type compare_type;
2421                         bool error = false;
2422                         Elements = new Hashtable ();
2423                                 
2424                         got_default = false;
2425
2426                         if (TypeManager.IsEnumType (SwitchType)){
2427                                 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2428                         } else
2429                                 compare_type = SwitchType;
2430                         
2431                         foreach (SwitchSection ss in Sections){
2432                                 foreach (SwitchLabel sl in ss.Labels){
2433                                         if (!sl.ResolveAndReduce (ec, SwitchType)){
2434                                                 error = true;
2435                                                 continue;
2436                                         }
2437
2438                                         if (sl.Label == null){
2439                                                 if (got_default){
2440                                                         Report.Error (152, sl.loc, Error152, "default");
2441                                                         error = true;
2442                                                 }
2443                                                 got_default = true;
2444                                                 continue;
2445                                         }
2446                                         
2447                                         object key = sl.Converted;
2448
2449                                         if (key is Constant)
2450                                                 key = ((Constant) key).GetValue ();
2451
2452                                         if (key == null)
2453                                                 key = NullLiteral.Null;
2454                                         
2455                                         string lname = null;
2456                                         if (compare_type == TypeManager.uint64_type){
2457                                                 ulong v = (ulong) key;
2458
2459                                                 if (Elements.Contains (v))
2460                                                         lname = v.ToString ();
2461                                                 else
2462                                                         Elements.Add (v, sl);
2463                                         } else if (compare_type == TypeManager.int64_type){
2464                                                 long v = (long) key;
2465
2466                                                 if (Elements.Contains (v))
2467                                                         lname = v.ToString ();
2468                                                 else
2469                                                         Elements.Add (v, sl);
2470                                         } else if (compare_type == TypeManager.uint32_type){
2471                                                 uint v = (uint) key;
2472
2473                                                 if (Elements.Contains (v))
2474                                                         lname = v.ToString ();
2475                                                 else
2476                                                         Elements.Add (v, sl);
2477                                         } else if (compare_type == TypeManager.char_type){
2478                                                 char v = (char) key;
2479                                                 
2480                                                 if (Elements.Contains (v))
2481                                                         lname = v.ToString ();
2482                                                 else
2483                                                         Elements.Add (v, sl);
2484                                         } else if (compare_type == TypeManager.byte_type){
2485                                                 byte v = (byte) key;
2486                                                 
2487                                                 if (Elements.Contains (v))
2488                                                         lname = v.ToString ();
2489                                                 else
2490                                                         Elements.Add (v, sl);
2491                                         } else if (compare_type == TypeManager.sbyte_type){
2492                                                 sbyte v = (sbyte) key;
2493                                                 
2494                                                 if (Elements.Contains (v))
2495                                                         lname = v.ToString ();
2496                                                 else
2497                                                         Elements.Add (v, sl);
2498                                         } else if (compare_type == TypeManager.short_type){
2499                                                 short v = (short) key;
2500                                                 
2501                                                 if (Elements.Contains (v))
2502                                                         lname = v.ToString ();
2503                                                 else
2504                                                         Elements.Add (v, sl);
2505                                         } else if (compare_type == TypeManager.ushort_type){
2506                                                 ushort v = (ushort) key;
2507                                                 
2508                                                 if (Elements.Contains (v))
2509                                                         lname = v.ToString ();
2510                                                 else
2511                                                         Elements.Add (v, sl);
2512                                         } else if (compare_type == TypeManager.string_type){
2513                                                 if (key is NullLiteral){
2514                                                         if (Elements.Contains (NullLiteral.Null))
2515                                                                 lname = "null";
2516                                                         else
2517                                                                 Elements.Add (NullLiteral.Null, null);
2518                                                 } else {
2519                                                         string s = (string) key;
2520
2521                                                         if (Elements.Contains (s))
2522                                                                 lname = s;
2523                                                         else
2524                                                                 Elements.Add (s, sl);
2525                                                 }
2526                                         } else if (compare_type == TypeManager.int32_type) {
2527                                                 int v = (int) key;
2528
2529                                                 if (Elements.Contains (v))
2530                                                         lname = v.ToString ();
2531                                                 else
2532                                                         Elements.Add (v, sl);
2533                                         } else if (compare_type == TypeManager.bool_type) {
2534                                                 bool v = (bool) key;
2535
2536                                                 if (Elements.Contains (v))
2537                                                         lname = v.ToString ();
2538                                                 else
2539                                                         Elements.Add (v, sl);
2540                                         }
2541                                         else
2542                                         {
2543                                                 throw new Exception ("Unknown switch type!" +
2544                                                                      SwitchType + " " + compare_type);
2545                                         }
2546
2547                                         if (lname != null){
2548                                                 Report.Error (152, sl.loc, Error152, "case " + lname);
2549                                                 error = true;
2550                                         }
2551                                 }
2552                         }
2553                         if (error)
2554                                 return false;
2555                         
2556                         return true;
2557                 }
2558
2559                 void EmitObjectInteger (ILGenerator ig, object k)
2560                 {
2561                         if (k is int)
2562                                 IntConstant.EmitInt (ig, (int) k);
2563                         else if (k is Constant) {
2564                                 EmitObjectInteger (ig, ((Constant) k).GetValue ());
2565                         } 
2566                         else if (k is uint)
2567                                 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
2568                         else if (k is long)
2569                         {
2570                                 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
2571                                 {
2572                                         IntConstant.EmitInt (ig, (int) (long) k);
2573                                         ig.Emit (OpCodes.Conv_I8);
2574                                 }
2575                                 else
2576                                         LongConstant.EmitLong (ig, (long) k);
2577                         }
2578                         else if (k is ulong)
2579                         {
2580                                 if ((ulong) k < (1L<<32))
2581                                 {
2582                                         IntConstant.EmitInt (ig, (int) (long) k);
2583                                         ig.Emit (OpCodes.Conv_U8);
2584                                 }
2585                                 else
2586                                 {
2587                                         LongConstant.EmitLong (ig, unchecked ((long) (ulong) k));
2588                                 }
2589                         }
2590                         else if (k is char)
2591                                 IntConstant.EmitInt (ig, (int) ((char) k));
2592                         else if (k is sbyte)
2593                                 IntConstant.EmitInt (ig, (int) ((sbyte) k));
2594                         else if (k is byte)
2595                                 IntConstant.EmitInt (ig, (int) ((byte) k));
2596                         else if (k is short)
2597                                 IntConstant.EmitInt (ig, (int) ((short) k));
2598                         else if (k is ushort)
2599                                 IntConstant.EmitInt (ig, (int) ((ushort) k));
2600                         else if (k is bool)
2601                                 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
2602                         else
2603                                 throw new Exception ("Unhandled case");
2604                 }
2605                 
2606                 // structure used to hold blocks of keys while calculating table switch
2607                 class KeyBlock : IComparable
2608                 {
2609                         public KeyBlock (long _nFirst)
2610                         {
2611                                 nFirst = nLast = _nFirst;
2612                         }
2613                         public long nFirst;
2614                         public long nLast;
2615                         public ArrayList rgKeys = null;
2616                         // how many items are in the bucket
2617                         public int Size = 1;
2618                         public int Length
2619                         {
2620                                 get { return (int) (nLast - nFirst + 1); }
2621                         }
2622                         public static long TotalLength (KeyBlock kbFirst, KeyBlock kbLast)
2623                         {
2624                                 return kbLast.nLast - kbFirst.nFirst + 1;
2625                         }
2626                         public int CompareTo (object obj)
2627                         {
2628                                 KeyBlock kb = (KeyBlock) obj;
2629                                 int nLength = Length;
2630                                 int nLengthOther = kb.Length;
2631                                 if (nLengthOther == nLength)
2632                                         return (int) (kb.nFirst - nFirst);
2633                                 return nLength - nLengthOther;
2634                         }
2635                 }
2636
2637                 /// <summary>
2638                 /// This method emits code for a lookup-based switch statement (non-string)
2639                 /// Basically it groups the cases into blocks that are at least half full,
2640                 /// and then spits out individual lookup opcodes for each block.
2641                 /// It emits the longest blocks first, and short blocks are just
2642                 /// handled with direct compares.
2643                 /// </summary>
2644                 /// <param name="ec"></param>
2645                 /// <param name="val"></param>
2646                 /// <returns></returns>
2647                 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
2648                 {
2649                         int cElements = Elements.Count;
2650                         object [] rgKeys = new object [cElements];
2651                         Elements.Keys.CopyTo (rgKeys, 0);
2652                         Array.Sort (rgKeys);
2653
2654                         // initialize the block list with one element per key
2655                         ArrayList rgKeyBlocks = new ArrayList ();
2656                         foreach (object key in rgKeys)
2657                                 rgKeyBlocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
2658
2659                         KeyBlock kbCurr;
2660                         // iteratively merge the blocks while they are at least half full
2661                         // there's probably a really cool way to do this with a tree...
2662                         while (rgKeyBlocks.Count > 1)
2663                         {
2664                                 ArrayList rgKeyBlocksNew = new ArrayList ();
2665                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
2666                                 for (int ikb = 1; ikb < rgKeyBlocks.Count; ikb++)
2667                                 {
2668                                         KeyBlock kb = (KeyBlock) rgKeyBlocks [ikb];
2669                                         if ((kbCurr.Size + kb.Size) * 2 >=  KeyBlock.TotalLength (kbCurr, kb))
2670                                         {
2671                                                 // merge blocks
2672                                                 kbCurr.nLast = kb.nLast;
2673                                                 kbCurr.Size += kb.Size;
2674                                         }
2675                                         else
2676                                         {
2677                                                 // start a new block
2678                                                 rgKeyBlocksNew.Add (kbCurr);
2679                                                 kbCurr = kb;
2680                                         }
2681                                 }
2682                                 rgKeyBlocksNew.Add (kbCurr);
2683                                 if (rgKeyBlocks.Count == rgKeyBlocksNew.Count)
2684                                         break;
2685                                 rgKeyBlocks = rgKeyBlocksNew;
2686                         }
2687
2688                         // initialize the key lists
2689                         foreach (KeyBlock kb in rgKeyBlocks)
2690                                 kb.rgKeys = new ArrayList ();
2691
2692                         // fill the key lists
2693                         int iBlockCurr = 0;
2694                         if (rgKeyBlocks.Count > 0) {
2695                                 kbCurr = (KeyBlock) rgKeyBlocks [0];
2696                                 foreach (object key in rgKeys)
2697                                 {
2698                                         bool fNextBlock = (key is UInt64) ? (ulong) key > (ulong) kbCurr.nLast :
2699                                                 System.Convert.ToInt64 (key) > kbCurr.nLast;
2700                                         if (fNextBlock)
2701                                                 kbCurr = (KeyBlock) rgKeyBlocks [++iBlockCurr];
2702                                         kbCurr.rgKeys.Add (key);
2703                                 }
2704                         }
2705
2706                         // sort the blocks so we can tackle the largest ones first
2707                         rgKeyBlocks.Sort ();
2708
2709                         // okay now we can start...
2710                         ILGenerator ig = ec.ig;
2711                         Label lblEnd = ig.DefineLabel ();       // at the end ;-)
2712                         Label lblDefault = ig.DefineLabel ();
2713
2714                         Type typeKeys = null;
2715                         if (rgKeys.Length > 0)
2716                                 typeKeys = rgKeys [0].GetType ();       // used for conversions
2717
2718                         Type compare_type;
2719                         
2720                         if (TypeManager.IsEnumType (SwitchType))
2721                                 compare_type = TypeManager.EnumToUnderlying (SwitchType);
2722                         else
2723                                 compare_type = SwitchType;
2724                         
2725                         for (int iBlock = rgKeyBlocks.Count - 1; iBlock >= 0; --iBlock)
2726                         {
2727                                 KeyBlock kb = ((KeyBlock) rgKeyBlocks [iBlock]);
2728                                 lblDefault = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
2729                                 if (kb.Length <= 2)
2730                                 {
2731                                         foreach (object key in kb.rgKeys)
2732                                         {
2733                                                 ig.Emit (OpCodes.Ldloc, val);
2734                                                 EmitObjectInteger (ig, key);
2735                                                 SwitchLabel sl = (SwitchLabel) Elements [key];
2736                                                 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
2737                                         }
2738                                 }
2739                                 else
2740                                 {
2741                                         // TODO: if all the keys in the block are the same and there are
2742                                         //       no gaps/defaults then just use a range-check.
2743                                         if (compare_type == TypeManager.int64_type ||
2744                                                 compare_type == TypeManager.uint64_type)
2745                                         {
2746                                                 // TODO: optimize constant/I4 cases
2747
2748                                                 // check block range (could be > 2^31)
2749                                                 ig.Emit (OpCodes.Ldloc, val);
2750                                                 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2751                                                 ig.Emit (OpCodes.Blt, lblDefault);
2752                                                 ig.Emit (OpCodes.Ldloc, val);
2753                                                 EmitObjectInteger (ig, System.Convert.ChangeType (kb.nLast, typeKeys));
2754                                                 ig.Emit (OpCodes.Bgt, lblDefault);
2755
2756                                                 // normalize range
2757                                                 ig.Emit (OpCodes.Ldloc, val);
2758                                                 if (kb.nFirst != 0)
2759                                                 {
2760                                                         EmitObjectInteger (ig, System.Convert.ChangeType (kb.nFirst, typeKeys));
2761                                                         ig.Emit (OpCodes.Sub);
2762                                                 }
2763                                                 ig.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
2764                                         }
2765                                         else
2766                                         {
2767                                                 // normalize range
2768                                                 ig.Emit (OpCodes.Ldloc, val);
2769                                                 int nFirst = (int) kb.nFirst;
2770                                                 if (nFirst > 0)
2771                                                 {
2772                                                         IntConstant.EmitInt (ig, nFirst);
2773                                                         ig.Emit (OpCodes.Sub);
2774                                                 }
2775                                                 else if (nFirst < 0)
2776                                                 {
2777                                                         IntConstant.EmitInt (ig, -nFirst);
2778                                                         ig.Emit (OpCodes.Add);
2779                                                 }
2780                                         }
2781
2782                                         // first, build the list of labels for the switch
2783                                         int iKey = 0;
2784                                         int cJumps = kb.Length;
2785                                         Label [] rgLabels = new Label [cJumps];
2786                                         for (int iJump = 0; iJump < cJumps; iJump++)
2787                                         {
2788                                                 object key = kb.rgKeys [iKey];
2789                                                 if (System.Convert.ToInt64 (key) == kb.nFirst + iJump)
2790                                                 {
2791                                                         SwitchLabel sl = (SwitchLabel) Elements [key];
2792                                                         rgLabels [iJump] = sl.GetILLabel (ec);
2793                                                         iKey++;
2794                                                 }
2795                                                 else
2796                                                         rgLabels [iJump] = lblDefault;
2797                                         }
2798                                         // emit the switch opcode
2799                                         ig.Emit (OpCodes.Switch, rgLabels);
2800                                 }
2801
2802                                 // mark the default for this block
2803                                 if (iBlock != 0)
2804                                         ig.MarkLabel (lblDefault);
2805                         }
2806
2807                         // TODO: find the default case and emit it here,
2808                         //       to prevent having to do the following jump.
2809                         //       make sure to mark other labels in the default section
2810
2811                         // the last default just goes to the end
2812                         ig.Emit (OpCodes.Br, lblDefault);
2813
2814                         // now emit the code for the sections
2815                         bool fFoundDefault = false;
2816                         foreach (SwitchSection ss in Sections)
2817                         {
2818                                 foreach (SwitchLabel sl in ss.Labels)
2819                                 {
2820                                         ig.MarkLabel (sl.GetILLabel (ec));
2821                                         ig.MarkLabel (sl.GetILLabelCode (ec));
2822                                         if (sl.Label == null)
2823                                         {
2824                                                 ig.MarkLabel (lblDefault);
2825                                                 fFoundDefault = true;
2826                                         }
2827                                 }
2828                                 ss.Block.Emit (ec);
2829                                 //ig.Emit (OpCodes.Br, lblEnd);
2830                         }
2831                         
2832                         if (!fFoundDefault) {
2833                                 ig.MarkLabel (lblDefault);
2834                         }
2835                         ig.MarkLabel (lblEnd);
2836                 }
2837                 //
2838                 // This simple emit switch works, but does not take advantage of the
2839                 // `switch' opcode. 
2840                 // TODO: remove non-string logic from here
2841                 // TODO: binary search strings?
2842                 //
2843                 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
2844                 {
2845                         ILGenerator ig = ec.ig;
2846                         Label end_of_switch = ig.DefineLabel ();
2847                         Label next_test = ig.DefineLabel ();
2848                         Label null_target = ig.DefineLabel ();
2849                         bool default_found = false;
2850                         bool first_test = true;
2851                         bool pending_goto_end = false;
2852                         bool null_found;
2853                         bool default_at_end = false;
2854                         
2855                         ig.Emit (OpCodes.Ldloc, val);
2856                         
2857                         if (Elements.Contains (NullLiteral.Null)){
2858                                 ig.Emit (OpCodes.Brfalse, null_target);
2859                         } else
2860                                 ig.Emit (OpCodes.Brfalse, default_target);
2861                         
2862                         ig.Emit (OpCodes.Ldloc, val);
2863                         ig.Emit (OpCodes.Call, TypeManager.string_isinterneted_string);
2864                         ig.Emit (OpCodes.Stloc, val);
2865                 
2866                         int section_count = Sections.Count;
2867                         for (int section = 0; section < section_count; section++){
2868                                 SwitchSection ss = (SwitchSection) Sections [section];
2869                                 Label sec_begin = ig.DefineLabel ();
2870
2871                                 if (pending_goto_end)
2872                                         ig.Emit (OpCodes.Br, end_of_switch);
2873
2874                                 int label_count = ss.Labels.Count;
2875                                 bool mark_default = false;
2876                                 null_found = false;
2877                                 for (int label = 0; label < label_count; label++){
2878                                         SwitchLabel sl = (SwitchLabel) ss.Labels [label];
2879                                         ig.MarkLabel (sl.GetILLabel (ec));
2880                                         
2881                                         if (!first_test){
2882                                                 ig.MarkLabel (next_test);
2883                                                 next_test = ig.DefineLabel ();
2884                                         }
2885                                         //
2886                                         // If we are the default target
2887                                         //
2888                                         if (sl.Label == null){
2889                                                 if (label+1 == label_count)
2890                                                         default_at_end = true;
2891                                                 mark_default = true;
2892                                                 default_found = true;
2893                                         } else {
2894                                                 object lit = sl.Converted;
2895
2896                                                 if (lit is NullLiteral){
2897                                                         null_found = true;
2898                                                         if (label_count == 1)
2899                                                                 ig.Emit (OpCodes.Br, next_test);
2900                                                         continue;
2901                                                                               
2902                                                 }
2903                                                 StringConstant str = (StringConstant) lit;
2904                                                 
2905                                                 ig.Emit (OpCodes.Ldloc, val);
2906                                                 ig.Emit (OpCodes.Ldstr, str.Value);
2907                                                 if (label_count == 1)
2908                                                         ig.Emit (OpCodes.Bne_Un, next_test);
2909                                                 else {
2910                                                         if (label+1 == label_count)
2911                                                                 ig.Emit (OpCodes.Bne_Un, next_test);
2912                                                         else
2913                                                                 ig.Emit (OpCodes.Beq, sec_begin);
2914                                                 }
2915                                         }
2916                                 }
2917                                 if (null_found)
2918                                         ig.MarkLabel (null_target);
2919                                 ig.MarkLabel (sec_begin);
2920                                 foreach (SwitchLabel sl in ss.Labels)
2921                                         ig.MarkLabel (sl.GetILLabelCode (ec));
2922
2923                                 if (mark_default)
2924                                         ig.MarkLabel (default_target);
2925                                 ss.Block.Emit (ec);
2926                                 pending_goto_end = !ss.Block.HasRet;
2927                                 first_test = false;
2928                         }
2929                         ig.MarkLabel (next_test);
2930                         if (default_found){
2931                                 if (!default_at_end)
2932                                         ig.Emit (OpCodes.Br, default_target);
2933                         } else 
2934                                 ig.MarkLabel (default_target);
2935                         ig.MarkLabel (end_of_switch);
2936                 }
2937
2938                 SwitchSection FindSection (SwitchLabel label)
2939                 {
2940                         foreach (SwitchSection ss in Sections){
2941                                 foreach (SwitchLabel sl in ss.Labels){
2942                                         if (label == sl)
2943                                                 return ss;
2944                                 }
2945                         }
2946
2947                         return null;
2948                 }
2949
2950                 bool ResolveConstantSwitch (EmitContext ec)
2951                 {
2952                         object key = ((Constant) new_expr).GetValue ();
2953                         SwitchLabel label = (SwitchLabel) Elements [key];
2954
2955                         if (label == null)
2956                                 return true;
2957
2958                         constant_section = FindSection (label);
2959                         if (constant_section == null)
2960                                 return true;
2961
2962                         if (constant_section.Block.Resolve (ec) != true)
2963                                 return false;
2964
2965                         return true;
2966                 }
2967
2968                 public override bool Resolve (EmitContext ec)
2969                 {
2970                         Expr = Expr.Resolve (ec);
2971                         if (Expr == null)
2972                                 return false;
2973
2974                         new_expr = SwitchGoverningType (ec, Expr.Type);
2975                         if (new_expr == null){
2976                                 Report.Error (151, loc, "An integer type or string was expected for switch");
2977                                 return false;
2978                         }
2979
2980                         // Validate switch.
2981                         SwitchType = new_expr.Type;
2982
2983                         if (!CheckSwitch (ec))
2984                                 return false;
2985
2986                         Switch old_switch = ec.Switch;
2987                         ec.Switch = this;
2988                         ec.Switch.SwitchType = SwitchType;
2989
2990                         Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
2991                         ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
2992
2993                         is_constant = new_expr is Constant;
2994                         if (is_constant) {
2995                                 object key = ((Constant) new_expr).GetValue ();
2996                                 SwitchLabel label = (SwitchLabel) Elements [key];
2997
2998                                 constant_section = FindSection (label);
2999                         }
3000
3001                         bool first = true;
3002                         foreach (SwitchSection ss in Sections){
3003                                 if (!first)
3004                                         ec.CurrentBranching.CreateSibling (
3005                                                 null, FlowBranching.SiblingType.SwitchSection);
3006                                 else
3007                                         first = false;
3008
3009                                 if (is_constant && (ss != constant_section)) {
3010                                         // If we're a constant switch, we're only emitting
3011                                         // one single section - mark all the others as
3012                                         // unreachable.
3013                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
3014                                         if (!ss.Block.ResolveUnreachable (ec, true))
3015                                                 return false;
3016                                 } else {
3017                                         if (!ss.Block.Resolve (ec))
3018                                         return false;
3019                         }
3020                         }
3021
3022                         if (!got_default)
3023                                 ec.CurrentBranching.CreateSibling (
3024                                         null, FlowBranching.SiblingType.SwitchSection);
3025
3026                         FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3027                         ec.Switch = old_switch;
3028
3029                         Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching,
3030                                       reachability);
3031
3032                         return true;
3033                 }
3034                 
3035                 protected override void DoEmit (EmitContext ec)
3036                 {
3037                         ILGenerator ig = ec.ig;
3038
3039                         // Store variable for comparission purposes
3040                         LocalBuilder value;
3041                         if (!is_constant) {
3042                                 value = ig.DeclareLocal (SwitchType);
3043                         new_expr.Emit (ec);
3044                                 ig.Emit (OpCodes.Stloc, value);
3045                         } else
3046                                 value = null;
3047
3048                         default_target = ig.DefineLabel ();
3049
3050                         //
3051                         // Setup the codegen context
3052                         //
3053                         Label old_end = ec.LoopEnd;
3054                         Switch old_switch = ec.Switch;
3055                         
3056                         ec.LoopEnd = ig.DefineLabel ();
3057                         ec.Switch = this;
3058
3059                         // Emit Code.
3060                         if (is_constant) {
3061                                 if (constant_section != null)
3062                                         constant_section.Block.Emit (ec);
3063                         } else if (SwitchType == TypeManager.string_type)
3064                                 SimpleSwitchEmit (ec, value);
3065                         else
3066                                 TableSwitchEmit (ec, value);
3067
3068                         // Restore context state. 
3069                         ig.MarkLabel (ec.LoopEnd);
3070
3071                         //
3072                         // Restore the previous context
3073                         //
3074                         ec.LoopEnd = old_end;
3075                         ec.Switch = old_switch;
3076                 }
3077         }
3078
3079         public abstract class ExceptionStatement : Statement
3080         {
3081                 public abstract void EmitFinally (EmitContext ec);
3082
3083                 protected bool emit_finally = true;
3084                 ArrayList parent_vectors;
3085
3086                 protected void DoEmitFinally (EmitContext ec)
3087                 {
3088                         if (emit_finally)
3089                                 ec.ig.BeginFinallyBlock ();
3090                         else
3091                                 ec.CurrentIterator.MarkFinally (ec, parent_vectors);
3092                         EmitFinally (ec);
3093                 }
3094
3095                 protected void ResolveFinally (FlowBranchingException branching)
3096                 {
3097                         emit_finally = branching.EmitFinally;
3098                         if (!emit_finally)
3099                                 branching.Parent.StealFinallyClauses (ref parent_vectors);
3100                 }
3101         }
3102
3103         public class Lock : ExceptionStatement {
3104                 Expression expr;
3105                 Statement Statement;
3106                 LocalBuilder temp;
3107                         
3108                 public Lock (Expression expr, Statement stmt, Location l)
3109                 {
3110                         this.expr = expr;
3111                         Statement = stmt;
3112                         loc = l;
3113                 }
3114
3115                 public override bool Resolve (EmitContext ec)
3116                 {
3117                         expr = expr.Resolve (ec);
3118                         if (expr == null)
3119                                 return false;
3120
3121                         if (expr.Type.IsValueType){
3122                                 Error (185, "lock statement requires the expression to be " +
3123                                        " a reference type (type is: `{0}'",
3124                                        TypeManager.CSharpName (expr.Type));
3125                                 return false;
3126                         }
3127
3128                         FlowBranchingException branching = ec.StartFlowBranching (this);
3129                         bool ok = Statement.Resolve (ec);
3130                         if (!ok) {
3131                                 ec.KillFlowBranching ();
3132                                 return false;
3133                         }
3134
3135                         ResolveFinally (branching);
3136
3137                         FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3138                         if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3139                                 // Unfortunately, System.Reflection.Emit automatically emits
3140                                 // a leave to the end of the finally block.
3141                                 // This is a problem if `returns' is true since we may jump
3142                                 // to a point after the end of the method.
3143                                 // As a workaround, emit an explicit ret here.
3144                                 ec.NeedReturnLabel ();
3145                         }
3146
3147                         return true;
3148                 }
3149                 
3150                 protected override void DoEmit (EmitContext ec)
3151                 {
3152                         Type type = expr.Type;
3153                         
3154                         ILGenerator ig = ec.ig;
3155                         temp = ig.DeclareLocal (type);
3156                                 
3157                         expr.Emit (ec);
3158                         ig.Emit (OpCodes.Dup);
3159                         ig.Emit (OpCodes.Stloc, temp);
3160                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3161
3162                         // try
3163                         if (emit_finally)
3164                                 ig.BeginExceptionBlock ();
3165                         Statement.Emit (ec);
3166                         
3167                         // finally
3168                         DoEmitFinally (ec);
3169                         if (emit_finally)
3170                                 ig.EndExceptionBlock ();
3171                 }
3172
3173                 public override void EmitFinally (EmitContext ec)
3174                 {
3175                         ILGenerator ig = ec.ig;
3176                         ig.Emit (OpCodes.Ldloc, temp);
3177                         ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
3178                 }
3179         }
3180
3181         public class Unchecked : Statement {
3182                 public readonly Block Block;
3183                 
3184                 public Unchecked (Block b)
3185                 {
3186                         Block = b;
3187                         b.Unchecked = true;
3188                 }
3189
3190                 public override bool Resolve (EmitContext ec)
3191                 {
3192                         bool previous_state = ec.CheckState;
3193                         bool previous_state_const = ec.ConstantCheckState;
3194
3195                         ec.CheckState = false;
3196                         ec.ConstantCheckState = false;
3197                         bool ret = Block.Resolve (ec);
3198                         ec.CheckState = previous_state;
3199                         ec.ConstantCheckState = previous_state_const;
3200
3201                         return ret;
3202                 }
3203                 
3204                 protected override void DoEmit (EmitContext ec)
3205                 {
3206                         bool previous_state = ec.CheckState;
3207                         bool previous_state_const = ec.ConstantCheckState;
3208                         
3209                         ec.CheckState = false;
3210                         ec.ConstantCheckState = false;
3211                         Block.Emit (ec);
3212                         ec.CheckState = previous_state;
3213                         ec.ConstantCheckState = previous_state_const;
3214                 }
3215         }
3216
3217         public class Checked : Statement {
3218                 public readonly Block Block;
3219                 
3220                 public Checked (Block b)
3221                 {
3222                         Block = b;
3223                         b.Unchecked = false;
3224                 }
3225
3226                 public override bool Resolve (EmitContext ec)
3227                 {
3228                         bool previous_state = ec.CheckState;
3229                         bool previous_state_const = ec.ConstantCheckState;
3230                         
3231                         ec.CheckState = true;
3232                         ec.ConstantCheckState = true;
3233                         bool ret = Block.Resolve (ec);
3234                         ec.CheckState = previous_state;
3235                         ec.ConstantCheckState = previous_state_const;
3236
3237                         return ret;
3238                 }
3239
3240                 protected override void DoEmit (EmitContext ec)
3241                 {
3242                         bool previous_state = ec.CheckState;
3243                         bool previous_state_const = ec.ConstantCheckState;
3244                         
3245                         ec.CheckState = true;
3246                         ec.ConstantCheckState = true;
3247                         Block.Emit (ec);
3248                         ec.CheckState = previous_state;
3249                         ec.ConstantCheckState = previous_state_const;
3250                 }
3251         }
3252
3253         public class Unsafe : Statement {
3254                 public readonly Block Block;
3255
3256                 public Unsafe (Block b)
3257                 {
3258                         Block = b;
3259                         Block.Unsafe = true;
3260                 }
3261
3262                 public override bool Resolve (EmitContext ec)
3263                 {
3264                         bool previous_state = ec.InUnsafe;
3265                         bool val;
3266                         
3267                         ec.InUnsafe = true;
3268                         val = Block.Resolve (ec);
3269                         ec.InUnsafe = previous_state;
3270
3271                         return val;
3272                 }
3273                 
3274                 protected override void DoEmit (EmitContext ec)
3275                 {
3276                         bool previous_state = ec.InUnsafe;
3277                         
3278                         ec.InUnsafe = true;
3279                         Block.Emit (ec);
3280                         ec.InUnsafe = previous_state;
3281                 }
3282         }
3283
3284         // 
3285         // Fixed statement
3286         //
3287         public class Fixed : Statement {
3288                 Expression type;
3289                 ArrayList declarators;
3290                 Statement statement;
3291                 Type expr_type;
3292                 FixedData[] data;
3293                 bool has_ret;
3294
3295                 struct FixedData {
3296                         public bool is_object;
3297                         public LocalInfo vi;
3298                         public Expression expr;
3299                         public Expression converted;
3300                 }                       
3301
3302                 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
3303                 {
3304                         this.type = type;
3305                         declarators = decls;
3306                         statement = stmt;
3307                         loc = l;
3308                 }
3309
3310                 public override bool Resolve (EmitContext ec)
3311                 {
3312                         if (!ec.InUnsafe){
3313                                 Expression.UnsafeError (loc);
3314                                 return false;
3315                         }
3316                         
3317                         TypeExpr texpr = type.ResolveAsTypeTerminal (ec);
3318                         if (texpr == null)
3319                                 return false;
3320
3321                         expr_type = texpr.Type;
3322
3323                         CheckObsolete (expr_type);
3324
3325                         if (ec.RemapToProxy){
3326                                 Report.Error (-210, loc, "Fixed statement not allowed in iterators");
3327                                 return false;
3328                         }
3329                         
3330                         data = new FixedData [declarators.Count];
3331
3332                         if (!expr_type.IsPointer){
3333                                 Report.Error (209, loc, "Variables in a fixed statement must be pointers");
3334                                 return false;
3335                         }
3336                         
3337                         int i = 0;
3338                         foreach (Pair p in declarators){
3339                                 LocalInfo vi = (LocalInfo) p.First;
3340                                 Expression e = (Expression) p.Second;
3341
3342                                 vi.VariableInfo.SetAssigned (ec);
3343                                 vi.ReadOnly = true;
3344
3345                                 //
3346                                 // The rules for the possible declarators are pretty wise,
3347                                 // but the production on the grammar is more concise.
3348                                 //
3349                                 // So we have to enforce these rules here.
3350                                 //
3351                                 // We do not resolve before doing the case 1 test,
3352                                 // because the grammar is explicit in that the token &
3353                                 // is present, so we need to test for this particular case.
3354                                 //
3355
3356                                 if (e is Cast){
3357                                         Report.Error (254, loc, "Cast expression not allowed as right hand expression in fixed statement");
3358                                         return false;
3359                                 }
3360                                 
3361                                 //
3362                                 // Case 1: & object.
3363                                 //
3364                                 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
3365                                         Expression child = ((Unary) e).Expr;
3366
3367                                         if (child is ParameterReference || child is LocalVariableReference){
3368                                                 Report.Error (
3369                                                         213, loc, 
3370                                                         "No need to use fixed statement for parameters or " +
3371                                                         "local variable declarations (address is already " +
3372                                                         "fixed)");
3373                                                 return false;
3374                                         }
3375
3376                                         ec.InFixedInitializer = true;
3377                                         e = e.Resolve (ec);
3378                                         ec.InFixedInitializer = false;
3379                                         if (e == null)
3380                                                 return false;
3381
3382                                         child = ((Unary) e).Expr;
3383                                         
3384                                         if (!TypeManager.VerifyUnManaged (child.Type, loc))
3385                                                 return false;
3386
3387                                         data [i].is_object = true;
3388                                         data [i].expr = e;
3389                                         data [i].converted = null;
3390                                         data [i].vi = vi;
3391                                         i++;
3392
3393                                         continue;
3394                                 }
3395
3396                                 ec.InFixedInitializer = true;
3397                                 e = e.Resolve (ec);
3398                                 ec.InFixedInitializer = false;
3399                                 if (e == null)
3400                                         return false;
3401
3402                                 //
3403                                 // Case 2: Array
3404                                 //
3405                                 if (e.Type.IsArray){
3406                                         Type array_type = TypeManager.GetElementType (e.Type);
3407                                         
3408                                         //
3409                                         // Provided that array_type is unmanaged,
3410                                         //
3411                                         if (!TypeManager.VerifyUnManaged (array_type, loc))
3412                                                 return false;
3413
3414                                         //
3415                                         // and T* is implicitly convertible to the
3416                                         // pointer type given in the fixed statement.
3417                                         //
3418                                         ArrayPtr array_ptr = new ArrayPtr (e, loc);
3419                                         
3420                                         Expression converted = Convert.ImplicitConversionRequired (
3421                                                 ec, array_ptr, vi.VariableType, loc);
3422                                         if (converted == null)
3423                                                 return false;
3424
3425                                         data [i].is_object = false;
3426                                         data [i].expr = e;
3427                                         data [i].converted = converted;
3428                                         data [i].vi = vi;
3429                                         i++;
3430
3431                                         continue;
3432                                 }
3433
3434                                 //
3435                                 // Case 3: string
3436                                 //
3437                                 if (e.Type == TypeManager.string_type){
3438                                         data [i].is_object = false;
3439                                         data [i].expr = e;
3440                                         data [i].converted = null;
3441                                         data [i].vi = vi;
3442                                         i++;
3443                                         continue;
3444                                 }
3445
3446                                 //
3447                                 // For other cases, flag a `this is already fixed expression'
3448                                 //
3449                                 if (e is LocalVariableReference || e is ParameterReference ||
3450                                     Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
3451                                     
3452                                         Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
3453                                         return false;
3454                                 }
3455
3456                                 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
3457                                 return false;
3458                         }
3459
3460                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
3461
3462                         if (!statement.Resolve (ec)) {
3463                                 ec.KillFlowBranching ();
3464                                 return false;
3465                         }
3466
3467                         FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3468                         has_ret = reachability.IsUnreachable;
3469
3470                         return true;
3471                 }
3472                 
3473                 protected override void DoEmit (EmitContext ec)
3474                 {
3475                         ILGenerator ig = ec.ig;
3476
3477                         LocalBuilder [] clear_list = new LocalBuilder [data.Length];
3478                         
3479                         for (int i = 0; i < data.Length; i++) {
3480                                 LocalInfo vi = data [i].vi;
3481
3482                                 //
3483                                 // Case 1: & object.
3484                                 //
3485                                 if (data [i].is_object) {
3486                                         //
3487                                         // Store pointer in pinned location
3488                                         //
3489                                         data [i].expr.Emit (ec);
3490                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3491                                         clear_list [i] = vi.LocalBuilder;
3492                                         continue;
3493                                 }
3494
3495                                 //
3496                                 // Case 2: Array
3497                                 //
3498                                 if (data [i].expr.Type.IsArray){
3499                                         //
3500                                         // Store pointer in pinned location
3501                                         //
3502                                         data [i].converted.Emit (ec);
3503                                         
3504                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3505                                         clear_list [i] = vi.LocalBuilder;
3506                                         continue;
3507                                 }
3508
3509                                 //
3510                                 // Case 3: string
3511                                 //
3512                                 if (data [i].expr.Type == TypeManager.string_type){
3513                                         LocalBuilder pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
3514                                         clear_list [i] = pinned_string;
3515                                         
3516                                         data [i].expr.Emit (ec);
3517                                         ig.Emit (OpCodes.Stloc, pinned_string);
3518
3519                                         Expression sptr = new StringPtr (pinned_string, loc);
3520                                         Expression converted = Convert.ImplicitConversionRequired (
3521                                                 ec, sptr, vi.VariableType, loc);
3522                                         
3523                                         if (converted == null)
3524                                                 continue;
3525
3526                                         converted.Emit (ec);
3527                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3528                                 }
3529                         }
3530
3531                         statement.Emit (ec);
3532
3533                         if (has_ret)
3534                                 return;
3535
3536                         //
3537                         // Clear the pinned variable
3538                         //
3539                         for (int i = 0; i < data.Length; i++) {
3540                                 if (data [i].is_object || data [i].expr.Type.IsArray) {
3541                                         ig.Emit (OpCodes.Ldc_I4_0);
3542                                         ig.Emit (OpCodes.Conv_U);
3543                                         ig.Emit (OpCodes.Stloc, clear_list [i]);
3544                                 } else if (data [i].expr.Type == TypeManager.string_type){
3545                                         ig.Emit (OpCodes.Ldnull);
3546                                         ig.Emit (OpCodes.Stloc, clear_list [i]);
3547                                 }
3548                         }
3549                 }
3550         }
3551         
3552         public class Catch: Statement {
3553                 public readonly string Name;
3554                 public readonly Block  Block;
3555
3556                 Expression type_expr;
3557                 Type type;
3558                 
3559                 public Catch (Expression type, string name, Block block, Location l)
3560                 {
3561                         type_expr = type;
3562                         Name = name;
3563                         Block = block;
3564                         loc = l;
3565                 }
3566
3567                 public Type CatchType {
3568                         get {
3569                                 return type;
3570                         }
3571                 }
3572
3573                 public bool IsGeneral {
3574                         get {
3575                                 return type_expr == null;
3576                         }
3577                 }
3578
3579                 protected override void DoEmit(EmitContext ec)
3580                 {
3581                 }
3582
3583                 public override bool Resolve (EmitContext ec)
3584                 {
3585                         if (type_expr != null) {
3586                                 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec);
3587                                 if (te == null)
3588                                         return false;
3589
3590                                 type = te.Type;
3591
3592                                 CheckObsolete (type);
3593
3594                                 if (type != TypeManager.exception_type && !type.IsSubclassOf (TypeManager.exception_type)){
3595                                         Error (155, "The type caught or thrown must be derived from System.Exception");
3596                                         return false;
3597                                 }
3598                         } else
3599                                 type = null;
3600
3601                         return Block.Resolve (ec);
3602                 }
3603         }
3604
3605         public class Try : ExceptionStatement {
3606                 public readonly Block Fini, Block;
3607                 public readonly ArrayList Specific;
3608                 public readonly Catch General;
3609
3610                 bool need_exc_block;
3611                 
3612                 //
3613                 // specific, general and fini might all be null.
3614                 //
3615                 public Try (Block block, ArrayList specific, Catch general, Block fini, Location l)
3616                 {
3617                         if (specific == null && general == null){
3618                                 Console.WriteLine ("CIR.Try: Either specific or general have to be non-null");
3619                         }
3620                         
3621                         this.Block = block;
3622                         this.Specific = specific;
3623                         this.General = general;
3624                         this.Fini = fini;
3625                         loc = l;
3626                 }
3627
3628                 public override bool Resolve (EmitContext ec)
3629                 {
3630                         bool ok = true;
3631                         
3632                         FlowBranchingException branching = ec.StartFlowBranching (this);
3633
3634                         Report.Debug (1, "START OF TRY BLOCK", Block.StartLocation);
3635
3636                         if (!Block.Resolve (ec))
3637                                 ok = false;
3638
3639                         FlowBranching.UsageVector vector = ec.CurrentBranching.CurrentUsageVector;
3640
3641                         Report.Debug (1, "START OF CATCH BLOCKS", vector);
3642
3643                         Type[] prevCatches = new Type [Specific.Count];
3644                         int last_index = 0;
3645                         foreach (Catch c in Specific){
3646                                 ec.CurrentBranching.CreateSibling (
3647                                         c.Block, FlowBranching.SiblingType.Catch);
3648
3649                                 Report.Debug (1, "STARTED SIBLING FOR CATCH", ec.CurrentBranching);
3650
3651                                 if (c.Name != null) {
3652                                         LocalInfo vi = c.Block.GetLocalInfo (c.Name);
3653                                         if (vi == null)
3654                                                 throw new Exception ();
3655
3656                                         vi.VariableInfo = null;
3657                                 }
3658
3659                                 if (!c.Resolve (ec))
3660                                         return false;
3661
3662                                 Type resolvedType = c.CatchType;
3663                                 for (int ii = 0; ii < last_index; ++ii) {
3664                                         if (resolvedType == prevCatches [ii] || resolvedType.IsSubclassOf (prevCatches [ii])) {
3665                                                 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type '{0}'", prevCatches [ii].FullName);
3666                                                 return false;
3667                                         }
3668                                 }
3669
3670                                 prevCatches [last_index++] = resolvedType;
3671                                 need_exc_block = true;
3672                         }
3673
3674                         Report.Debug (1, "END OF CATCH BLOCKS", ec.CurrentBranching);
3675
3676                         if (General != null){
3677                                 ec.CurrentBranching.CreateSibling (
3678                                         General.Block, FlowBranching.SiblingType.Catch);
3679
3680                                 Report.Debug (1, "STARTED SIBLING FOR GENERAL", ec.CurrentBranching);
3681
3682                                 if (!General.Resolve (ec))
3683                                         ok = false;
3684
3685                                 need_exc_block = true;
3686                         }
3687
3688                         Report.Debug (1, "END OF GENERAL CATCH BLOCKS", ec.CurrentBranching);
3689
3690                         if (Fini != null) {
3691                                 if (ok)
3692                                         ec.CurrentBranching.CreateSibling (
3693                                                 Fini, FlowBranching.SiblingType.Finally);
3694
3695                                 Report.Debug (1, "STARTED SIBLING FOR FINALLY", ec.CurrentBranching, vector);
3696
3697                                 if (!Fini.Resolve (ec))
3698                                         ok = false;
3699                         }
3700
3701                         ResolveFinally (branching);
3702                         need_exc_block |= emit_finally;
3703
3704                         FlowBranching.Reachability reachability = ec.EndFlowBranching ();
3705
3706                         FlowBranching.UsageVector f_vector = ec.CurrentBranching.CurrentUsageVector;
3707
3708                         Report.Debug (1, "END OF TRY", ec.CurrentBranching, reachability, vector, f_vector);
3709
3710                         if (reachability.Returns != FlowBranching.FlowReturns.Always) {
3711                                 // Unfortunately, System.Reflection.Emit automatically emits
3712                                 // a leave to the end of the finally block.  This is a problem
3713                                 // if `returns' is true since we may jump to a point after the
3714                                 // end of the method.
3715                                 // As a workaround, emit an explicit ret here.
3716                                 ec.NeedReturnLabel ();
3717                         }
3718
3719                         return ok;
3720                 }
3721                 
3722                 protected override void DoEmit (EmitContext ec)
3723                 {
3724                         ILGenerator ig = ec.ig;
3725
3726                         if (need_exc_block)
3727                                 ig.BeginExceptionBlock ();
3728                         Block.Emit (ec);
3729
3730                         foreach (Catch c in Specific){
3731                                 LocalInfo vi;
3732                                 
3733                                 ig.BeginCatchBlock (c.CatchType);
3734
3735                                 if (c.Name != null){
3736                                         vi = c.Block.GetLocalInfo (c.Name);
3737                                         if (vi == null)
3738                                                 throw new Exception ("Variable does not exist in this block");
3739
3740                                         ig.Emit (OpCodes.Stloc, vi.LocalBuilder);
3741                                 } else
3742                                         ig.Emit (OpCodes.Pop);
3743                                 
3744                                 c.Block.Emit (ec);
3745                         }
3746
3747                         if (General != null){
3748                                 ig.BeginCatchBlock (TypeManager.object_type);
3749                                 ig.Emit (OpCodes.Pop);
3750                                 General.Block.Emit (ec);
3751                         }
3752
3753                         DoEmitFinally (ec);
3754                         if (need_exc_block)
3755                                 ig.EndExceptionBlock ();
3756                 }
3757
3758                 public override void EmitFinally (EmitContext ec)
3759                 {
3760                         if (Fini != null){
3761                                 Fini.Emit (ec);
3762                         }
3763                 }
3764         }
3765
3766         public class Using : ExceptionStatement {
3767                 object expression_or_block;
3768                 Statement Statement;
3769                 ArrayList var_list;
3770                 Expression expr;
3771                 Type expr_type;
3772                 Expression conv;
3773                 Expression [] resolved_vars;
3774                 Expression [] converted_vars;
3775                 ExpressionStatement [] assign;
3776                 LocalBuilder local_copy;
3777                 
3778                 public Using (object expression_or_block, Statement stmt, Location l)
3779                 {
3780                         this.expression_or_block = expression_or_block;
3781                         Statement = stmt;
3782                         loc = l;
3783                 }
3784
3785                 //
3786                 // Resolves for the case of using using a local variable declaration.
3787                 //
3788                 bool ResolveLocalVariableDecls (EmitContext ec)
3789                 {
3790                         int i = 0;
3791
3792                         TypeExpr texpr = expr.ResolveAsTypeTerminal (ec);
3793                         if (texpr == null)
3794                                 return false;
3795
3796                         expr_type = texpr.Type;
3797
3798                         //
3799                         // The type must be an IDisposable or an implicit conversion
3800                         // must exist.
3801                         //
3802                         converted_vars = new Expression [var_list.Count];
3803                         resolved_vars = new Expression [var_list.Count];
3804                         assign = new ExpressionStatement [var_list.Count];
3805
3806                         bool need_conv = !TypeManager.ImplementsInterface (
3807                                 expr_type, TypeManager.idisposable_type);
3808
3809                         foreach (DictionaryEntry e in var_list){
3810                                 Expression var = (Expression) e.Key;
3811
3812                                 var = var.ResolveLValue (ec, new EmptyExpression ());
3813                                 if (var == null)
3814                                         return false;
3815
3816                                 resolved_vars [i] = var;
3817
3818                                 if (!need_conv) {
3819                                         i++;
3820                                         continue;
3821                                 }
3822
3823                                 converted_vars [i] = Convert.ImplicitConversionRequired (
3824                                         ec, var, TypeManager.idisposable_type, loc);
3825
3826                                 if (converted_vars [i] == null)
3827                                         return false;
3828
3829                                 i++;
3830                         }
3831
3832                         i = 0;
3833                         foreach (DictionaryEntry e in var_list){
3834                                 Expression var = resolved_vars [i];
3835                                 Expression new_expr = (Expression) e.Value;
3836                                 Expression a;
3837
3838                                 a = new Assign (var, new_expr, loc);
3839                                 a = a.Resolve (ec);
3840                                 if (a == null)
3841                                         return false;
3842
3843                                 if (!need_conv)
3844                                         converted_vars [i] = var;
3845                                 assign [i] = (ExpressionStatement) a;
3846                                 i++;
3847                         }
3848
3849                         return true;
3850                 }
3851
3852                 bool ResolveExpression (EmitContext ec)
3853                 {
3854                         if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)){
3855                                 conv = Convert.ImplicitConversionRequired (
3856                                         ec, expr, TypeManager.idisposable_type, loc);
3857
3858                                 if (conv == null)
3859                                         return false;
3860                         }
3861
3862                         return true;
3863                 }
3864                 
3865                 //
3866                 // Emits the code for the case of using using a local variable declaration.
3867                 //
3868                 void EmitLocalVariableDecls (EmitContext ec)
3869                 {
3870                         ILGenerator ig = ec.ig;
3871                         int i = 0;
3872
3873                         for (i = 0; i < assign.Length; i++) {
3874                                 assign [i].EmitStatement (ec);
3875
3876                                 if (emit_finally)
3877                                         ig.BeginExceptionBlock ();
3878                         }
3879                         Statement.Emit (ec);
3880
3881                         var_list.Reverse ();
3882
3883                         DoEmitFinally (ec);
3884                 }
3885
3886                 void EmitLocalVariableDeclFinally (EmitContext ec)
3887                 {
3888                         ILGenerator ig = ec.ig;
3889
3890                         int i = assign.Length;
3891                         for (int ii = 0; ii < var_list.Count; ++ii){
3892                                 Expression var = resolved_vars [--i];
3893                                 Label skip = ig.DefineLabel ();
3894                                 
3895                                 ig.BeginFinallyBlock ();
3896                                 
3897                                 if (!var.Type.IsValueType) {
3898                                         var.Emit (ec);
3899                                         ig.Emit (OpCodes.Brfalse, skip);
3900                                         converted_vars [i].Emit (ec);
3901                                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3902                                 } else {
3903                                         Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
3904
3905                                         if (!(ml is MethodGroupExpr)) {
3906                                                 var.Emit (ec);
3907                                                 ig.Emit (OpCodes.Box, var.Type);
3908                                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3909                                         } else {
3910                                                 MethodInfo mi = null;
3911
3912                                                 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
3913                                                         if (TypeManager.GetArgumentTypes (mk).Length == 0) {
3914                                                                 mi = mk;
3915                                                                 break;
3916                                                         }
3917                                                 }
3918
3919                                                 if (mi == null) {
3920                                                         Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
3921                                                         return;
3922                                                 }
3923
3924                                                 IMemoryLocation mloc = (IMemoryLocation) var;
3925
3926                                                 mloc.AddressOf (ec, AddressOp.Load);
3927                                                 ig.Emit (OpCodes.Call, mi);
3928                                         }
3929                                 }
3930
3931                                 ig.MarkLabel (skip);
3932
3933                                 if (emit_finally) {
3934                                         ig.EndExceptionBlock ();
3935                                         if (i > 0)
3936                                                 ig.BeginFinallyBlock ();
3937                                 }
3938                         }
3939                 }
3940
3941                 void EmitExpression (EmitContext ec)
3942                 {
3943                         //
3944                         // Make a copy of the expression and operate on that.
3945                         //
3946                         ILGenerator ig = ec.ig;
3947                         local_copy = ig.DeclareLocal (expr_type);
3948                         if (conv != null)
3949                                 conv.Emit (ec);
3950                         else
3951                                 expr.Emit (ec);
3952                         ig.Emit (OpCodes.Stloc, local_copy);
3953
3954                         if (emit_finally)
3955                                 ig.BeginExceptionBlock ();
3956
3957                         Statement.Emit (ec);
3958                         
3959                         DoEmitFinally (ec);
3960                         if (emit_finally)
3961                                 ig.EndExceptionBlock ();
3962                 }
3963
3964                 void EmitExpressionFinally (EmitContext ec)
3965                 {
3966                         ILGenerator ig = ec.ig;
3967                         if (!local_copy.LocalType.IsValueType) {
3968                                 Label skip = ig.DefineLabel ();
3969                                 ig.Emit (OpCodes.Ldloc, local_copy);
3970                                 ig.Emit (OpCodes.Brfalse, skip);
3971                                 ig.Emit (OpCodes.Ldloc, local_copy);
3972                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3973                                 ig.MarkLabel (skip);
3974                         } else {
3975                                 Expression ml = Expression.MemberLookup(ec, TypeManager.idisposable_type, local_copy.LocalType, "Dispose", Mono.CSharp.Location.Null);
3976
3977                                 if (!(ml is MethodGroupExpr)) {
3978                                         ig.Emit (OpCodes.Ldloc, local_copy);
3979                                         ig.Emit (OpCodes.Box, local_copy.LocalType);
3980                                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
3981                                 } else {
3982                                         MethodInfo mi = null;
3983
3984                                         foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
3985                                                 if (TypeManager.GetArgumentTypes (mk).Length == 0) {
3986                                                         mi = mk;
3987                                                         break;
3988                                                 }
3989                                         }
3990
3991                                         if (mi == null) {
3992                                                 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
3993                                                 return;
3994                                         }
3995
3996                                         ig.Emit (OpCodes.Ldloca, local_copy);
3997                                         ig.Emit (OpCodes.Call, mi);
3998                                 }
3999                         }
4000                 }
4001                 
4002                 public override bool Resolve (EmitContext ec)
4003                 {
4004                         if (expression_or_block is DictionaryEntry){
4005                                 expr = (Expression) ((DictionaryEntry) expression_or_block).Key;
4006                                 var_list = (ArrayList)((DictionaryEntry)expression_or_block).Value;
4007
4008                                 if (!ResolveLocalVariableDecls (ec))
4009                                         return false;
4010
4011                         } else if (expression_or_block is Expression){
4012                                 expr = (Expression) expression_or_block;
4013
4014                                 expr = expr.Resolve (ec);
4015                                 if (expr == null)
4016                                         return false;
4017
4018                                 expr_type = expr.Type;
4019
4020                                 if (!ResolveExpression (ec))
4021                                         return false;
4022                         }
4023
4024                         FlowBranchingException branching = ec.StartFlowBranching (this);
4025
4026                         bool ok = Statement.Resolve (ec);
4027
4028                         if (!ok) {
4029                                 ec.KillFlowBranching ();
4030                                 return false;
4031                         }
4032
4033                         ResolveFinally (branching);                                     
4034                         FlowBranching.Reachability reachability = ec.EndFlowBranching ();
4035
4036                         if (reachability.Returns != FlowBranching.FlowReturns.Always) {
4037                                 // Unfortunately, System.Reflection.Emit automatically emits a leave
4038                                 // to the end of the finally block.  This is a problem if `returns'
4039                                 // is true since we may jump to a point after the end of the method.
4040                                 // As a workaround, emit an explicit ret here.
4041                                 ec.NeedReturnLabel ();
4042                         }
4043
4044                         return true;
4045                 }
4046                 
4047                 protected override void DoEmit (EmitContext ec)
4048                 {
4049                         if (expression_or_block is DictionaryEntry)
4050                                 EmitLocalVariableDecls (ec);
4051                         else if (expression_or_block is Expression)
4052                                 EmitExpression (ec);
4053                 }
4054
4055                 public override void EmitFinally (EmitContext ec)
4056                 {
4057                         if (expression_or_block is DictionaryEntry)
4058                                 EmitLocalVariableDeclFinally (ec);
4059                         else if (expression_or_block is Expression)
4060                                 EmitExpressionFinally (ec);
4061                 }
4062         }
4063
4064         /// <summary>
4065         ///   Implementation of the foreach C# statement
4066         /// </summary>
4067         public class Foreach : ExceptionStatement {
4068                 Expression type;
4069                 Expression variable;
4070                 Expression expr;
4071                 Statement statement;
4072                 ForeachHelperMethods hm;
4073                 Expression empty, conv;
4074                 Type array_type, element_type;
4075                 Type var_type;
4076                 VariableStorage enumerator;
4077                 
4078                 public Foreach (Expression type, LocalVariableReference var, Expression expr,
4079                                 Statement stmt, Location l)
4080                 {
4081                         this.type = type;
4082                         this.variable = var;
4083                         this.expr = expr;
4084                         statement = stmt;
4085                         loc = l;
4086                 }
4087                 
4088                 public override bool Resolve (EmitContext ec)
4089                 {
4090                         expr = expr.Resolve (ec);
4091                         if (expr == null)
4092                                 return false;
4093
4094                         TypeExpr texpr = type.ResolveAsTypeTerminal (ec);
4095                         if (texpr == null)
4096                                 return false;
4097                         
4098                         var_type = texpr.Type;
4099
4100                         //
4101                         // We need an instance variable.  Not sure this is the best
4102                         // way of doing this.
4103                         //
4104                         // FIXME: When we implement propertyaccess, will those turn
4105                         // out to return values in ExprClass?  I think they should.
4106                         //
4107                         if (!(expr.eclass == ExprClass.Variable || expr.eclass == ExprClass.Value ||
4108                               expr.eclass == ExprClass.PropertyAccess || expr.eclass == ExprClass.IndexerAccess)){
4109                                 error1579 (expr.Type);
4110                                 return false;
4111                         }
4112
4113                         if (expr.Type.IsArray) {
4114                                 array_type = expr.Type;
4115                                 element_type = TypeManager.GetElementType (array_type);
4116
4117                                 empty = new EmptyExpression (element_type);
4118                         } else {
4119                                 hm = ProbeCollectionType (ec, expr.Type);
4120                                 if (hm == null){
4121                                         error1579 (expr.Type);
4122                                         return false;
4123                                 }                       
4124
4125                                 array_type = expr.Type;
4126                                 element_type = hm.element_type;
4127
4128                                 empty = new EmptyExpression (hm.element_type);
4129                         }
4130
4131                         bool ok = true;
4132
4133                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
4134                         ec.CurrentBranching.CreateSibling ();
4135
4136                         //
4137                         //
4138                         // FIXME: maybe we can apply the same trick we do in the
4139                         // array handling to avoid creating empty and conv in some cases.
4140                         //
4141                         // Although it is not as important in this case, as the type
4142                         // will not likely be object (what the enumerator will return).
4143                         //
4144                         conv = Convert.ExplicitConversion (ec, empty, var_type, loc);
4145                         if (conv == null)
4146                                 ok = false;
4147
4148                         variable = variable.ResolveLValue (ec, empty);
4149                         if (variable == null)
4150                                 ok = false;
4151
4152                         bool disposable = (hm != null) && hm.is_disposable;
4153                         FlowBranchingException branching = null;
4154                         if (disposable)
4155                                 branching = ec.StartFlowBranching (this);
4156
4157                         if (!statement.Resolve (ec))
4158                                 ok = false;
4159
4160                         if (disposable) {
4161                                 ResolveFinally (branching);
4162                                 ec.EndFlowBranching ();
4163                         } else
4164                                 emit_finally = true;
4165
4166                         ec.EndFlowBranching ();
4167
4168                         return ok;
4169                 }
4170                 
4171                 //
4172                 // Retrieves a `public bool MoveNext ()' method from the Type `t'
4173                 //
4174                 static MethodInfo FetchMethodMoveNext (Type t)
4175                 {
4176                         MemberList move_next_list;
4177                         
4178                         move_next_list = TypeContainer.FindMembers (
4179                                 t, MemberTypes.Method,
4180                                 BindingFlags.Public | BindingFlags.Instance,
4181                                 Type.FilterName, "MoveNext");
4182                         if (move_next_list.Count == 0)
4183                                 return null;
4184
4185                         foreach (MemberInfo m in move_next_list){
4186                                 MethodInfo mi = (MethodInfo) m;
4187                                 Type [] args;
4188                                 
4189                                 args = TypeManager.GetArgumentTypes (mi);
4190                                 if (args != null && args.Length == 0){
4191                                         if (TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type)
4192                                                 return mi;
4193                                 }
4194                         }
4195                         return null;
4196                 }
4197                 
4198                 //
4199                 // Retrieves a `public T get_Current ()' method from the Type `t'
4200                 //
4201                 static MethodInfo FetchMethodGetCurrent (Type t)
4202                 {
4203                         MemberList get_current_list;
4204
4205                         get_current_list = TypeContainer.FindMembers (
4206                                 t, MemberTypes.Method,
4207                                 BindingFlags.Public | BindingFlags.Instance,
4208                                 Type.FilterName, "get_Current");
4209                         if (get_current_list.Count == 0)
4210                                 return null;
4211
4212                         foreach (MemberInfo m in get_current_list){
4213                                 MethodInfo mi = (MethodInfo) m;
4214                                 Type [] args;
4215
4216                                 args = TypeManager.GetArgumentTypes (mi);
4217                                 if (args != null && args.Length == 0)
4218                                         return mi;
4219                         }
4220                         return null;
4221                 }
4222
4223                 // 
4224                 // Retrieves a `public void Dispose ()' method from the Type `t'
4225                 //
4226                 static MethodInfo FetchMethodDispose (Type t)
4227                 {
4228                         MemberList dispose_list;
4229                         
4230                         dispose_list = TypeContainer.FindMembers (
4231                                 t, MemberTypes.Method,
4232                                 BindingFlags.Public | BindingFlags.Instance,
4233                                 Type.FilterName, "Dispose");
4234                         if (dispose_list.Count == 0)
4235                                 return null;
4236
4237                         foreach (MemberInfo m in dispose_list){
4238                                 MethodInfo mi = (MethodInfo) m;
4239                                 Type [] args;
4240                                 
4241                                 args = TypeManager.GetArgumentTypes (mi);
4242                                 if (args != null && args.Length == 0){
4243                                         if (mi.ReturnType == TypeManager.void_type)
4244                                                 return mi;
4245                                 }
4246                         }
4247                         return null;
4248                 }
4249
4250                 // 
4251                 // This struct records the helper methods used by the Foreach construct
4252                 //
4253                 class ForeachHelperMethods {
4254                         public EmitContext ec;
4255                         public MethodInfo get_enumerator;
4256                         public MethodInfo move_next;
4257                         public MethodInfo get_current;
4258                         public Type element_type;
4259                         public Type enumerator_type;
4260                         public bool is_disposable;
4261
4262                         public ForeachHelperMethods (EmitContext ec)
4263                         {
4264                                 this.ec = ec;
4265                                 this.element_type = TypeManager.object_type;
4266                                 this.enumerator_type = TypeManager.ienumerator_type;
4267                                 this.is_disposable = true;
4268                         }
4269                 }
4270                 
4271                 static bool GetEnumeratorFilter (MemberInfo m, object criteria)
4272                 {
4273                         if (m == null)
4274                                 return false;
4275
4276                         if (!(m is MethodInfo))
4277                                 return false;
4278                         
4279                         if (m.Name != "GetEnumerator")
4280                                 return false;
4281
4282                         MethodInfo mi = (MethodInfo) m;
4283                         Type [] args = TypeManager.GetArgumentTypes (mi);
4284                         if (args != null){
4285                                 if (args.Length != 0)
4286                                         return false;
4287                         }
4288                         ForeachHelperMethods hm = (ForeachHelperMethods) criteria;
4289
4290                         // Check whether GetEnumerator is public
4291                         if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
4292                                         return false;
4293
4294                         if ((mi.ReturnType == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
4295                                 //
4296                                 // Apply the same optimization as MS: skip the GetEnumerator
4297                                 // returning an IEnumerator, and use the one returning a 
4298                                 // CharEnumerator instead. This allows us to avoid the 
4299                                 // try-finally block and the boxing.
4300                                 //
4301                                 return false;
4302
4303                         //
4304                         // Ok, we can access it, now make sure that we can do something
4305                         // with this `GetEnumerator'
4306                         //
4307
4308                         Type return_type = mi.ReturnType;
4309                         if (mi.ReturnType == TypeManager.ienumerator_type ||
4310                             TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
4311                             (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
4312                                 
4313                                 //
4314                                 // If it is not an interface, lets try to find the methods ourselves.
4315                                 // For example, if we have:
4316                                 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
4317                                 // We can avoid the iface call. This is a runtime perf boost.
4318                                 // even bigger if we have a ValueType, because we avoid the cost
4319                                 // of boxing.
4320                                 //
4321                                 // We have to make sure that both methods exist for us to take
4322                                 // this path. If one of the methods does not exist, we will just
4323                                 // use the interface. Sadly, this complex if statement is the only
4324                                 // way I could do this without a goto
4325                                 //
4326                                 
4327                                 if (return_type.IsInterface ||
4328                                     (hm.move_next = FetchMethodMoveNext (return_type)) == null ||
4329                                     (hm.get_current = FetchMethodGetCurrent (return_type)) == null) {
4330                                         
4331                                         hm.move_next = TypeManager.bool_movenext_void;
4332                                         hm.get_current = TypeManager.object_getcurrent_void;
4333                                         return true;    
4334                                 }
4335
4336                         } else {
4337
4338                                 //
4339                                 // Ok, so they dont return an IEnumerable, we will have to
4340                                 // find if they support the GetEnumerator pattern.
4341                                 //
4342                                 
4343                                 hm.move_next = FetchMethodMoveNext (return_type);
4344                                 if (hm.move_next == null)
4345                                         return false;
4346                                 
4347                                 hm.get_current = FetchMethodGetCurrent (return_type);
4348                                 if (hm.get_current == null)
4349                                         return false;
4350                         }
4351                         
4352                         hm.element_type = hm.get_current.ReturnType;
4353                         hm.enumerator_type = return_type;
4354                         hm.is_disposable = !hm.enumerator_type.IsSealed ||
4355                                 TypeManager.ImplementsInterface (
4356                                         hm.enumerator_type, TypeManager.idisposable_type);
4357
4358                         return true;
4359                 }
4360                 
4361                 /// <summary>
4362                 ///   This filter is used to find the GetEnumerator method
4363                 ///   on which IEnumerator operates
4364                 /// </summary>
4365                 static MemberFilter FilterEnumerator;
4366                 
4367                 static Foreach ()
4368                 {
4369                         FilterEnumerator = new MemberFilter (GetEnumeratorFilter);
4370                 }
4371
4372                 void error1579 (Type t)
4373                 {
4374                         Report.Error (1579, loc,
4375                                       "foreach statement cannot operate on variables of type `" +
4376                                       t.FullName + "' because that class does not provide a " +
4377                                       " GetEnumerator method or it is inaccessible");
4378                 }
4379
4380                 static bool TryType (Type t, ForeachHelperMethods hm)
4381                 {
4382                         MemberList mi;
4383                         
4384                         mi = TypeContainer.FindMembers (t, MemberTypes.Method,
4385                                                         BindingFlags.Public | BindingFlags.NonPublic |
4386                                                         BindingFlags.Instance | BindingFlags.DeclaredOnly,
4387                                                         FilterEnumerator, hm);
4388
4389                         if (mi.Count == 0)
4390                                 return false;
4391
4392                         hm.get_enumerator = (MethodInfo) mi [0];
4393                         return true;    
4394                 }
4395                 
4396                 //
4397                 // Looks for a usable GetEnumerator in the Type, and if found returns
4398                 // the three methods that participate: GetEnumerator, MoveNext and get_Current
4399                 //
4400                 ForeachHelperMethods ProbeCollectionType (EmitContext ec, Type t)
4401                 {
4402                         ForeachHelperMethods hm = new ForeachHelperMethods (ec);
4403
4404                         for (Type tt = t; tt != null && tt != TypeManager.object_type;){
4405                                 if (TryType (tt, hm))
4406                                         return hm;
4407                                 tt = tt.BaseType;
4408                         }
4409
4410                         //
4411                         // Now try to find the method in the interfaces
4412                         //
4413                         while (t != null){
4414                                 Type [] ifaces = t.GetInterfaces ();
4415
4416                                 foreach (Type i in ifaces){
4417                                         if (TryType (i, hm))
4418                                                 return hm;
4419                                 }
4420                                 
4421                                 //
4422                                 // Since TypeBuilder.GetInterfaces only returns the interface
4423                                 // types for this type, we have to keep looping, but once
4424                                 // we hit a non-TypeBuilder (ie, a Type), then we know we are
4425                                 // done, because it returns all the types
4426                                 //
4427                                 if ((t is TypeBuilder))
4428                                         t = t.BaseType;
4429                                 else
4430                                         break;
4431                         } 
4432
4433                         return null;
4434                 }
4435
4436                 //
4437                 // FIXME: possible optimization.
4438                 // We might be able to avoid creating `empty' if the type is the sam
4439                 //
4440                 bool EmitCollectionForeach (EmitContext ec)
4441                 {
4442                         ILGenerator ig = ec.ig;
4443
4444                         enumerator = new VariableStorage (ec, hm.enumerator_type);
4445                         enumerator.EmitThis (ig);
4446                         //
4447                         // Instantiate the enumerator
4448                         //
4449                         if (expr.Type.IsValueType){
4450                                 IMemoryLocation ml = expr as IMemoryLocation;
4451                                 // Load the address of the value type.
4452                                 if (ml == null) {
4453                                         // This happens if, for example, you have a property
4454                                         // returning a struct which is IEnumerable
4455                                         LocalBuilder t = ec.GetTemporaryLocal (expr.Type);
4456                                                 expr.Emit(ec);
4457                                         ig.Emit (OpCodes.Stloc, t);
4458                                         ig.Emit (OpCodes.Ldloca, t);
4459                                         ec.FreeTemporaryLocal (t, expr.Type);
4460                                         } else {
4461                                                 ml.AddressOf (ec, AddressOp.Load);
4462                                         }
4463                                 
4464                                 // Emit the call.
4465                                 if (hm.get_enumerator.DeclaringType.IsValueType) {
4466                                         // the method is declared on the value type
4467                                         ig.Emit (OpCodes.Call, hm.get_enumerator);
4468                                 } else {
4469                                         // it is an interface method, so we must box
4470                                         ig.Emit (OpCodes.Box, expr.Type);
4471                                 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
4472                                 }
4473                         } else {
4474                                 expr.Emit (ec);
4475                                 ig.Emit (OpCodes.Callvirt, hm.get_enumerator);
4476                         }
4477                         enumerator.EmitStore (ig);
4478
4479                         //
4480                         // Protect the code in a try/finalize block, so that
4481                         // if the beast implement IDisposable, we get rid of it
4482                         //
4483                         if (hm.is_disposable && emit_finally)
4484                                 ig.BeginExceptionBlock ();
4485                         
4486                         Label end_try = ig.DefineLabel ();
4487                         
4488                         ig.MarkLabel (ec.LoopBegin);
4489                         
4490                         enumerator.EmitCall (ig, hm.move_next);
4491                         
4492                         ig.Emit (OpCodes.Brfalse, end_try);
4493
4494                         if (ec.InIterator)
4495                                 ig.Emit (OpCodes.Ldarg_0);
4496                         
4497                         enumerator.EmitCall (ig, hm.get_current);
4498
4499                         if (ec.InIterator){
4500                                 conv.Emit (ec);
4501                                 ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
4502                         } else 
4503                                 ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
4504                                 
4505                         statement.Emit (ec);
4506                         ig.Emit (OpCodes.Br, ec.LoopBegin);
4507                         ig.MarkLabel (end_try);
4508                         
4509                         // The runtime provides this for us.
4510                         // ig.Emit (OpCodes.Leave, end);
4511
4512                         //
4513                         // Now the finally block
4514                         //
4515                         if (hm.is_disposable) {
4516                                 DoEmitFinally (ec);
4517                                 if (emit_finally)
4518                                         ig.EndExceptionBlock ();
4519                         }
4520
4521                         ig.MarkLabel (ec.LoopEnd);
4522                         return false;
4523                 }
4524
4525                 public override void EmitFinally (EmitContext ec)
4526                 {
4527                         ILGenerator ig = ec.ig;
4528
4529                         if (hm.enumerator_type.IsValueType) {
4530                                 enumerator.EmitThis (ig);
4531
4532                                 MethodInfo mi = FetchMethodDispose (hm.enumerator_type);
4533                                 if (mi != null) {
4534                                         enumerator.EmitLoadAddress (ig);
4535                                         ig.Emit (OpCodes.Call, mi);
4536                                 } else {
4537                                         enumerator.EmitLoad (ig);
4538                                         ig.Emit (OpCodes.Box, hm.enumerator_type);
4539                                         ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4540                                 }
4541                         } else {
4542                                 Label call_dispose = ig.DefineLabel ();
4543
4544                                 enumerator.EmitThis (ig);
4545                                 enumerator.EmitLoad (ig);
4546                                 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
4547                                 ig.Emit (OpCodes.Dup);
4548                                 ig.Emit (OpCodes.Brtrue_S, call_dispose);
4549                                 ig.Emit (OpCodes.Pop);
4550
4551                                 Label end_finally = ig.DefineLabel ();
4552                                 ig.Emit (OpCodes.Br, end_finally);
4553
4554                                 ig.MarkLabel (call_dispose);
4555                                 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4556                                 ig.MarkLabel (end_finally);
4557
4558                                 if (emit_finally)
4559                                         ig.Emit (OpCodes.Endfinally);
4560                         }
4561                 }
4562
4563                 //
4564                 // FIXME: possible optimization.
4565                 // We might be able to avoid creating `empty' if the type is the sam
4566                 //
4567                 bool EmitArrayForeach (EmitContext ec)
4568                 {
4569                         int rank = array_type.GetArrayRank ();
4570                         ILGenerator ig = ec.ig;
4571
4572                         VariableStorage copy = new VariableStorage (ec, array_type);
4573                         
4574                         //
4575                         // Make our copy of the array
4576                         //
4577                         copy.EmitThis (ig);
4578                         expr.Emit (ec);
4579                         copy.EmitStore (ig);
4580                         
4581                         if (rank == 1){
4582                                 VariableStorage counter = new VariableStorage (ec,TypeManager.int32_type);
4583
4584                                 Label loop, test;
4585
4586                                 counter.EmitThis (ig);
4587                                 ig.Emit (OpCodes.Ldc_I4_0);
4588                                 counter.EmitStore (ig);
4589                                 test = ig.DefineLabel ();
4590                                 ig.Emit (OpCodes.Br, test);
4591
4592                                 loop = ig.DefineLabel ();
4593                                 ig.MarkLabel (loop);
4594
4595                                 if (ec.InIterator)
4596                                         ig.Emit (OpCodes.Ldarg_0);
4597                                 
4598                                 copy.EmitThis (ig);
4599                                 copy.EmitLoad (ig);
4600                                 counter.EmitThis (ig);
4601                                 counter.EmitLoad (ig);
4602
4603                                 //
4604                                 // Load the value, we load the value using the underlying type,
4605                                 // then we use the variable.EmitAssign to load using the proper cast.
4606                                 //
4607                                 ArrayAccess.EmitLoadOpcode (ig, element_type);
4608                                 if (ec.InIterator){
4609                                         conv.Emit (ec);
4610                                         ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
4611                                 } else 
4612                                         ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
4613
4614                                 statement.Emit (ec);
4615
4616                                 ig.MarkLabel (ec.LoopBegin);
4617                                 counter.EmitThis (ig);
4618                                 counter.EmitThis (ig);
4619                                 counter.EmitLoad (ig);
4620                                 ig.Emit (OpCodes.Ldc_I4_1);
4621                                 ig.Emit (OpCodes.Add);
4622                                 counter.EmitStore (ig);
4623
4624                                 ig.MarkLabel (test);
4625                                 counter.EmitThis (ig);
4626                                 counter.EmitLoad (ig);
4627                                 copy.EmitThis (ig);
4628                                 copy.EmitLoad (ig);
4629                                 ig.Emit (OpCodes.Ldlen);
4630                                 ig.Emit (OpCodes.Conv_I4);
4631                                 ig.Emit (OpCodes.Blt, loop);
4632                         } else {
4633                                 VariableStorage [] dim_len   = new VariableStorage [rank];
4634                                 VariableStorage [] dim_count = new VariableStorage [rank];
4635                                 Label [] loop = new Label [rank];
4636                                 Label [] test = new Label [rank];
4637                                 int dim;
4638                                 
4639                                 for (dim = 0; dim < rank; dim++){
4640                                         dim_len [dim] = new VariableStorage (ec, TypeManager.int32_type);
4641                                         dim_count [dim] = new VariableStorage (ec, TypeManager.int32_type);
4642                                         test [dim] = ig.DefineLabel ();
4643                                         loop [dim] = ig.DefineLabel ();
4644                                 }
4645                                         
4646                                 for (dim = 0; dim < rank; dim++){
4647                                         dim_len [dim].EmitThis (ig);
4648                                         copy.EmitThis (ig);
4649                                         copy.EmitLoad (ig);
4650                                         IntLiteral.EmitInt (ig, dim);
4651                                         ig.Emit (OpCodes.Callvirt, TypeManager.int_getlength_int);
4652                                         dim_len [dim].EmitStore (ig);
4653                                         
4654                                 }
4655
4656                                 for (dim = 0; dim < rank; dim++){
4657                                         dim_count [dim].EmitThis (ig);
4658                                         ig.Emit (OpCodes.Ldc_I4_0);
4659                                         dim_count [dim].EmitStore (ig);
4660                                         ig.Emit (OpCodes.Br, test [dim]);
4661                                         ig.MarkLabel (loop [dim]);
4662                                 }
4663
4664                                 if (ec.InIterator)
4665                                         ig.Emit (OpCodes.Ldarg_0);
4666                                 
4667                                 copy.EmitThis (ig);
4668                                 copy.EmitLoad (ig);
4669                                 for (dim = 0; dim < rank; dim++){
4670                                         dim_count [dim].EmitThis (ig);
4671                                         dim_count [dim].EmitLoad (ig);
4672                                 }
4673
4674                                 //
4675                                 // FIXME: Maybe we can cache the computation of `get'?
4676                                 //
4677                                 Type [] args = new Type [rank];
4678                                 MethodInfo get;
4679
4680                                 for (int i = 0; i < rank; i++)
4681                                         args [i] = TypeManager.int32_type;
4682
4683                                 ModuleBuilder mb = CodeGen.Module.Builder;
4684                                 get = mb.GetArrayMethod (
4685                                         array_type, "Get",
4686                                         CallingConventions.HasThis| CallingConventions.Standard,
4687                                         var_type, args);
4688                                 ig.Emit (OpCodes.Call, get);
4689                                 if (ec.InIterator){
4690                                         conv.Emit (ec);
4691                                         ig.Emit (OpCodes.Stfld, ((LocalVariableReference) variable).local_info.FieldBuilder);
4692                                 } else 
4693                                         ((IAssignMethod)variable).EmitAssign (ec, conv, false, false);
4694                                 statement.Emit (ec);
4695                                 ig.MarkLabel (ec.LoopBegin);
4696                                 for (dim = rank - 1; dim >= 0; dim--){
4697                                         dim_count [dim].EmitThis (ig);
4698                                         dim_count [dim].EmitThis (ig);
4699                                         dim_count [dim].EmitLoad (ig);
4700                                         ig.Emit (OpCodes.Ldc_I4_1);
4701                                         ig.Emit (OpCodes.Add);
4702                                         dim_count [dim].EmitStore (ig);
4703
4704                                         ig.MarkLabel (test [dim]);
4705                                         dim_count [dim].EmitThis (ig);
4706                                         dim_count [dim].EmitLoad (ig);
4707                                         dim_len [dim].EmitThis (ig);
4708                                         dim_len [dim].EmitLoad (ig);
4709                                         ig.Emit (OpCodes.Blt, loop [dim]);
4710                                 }
4711                         }
4712                         ig.MarkLabel (ec.LoopEnd);
4713                         
4714                         return false;
4715                 }
4716                 
4717                 protected override void DoEmit (EmitContext ec)
4718                 {
4719                         ILGenerator ig = ec.ig;
4720                         
4721                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
4722                         ec.LoopBegin = ig.DefineLabel ();
4723                         ec.LoopEnd = ig.DefineLabel ();
4724                         
4725                         if (hm != null)
4726                                 EmitCollectionForeach (ec);
4727                         else
4728                                 EmitArrayForeach (ec);
4729                         
4730                         ec.LoopBegin = old_begin;
4731                         ec.LoopEnd = old_end;
4732                 }
4733         }
4734 }