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