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