Merge branch 'master' of github.com:mono/mono
[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@ximian.com)
7 //   Marek Safar (marek.safar@seznam.cz)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 //
12
13 using System;
14 using System.Reflection.Emit;
15 using System.Collections.Generic;
16
17 namespace Mono.CSharp {
18         
19         public abstract class Statement {
20                 public Location loc;
21                 
22                 /// <summary>
23                 ///   Resolves the statement, true means that all sub-statements
24                 ///   did resolve ok.
25                 //  </summary>
26                 public virtual bool Resolve (BlockContext ec)
27                 {
28                         return true;
29                 }
30
31                 /// <summary>
32                 ///   We already know that the statement is unreachable, but we still
33                 ///   need to resolve it to catch errors.
34                 /// </summary>
35                 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
36                 {
37                         //
38                         // This conflicts with csc's way of doing this, but IMHO it's
39                         // the right thing to do.
40                         //
41                         // If something is unreachable, we still check whether it's
42                         // correct.  This means that you cannot use unassigned variables
43                         // in unreachable code, for instance.
44                         //
45
46                         if (warn)
47                                 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
48
49                         ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
50                         bool ok = Resolve (ec);
51                         ec.KillFlowBranching ();
52
53                         return ok;
54                 }
55                                 
56                 /// <summary>
57                 ///   Return value indicates whether all code paths emitted return.
58                 /// </summary>
59                 protected abstract void DoEmit (EmitContext ec);
60
61                 public virtual void Emit (EmitContext ec)
62                 {
63                         ec.Mark (loc);
64                         DoEmit (ec);
65                 }
66
67                 //
68                 // This routine must be overrided in derived classes and make copies
69                 // of all the data that might be modified if resolved
70                 // 
71                 protected abstract void CloneTo (CloneContext clonectx, Statement target);
72
73                 public Statement Clone (CloneContext clonectx)
74                 {
75                         Statement s = (Statement) this.MemberwiseClone ();
76                         CloneTo (clonectx, s);
77                         return s;
78                 }
79
80                 public virtual Expression CreateExpressionTree (ResolveContext ec)
81                 {
82                         ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
83                         return null;
84                 }
85
86                 public Statement PerformClone ()
87                 {
88                         CloneContext clonectx = new CloneContext ();
89
90                         return Clone (clonectx);
91                 }
92         }
93
94         public sealed class EmptyStatement : Statement
95         {
96                 public EmptyStatement (Location loc)
97                 {
98                         this.loc = loc;
99                 }
100                 
101                 public override bool Resolve (BlockContext ec)
102                 {
103                         return true;
104                 }
105
106                 public override bool ResolveUnreachable (BlockContext ec, bool warn)
107                 {
108                         return true;
109                 }
110
111                 public override void Emit (EmitContext ec)
112                 {
113                 }
114
115                 protected override void DoEmit (EmitContext ec)
116                 {
117                         throw new NotSupportedException ();
118                 }
119
120                 protected override void CloneTo (CloneContext clonectx, Statement target)
121                 {
122                         // nothing needed.
123                 }
124         }
125         
126         public class If : Statement {
127                 Expression expr;
128                 public Statement TrueStatement;
129                 public Statement FalseStatement;
130
131                 bool is_true_ret;
132
133                 public If (Expression bool_expr, Statement true_statement, Location l)
134                         : this (bool_expr, true_statement, null, l)
135                 {
136                 }
137
138                 public If (Expression bool_expr,
139                            Statement true_statement,
140                            Statement false_statement,
141                            Location l)
142                 {
143                         this.expr = bool_expr;
144                         TrueStatement = true_statement;
145                         FalseStatement = false_statement;
146                         loc = l;
147                 }
148
149                 public override bool Resolve (BlockContext ec)
150                 {
151                         bool ok = true;
152
153                         Report.Debug (1, "START IF BLOCK", loc);
154
155                         expr = expr.Resolve (ec);
156                         if (expr == null) {
157                                 ok = false;
158                         } else {
159                                 //
160                                 // Dead code elimination
161                                 //
162                                 if (expr is Constant) {
163                                         bool take = !((Constant) expr).IsDefaultValue;
164
165                                         if (take) {
166                                                 if (!TrueStatement.Resolve (ec))
167                                                         return false;
168
169                                                 if ((FalseStatement != null) &&
170                                                         !FalseStatement.ResolveUnreachable (ec, true))
171                                                         return false;
172                                                 FalseStatement = null;
173                                         } else {
174                                                 if (!TrueStatement.ResolveUnreachable (ec, true))
175                                                         return false;
176                                                 TrueStatement = null;
177
178                                                 if ((FalseStatement != null) &&
179                                                         !FalseStatement.Resolve (ec))
180                                                         return false;
181                                         }
182
183                                         return true;
184                                 }
185                         }
186
187                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
188                         
189                         ok &= TrueStatement.Resolve (ec);
190
191                         is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
192
193                         ec.CurrentBranching.CreateSibling ();
194
195                         if (FalseStatement != null)
196                                 ok &= FalseStatement.Resolve (ec);
197                                         
198                         ec.EndFlowBranching ();
199
200                         Report.Debug (1, "END IF BLOCK", loc);
201
202                         return ok;
203                 }
204                 
205                 protected override void DoEmit (EmitContext ec)
206                 {
207                         Label false_target = ec.DefineLabel ();
208                         Label end;
209
210                         //
211                         // If we're a boolean constant, Resolve() already
212                         // eliminated dead code for us.
213                         //
214                         Constant c = expr as Constant;
215                         if (c != null){
216                                 c.EmitSideEffect (ec);
217
218                                 if (!c.IsDefaultValue)
219                                         TrueStatement.Emit (ec);
220                                 else if (FalseStatement != null)
221                                         FalseStatement.Emit (ec);
222
223                                 return;
224                         }                       
225                         
226                         expr.EmitBranchable (ec, false_target, false);
227                         
228                         TrueStatement.Emit (ec);
229
230                         if (FalseStatement != null){
231                                 bool branch_emitted = false;
232                                 
233                                 end = ec.DefineLabel ();
234                                 if (!is_true_ret){
235                                         ec.Emit (OpCodes.Br, end);
236                                         branch_emitted = true;
237                                 }
238
239                                 ec.MarkLabel (false_target);
240                                 FalseStatement.Emit (ec);
241
242                                 if (branch_emitted)
243                                         ec.MarkLabel (end);
244                         } else {
245                                 ec.MarkLabel (false_target);
246                         }
247                 }
248
249                 protected override void CloneTo (CloneContext clonectx, Statement t)
250                 {
251                         If target = (If) t;
252
253                         target.expr = expr.Clone (clonectx);
254                         target.TrueStatement = TrueStatement.Clone (clonectx);
255                         if (FalseStatement != null)
256                                 target.FalseStatement = FalseStatement.Clone (clonectx);
257                 }
258         }
259
260         public class Do : Statement {
261                 public Expression expr;
262                 public Statement  EmbeddedStatement;
263
264                 public Do (Statement statement, BooleanExpression bool_expr, Location l)
265                 {
266                         expr = bool_expr;
267                         EmbeddedStatement = statement;
268                         loc = l;
269                 }
270
271                 public override bool Resolve (BlockContext ec)
272                 {
273                         bool ok = true;
274
275                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
276
277                         bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
278
279                         ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
280                         if (!EmbeddedStatement.Resolve (ec))
281                                 ok = false;
282                         ec.EndFlowBranching ();
283
284                         if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
285                                 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
286
287                         expr = expr.Resolve (ec);
288                         if (expr == null)
289                                 ok = false;
290                         else if (expr is Constant){
291                                 bool infinite = !((Constant) expr).IsDefaultValue;
292                                 if (infinite)
293                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
294                         }
295
296                         ec.EndFlowBranching ();
297
298                         return ok;
299                 }
300                 
301                 protected override void DoEmit (EmitContext ec)
302                 {
303                         Label loop = ec.DefineLabel ();
304                         Label old_begin = ec.LoopBegin;
305                         Label old_end = ec.LoopEnd;
306                         
307                         ec.LoopBegin = ec.DefineLabel ();
308                         ec.LoopEnd = ec.DefineLabel ();
309                                 
310                         ec.MarkLabel (loop);
311                         EmbeddedStatement.Emit (ec);
312                         ec.MarkLabel (ec.LoopBegin);
313
314                         //
315                         // Dead code elimination
316                         //
317                         if (expr is Constant){
318                                 bool res = !((Constant) expr).IsDefaultValue;
319
320                                 expr.EmitSideEffect (ec);
321                                 if (res)
322                                         ec.Emit (OpCodes.Br, loop); 
323                         } else
324                                 expr.EmitBranchable (ec, loop, true);
325                         
326                         ec.MarkLabel (ec.LoopEnd);
327
328                         ec.LoopBegin = old_begin;
329                         ec.LoopEnd = old_end;
330                 }
331
332                 protected override void CloneTo (CloneContext clonectx, Statement t)
333                 {
334                         Do target = (Do) t;
335
336                         target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
337                         target.expr = expr.Clone (clonectx);
338                 }
339         }
340
341         public class While : Statement {
342                 public Expression expr;
343                 public Statement Statement;
344                 bool infinite, empty;
345
346                 public While (BooleanExpression bool_expr, Statement statement, Location l)
347                 {
348                         this.expr = bool_expr;
349                         Statement = statement;
350                         loc = l;
351                 }
352
353                 public override bool Resolve (BlockContext ec)
354                 {
355                         bool ok = true;
356
357                         expr = expr.Resolve (ec);
358                         if (expr == null)
359                                 ok = false;
360
361                         //
362                         // Inform whether we are infinite or not
363                         //
364                         if (expr is Constant){
365                                 bool value = !((Constant) expr).IsDefaultValue;
366
367                                 if (value == false){
368                                         if (!Statement.ResolveUnreachable (ec, true))
369                                                 return false;
370                                         empty = true;
371                                         return true;
372                                 } else
373                                         infinite = true;
374                         }
375
376                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
377                         if (!infinite)
378                                 ec.CurrentBranching.CreateSibling ();
379
380                         ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
381                         if (!Statement.Resolve (ec))
382                                 ok = false;
383                         ec.EndFlowBranching ();
384
385                         // There's no direct control flow from the end of the embedded statement to the end of the loop
386                         ec.CurrentBranching.CurrentUsageVector.Goto ();
387
388                         ec.EndFlowBranching ();
389
390                         return ok;
391                 }
392                 
393                 protected override void DoEmit (EmitContext ec)
394                 {
395                         if (empty) {
396                                 expr.EmitSideEffect (ec);
397                                 return;
398                         }
399
400                         Label old_begin = ec.LoopBegin;
401                         Label old_end = ec.LoopEnd;
402                         
403                         ec.LoopBegin = ec.DefineLabel ();
404                         ec.LoopEnd = ec.DefineLabel ();
405
406                         //
407                         // Inform whether we are infinite or not
408                         //
409                         if (expr is Constant){
410                                 // expr is 'true', since the 'empty' case above handles the 'false' case
411                                 ec.MarkLabel (ec.LoopBegin);
412                                 expr.EmitSideEffect (ec);
413                                 Statement.Emit (ec);
414                                 ec.Emit (OpCodes.Br, ec.LoopBegin);
415                                         
416                                 //
417                                 // Inform that we are infinite (ie, `we return'), only
418                                 // if we do not `break' inside the code.
419                                 //
420                                 ec.MarkLabel (ec.LoopEnd);
421                         } else {
422                                 Label while_loop = ec.DefineLabel ();
423
424                                 ec.Emit (OpCodes.Br, ec.LoopBegin);
425                                 ec.MarkLabel (while_loop);
426
427                                 Statement.Emit (ec);
428                         
429                                 ec.MarkLabel (ec.LoopBegin);
430                                 ec.Mark (loc);
431
432                                 expr.EmitBranchable (ec, while_loop, true);
433                                 
434                                 ec.MarkLabel (ec.LoopEnd);
435                         }       
436
437                         ec.LoopBegin = old_begin;
438                         ec.LoopEnd = old_end;
439                 }
440
441                 public override void Emit (EmitContext ec)
442                 {
443                         DoEmit (ec);
444                 }
445
446                 protected override void CloneTo (CloneContext clonectx, Statement t)
447                 {
448                         While target = (While) t;
449
450                         target.expr = expr.Clone (clonectx);
451                         target.Statement = Statement.Clone (clonectx);
452                 }
453         }
454
455         public class For : Statement {
456                 Expression Test;
457                 Statement InitStatement;
458                 Statement Increment;
459                 public Statement Statement;
460                 bool infinite, empty;
461                 
462                 public For (Statement init_statement,
463                             BooleanExpression test,
464                             Statement increment,
465                             Statement statement,
466                             Location l)
467                 {
468                         InitStatement = init_statement;
469                         Test = test;
470                         Increment = increment;
471                         Statement = statement;
472                         loc = l;
473                 }
474
475                 public override bool Resolve (BlockContext ec)
476                 {
477                         bool ok = true;
478
479                         if (InitStatement != null){
480                                 if (!InitStatement.Resolve (ec))
481                                         ok = false;
482                         }
483
484                         if (Test != null){
485                                 Test = Test.Resolve (ec);
486                                 if (Test == null)
487                                         ok = false;
488                                 else if (Test is Constant){
489                                         bool value = !((Constant) Test).IsDefaultValue;
490
491                                         if (value == false){
492                                                 if (!Statement.ResolveUnreachable (ec, true))
493                                                         return false;
494                                                 if ((Increment != null) &&
495                                                     !Increment.ResolveUnreachable (ec, false))
496                                                         return false;
497                                                 empty = true;
498                                                 return true;
499                                         } else
500                                                 infinite = true;
501                                 }
502                         } else
503                                 infinite = true;
504
505                         ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
506                         if (!infinite)
507                                 ec.CurrentBranching.CreateSibling ();
508
509                         bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
510
511                         ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
512                         if (!Statement.Resolve (ec))
513                                 ok = false;
514                         ec.EndFlowBranching ();
515
516                         if (Increment != null){
517                                 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
518                                         if (!Increment.ResolveUnreachable (ec, !was_unreachable))
519                                                 ok = false;
520                                 } else {
521                                         if (!Increment.Resolve (ec))
522                                                 ok = false;
523                                 }
524                         }
525
526                         // There's no direct control flow from the end of the embedded statement to the end of the loop
527                         ec.CurrentBranching.CurrentUsageVector.Goto ();
528
529                         ec.EndFlowBranching ();
530
531                         return ok;
532                 }
533
534                 protected override void DoEmit (EmitContext ec)
535                 {
536                         if (InitStatement != null)
537                                 InitStatement.Emit (ec);
538
539                         if (empty) {
540                                 Test.EmitSideEffect (ec);
541                                 return;
542                         }
543
544                         Label old_begin = ec.LoopBegin;
545                         Label old_end = ec.LoopEnd;
546                         Label loop = ec.DefineLabel ();
547                         Label test = ec.DefineLabel ();
548
549                         ec.LoopBegin = ec.DefineLabel ();
550                         ec.LoopEnd = ec.DefineLabel ();
551
552                         ec.Emit (OpCodes.Br, test);
553                         ec.MarkLabel (loop);
554                         Statement.Emit (ec);
555
556                         ec.MarkLabel (ec.LoopBegin);
557                         Increment.Emit (ec);
558
559                         ec.MarkLabel (test);
560                         //
561                         // If test is null, there is no test, and we are just
562                         // an infinite loop
563                         //
564                         if (Test != null){
565                                 //
566                                 // The Resolve code already catches the case for
567                                 // Test == Constant (false) so we know that
568                                 // this is true
569                                 //
570                                 if (Test is Constant) {
571                                         Test.EmitSideEffect (ec);
572                                         ec.Emit (OpCodes.Br, loop);
573                                 } else {
574                                         Test.EmitBranchable (ec, loop, true);
575                                 }
576                                 
577                         } else
578                                 ec.Emit (OpCodes.Br, loop);
579                         ec.MarkLabel (ec.LoopEnd);
580
581                         ec.LoopBegin = old_begin;
582                         ec.LoopEnd = old_end;
583                 }
584
585                 protected override void CloneTo (CloneContext clonectx, Statement t)
586                 {
587                         For target = (For) t;
588
589                         if (InitStatement != null)
590                                 target.InitStatement = InitStatement.Clone (clonectx);
591                         if (Test != null)
592                                 target.Test = Test.Clone (clonectx);
593                         if (Increment != null)
594                                 target.Increment = Increment.Clone (clonectx);
595                         target.Statement = Statement.Clone (clonectx);
596                 }
597         }
598         
599         public class StatementExpression : Statement {
600                 ExpressionStatement expr;
601                 
602                 public StatementExpression (ExpressionStatement expr)
603                 {
604                         this.expr = expr;
605                         loc = expr.Location;
606                 }
607
608                 public override bool Resolve (BlockContext ec)
609                 {
610                         expr = expr.ResolveStatement (ec);
611                         return expr != null;
612                 }
613                 
614                 protected override void DoEmit (EmitContext ec)
615                 {
616                         expr.EmitStatement (ec);
617                 }
618
619                 public override string ToString ()
620                 {
621                         return "StatementExpression (" + expr + ")";
622                 }
623
624                 protected override void CloneTo (CloneContext clonectx, Statement t)
625                 {
626                         StatementExpression target = (StatementExpression) t;
627
628                         target.expr = (ExpressionStatement) expr.Clone (clonectx);
629                 }
630         }
631
632         //
633         // Simple version of statement list not requiring a block
634         //
635         public class StatementList : Statement
636         {
637                 List<Statement> statements;
638
639                 public StatementList (Statement first, Statement second)
640                 {
641                         statements = new List<Statement> () { first, second };
642                 }
643
644                 #region Properties
645                 public IList<Statement> Statements {
646                         get {
647                                 return statements;
648                         }
649                 }
650                 #endregion
651
652                 public void Add (Statement statement)
653                 {
654                         statements.Add (statement);
655                 }
656
657                 public override bool Resolve (BlockContext ec)
658                 {
659                         foreach (var s in statements)
660                                 s.Resolve (ec);
661
662                         return true;
663                 }
664
665                 protected override void DoEmit (EmitContext ec)
666                 {
667                         foreach (var s in statements)
668                                 s.Emit (ec);
669                 }
670
671                 protected override void CloneTo (CloneContext clonectx, Statement target)
672                 {
673                         StatementList t = (StatementList) target;
674
675                         t.statements = new List<Statement> (statements.Count);
676                         foreach (Statement s in statements)
677                                 t.statements.Add (s.Clone (clonectx));
678                 }
679         }
680
681         // A 'return' or a 'yield break'
682         public abstract class ExitStatement : Statement
683         {
684                 protected bool unwind_protect;
685                 protected abstract bool DoResolve (BlockContext ec);
686
687                 public virtual void Error_FinallyClause (Report Report)
688                 {
689                         Report.Error (157, loc, "Control cannot leave the body of a finally clause");
690                 }
691
692                 public sealed override bool Resolve (BlockContext ec)
693                 {
694                         if (!DoResolve (ec))
695                                 return false;
696
697                         unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
698                         if (unwind_protect)
699                                 ec.NeedReturnLabel ();
700                         ec.CurrentBranching.CurrentUsageVector.Goto ();
701                         return true;
702                 }
703         }
704
705         /// <summary>
706         ///   Implements the return statement
707         /// </summary>
708         public class Return : ExitStatement
709         {
710                 protected Expression Expr;
711                 public Return (Expression expr, Location l)
712                 {
713                         Expr = expr;
714                         loc = l;
715                 }
716
717                 #region Properties
718                 public Expression Expression {
719                         get {
720                                 return Expr;
721                         }
722                 }
723                 #endregion
724
725                 protected override bool DoResolve (BlockContext ec)
726                 {
727                         if (Expr == null) {
728                                 if (ec.ReturnType == TypeManager.void_type)
729                                         return true;
730
731                                 if (ec.CurrentIterator != null) {
732                                         Error_ReturnFromIterator (ec);
733                                 } else {
734                                         ec.Report.Error (126, loc,
735                                                 "An object of a type convertible to `{0}' is required for the return statement",
736                                                 ec.ReturnType.GetSignatureForError ());
737                                 }
738
739                                 return false;
740                         }
741
742                         Expr = Expr.Resolve (ec);
743
744                         AnonymousExpression am = ec.CurrentAnonymousMethod;
745                         if (am == null) {
746                                 if (ec.ReturnType == TypeManager.void_type) {
747                                         ec.Report.Error (127, loc,
748                                                 "`{0}': A return keyword must not be followed by any expression when method returns void",
749                                                 ec.GetSignatureForError ());
750                                 }
751                         } else {
752                                 if (am.IsIterator) {
753                                         Error_ReturnFromIterator (ec);
754                                         return false;
755                                 }
756
757                                 var l = am as AnonymousMethodBody;
758                                 if (l != null && l.ReturnTypeInference != null && Expr != null) {
759                                         l.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
760                                         return true;
761                                 }
762                         }
763
764                         if (Expr == null)
765                                 return false;
766
767                         if (Expr.Type != ec.ReturnType) {
768                                 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
769
770                                 if (Expr == null) {
771                                         if (am != null) {
772                                                 ec.Report.Error (1662, loc,
773                                                         "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
774                                                         am.ContainerType, am.GetSignatureForError ());
775                                         }
776                                         return false;
777                                 }
778                         }
779
780                         return true;                    
781                 }
782                 
783                 protected override void DoEmit (EmitContext ec)
784                 {
785                         if (Expr != null) {
786                                 Expr.Emit (ec);
787
788                                 if (unwind_protect)
789                                         ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
790                         }
791
792                         if (unwind_protect)
793                                 ec.Emit (OpCodes.Leave, ec.ReturnLabel);
794                         else
795                                 ec.Emit (OpCodes.Ret);
796                 }
797
798                 void Error_ReturnFromIterator (ResolveContext rc)
799                 {
800                         rc.Report.Error (1622, loc,
801                                 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
802                 }
803
804                 protected override void CloneTo (CloneContext clonectx, Statement t)
805                 {
806                         Return target = (Return) t;
807                         // It's null for simple return;
808                         if (Expr != null)
809                                 target.Expr = Expr.Clone (clonectx);
810                 }
811         }
812
813         public class Goto : Statement {
814                 string target;
815                 LabeledStatement label;
816                 bool unwind_protect;
817
818                 public override bool Resolve (BlockContext ec)
819                 {
820                         unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
821                         ec.CurrentBranching.CurrentUsageVector.Goto ();
822                         return true;
823                 }
824                 
825                 public Goto (string label, Location l)
826                 {
827                         loc = l;
828                         target = label;
829                 }
830
831                 public string Target {
832                         get { return target; }
833                 }
834
835                 public void SetResolvedTarget (LabeledStatement label)
836                 {
837                         this.label = label;
838                         label.AddReference ();
839                 }
840
841                 protected override void CloneTo (CloneContext clonectx, Statement target)
842                 {
843                         // Nothing to clone
844                 }
845
846                 protected override void DoEmit (EmitContext ec)
847                 {
848                         if (label == null)
849                                 throw new InternalErrorException ("goto emitted before target resolved");
850                         Label l = label.LabelTarget (ec);
851                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
852                 }
853         }
854
855         public class LabeledStatement : Statement {
856                 string name;
857                 bool defined;
858                 bool referenced;
859                 Label label;
860                 Block block;
861
862                 FlowBranching.UsageVector vectors;
863                 
864                 public LabeledStatement (string name, Block block, Location l)
865                 {
866                         this.name = name;
867                         this.block = block;
868                         this.loc = l;
869                 }
870
871                 public Label LabelTarget (EmitContext ec)
872                 {
873                         if (defined)
874                                 return label;
875
876                         label = ec.DefineLabel ();
877                         defined = true;
878                         return label;
879                 }
880
881                 public Block Block {
882                         get {
883                                 return block;
884                         }
885                 }
886
887                 public string Name {
888                         get { return name; }
889                 }
890
891                 public bool IsDefined {
892                         get { return defined; }
893                 }
894
895                 public bool HasBeenReferenced {
896                         get { return referenced; }
897                 }
898
899                 public FlowBranching.UsageVector JumpOrigins {
900                         get { return vectors; }
901                 }
902
903                 public void AddUsageVector (FlowBranching.UsageVector vector)
904                 {
905                         vector = vector.Clone ();
906                         vector.Next = vectors;
907                         vectors = vector;
908                 }
909
910                 protected override void CloneTo (CloneContext clonectx, Statement target)
911                 {
912                         // nothing to clone
913                 }
914
915                 public override bool Resolve (BlockContext ec)
916                 {
917                         // this flow-branching will be terminated when the surrounding block ends
918                         ec.StartFlowBranching (this);
919                         return true;
920                 }
921
922                 protected override void DoEmit (EmitContext ec)
923                 {
924                         if (!HasBeenReferenced)
925                                 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
926
927                         LabelTarget (ec);
928                         ec.MarkLabel (label);
929                 }
930
931                 public void AddReference ()
932                 {
933                         referenced = true;
934                 }
935         }
936         
937
938         /// <summary>
939         ///   `goto default' statement
940         /// </summary>
941         public class GotoDefault : Statement {
942                 
943                 public GotoDefault (Location l)
944                 {
945                         loc = l;
946                 }
947
948                 protected override void CloneTo (CloneContext clonectx, Statement target)
949                 {
950                         // nothing to clone
951                 }
952
953                 public override bool Resolve (BlockContext ec)
954                 {
955                         ec.CurrentBranching.CurrentUsageVector.Goto ();
956
957                         if (ec.Switch == null) {
958                                 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
959                                 return false;
960                         }
961
962                         if (!ec.Switch.GotDefault) {
963                                 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
964                                 return false;
965                         }
966
967                         return true;
968                 }
969
970                 protected override void DoEmit (EmitContext ec)
971                 {
972                         ec.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
973                 }
974         }
975
976         /// <summary>
977         ///   `goto case' statement
978         /// </summary>
979         public class GotoCase : Statement {
980                 Expression expr;
981                 SwitchLabel sl;
982                 
983                 public GotoCase (Expression e, Location l)
984                 {
985                         expr = e;
986                         loc = l;
987                 }
988
989                 public override bool Resolve (BlockContext ec)
990                 {
991                         if (ec.Switch == null){
992                                 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
993                                 return false;
994                         }
995
996                         ec.CurrentBranching.CurrentUsageVector.Goto ();
997
998                         expr = expr.Resolve (ec);
999                         if (expr == null)
1000                                 return false;
1001
1002                         Constant c = expr as Constant;
1003                         if (c == null) {
1004                                 ec.Report.Error (150, expr.Location, "A constant value is expected");
1005                                 return false;
1006                         }
1007
1008                         TypeSpec type = ec.Switch.SwitchType;
1009                         Constant res = c.TryReduce (ec, type, c.Location);
1010                         if (res == null) {
1011                                 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1012                                 return false;
1013                         }
1014
1015                         if (!Convert.ImplicitStandardConversionExists (c, type))
1016                                 ec.Report.Warning (469, 2, loc,
1017                                         "The `goto case' value is not implicitly convertible to type `{0}'",
1018                                         TypeManager.CSharpName (type));
1019
1020                         object val = res.GetValue ();
1021                         if (val == null)
1022                                 val = SwitchLabel.NullStringCase;
1023                                         
1024                         if (!ec.Switch.Elements.TryGetValue (val, out sl)) {
1025                                 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + 
1026                                         (c.GetValue () == null ? "null" : val.ToString ()), ec.Report);
1027                                 return false;
1028                         }
1029
1030                         return true;
1031                 }
1032
1033                 protected override void DoEmit (EmitContext ec)
1034                 {
1035                         ec.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1036                 }
1037
1038                 protected override void CloneTo (CloneContext clonectx, Statement t)
1039                 {
1040                         GotoCase target = (GotoCase) t;
1041
1042                         target.expr = expr.Clone (clonectx);
1043                 }
1044         }
1045         
1046         public class Throw : Statement {
1047                 Expression expr;
1048                 
1049                 public Throw (Expression expr, Location l)
1050                 {
1051                         this.expr = expr;
1052                         loc = l;
1053                 }
1054
1055                 public override bool Resolve (BlockContext ec)
1056                 {
1057                         if (expr == null) {
1058                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
1059                                 return ec.CurrentBranching.CheckRethrow (loc);
1060                         }
1061
1062                         expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1063                         ec.CurrentBranching.CurrentUsageVector.Goto ();
1064
1065                         if (expr == null)
1066                                 return false;
1067
1068                         if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1069                                 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1070                         else
1071                                 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1072
1073                         return true;
1074                 }
1075                         
1076                 protected override void DoEmit (EmitContext ec)
1077                 {
1078                         if (expr == null)
1079                                 ec.Emit (OpCodes.Rethrow);
1080                         else {
1081                                 expr.Emit (ec);
1082
1083                                 ec.Emit (OpCodes.Throw);
1084                         }
1085                 }
1086
1087                 protected override void CloneTo (CloneContext clonectx, Statement t)
1088                 {
1089                         Throw target = (Throw) t;
1090
1091                         if (expr != null)
1092                                 target.expr = expr.Clone (clonectx);
1093                 }
1094         }
1095
1096         public class Break : Statement {
1097                 
1098                 public Break (Location l)
1099                 {
1100                         loc = l;
1101                 }
1102
1103                 bool unwind_protect;
1104
1105                 public override bool Resolve (BlockContext ec)
1106                 {
1107                         unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1108                         ec.CurrentBranching.CurrentUsageVector.Goto ();
1109                         return true;
1110                 }
1111
1112                 protected override void DoEmit (EmitContext ec)
1113                 {
1114                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1115                 }
1116
1117                 protected override void CloneTo (CloneContext clonectx, Statement t)
1118                 {
1119                         // nothing needed
1120                 }
1121         }
1122
1123         public class Continue : Statement {
1124                 
1125                 public Continue (Location l)
1126                 {
1127                         loc = l;
1128                 }
1129
1130                 bool unwind_protect;
1131
1132                 public override bool Resolve (BlockContext ec)
1133                 {
1134                         unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1135                         ec.CurrentBranching.CurrentUsageVector.Goto ();
1136                         return true;
1137                 }
1138
1139                 protected override void DoEmit (EmitContext ec)
1140                 {
1141                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1142                 }
1143
1144                 protected override void CloneTo (CloneContext clonectx, Statement t)
1145                 {
1146                         // nothing needed.
1147                 }
1148         }
1149
1150         public interface ILocalVariable
1151         {
1152                 void Emit (EmitContext ec);
1153                 void EmitAssign (EmitContext ec);
1154                 void EmitAddressOf (EmitContext ec);
1155         }
1156
1157         public interface INamedBlockVariable
1158         {
1159                 Block Block { get; }
1160                 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1161                 bool IsDeclared { get; }
1162                 Location Location { get; }
1163         }
1164
1165         public class BlockVariableDeclaration : Statement
1166         {
1167                 public class Declarator
1168                 {
1169                         LocalVariable li;
1170                         Expression initializer;
1171
1172                         public Declarator (LocalVariable li, Expression initializer)
1173                         {
1174                                 if (li.Type != null)
1175                                         throw new ArgumentException ("Expected null variable type");
1176
1177                                 this.li = li;
1178                                 this.initializer = initializer;
1179                         }
1180
1181                         public Declarator (Declarator clone, Expression initializer)
1182                         {
1183                                 this.li = clone.li;
1184                                 this.initializer = initializer;
1185                         }
1186
1187                         #region Properties
1188
1189                         public LocalVariable Variable {
1190                                 get {
1191                                         return li;
1192                                 }
1193                         }
1194
1195                         public Expression Initializer {
1196                                 get {
1197                                         return initializer;
1198                                 }
1199                                 set {
1200                                         initializer = value;
1201                                 }
1202                         }
1203
1204                         #endregion
1205                 }
1206
1207                 Expression initializer;
1208                 protected FullNamedExpression type_expr;
1209                 protected LocalVariable li;
1210                 protected List<Declarator> declarators;
1211
1212                 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1213                 {
1214                         this.type_expr = type;
1215                         this.li = li;
1216                         this.loc = type_expr.Location;
1217                 }
1218
1219                 protected BlockVariableDeclaration (LocalVariable li)
1220                 {
1221                         this.li = li;
1222                 }
1223
1224                 #region Properties
1225
1226                 public List<Declarator> Declarators {
1227                         get {
1228                                 return declarators;
1229                         }
1230                 }
1231
1232                 public Expression Initializer {
1233                         get {
1234                                 return initializer;
1235                         }
1236                         set {
1237                                 initializer = value;
1238                         }
1239                 }
1240
1241                 public FullNamedExpression TypeExpression {
1242                         get {
1243                                 return type_expr;
1244                         }
1245                 }
1246
1247                 public LocalVariable Variable {
1248                         get {
1249                                 return li;
1250                         }
1251                 }
1252
1253                 #endregion
1254
1255                 public void AddDeclarator (Declarator decl)
1256                 {
1257                         if (declarators == null)
1258                                 declarators = new List<Declarator> ();
1259
1260                         declarators.Add (decl);
1261                 }
1262
1263                 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1264                 {
1265                         var container = bc.CurrentMemberDefinition.Parent;
1266
1267                         Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1268                                 new MemberName (li.Name, li.Location), null);
1269
1270                         container.AddField (f);
1271                         f.Define ();
1272                         Evaluator.QueueField (f);
1273
1274                         li.HoistedVariant = new HoistedEvaluatorVariable (f);
1275                         li.SetIsUsed ();
1276                 }
1277
1278                 public override bool Resolve (BlockContext bc)
1279                 {
1280                         if (li.Type == null) {
1281                                 TypeSpec type = null;
1282                                 if (type_expr is VarExpr) {
1283                                         //
1284                                         // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1285                                         // same name exists or as a keyword when no type was found
1286                                         // 
1287                                         var texpr = type_expr.ResolveAsTypeTerminal (bc, true);
1288                                         if (texpr == null) {
1289                                                 if (RootContext.Version < LanguageVersion.V_3)
1290                                                         bc.Report.FeatureIsNotAvailable (loc, "implicitly typed local variable");
1291
1292                                                 if (li.IsFixed) {
1293                                                         bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1294                                                         return false;
1295                                                 }
1296
1297                                                 if (li.IsConstant) {
1298                                                         bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1299                                                         return false;
1300                                                 }
1301
1302                                                 if (Initializer == null) {
1303                                                         bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1304                                                         return false;
1305                                                 }
1306
1307                                                 if (declarators != null) {
1308                                                         bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1309                                                         declarators = null;
1310                                                 }
1311
1312                                                 Initializer = Initializer.Resolve (bc);
1313                                                 if (Initializer != null) {
1314                                                         ((VarExpr) type_expr).InferType (bc, Initializer);
1315                                                         type = type_expr.Type;
1316                                                 }
1317                                         }
1318                                 }
1319
1320                                 if (type == null) {
1321                                         var texpr = type_expr.ResolveAsTypeTerminal (bc, false);
1322                                         if (texpr == null)
1323                                                 return false;
1324
1325                                         type = texpr.Type;
1326
1327                                         if (li.IsConstant && !type.IsConstantCompatible) {
1328                                                 Const.Error_InvalidConstantType (type, loc, bc.Report);
1329                                         }
1330                                 }
1331
1332                                 if (type.IsStatic)
1333                                         FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1334
1335                                 if (type.IsPointer && !bc.IsUnsafe)
1336                                         Expression.UnsafeError (bc, loc);
1337
1338                                 li.Type = type;
1339                         }
1340
1341                         bool eval_global = RootContext.StatementMode && bc.CurrentBlock is ToplevelBlock;
1342                         if (eval_global) {
1343                                 CreateEvaluatorVariable (bc, li);
1344                         } else {
1345                                 li.PrepareForFlowAnalysis (bc);
1346                         }
1347
1348                         if (initializer != null) {
1349                                 initializer = ResolveInitializer (bc, li, initializer);
1350                                 // li.Variable.DefinitelyAssigned 
1351                         }
1352
1353                         if (declarators != null) {
1354                                 foreach (var d in declarators) {
1355                                         d.Variable.Type = li.Type;
1356                                         if (eval_global) {
1357                                                 CreateEvaluatorVariable (bc, d.Variable);
1358                                         } else {
1359                                                 d.Variable.PrepareForFlowAnalysis (bc);
1360                                         }
1361
1362                                         if (d.Initializer != null) {
1363                                                 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1364                                                 // d.Variable.DefinitelyAssigned 
1365                                         }
1366                                 }
1367                         }
1368
1369                         return true;
1370                 }
1371
1372                 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1373                 {
1374                         var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1375                         return a.ResolveStatement (bc);
1376                 }
1377
1378                 protected override void DoEmit (EmitContext ec)
1379                 {
1380                         if (li.IsConstant)
1381                                 return;
1382
1383                         li.CreateBuilder (ec);
1384
1385                         if (Initializer != null)
1386                                 ((ExpressionStatement) Initializer).EmitStatement (ec);
1387
1388                         if (declarators != null) {
1389                                 foreach (var d in declarators) {
1390                                         d.Variable.CreateBuilder (ec);
1391                                         if (d.Initializer != null)
1392                                                 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1393                                 }
1394                         }
1395                 }
1396
1397                 protected override void CloneTo (CloneContext clonectx, Statement target)
1398                 {
1399                         BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1400
1401                         if (type_expr != null)
1402                                 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1403
1404                         if (initializer != null)
1405                                 t.initializer = initializer.Clone (clonectx);
1406
1407                         if (declarators != null) {
1408                                 t.declarators = null;
1409                                 foreach (var d in declarators)
1410                                         t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1411                         }
1412                 }
1413         }
1414
1415         class BlockConstantDeclaration : BlockVariableDeclaration
1416         {
1417                 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1418                         : base (type, li)
1419                 {
1420                 }
1421
1422                 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1423                 {
1424                         initializer = initializer.Resolve (bc);
1425                         if (initializer == null)
1426                                 return null;
1427
1428                         var c = initializer as Constant;
1429                         if (c == null) {
1430                                 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1431                                 return null;
1432                         }
1433
1434                         c = c.ConvertImplicitly (bc, li.Type);
1435                         if (c == null) {
1436                                 if (TypeManager.IsReferenceType (li.Type))
1437                                         initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1438                                 else
1439                                         initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1440
1441                                 return null;
1442                         }
1443
1444                         li.ConstantValue = c;
1445                         return initializer;
1446                 }
1447         }
1448
1449         //
1450         // The information about a user-perceived local variable
1451         //
1452         public class LocalVariable : INamedBlockVariable, ILocalVariable
1453         {
1454                 [Flags]
1455                 public enum Flags
1456                 {
1457                         Used = 1,
1458                         IsThis = 1 << 1,
1459                         AddressTaken = 1 << 2,
1460                         CompilerGenerated = 1 << 3,
1461                         Constant = 1 << 4,
1462                         ForeachVariable = 1 << 5,
1463                         FixedVariable = 1 << 6,
1464                         UsingVariable = 1 << 7,
1465 //                      DefinitelyAssigned = 1 << 8,
1466                         IsLocked = 1 << 9,
1467
1468                         ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1469                 }
1470
1471                 TypeSpec type;
1472                 readonly string name;
1473                 readonly Location loc;
1474                 readonly Block block;
1475                 Flags flags;
1476                 Constant const_value;
1477
1478                 public VariableInfo VariableInfo;
1479                 HoistedVariable hoisted_variant;
1480
1481                 LocalBuilder builder;
1482
1483                 public LocalVariable (Block block, string name, Location loc)
1484                 {
1485                         this.block = block;
1486                         this.name = name;
1487                         this.loc = loc;
1488                 }
1489
1490                 public LocalVariable (Block block, string name, Flags flags, Location loc)
1491                         : this (block, name, loc)
1492                 {
1493                         this.flags = flags;
1494                 }
1495
1496                 //
1497                 // Used by variable declarators
1498                 //
1499                 public LocalVariable (LocalVariable li, string name, Location loc)
1500                         : this (li.block, name, li.flags, loc)
1501                 {
1502                 }
1503
1504                 #region Properties
1505
1506                 public bool AddressTaken {
1507                         get { return (flags & Flags.AddressTaken) != 0; }
1508                         set { flags |= Flags.AddressTaken; }
1509                 }
1510
1511                 public Block Block {
1512                         get {
1513                                 return block;
1514                         }
1515                 }
1516
1517                 public Constant ConstantValue {
1518                         get {
1519                                 return const_value;
1520                         }
1521                         set {
1522                                 const_value = value;
1523                         }
1524                 }
1525
1526                 //
1527                 // Hoisted local variable variant
1528                 //
1529                 public HoistedVariable HoistedVariant {
1530                         get {
1531                                 return hoisted_variant;
1532                         }
1533                         set {
1534                                 hoisted_variant = value;
1535                         }
1536                 }
1537
1538                 public bool IsDeclared {
1539                         get {
1540                                 return type != null;
1541                         }
1542                 }
1543
1544                 public bool IsConstant {
1545                         get {
1546                                 return (flags & Flags.Constant) != 0;
1547                         }
1548                 }
1549
1550                 public bool IsLocked {
1551                         get {
1552                                 return (flags & Flags.IsLocked) != 0;
1553                         }
1554                         set {
1555                                 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1556                         }
1557                 }
1558
1559                 public bool IsThis {
1560                         get {
1561                                 return (flags & Flags.IsThis) != 0;
1562                         }
1563                 }
1564
1565                 public bool IsFixed {
1566                         get {
1567                                 return (flags & Flags.FixedVariable) != 0;
1568                         }
1569                 }
1570
1571                 public bool IsReadonly {
1572                         get {
1573                                 return (flags & Flags.ReadonlyMask) != 0;
1574                         }
1575                 }
1576
1577                 public Location Location {
1578                         get {
1579                                 return loc;
1580                         }
1581                 }
1582
1583                 public string Name {
1584                         get {
1585                                 return name;
1586                         }
1587                 }
1588
1589                 public TypeSpec Type {
1590                     get {
1591                                 return type;
1592                         }
1593                     set {
1594                                 type = value;
1595                         }
1596                 }
1597
1598                 #endregion
1599
1600                 public void CreateBuilder (EmitContext ec)
1601                 {
1602                         if ((flags & Flags.Used) == 0) {
1603                                 if (VariableInfo == null) {
1604                                         // Missing flow analysis or wrong variable flags
1605                                         throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1606                                 }
1607
1608                                 if (VariableInfo.IsEverAssigned)
1609                                         ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1610                                 else
1611                                         ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1612                         }
1613
1614                         if (HoistedVariant != null)
1615                                 return;
1616
1617                         if (builder != null) {
1618                                 if ((flags & Flags.CompilerGenerated) != 0)
1619                                         return;
1620
1621                                 // To avoid Used warning duplicates
1622                                 throw new InternalErrorException ("Already created variable `{0}'", name);
1623                         }
1624
1625                         //
1626                         // All fixed variabled are pinned, a slot has to be alocated
1627                         //
1628                         builder = ec.DeclareLocal (Type, IsFixed);
1629                         if (SymbolWriter.HasSymbolWriter)
1630                                 ec.DefineLocalVariable (name, builder);
1631                 }
1632
1633                 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1634                 {
1635                         LocalVariable li = new LocalVariable (block, "<$$>", Flags.CompilerGenerated | Flags.Used, loc);
1636                         li.Type = type;
1637                         return li;
1638                 }
1639
1640                 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1641                 {
1642                         if (IsConstant && const_value != null)
1643                                 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc).Resolve (rc);
1644
1645                         return new LocalVariableReference (this, loc);
1646                 }
1647
1648                 public void Emit (EmitContext ec)
1649                 {
1650                         // TODO: Need something better for temporary variables
1651                         if ((flags & Flags.CompilerGenerated) != 0)
1652                                 CreateBuilder (ec);
1653
1654                         ec.Emit (OpCodes.Ldloc, builder);
1655                 }
1656
1657                 public void EmitAssign (EmitContext ec)
1658                 {
1659                         // TODO: Need something better for temporary variables
1660                         if ((flags & Flags.CompilerGenerated) != 0)
1661                                 CreateBuilder (ec);
1662
1663                         ec.Emit (OpCodes.Stloc, builder);
1664                 }
1665
1666                 public void EmitAddressOf (EmitContext ec)
1667                 {
1668                         ec.Emit (OpCodes.Ldloca, builder);
1669                 }
1670
1671                 public string GetReadOnlyContext ()
1672                 {
1673                         switch (flags & Flags.ReadonlyMask) {
1674                         case Flags.FixedVariable:
1675                                 return "fixed variable";
1676                         case Flags.ForeachVariable:
1677                                 return "foreach iteration variable";
1678                         case Flags.UsingVariable:
1679                                 return "using variable";
1680                         }
1681
1682                         throw new InternalErrorException ("Variable is not readonly");
1683                 }
1684
1685                 public bool IsThisAssigned (BlockContext ec, Block block)
1686                 {
1687                         if (VariableInfo == null)
1688                                 throw new Exception ();
1689
1690                         if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1691                                 return true;
1692
1693                         return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1694                 }
1695
1696                 public bool IsAssigned (BlockContext ec)
1697                 {
1698                         if (VariableInfo == null)
1699                                 throw new Exception ();
1700
1701                         return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1702                 }
1703
1704                 public void PrepareForFlowAnalysis (BlockContext bc)
1705                 {
1706                         //
1707                         // No need for definitely assigned check for these guys
1708                         //
1709                         if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1710                                 return;
1711
1712                         VariableInfo = new VariableInfo (this, bc.FlowOffset);
1713                         bc.FlowOffset += VariableInfo.Length;
1714                 }
1715
1716                 //
1717                 // Mark the variables as referenced in the user code
1718                 //
1719                 public void SetIsUsed ()
1720                 {
1721                         flags |= Flags.Used;
1722                 }
1723
1724                 public override string ToString ()
1725                 {
1726                         return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1727                 }
1728         }
1729
1730         /// <summary>
1731         ///   Block represents a C# block.
1732         /// </summary>
1733         ///
1734         /// <remarks>
1735         ///   This class is used in a number of places: either to represent
1736         ///   explicit blocks that the programmer places or implicit blocks.
1737         ///
1738         ///   Implicit blocks are used as labels or to introduce variable
1739         ///   declarations.
1740         ///
1741         ///   Top-level blocks derive from Block, and they are called ToplevelBlock
1742         ///   they contain extra information that is not necessary on normal blocks.
1743         /// </remarks>
1744         public class Block : Statement {
1745                 [Flags]
1746                 public enum Flags
1747                 {
1748                         Unchecked = 1,
1749                         HasRet = 8,
1750                         Unsafe = 16,
1751                         IsIterator = 32,
1752                         HasCapturedVariable = 64,
1753                         HasCapturedThis = 1 << 7,
1754                         IsExpressionTree = 1 << 8,
1755                         CompilerGenerated = 1 << 9
1756                 }
1757
1758                 public Block Parent;
1759                 public Location StartLocation;
1760                 public Location EndLocation;
1761
1762                 public ExplicitBlock Explicit;
1763                 public ParametersBlock ParametersBlock;
1764
1765                 protected Flags flags;
1766
1767                 //
1768                 // The statements in this block
1769                 //
1770                 protected List<Statement> statements;
1771
1772                 protected List<Statement> scope_initializers;
1773
1774                 int? resolving_init_idx;
1775
1776                 Block original;
1777
1778 #if DEBUG
1779                 static int id;
1780                 public int ID = id++;
1781
1782                 static int clone_id_counter;
1783                 int clone_id;
1784 #endif
1785
1786 //              int assignable_slots;
1787                 bool unreachable_shown;
1788                 bool unreachable;
1789                 
1790                 public Block (Block parent, Location start, Location end)
1791                         : this (parent, 0, start, end)
1792                 {
1793                 }
1794
1795                 public Block (Block parent, Flags flags, Location start, Location end)
1796                 {
1797                         if (parent != null) {
1798                                 // the appropriate constructors will fixup these fields
1799                                 ParametersBlock = parent.ParametersBlock;
1800                                 Explicit = parent.Explicit;
1801                         }
1802                         
1803                         this.Parent = parent;
1804                         this.flags = flags;
1805                         this.StartLocation = start;
1806                         this.EndLocation = end;
1807                         this.loc = start;
1808                         statements = new List<Statement> (4);
1809
1810                         this.original = this;
1811                 }
1812
1813                 #region Properties
1814
1815                 public bool HasRet {
1816                         get { return (flags & Flags.HasRet) != 0; }
1817                 }
1818
1819                 public Block Original {
1820                         get {
1821                                 return original;
1822                         }
1823                 }
1824
1825                 public bool IsCompilerGenerated {
1826                         get { return (flags & Flags.CompilerGenerated) != 0; }
1827                         set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1828                 }
1829
1830                 public bool Unchecked {
1831                         get { return (flags & Flags.Unchecked) != 0; }
1832                         set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1833                 }
1834
1835                 public bool Unsafe {
1836                         get { return (flags & Flags.Unsafe) != 0; }
1837                         set { flags |= Flags.Unsafe; }
1838                 }
1839
1840                 #endregion
1841
1842                 public Block CreateSwitchBlock (Location start)
1843                 {
1844                         // FIXME: Only explicit block should be created
1845                         var new_block = new Block (this, start, start);
1846                         new_block.IsCompilerGenerated = true;
1847                         return new_block;
1848                 }
1849
1850                 public void SetEndLocation (Location loc)
1851                 {
1852                         EndLocation = loc;
1853                 }
1854
1855                 public void AddLabel (LabeledStatement target)
1856                 {
1857                         ParametersBlock.TopBlock.AddLabel (target.Name, target);
1858                 }
1859
1860                 public void AddLocalName (LocalVariable li)
1861                 {
1862                         AddLocalName (li.Name, li);
1863                 }
1864
1865                 public virtual void AddLocalName (string name, INamedBlockVariable li)
1866                 {
1867                         ParametersBlock.TopBlock.AddLocalName (name, li);
1868                 }
1869
1870                 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1871                 {
1872                         if (reason == null) {
1873                                 Error_AlreadyDeclared (name, variable);
1874                                 return;
1875                         }
1876
1877                         ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1878                                 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1879                                 "to `{0}', which is already used in a `{1}' scope to denote something else",
1880                                 name, reason);
1881                 }
1882
1883                 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1884                 {
1885                         var pi = variable as ParametersBlock.ParameterInfo;
1886                         if (pi != null) {
1887                                 var p = pi.Parameter;
1888                                 if (p is AnonymousTypeClass.GeneratedParameter) {
1889                                         ParametersBlock.TopBlock.Report.Error (833, p.Location, "`{0}': An anonymous type cannot have multiple properties with the same name",
1890                                                 p.Name);
1891                                 } else {
1892                                         ParametersBlock.TopBlock.Report.Error (100, p.Location, "The parameter name `{0}' is a duplicate", p.Name);
1893                                 }
1894
1895                                 return;
1896                         }
1897
1898                         ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1899                                 "A local variable named `{0}' is already defined in this scope", name);
1900                 }
1901                                         
1902                 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1903                 {
1904                         ParametersBlock.TopBlock.Report.Error (412, loc,
1905                                 "The type parameter name `{0}' is the same as local variable or parameter name",
1906                                 name);
1907                 }
1908
1909                 //
1910                 // It should be used by expressions which require to
1911                 // register a statement during resolve process.
1912                 //
1913                 public void AddScopeStatement (Statement s)
1914                 {
1915                         if (scope_initializers == null)
1916                                 scope_initializers = new List<Statement> ();
1917
1918                         //
1919                         // Simple recursive helper, when resolve scope initializer another
1920                         // new scope initializer can be added, this ensures it's initialized
1921                         // before existing one. For now this can happen with expression trees
1922                         // in base ctor initializer only
1923                         //
1924                         if (resolving_init_idx.HasValue) {
1925                                 scope_initializers.Insert (resolving_init_idx.Value, s);
1926                                 ++resolving_init_idx;
1927                         } else {
1928                                 scope_initializers.Add (s);
1929                         }
1930                 }
1931                 
1932                 public void AddStatement (Statement s)
1933                 {
1934                         statements.Add (s);
1935                 }
1936
1937                 public int AssignableSlots {
1938                         get {
1939                                 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1940                                 return 4096;
1941 //                              return assignable_slots;
1942                         }
1943                 }
1944
1945                 public LabeledStatement LookupLabel (string name)
1946                 {
1947                         return ParametersBlock.TopBlock.GetLabel (name, this);
1948                 }
1949
1950                 public override bool Resolve (BlockContext ec)
1951                 {
1952                         Block prev_block = ec.CurrentBlock;
1953                         bool ok = true;
1954
1955                         ec.CurrentBlock = this;
1956                         ec.StartFlowBranching (this);
1957
1958                         Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
1959
1960                         //
1961                         // Compiler generated scope statements
1962                         //
1963                         if (scope_initializers != null) {
1964                                 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
1965                                         scope_initializers[resolving_init_idx.Value].Resolve (ec);
1966                                 }
1967
1968                                 resolving_init_idx = null;
1969                         }
1970
1971                         //
1972                         // This flag is used to notate nested statements as unreachable from the beginning of this block.
1973                         // For the purposes of this resolution, it doesn't matter that the whole block is unreachable 
1974                         // from the beginning of the function.  The outer Resolve() that detected the unreachability is
1975                         // responsible for handling the situation.
1976                         //
1977                         int statement_count = statements.Count;
1978                         for (int ix = 0; ix < statement_count; ix++){
1979                                 Statement s = statements [ix];
1980
1981                                 //
1982                                 // Warn if we detect unreachable code.
1983                                 //
1984                                 if (unreachable) {
1985                                         if (s is EmptyStatement)
1986                                                 continue;
1987
1988                                         if (!unreachable_shown && !(s is LabeledStatement)) {
1989                                                 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
1990                                                 unreachable_shown = true;
1991                                         }
1992
1993                                         Block c_block = s as Block;
1994                                         if (c_block != null)
1995                                                 c_block.unreachable = c_block.unreachable_shown = true;
1996                                 }
1997
1998                                 //
1999                                 // Note that we're not using ResolveUnreachable() for unreachable
2000                                 // statements here.  ResolveUnreachable() creates a temporary
2001                                 // flow branching and kills it afterwards.  This leads to problems
2002                                 // if you have two unreachable statements where the first one
2003                                 // assigns a variable and the second one tries to access it.
2004                                 //
2005
2006                                 if (!s.Resolve (ec)) {
2007                                         ok = false;
2008                                         if (ec.IsInProbingMode)
2009                                                 break;
2010
2011                                         statements [ix] = new EmptyStatement (s.loc);
2012                                         continue;
2013                                 }
2014
2015                                 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2016                                         statements [ix] = new EmptyStatement (s.loc);
2017
2018                                 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2019                                 if (unreachable && s is LabeledStatement)
2020                                         throw new InternalErrorException ("should not happen");
2021                         }
2022
2023                         Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2024                                       ec.CurrentBranching, statement_count);
2025
2026                         while (ec.CurrentBranching is FlowBranchingLabeled)
2027                                 ec.EndFlowBranching ();
2028
2029                         bool flow_unreachable = ec.EndFlowBranching ();
2030
2031                         ec.CurrentBlock = prev_block;
2032
2033                         if (flow_unreachable)
2034                                 flags |= Flags.HasRet;
2035
2036                         // If we're a non-static `struct' constructor which doesn't have an
2037                         // initializer, then we must initialize all of the struct's fields.
2038                         if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2039                                 ok = false;
2040
2041                         return ok;
2042                 }
2043
2044                 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2045                 {
2046                         unreachable_shown = true;
2047                         unreachable = true;
2048
2049                         if (warn)
2050                                 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2051
2052                         ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2053                         bool ok = Resolve (ec);
2054                         ec.KillFlowBranching ();
2055
2056                         return ok;
2057                 }
2058                 
2059                 protected override void DoEmit (EmitContext ec)
2060                 {
2061                         for (int ix = 0; ix < statements.Count; ix++){
2062                                 statements [ix].Emit (ec);
2063                         }
2064                 }
2065
2066                 public override void Emit (EmitContext ec)
2067                 {
2068                         if (scope_initializers != null)
2069                                 EmitScopeInitializers (ec);
2070
2071                         ec.Mark (StartLocation);
2072                         DoEmit (ec);
2073
2074                         if (SymbolWriter.HasSymbolWriter)
2075                                 EmitSymbolInfo (ec);
2076                 }
2077
2078                 protected void EmitScopeInitializers (EmitContext ec)
2079                 {
2080                         SymbolWriter.OpenCompilerGeneratedBlock (ec);
2081
2082                         using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2083                                 foreach (Statement s in scope_initializers)
2084                                         s.Emit (ec);
2085                         }
2086
2087                         SymbolWriter.CloseCompilerGeneratedBlock (ec);
2088                 }
2089
2090                 protected virtual void EmitSymbolInfo (EmitContext ec)
2091                 {
2092                 }
2093
2094 #if DEBUG
2095                 public override string ToString ()
2096                 {
2097                         return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2098                 }
2099 #endif
2100
2101                 protected override void CloneTo (CloneContext clonectx, Statement t)
2102                 {
2103                         Block target = (Block) t;
2104 #if DEBUG
2105                         target.clone_id = clone_id_counter++;
2106 #endif
2107
2108                         clonectx.AddBlockMap (this, target);
2109
2110                         target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2111                         target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2112
2113                         if (Parent != null)
2114                                 target.Parent = clonectx.RemapBlockCopy (Parent);
2115
2116                         target.statements = new List<Statement> (statements.Count);
2117                         foreach (Statement s in statements)
2118                                 target.statements.Add (s.Clone (clonectx));
2119                 }
2120         }
2121
2122         public class ExplicitBlock : Block
2123         {
2124                 protected AnonymousMethodStorey am_storey;
2125
2126                 public ExplicitBlock (Block parent, Location start, Location end)
2127                         : this (parent, (Flags) 0, start, end)
2128                 {
2129                 }
2130
2131                 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2132                         : base (parent, flags, start, end)
2133                 {
2134                         this.Explicit = this;
2135                 }
2136
2137                 #region Properties
2138
2139                 public AnonymousMethodStorey AnonymousMethodStorey {
2140                         get {
2141                                 return am_storey;
2142                         }
2143                 }
2144
2145                 public bool HasCapturedThis {
2146                         set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2147                         get {
2148                                 return (flags & Flags.HasCapturedThis) != 0;
2149                         }
2150                 }
2151
2152                 public bool HasCapturedVariable {
2153                         set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2154                         get {
2155                                 return (flags & Flags.HasCapturedVariable) != 0;
2156                         }
2157                 }
2158
2159                 #endregion
2160
2161                 //
2162                 // Creates anonymous method storey in current block
2163                 //
2164                 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2165                 {
2166                         //
2167                         // An iterator has only 1 storey block
2168                         //
2169                         if (ec.CurrentIterator != null)
2170                             return ec.CurrentIterator.Storey;
2171
2172                         //
2173                         // When referencing a variable in iterator storey from children anonymous method
2174                         //
2175                         if (ParametersBlock.am_storey is IteratorStorey) {
2176                                 return ParametersBlock.am_storey;
2177                         }
2178
2179                         if (am_storey == null) {
2180                                 MemberBase mc = ec.MemberContext as MemberBase;
2181
2182                                 //
2183                                 // Creates anonymous method storey for this block
2184                                 //
2185                                 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2186                         }
2187
2188                         return am_storey;
2189                 }
2190
2191                 public override void Emit (EmitContext ec)
2192                 {
2193                         if (am_storey != null) {
2194                                 DefineAnonymousStorey (ec);
2195                                 am_storey.EmitStoreyInstantiation (ec, this);
2196                         }
2197
2198                         bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2199                         if (emit_debug_info)
2200                                 ec.BeginScope ();
2201
2202                         base.Emit (ec);
2203
2204                         if (emit_debug_info)
2205                                 ec.EndScope ();
2206                 }
2207
2208                 void DefineAnonymousStorey (EmitContext ec)
2209                 {
2210                         //
2211                         // Creates anonymous method storey
2212                         //
2213                         if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2214                                 //
2215                                 // Creates parent storey reference when hoisted this is accessible
2216                                 //
2217                                 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2218                                         ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2219
2220                                         //
2221                                         // Hoisted this exists in top-level parent storey only
2222                                         //
2223                                         while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2224                                                 parent = parent.Parent.Explicit;
2225
2226                                         am_storey.AddParentStoreyReference (ec, parent.am_storey);
2227                                 }
2228
2229                                 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2230
2231                                 // TODO MemberCache: Review
2232                                 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2233                         }
2234
2235                         am_storey.CreateType ();
2236                         if (am_storey.Mutator == null && ec.CurrentTypeParameters != null)
2237                                 am_storey.Mutator = new TypeParameterMutator (ec.CurrentTypeParameters, am_storey.CurrentTypeParameters);
2238
2239                         am_storey.DefineType ();
2240                         am_storey.ResolveTypeParameters ();
2241
2242                         var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2243                         if (ref_blocks != null) {
2244                                 foreach (ExplicitBlock ref_block in ref_blocks) {
2245                                         for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2246                                                 if (b.am_storey != null) {
2247                                                         b.am_storey.AddParentStoreyReference (ec, am_storey);
2248
2249                                                         // Stop propagation inside same top block
2250                                                         if (b.ParametersBlock.am_storey == ParametersBlock.am_storey)
2251                                                                 break;
2252
2253                                                         b = b.ParametersBlock;
2254                                                 }
2255
2256                                                 b.HasCapturedVariable = true;
2257                                         }
2258                                 }
2259                         }
2260
2261                         am_storey.Define ();
2262                         am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2263                 }
2264
2265                 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2266                 {
2267                         tryBlock.statements = statements;
2268                         statements = new List<Statement> (1);
2269                         statements.Add (tf);
2270                 }
2271         }
2272
2273         //
2274         // ParametersBlock was introduced to support anonymous methods
2275         // and lambda expressions
2276         // 
2277         public class ParametersBlock : ExplicitBlock
2278         {
2279                 public class ParameterInfo : INamedBlockVariable
2280                 {
2281                         readonly ParametersBlock block;
2282                         readonly int index;
2283                         public VariableInfo VariableInfo;
2284                         bool is_locked;
2285
2286                         public ParameterInfo (ParametersBlock block, int index)
2287                         {
2288                                 this.block = block;
2289                                 this.index = index;
2290                         }
2291
2292                         #region Properties
2293
2294                         public Block Block {
2295                                 get {
2296                                         return block;
2297                                 }
2298                         }
2299
2300                         public bool IsDeclared {
2301                                 get {
2302                                         return true;
2303                                 }
2304                         }
2305
2306                         public bool IsLocked {
2307                                 get {
2308                                         return is_locked;
2309                                 }
2310                                 set {
2311                                         is_locked = value;
2312                                 }
2313                         }
2314
2315                         public Location Location {
2316                                 get {
2317                                         return Parameter.Location;
2318                                 }
2319                         }
2320
2321                         public Parameter Parameter {
2322                                 get {
2323                                         return block.Parameters [index];
2324                                 }
2325                         }
2326
2327                         public TypeSpec ParameterType {
2328                                 get {
2329                                         return Parameter.Type;
2330                                 }
2331                         }
2332
2333                         #endregion
2334
2335                         public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2336                         {
2337                                 return new ParameterReference (this, loc);
2338                         }
2339                 }
2340
2341                 // 
2342                 // Block is converted to an expression
2343                 //
2344                 sealed class BlockScopeExpression : Expression
2345                 {
2346                         Expression child;
2347                         readonly ParametersBlock block;
2348
2349                         public BlockScopeExpression (Expression child, ParametersBlock block)
2350                         {
2351                                 this.child = child;
2352                                 this.block = block;
2353                         }
2354
2355                         public override Expression CreateExpressionTree (ResolveContext ec)
2356                         {
2357                                 throw new NotSupportedException ();
2358                         }
2359
2360                         protected override Expression DoResolve (ResolveContext ec)
2361                         {
2362                                 if (child == null)
2363                                         return null;
2364
2365                                 child = child.Resolve (ec);
2366                                 if (child == null)
2367                                         return null;
2368
2369                                 eclass = child.eclass;
2370                                 type = child.Type;
2371                                 return this;
2372                         }
2373
2374                         public override void Emit (EmitContext ec)
2375                         {
2376                                 block.EmitScopeInitializers (ec);
2377                                 child.Emit (ec);
2378                         }
2379                 }
2380
2381                 protected ParametersCompiled parameters;
2382                 protected ParameterInfo[] parameter_info;
2383                 bool resolved;
2384                 protected bool unreachable;
2385                 protected ToplevelBlock top_block;
2386
2387                 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2388                         : base (parent, 0, start, start)
2389                 {
2390                         if (parameters == null)
2391                                 throw new ArgumentNullException ("parameters");
2392
2393                         this.parameters = parameters;
2394                         ParametersBlock = this;
2395
2396                         this.top_block = parent.ParametersBlock.top_block;
2397                         ProcessParameters ();
2398                 }
2399
2400                 protected ParametersBlock (ParametersCompiled parameters, Location start)
2401                         : base (null, 0, start, start)
2402                 {
2403                         if (parameters == null)
2404                                 throw new ArgumentNullException ("parameters");
2405
2406                         this.parameters = parameters;
2407                         ParametersBlock = this;
2408                 }
2409
2410                 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2411                         : base (null, 0, source.StartLocation, source.EndLocation)
2412                 {
2413                         this.parameters = parameters;
2414                         this.statements = source.statements;
2415                         this.scope_initializers = source.scope_initializers;
2416
2417                         this.resolved = true;
2418                         this.unreachable = source.unreachable;
2419                         this.am_storey = source.am_storey;
2420
2421                         ParametersBlock = this;
2422                 }
2423
2424                 #region Properties
2425
2426                 //
2427                 // Block has been converted to expression tree
2428                 //
2429                 public bool IsExpressionTree {
2430                         get {
2431                                 return (flags & Flags.IsExpressionTree) != 0;
2432                         }
2433                 }
2434
2435                 //
2436                 // The parameters for the block.
2437                 //
2438                 public ParametersCompiled Parameters {
2439                         get {
2440                                 return parameters;
2441                         }
2442                 }
2443
2444                 public ToplevelBlock TopBlock {
2445                         get {
2446                                 return top_block;
2447                         }
2448                 }
2449
2450                 public bool Resolved {
2451                         get {
2452                                 return resolved;
2453                         }
2454                 }
2455
2456                 #endregion
2457
2458                 // <summary>
2459                 //   Check whether all `out' parameters have been assigned.
2460                 // </summary>
2461                 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2462                 {
2463                         if (vector.IsUnreachable)
2464                                 return;
2465
2466                         int n = parameter_info == null ? 0 : parameter_info.Length;
2467
2468                         for (int i = 0; i < n; i++) {
2469                                 VariableInfo var = parameter_info[i].VariableInfo;
2470
2471                                 if (var == null)
2472                                         continue;
2473
2474                                 if (vector.IsAssigned (var, false))
2475                                         continue;
2476
2477                                 TopBlock.Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2478                                         var.Name);
2479                         }
2480                 }
2481
2482                 public override Expression CreateExpressionTree (ResolveContext ec)
2483                 {
2484                         if (statements.Count == 1) {
2485                                 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2486                                 if (scope_initializers != null)
2487                                         expr = new BlockScopeExpression (expr, this);
2488
2489                                 return expr;
2490                         }
2491
2492                         return base.CreateExpressionTree (ec);
2493                 }
2494
2495                 public ParameterInfo GetParameterInfo (Parameter p)
2496                 {
2497                         for (int i = 0; i < parameters.Count; ++i) {
2498                                 if (parameters[i] == p)
2499                                         return parameter_info[i];
2500                         }
2501
2502                         throw new ArgumentException ("Invalid parameter");
2503                 }
2504
2505                 public Expression GetParameterReference (int index, Location loc)
2506                 {
2507                         return new ParameterReference (parameter_info[index], loc);
2508                 }
2509
2510                 protected void ProcessParameters ()
2511                 {
2512                         if (parameters.Count == 0)
2513                                 return;
2514
2515                         parameter_info = new ParameterInfo[parameters.Count];
2516                         for (int i = 0; i < parameter_info.Length; ++i) {
2517                                 var p = parameters.FixedParameters[i];
2518                                 if (p == null)
2519                                         continue;
2520
2521                                 // TODO: Should use Parameter only and more block there
2522                                 parameter_info[i] = new ParameterInfo (this, i);
2523                                 AddLocalName (p.Name, parameter_info[i]);
2524                         }
2525                 }
2526
2527                 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2528                 {
2529                         if (resolved)
2530                                 return true;
2531
2532                         resolved = true;
2533
2534                         if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2535                                 flags |= Flags.IsExpressionTree;
2536
2537                         try {
2538                                 ResolveMeta (rc);
2539
2540                                 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2541                                         FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2542
2543                                         if (!Resolve (rc))
2544                                                 return false;
2545
2546                                         unreachable = top_level.End ();
2547                                 }
2548                         } catch (Exception e) {
2549                                 if (e is CompletionResult || rc.Report.IsDisabled)
2550                                         throw;
2551
2552                                 if (rc.CurrentBlock != null) {
2553                                         rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2554                                 } else {
2555                                         rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2556                                 }
2557
2558                                 if (Report.DebugFlags > 0)
2559                                         throw;
2560                         }
2561
2562                         if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2563                                 if (rc.CurrentAnonymousMethod == null) {
2564                                         // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2565                                         if (md is IteratorMethod) {
2566                                                 unreachable = true;
2567                                         } else {
2568                                                 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2569                                                 return false;
2570                                         }
2571                                 } else {
2572                                         rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2573                                                           rc.CurrentAnonymousMethod.GetSignatureForError ());
2574                                         return false;
2575                                 }
2576                         }
2577
2578                         return true;
2579                 }
2580
2581                 void ResolveMeta (BlockContext ec)
2582                 {
2583                         int orig_count = parameters.Count;
2584
2585                         for (int i = 0; i < orig_count; ++i) {
2586                                 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2587
2588                                 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2589                                         continue;
2590
2591                                 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2592                                 parameter_info[i].VariableInfo = vi;
2593                                 ec.FlowOffset += vi.Length;
2594                         }
2595                 }
2596
2597                 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2598                 {
2599                         ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2600                         pb.EndLocation = EndLocation;
2601                         pb.statements = statements;
2602
2603                         var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2604                         am_storey = new IteratorStorey (iterator);
2605
2606                         statements = new List<Statement> (1);
2607                         AddStatement (new Return (iterator, iterator.Location));
2608                 }
2609         }
2610
2611         //
2612         //
2613         //
2614         public class ToplevelBlock : ParametersBlock
2615         {
2616                 LocalVariable this_variable;
2617                 CompilerContext compiler;
2618                 Dictionary<string, object> names;
2619                 Dictionary<string, object> labels;
2620
2621                 public HoistedVariable HoistedThisVariable;
2622
2623                 public Report Report {
2624                         get { return compiler.Report; }
2625                 }
2626
2627                 public ToplevelBlock (CompilerContext ctx, Location loc)
2628                         : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2629                 {
2630                 }
2631
2632                 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2633                         : base (parameters, start)
2634                 {
2635                         this.compiler = ctx;
2636                         top_block = this;
2637
2638                         ProcessParameters ();
2639                 }
2640
2641                 //
2642                 // Recreates a top level block from parameters block. Used for
2643                 // compiler generated methods where the original block comes from
2644                 // explicit child block. This works for already resolved blocks
2645                 // only to ensure we resolve them in the correct flow order
2646                 //
2647                 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2648                         : base (source, parameters)
2649                 {
2650                         this.compiler = source.TopBlock.compiler;
2651                         top_block = this;
2652                 }
2653
2654                 public override void AddLocalName (string name, INamedBlockVariable li)
2655                 {
2656                         if (names == null)
2657                                 names = new Dictionary<string, object> ();
2658
2659                         object value;
2660                         if (!names.TryGetValue (name, out value)) {
2661                                 names.Add (name, li);
2662                                 return;
2663                         }
2664
2665                         INamedBlockVariable existing = value as INamedBlockVariable;
2666                         List<INamedBlockVariable> existing_list;
2667                         if (existing != null) {
2668                                 existing_list = new List<INamedBlockVariable> ();
2669                                 existing_list.Add (existing);
2670                                 names[name] = existing_list;
2671                         } else {
2672                                 existing_list = (List<INamedBlockVariable>) value;
2673                         }
2674
2675                         //
2676                         // A collision checking between local names
2677                         //
2678                         for (int i = 0; i < existing_list.Count; ++i) {
2679                                 existing = existing_list[i];
2680                                 Block b = existing.Block;
2681
2682                                 // Collision at same level
2683                                 if (li.Block == b) {
2684                                         li.Block.Error_AlreadyDeclared (name, li);
2685                                         break;
2686                                 }
2687
2688                                 // Collision with parent
2689                                 b = li.Block;
2690                                 while ((b = b.Parent) != null) {
2691                                         if (existing.Block == b) {
2692                                                 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2693                                                 i = existing_list.Count;
2694                                                 break;
2695                                         }
2696                                 }
2697
2698                                 // Collision with with children
2699                                 b = existing.Block;
2700                                 while ((b = b.Parent) != null) {
2701                                         if (li.Block == b) {
2702                                                 li.Block.Error_AlreadyDeclared (name, li, "child");
2703                                                 i = existing_list.Count;
2704                                                 break;
2705                                         }
2706                                 }
2707                         }
2708
2709                         existing_list.Add (li);
2710                 }
2711
2712                 public void AddLabel (string name, LabeledStatement label)
2713                 {
2714                         if (labels == null)
2715                                 labels = new Dictionary<string, object> ();
2716
2717                         object value;
2718                         if (!labels.TryGetValue (name, out value)) {
2719                                 labels.Add (name, label);
2720                                 return;
2721                         }
2722
2723                         LabeledStatement existing = value as LabeledStatement;
2724                         List<LabeledStatement> existing_list;
2725                         if (existing != null) {
2726                                 existing_list = new List<LabeledStatement> ();
2727                                 existing_list.Add (existing);
2728                                 labels[name] = existing_list;
2729                         } else {
2730                                 existing_list = (List<LabeledStatement>) value;
2731                         }
2732
2733                         //
2734                         // A collision checking between labels
2735                         //
2736                         for (int i = 0; i < existing_list.Count; ++i) {
2737                                 existing = existing_list[i];
2738                                 Block b = existing.Block;
2739
2740                                 // Collision at same level
2741                                 if (label.Block == b) {
2742                                         Report.SymbolRelatedToPreviousError (existing.loc, name);
2743                                         Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2744                                         break;
2745                                 }
2746
2747                                 // Collision with parent
2748                                 b = label.Block;
2749                                 while ((b = b.Parent) != null) {
2750                                         if (existing.Block == b) {
2751                                                 Report.Error (158, label.loc,
2752                                                         "The label `{0}' shadows another label by the same name in a contained scope", name);
2753                                                 i = existing_list.Count;
2754                                                 break;
2755                                         }
2756                                 }
2757
2758                                 // Collision with with children
2759                                 b = existing.Block;
2760                                 while ((b = b.Parent) != null) {
2761                                         if (label.Block == b) {
2762                                                 Report.Error (158, label.loc,
2763                                                         "The label `{0}' shadows another label by the same name in a contained scope", name);
2764                                                 i = existing_list.Count;
2765                                                 break;
2766                                         }
2767                                 }
2768                         }
2769
2770                         existing_list.Add (label);
2771                 }
2772
2773                 //
2774                 // Creates an arguments set from all parameters, useful for method proxy calls
2775                 //
2776                 public Arguments GetAllParametersArguments ()
2777                 {
2778                         int count = parameters.Count;
2779                         Arguments args = new Arguments (count);
2780                         for (int i = 0; i < count; ++i) {
2781                                 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2782                                 args.Add (new Argument (arg_expr));
2783                         }
2784
2785                         return args;
2786                 }
2787
2788                 //
2789                 // Lookup inside a block, the returned value can represent 3 states
2790                 //
2791                 // true+variable: A local name was found and it's valid
2792                 // false+variable: A local name was found in a child block only
2793                 // false+null: No local name was found
2794                 //
2795                 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2796                 {
2797                         if (names == null)
2798                                 return false;
2799
2800                         object value;
2801                         if (!names.TryGetValue (name, out value))
2802                                 return false;
2803
2804                         variable = value as INamedBlockVariable;
2805                         Block b = block;
2806                         if (variable != null) {
2807                                 do {
2808                                         if (variable.Block == b.Original)
2809                                                 return true;
2810
2811                                         b = b.Parent;
2812                                 } while (b != null);
2813
2814                                 b = variable.Block;
2815                                 do {
2816                                         if (block == b)
2817                                                 return false;
2818
2819                                         b = b.Parent;
2820                                 } while (b != null);
2821                         } else {
2822                                 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2823                                 for (int i = 0; i < list.Count; ++i) {
2824                                         variable = list[i];
2825                                         do {
2826                                                 if (variable.Block == b.Original)
2827                                                         return true;
2828
2829                                                 b = b.Parent;
2830                                         } while (b != null);
2831
2832                                         b = variable.Block;
2833                                         do {
2834                                                 if (block == b)
2835                                                         return false;
2836
2837                                                 b = b.Parent;
2838                                         } while (b != null);
2839
2840                                         b = block;
2841                                 }
2842                         }
2843
2844                         variable = null;
2845                         return false;
2846                 }
2847
2848                 public LabeledStatement GetLabel (string name, Block block)
2849                 {
2850                         if (labels == null)
2851                                 return null;
2852
2853                         object value;
2854                         if (!labels.TryGetValue (name, out value)) {
2855                                 return null;
2856                         }
2857
2858                         var label = value as LabeledStatement;
2859                         Block b = block;
2860                         if (label != null) {
2861                                 if (label.Block == b.Original)
2862                                         return label;
2863
2864                                 // TODO: Temporary workaround for the switch block implicit label block
2865                                 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2866                                         return label;
2867                         } else {
2868                                 List<LabeledStatement> list = (List<LabeledStatement>) value;
2869                                 for (int i = 0; i < list.Count; ++i) {
2870                                         label = list[i];
2871                                         if (label.Block == b.Original)
2872                                                 return label;
2873
2874                                         // TODO: Temporary workaround for the switch block implicit label block
2875                                         if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2876                                                 return label;
2877                                 }
2878                         }
2879                                 
2880                         return null;
2881                 }
2882
2883                 // <summary>
2884                 //   Returns the "this" instance variable of this block.
2885                 //   See AddThisVariable() for more information.
2886                 // </summary>
2887                 public LocalVariable ThisVariable {
2888                         get { return this_variable; }
2889                 }
2890
2891                 // <summary>
2892                 //   This is used by non-static `struct' constructors which do not have an
2893                 //   initializer - in this case, the constructor must initialize all of the
2894                 //   struct's fields.  To do this, we add a "this" variable and use the flow
2895                 //   analysis code to ensure that it's been fully initialized before control
2896                 //   leaves the constructor.
2897                 // </summary>
2898                 public LocalVariable AddThisVariable (BlockContext bc, TypeContainer ds, Location l)
2899                 {
2900                         if (this_variable == null) {
2901                                 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, l);
2902                                 this_variable.Type = ds.CurrentType;
2903                                 this_variable.PrepareForFlowAnalysis (bc);
2904                         }
2905
2906                         return this_variable;
2907                 }
2908
2909                 public bool IsIterator {
2910                         get { return (flags & Flags.IsIterator) != 0; }
2911                         set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2912                 }
2913
2914                 public bool IsThisAssigned (BlockContext ec)
2915                 {
2916                         return this_variable == null || this_variable.IsThisAssigned (ec, this);
2917                 }
2918
2919                 public override void Emit (EmitContext ec)
2920                 {
2921                         if (Report.Errors > 0)
2922                                 return;
2923
2924 #if PRODUCTION
2925                         try {
2926 #endif
2927                         if (ec.HasReturnLabel)
2928                                 ec.ReturnLabel = ec.DefineLabel ();
2929
2930                         base.Emit (ec);
2931
2932                         ec.Mark (EndLocation);
2933
2934                         if (ec.HasReturnLabel)
2935                                 ec.MarkLabel (ec.ReturnLabel);
2936
2937                         if (ec.return_value != null) {
2938                                 ec.Emit (OpCodes.Ldloc, ec.return_value);
2939                                 ec.Emit (OpCodes.Ret);
2940                         } else {
2941                                 //
2942                                 // If `HasReturnLabel' is set, then we already emitted a
2943                                 // jump to the end of the method, so we must emit a `ret'
2944                                 // there.
2945                                 //
2946                                 // Unfortunately, System.Reflection.Emit automatically emits
2947                                 // a leave to the end of a finally block.  This is a problem
2948                                 // if no code is following the try/finally block since we may
2949                                 // jump to a point after the end of the method.
2950                                 // As a workaround, we're always creating a return label in
2951                                 // this case.
2952                                 //
2953
2954                                 if (ec.HasReturnLabel || !unreachable) {
2955                                         if (ec.ReturnType != TypeManager.void_type)
2956                                                 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
2957                                         ec.Emit (OpCodes.Ret);
2958                                 }
2959                         }
2960
2961 #if PRODUCTION
2962                         } catch (Exception e){
2963                                 Console.WriteLine ("Exception caught by the compiler while emitting:");
2964                                 Console.WriteLine ("   Block that caused the problem begin at: " + block.loc);
2965                                         
2966                                 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
2967                                 throw;
2968                         }
2969 #endif
2970                 }
2971
2972                 protected override void EmitSymbolInfo (EmitContext ec)
2973                 {
2974                         AnonymousExpression ae = ec.CurrentAnonymousMethod;
2975                         if ((ae != null) && (ae.Storey != null))
2976                                 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2977
2978                         base.EmitSymbolInfo (ec);
2979                 }
2980         }
2981         
2982         public class SwitchLabel {
2983                 Expression label;
2984                 object converted;
2985                 Location loc;
2986
2987                 Label il_label;
2988                 bool  il_label_set;
2989                 Label il_label_code;
2990                 bool  il_label_code_set;
2991
2992                 public static readonly object NullStringCase = new object ();
2993
2994                 //
2995                 // if expr == null, then it is the default case.
2996                 //
2997                 public SwitchLabel (Expression expr, Location l)
2998                 {
2999                         label = expr;
3000                         loc = l;
3001                 }
3002
3003                 public Expression Label {
3004                         get {
3005                                 return label;
3006                         }
3007                 }
3008
3009                 public Location Location {
3010                         get { return loc; }
3011                 }
3012
3013                 public object Converted {
3014                         get {
3015                                 return converted;
3016                         }
3017                 }
3018
3019                 public Label GetILLabel (EmitContext ec)
3020                 {
3021                         if (!il_label_set){
3022                                 il_label = ec.DefineLabel ();
3023                                 il_label_set = true;
3024                         }
3025                         return il_label;
3026                 }
3027
3028                 public Label GetILLabelCode (EmitContext ec)
3029                 {
3030                         if (!il_label_code_set){
3031                                 il_label_code = ec.DefineLabel ();
3032                                 il_label_code_set = true;
3033                         }
3034                         return il_label_code;
3035                 }                               
3036                 
3037                 //
3038                 // Resolves the expression, reduces it to a literal if possible
3039                 // and then converts it to the requested type.
3040                 //
3041                 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3042                 {       
3043                         Expression e = label.Resolve (ec);
3044
3045                         if (e == null)
3046                                 return false;
3047
3048                         Constant c = e as Constant;
3049                         if (c == null){
3050                                 ec.Report.Error (150, loc, "A constant value is expected");
3051                                 return false;
3052                         }
3053
3054                         if (required_type == TypeManager.string_type && c.GetValue () == null) {
3055                                 converted = NullStringCase;
3056                                 return true;
3057                         }
3058
3059                         if (allow_nullable && c.GetValue () == null) {
3060                                 converted = NullStringCase;
3061                                 return true;
3062                         }
3063                         
3064                         c = c.ImplicitConversionRequired (ec, required_type, loc);
3065                         if (c == null)
3066                                 return false;
3067
3068                         converted = c.GetValue ();
3069                         return true;
3070                 }
3071
3072                 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3073                 {
3074                         string label;
3075                         if (converted == null)
3076                                 label = "default";
3077                         else if (converted == NullStringCase)
3078                                 label = "null";
3079                         else
3080                                 label = converted.ToString ();
3081                         
3082                         ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3083                         ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3084                 }
3085
3086                 public SwitchLabel Clone (CloneContext clonectx)
3087                 {
3088                         return new SwitchLabel (label.Clone (clonectx), loc);
3089                 }
3090         }
3091
3092         public class SwitchSection {
3093                 public readonly List<SwitchLabel> Labels;
3094                 public readonly Block Block;
3095                 
3096                 public SwitchSection (List<SwitchLabel> labels, Block block)
3097                 {
3098                         Labels = labels;
3099                         Block = block;
3100                 }
3101
3102                 public SwitchSection Clone (CloneContext clonectx)
3103                 {
3104                         var cloned_labels = new List<SwitchLabel> ();
3105
3106                         foreach (SwitchLabel sl in cloned_labels)
3107                                 cloned_labels.Add (sl.Clone (clonectx));
3108                         
3109                         return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3110                 }
3111         }
3112         
3113         public class Switch : Statement {
3114                 public List<SwitchSection> Sections;
3115                 public Expression Expr;
3116
3117                 /// <summary>
3118                 ///   Maps constants whose type type SwitchType to their  SwitchLabels.
3119                 /// </summary>
3120                 public IDictionary<object, SwitchLabel> Elements;
3121
3122                 /// <summary>
3123                 ///   The governing switch type
3124                 /// </summary>
3125                 public TypeSpec SwitchType;
3126
3127                 //
3128                 // Computed
3129                 //
3130                 Label default_target;
3131                 Label null_target;
3132                 Expression new_expr;
3133                 bool is_constant;
3134                 bool has_null_case;
3135                 SwitchSection constant_section;
3136                 SwitchSection default_section;
3137
3138                 ExpressionStatement string_dictionary;
3139                 FieldExpr switch_cache_field;
3140                 static int unique_counter;
3141                 ExplicitBlock block;
3142
3143                 //
3144                 // Nullable Types support
3145                 //
3146                 Nullable.Unwrap unwrap;
3147
3148                 protected bool HaveUnwrap {
3149                         get { return unwrap != null; }
3150                 }
3151
3152                 //
3153                 // The types allowed to be implicitly cast from
3154                 // on the governing type
3155                 //
3156                 static TypeSpec [] allowed_types;
3157
3158                 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3159                 {
3160                         Expr = e;
3161                         this.block = block;
3162                         Sections = sects;
3163                         loc = l;
3164                 }
3165
3166                 public ExplicitBlock Block {
3167                         get {
3168                                 return block;
3169                         }
3170                 }
3171
3172                 public bool GotDefault {
3173                         get {
3174                                 return default_section != null;
3175                         }
3176                 }
3177
3178                 public Label DefaultTarget {
3179                         get {
3180                                 return default_target;
3181                         }
3182                 }
3183
3184                 //
3185                 // Determines the governing type for a switch.  The returned
3186                 // expression might be the expression from the switch, or an
3187                 // expression that includes any potential conversions to the
3188                 // integral types or to string.
3189                 //
3190                 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3191                 {
3192                         TypeSpec t = expr.Type;
3193
3194                         if (t == TypeManager.byte_type ||
3195                             t == TypeManager.sbyte_type ||
3196                             t == TypeManager.ushort_type ||
3197                             t == TypeManager.short_type ||
3198                             t == TypeManager.uint32_type ||
3199                             t == TypeManager.int32_type ||
3200                             t == TypeManager.uint64_type ||
3201                             t == TypeManager.int64_type ||
3202                             t == TypeManager.char_type ||
3203                             t == TypeManager.string_type ||
3204                             t == TypeManager.bool_type ||
3205                             TypeManager.IsEnumType (t))
3206                                 return expr;
3207
3208                         if (allowed_types == null){
3209                                 allowed_types = new TypeSpec [] {
3210                                         TypeManager.sbyte_type,
3211                                         TypeManager.byte_type,
3212                                         TypeManager.short_type,
3213                                         TypeManager.ushort_type,
3214                                         TypeManager.int32_type,
3215                                         TypeManager.uint32_type,
3216                                         TypeManager.int64_type,
3217                                         TypeManager.uint64_type,
3218                                         TypeManager.char_type,
3219                                         TypeManager.string_type
3220                                 };
3221                         }
3222
3223                         //
3224                         // Try to find a *user* defined implicit conversion.
3225                         //
3226                         // If there is no implicit conversion, or if there are multiple
3227                         // conversions, we have to report an error
3228                         //
3229                         Expression converted = null;
3230                         foreach (TypeSpec tt in allowed_types){
3231                                 Expression e;
3232                                 
3233                                 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3234                                 if (e == null)
3235                                         continue;
3236
3237                                 //
3238                                 // Ignore over-worked ImplicitUserConversions that do
3239                                 // an implicit conversion in addition to the user conversion.
3240                                 // 
3241                                 if (!(e is UserCast))
3242                                         continue;
3243
3244                                 if (converted != null){
3245                                         ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3246                                         return null;
3247                                 }
3248
3249                                 converted = e;
3250                         }
3251                         return converted;
3252                 }
3253
3254                 //
3255                 // Performs the basic sanity checks on the switch statement
3256                 // (looks for duplicate keys and non-constant expressions).
3257                 //
3258                 // It also returns a hashtable with the keys that we will later
3259                 // use to compute the switch tables
3260                 //
3261                 bool CheckSwitch (ResolveContext ec)
3262                 {
3263                         bool error = false;
3264                         Elements = new Dictionary<object, SwitchLabel> ();
3265                                 
3266                         foreach (SwitchSection ss in Sections){
3267                                 foreach (SwitchLabel sl in ss.Labels){
3268                                         if (sl.Label == null){
3269                                                 if (default_section != null){
3270                                                         sl.Error_AlreadyOccurs (ec, SwitchType, (SwitchLabel)default_section.Labels [0]);
3271                                                         error = true;
3272                                                 }
3273                                                 default_section = ss;
3274                                                 continue;
3275                                         }
3276
3277                                         if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3278                                                 error = true;
3279                                                 continue;
3280                                         }
3281                                         
3282                                         object key = sl.Converted;
3283                                         if (key == SwitchLabel.NullStringCase)
3284                                                 has_null_case = true;
3285
3286                                         try {
3287                                                 Elements.Add (key, sl);
3288                                         } catch (ArgumentException) {
3289                                                 sl.Error_AlreadyOccurs (ec, SwitchType, Elements [key]);
3290                                                 error = true;
3291                                         }
3292                                 }
3293                         }
3294                         return !error;
3295                 }
3296
3297                 void EmitObjectInteger (EmitContext ec, object k)
3298                 {
3299                         if (k is int)
3300                                 ec.EmitInt ((int) k);
3301                         else if (k is Constant) {
3302                                 EmitObjectInteger (ec, ((Constant) k).GetValue ());
3303                         } 
3304                         else if (k is uint)
3305                                 ec.EmitInt (unchecked ((int) (uint) k));
3306                         else if (k is long)
3307                         {
3308                                 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3309                                 {
3310                                         ec.EmitInt ((int) (long) k);
3311                                         ec.Emit (OpCodes.Conv_I8);
3312                                 }
3313                                 else
3314                                         ec.EmitLong ((long) k);
3315                         }
3316                         else if (k is ulong)
3317                         {
3318                                 ulong ul = (ulong) k;
3319                                 if (ul < (1L<<32))
3320                                 {
3321                                         ec.EmitInt (unchecked ((int) ul));
3322                                         ec.Emit (OpCodes.Conv_U8);
3323                                 }
3324                                 else
3325                                 {
3326                                         ec.EmitLong (unchecked ((long) ul));
3327                                 }
3328                         }
3329                         else if (k is char)
3330                                 ec.EmitInt ((int) ((char) k));
3331                         else if (k is sbyte)
3332                                 ec.EmitInt ((int) ((sbyte) k));
3333                         else if (k is byte)
3334                                 ec.EmitInt ((int) ((byte) k));
3335                         else if (k is short)
3336                                 ec.EmitInt ((int) ((short) k));
3337                         else if (k is ushort)
3338                                 ec.EmitInt ((int) ((ushort) k));
3339                         else if (k is bool)
3340                                 ec.EmitInt (((bool) k) ? 1 : 0);
3341                         else
3342                                 throw new Exception ("Unhandled case");
3343                 }
3344                 
3345                 // structure used to hold blocks of keys while calculating table switch
3346                 class KeyBlock : IComparable
3347                 {
3348                         public KeyBlock (long _first)
3349                         {
3350                                 first = last = _first;
3351                         }
3352                         public long first;
3353                         public long last;
3354                         public List<object> element_keys;
3355                         // how many items are in the bucket
3356                         public int Size = 1;
3357                         public int Length
3358                         {
3359                                 get { return (int) (last - first + 1); }
3360                         }
3361                         public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3362                         {
3363                                 return kb_last.last - kb_first.first + 1;
3364                         }
3365                         public int CompareTo (object obj)
3366                         {
3367                                 KeyBlock kb = (KeyBlock) obj;
3368                                 int nLength = Length;
3369                                 int nLengthOther = kb.Length;
3370                                 if (nLengthOther == nLength)
3371                                         return (int) (kb.first - first);
3372                                 return nLength - nLengthOther;
3373                         }
3374                 }
3375
3376                 /// <summary>
3377                 /// This method emits code for a lookup-based switch statement (non-string)
3378                 /// Basically it groups the cases into blocks that are at least half full,
3379                 /// and then spits out individual lookup opcodes for each block.
3380                 /// It emits the longest blocks first, and short blocks are just
3381                 /// handled with direct compares.
3382                 /// </summary>
3383                 /// <param name="ec"></param>
3384                 /// <param name="val"></param>
3385                 /// <returns></returns>
3386                 void TableSwitchEmit (EmitContext ec, Expression val)
3387                 {
3388                         int element_count = Elements.Count;
3389                         object [] element_keys = new object [element_count];
3390                         Elements.Keys.CopyTo (element_keys, 0);
3391                         Array.Sort (element_keys);
3392
3393                         // initialize the block list with one element per key
3394                         var key_blocks = new List<KeyBlock> (element_count);
3395                         foreach (object key in element_keys)
3396                                 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3397
3398                         KeyBlock current_kb;
3399                         // iteratively merge the blocks while they are at least half full
3400                         // there's probably a really cool way to do this with a tree...
3401                         while (key_blocks.Count > 1)
3402                         {
3403                                 var key_blocks_new = new List<KeyBlock> ();
3404                                 current_kb = (KeyBlock) key_blocks [0];
3405                                 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3406                                 {
3407                                         KeyBlock kb = (KeyBlock) key_blocks [ikb];
3408                                         if ((current_kb.Size + kb.Size) * 2 >=  KeyBlock.TotalLength (current_kb, kb))
3409                                         {
3410                                                 // merge blocks
3411                                                 current_kb.last = kb.last;
3412                                                 current_kb.Size += kb.Size;
3413                                         }
3414                                         else
3415                                         {
3416                                                 // start a new block
3417                                                 key_blocks_new.Add (current_kb);
3418                                                 current_kb = kb;
3419                                         }
3420                                 }
3421                                 key_blocks_new.Add (current_kb);
3422                                 if (key_blocks.Count == key_blocks_new.Count)
3423                                         break;
3424                                 key_blocks = key_blocks_new;
3425                         }
3426
3427                         // initialize the key lists
3428                         foreach (KeyBlock kb in key_blocks)
3429                                 kb.element_keys = new List<object> ();
3430
3431                         // fill the key lists
3432                         int iBlockCurr = 0;
3433                         if (key_blocks.Count > 0) {
3434                                 current_kb = (KeyBlock) key_blocks [0];
3435                                 foreach (object key in element_keys)
3436                                 {
3437                                         bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3438                                                 System.Convert.ToInt64 (key) > current_kb.last;
3439                                         if (next_block)
3440                                                 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3441                                         current_kb.element_keys.Add (key);
3442                                 }
3443                         }
3444
3445                         // sort the blocks so we can tackle the largest ones first
3446                         key_blocks.Sort ();
3447
3448                         // okay now we can start...
3449                         Label lbl_end = ec.DefineLabel ();      // at the end ;-)
3450                         Label lbl_default = default_target;
3451
3452                         Type type_keys = null;
3453                         if (element_keys.Length > 0)
3454                                 type_keys = element_keys [0].GetType ();        // used for conversions
3455
3456                         TypeSpec compare_type;
3457                         
3458                         if (TypeManager.IsEnumType (SwitchType))
3459                                 compare_type = EnumSpec.GetUnderlyingType (SwitchType);
3460                         else
3461                                 compare_type = SwitchType;
3462                         
3463                         for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3464                         {
3465                                 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3466                                 lbl_default = (iBlock == 0) ? default_target : ec.DefineLabel ();
3467                                 if (kb.Length <= 2)
3468                                 {
3469                                         foreach (object key in kb.element_keys) {
3470                                                 SwitchLabel sl = (SwitchLabel) Elements [key];
3471                                                 if (key is int && (int) key == 0) {
3472                                                         val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3473                                                 } else {
3474                                                         val.Emit (ec);
3475                                                         EmitObjectInteger (ec, key);
3476                                                         ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3477                                                 }
3478                                         }
3479                                 }
3480                                 else
3481                                 {
3482                                         // TODO: if all the keys in the block are the same and there are
3483                                         //       no gaps/defaults then just use a range-check.
3484                                         if (compare_type == TypeManager.int64_type ||
3485                                                 compare_type == TypeManager.uint64_type)
3486                                         {
3487                                                 // TODO: optimize constant/I4 cases
3488
3489                                                 // check block range (could be > 2^31)
3490                                                 val.Emit (ec);
3491                                                 EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3492                                                 ec.Emit (OpCodes.Blt, lbl_default);
3493                                                 val.Emit (ec);
3494                                                 EmitObjectInteger (ec, System.Convert.ChangeType (kb.last, type_keys));
3495                                                 ec.Emit (OpCodes.Bgt, lbl_default);
3496
3497                                                 // normalize range
3498                                                 val.Emit (ec);
3499                                                 if (kb.first != 0)
3500                                                 {
3501                                                         EmitObjectInteger (ec, System.Convert.ChangeType (kb.first, type_keys));
3502                                                         ec.Emit (OpCodes.Sub);
3503                                                 }
3504                                                 ec.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
3505                                         }
3506                                         else
3507                                         {
3508                                                 // normalize range
3509                                                 val.Emit (ec);
3510                                                 int first = (int) kb.first;
3511                                                 if (first > 0)
3512                                                 {
3513                                                         ec.EmitInt (first);
3514                                                         ec.Emit (OpCodes.Sub);
3515                                                 }
3516                                                 else if (first < 0)
3517                                                 {
3518                                                         ec.EmitInt (-first);
3519                                                         ec.Emit (OpCodes.Add);
3520                                                 }
3521                                         }
3522
3523                                         // first, build the list of labels for the switch
3524                                         int iKey = 0;
3525                                         int cJumps = kb.Length;
3526                                         Label [] switch_labels = new Label [cJumps];
3527                                         for (int iJump = 0; iJump < cJumps; iJump++)
3528                                         {
3529                                                 object key = kb.element_keys [iKey];
3530                                                 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3531                                                 {
3532                                                         SwitchLabel sl = (SwitchLabel) Elements [key];
3533                                                         switch_labels [iJump] = sl.GetILLabel (ec);
3534                                                         iKey++;
3535                                                 }
3536                                                 else
3537                                                         switch_labels [iJump] = lbl_default;
3538                                         }
3539                                         // emit the switch opcode
3540                                         ec.Emit (OpCodes.Switch, switch_labels);
3541                                 }
3542
3543                                 // mark the default for this block
3544                                 if (iBlock != 0)
3545                                         ec.MarkLabel (lbl_default);
3546                         }
3547
3548                         // TODO: find the default case and emit it here,
3549                         //       to prevent having to do the following jump.
3550                         //       make sure to mark other labels in the default section
3551
3552                         // the last default just goes to the end
3553                         if (element_keys.Length > 0)
3554                                 ec.Emit (OpCodes.Br, lbl_default);
3555
3556                         // now emit the code for the sections
3557                         bool found_default = false;
3558
3559                         foreach (SwitchSection ss in Sections) {
3560                                 foreach (SwitchLabel sl in ss.Labels) {
3561                                         if (sl.Converted == SwitchLabel.NullStringCase) {
3562                                                 ec.MarkLabel (null_target);
3563                                         } else if (sl.Label == null) {
3564                                                 ec.MarkLabel (lbl_default);
3565                                                 found_default = true;
3566                                                 if (!has_null_case)
3567                                                         ec.MarkLabel (null_target);
3568                                         }
3569                                         ec.MarkLabel (sl.GetILLabel (ec));
3570                                         ec.MarkLabel (sl.GetILLabelCode (ec));
3571                                 }
3572                                 ss.Block.Emit (ec);
3573                         }
3574                         
3575                         if (!found_default) {
3576                                 ec.MarkLabel (lbl_default);
3577                                 if (!has_null_case) {
3578                                         ec.MarkLabel (null_target);
3579                                 }
3580                         }
3581                         
3582                         ec.MarkLabel (lbl_end);
3583                 }
3584
3585                 SwitchSection FindSection (SwitchLabel label)
3586                 {
3587                         foreach (SwitchSection ss in Sections){
3588                                 foreach (SwitchLabel sl in ss.Labels){
3589                                         if (label == sl)
3590                                                 return ss;
3591                                 }
3592                         }
3593
3594                         return null;
3595                 }
3596
3597                 public static void Reset ()
3598                 {
3599                         unique_counter = 0;
3600                         allowed_types = null;
3601                 }
3602
3603                 public override bool Resolve (BlockContext ec)
3604                 {
3605                         Expr = Expr.Resolve (ec);
3606                         if (Expr == null)
3607                                 return false;
3608
3609                         new_expr = SwitchGoverningType (ec, Expr);
3610
3611                         if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3612                                 unwrap = Nullable.Unwrap.Create (Expr, false);
3613                                 if (unwrap == null)
3614                                         return false;
3615
3616                                 new_expr = SwitchGoverningType (ec, unwrap);
3617                         }
3618
3619                         if (new_expr == null){
3620                                 ec.Report.Error (151, loc,
3621                                         "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3622                                         TypeManager.CSharpName (Expr.Type));
3623                                 return false;
3624                         }
3625
3626                         // Validate switch.
3627                         SwitchType = new_expr.Type;
3628
3629                         if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3630                                 ec.Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3631                                 return false;
3632                         }
3633
3634                         if (!CheckSwitch (ec))
3635                                 return false;
3636
3637                         if (HaveUnwrap)
3638                                 Elements.Remove (SwitchLabel.NullStringCase);
3639
3640                         Switch old_switch = ec.Switch;
3641                         ec.Switch = this;
3642                         ec.Switch.SwitchType = SwitchType;
3643
3644                         Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3645                         ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3646
3647                         var constant = new_expr as Constant;
3648                         if (constant != null) {
3649                                 is_constant = true;
3650                                 object key = constant.GetValue ();
3651                                 SwitchLabel label;
3652                                 if (Elements.TryGetValue (key, out label))
3653                                         constant_section = FindSection (label);
3654
3655                                 if (constant_section == null)
3656                                         constant_section = default_section;
3657                         }
3658
3659                         bool first = true;
3660                         bool ok = true;
3661                         foreach (SwitchSection ss in Sections){
3662                                 if (!first)
3663                                         ec.CurrentBranching.CreateSibling (
3664                                                 null, FlowBranching.SiblingType.SwitchSection);
3665                                 else
3666                                         first = false;
3667
3668                                 if (is_constant && (ss != constant_section)) {
3669                                         // If we're a constant switch, we're only emitting
3670                                         // one single section - mark all the others as
3671                                         // unreachable.
3672                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
3673                                         if (!ss.Block.ResolveUnreachable (ec, true)) {
3674                                                 ok = false;
3675                                         }
3676                                 } else {
3677                                         if (!ss.Block.Resolve (ec))
3678                                                 ok = false;
3679                                 }
3680                         }
3681
3682                         if (default_section == null)
3683                                 ec.CurrentBranching.CreateSibling (
3684                                         null, FlowBranching.SiblingType.SwitchSection);
3685
3686                         ec.EndFlowBranching ();
3687                         ec.Switch = old_switch;
3688
3689                         Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3690
3691                         if (!ok)
3692                                 return false;
3693
3694                         if (SwitchType == TypeManager.string_type && !is_constant) {
3695                                 // TODO: Optimize single case, and single+default case
3696                                 ResolveStringSwitchMap (ec);
3697                         }
3698
3699                         return true;
3700                 }
3701
3702                 void ResolveStringSwitchMap (ResolveContext ec)
3703                 {
3704                         FullNamedExpression string_dictionary_type;
3705                         if (TypeManager.generic_ienumerable_type != null) {
3706                                 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3707                                         new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3708
3709                                 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3710                                         new TypeArguments (
3711                                                 new TypeExpression (TypeManager.string_type, loc),
3712                                                 new TypeExpression (TypeManager.int32_type, loc)), loc);
3713                         } else {
3714                                 MemberAccess system_collections_generic = new MemberAccess (
3715                                         new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3716
3717                                 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3718                         }
3719
3720                         var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3721                         Field field = new Field (ctype, string_dictionary_type,
3722                                 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3723                                 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3724                         if (!field.Define ())
3725                                 return;
3726                         ctype.AddField (field);
3727
3728                         var init = new List<Expression> ();
3729                         int counter = 0;
3730                         Elements.Clear ();
3731                         string value = null;
3732                         foreach (SwitchSection section in Sections) {
3733                                 int last_count = init.Count;
3734                                 foreach (SwitchLabel sl in section.Labels) {
3735                                         if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3736                                                 continue;
3737
3738                                         value = (string) sl.Converted;
3739                                         var init_args = new List<Expression> (2);
3740                                         init_args.Add (new StringLiteral (value, sl.Location));
3741                                         init_args.Add (new IntConstant (counter, loc));
3742                                         init.Add (new CollectionElementInitializer (init_args, loc));
3743                                 }
3744
3745                                 //
3746                                 // Don't add empty sections
3747                                 //
3748                                 if (last_count == init.Count)
3749                                         continue;
3750
3751                                 Elements.Add (counter, section.Labels [0]);
3752                                 ++counter;
3753                         }
3754
3755                         Arguments args = new Arguments (1);
3756                         args.Add (new Argument (new IntConstant (init.Count, loc)));
3757                         Expression initializer = new NewInitialize (string_dictionary_type, args,
3758                                 new CollectionOrObjectInitializers (init, loc), loc);
3759
3760                         switch_cache_field = new FieldExpr (field, loc);
3761                         string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3762                 }
3763
3764                 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3765                 {
3766                         Label l_initialized = ec.DefineLabel ();
3767
3768                         //
3769                         // Skip initialization when value is null
3770                         //
3771                         value.EmitBranchable (ec, null_target, false);
3772
3773                         //
3774                         // Check if string dictionary is initialized and initialize
3775                         //
3776                         switch_cache_field.EmitBranchable (ec, l_initialized, true);
3777                         string_dictionary.EmitStatement (ec);
3778                         ec.MarkLabel (l_initialized);
3779
3780                         LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3781
3782                         ResolveContext rc = new ResolveContext (ec.MemberContext);
3783
3784                         if (TypeManager.generic_ienumerable_type != null) {
3785                                 Arguments get_value_args = new Arguments (2);
3786                                 get_value_args.Add (new Argument (value));
3787                                 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3788                                 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3789                                 if (get_item == null)
3790                                         return;
3791
3792                                 //
3793                                 // A value was not found, go to default case
3794                                 //
3795                                 get_item.EmitBranchable (ec, default_target, false);
3796                         } else {
3797                                 Arguments get_value_args = new Arguments (1);
3798                                 get_value_args.Add (new Argument (value));
3799
3800                                 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3801                                 if (get_item == null)
3802                                         return;
3803
3804                                 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3805                                 get_item_object.EmitAssign (ec, get_item, true, false);
3806                                 ec.Emit (OpCodes.Brfalse, default_target);
3807
3808                                 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3809                                         new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3810
3811                                 get_item_int.EmitStatement (ec);
3812                                 get_item_object.Release (ec);
3813                         }
3814
3815                         TableSwitchEmit (ec, string_switch_variable);
3816                         string_switch_variable.Release (ec);
3817                 }
3818                 
3819                 protected override void DoEmit (EmitContext ec)
3820                 {
3821                         //
3822                         // Needed to emit anonymous storey initialization
3823                         // Otherwise it does not contain any statements for now
3824                         //
3825                         block.Emit (ec);
3826
3827                         default_target = ec.DefineLabel ();
3828                         null_target = ec.DefineLabel ();
3829
3830                         // Store variable for comparission purposes
3831                         // TODO: Don't duplicate non-captured VariableReference
3832                         LocalTemporary value;
3833                         if (HaveUnwrap) {
3834                                 value = new LocalTemporary (SwitchType);
3835                                 unwrap.EmitCheck (ec);
3836                                 ec.Emit (OpCodes.Brfalse, null_target);
3837                                 new_expr.Emit (ec);
3838                                 value.Store (ec);
3839                         } else if (!is_constant) {
3840                                 value = new LocalTemporary (SwitchType);
3841                                 new_expr.Emit (ec);
3842                                 value.Store (ec);
3843                         } else
3844                                 value = null;
3845
3846                         //
3847                         // Setup the codegen context
3848                         //
3849                         Label old_end = ec.LoopEnd;
3850                         Switch old_switch = ec.Switch;
3851                         
3852                         ec.LoopEnd = ec.DefineLabel ();
3853                         ec.Switch = this;
3854
3855                         // Emit Code.
3856                         if (is_constant) {
3857                                 if (constant_section != null)
3858                                         constant_section.Block.Emit (ec);
3859                         } else if (string_dictionary != null) {
3860                                 DoEmitStringSwitch (value, ec);
3861                         } else {
3862                                 TableSwitchEmit (ec, value);
3863                         }
3864
3865                         if (value != null)
3866                                 value.Release (ec);
3867
3868                         // Restore context state. 
3869                         ec.MarkLabel (ec.LoopEnd);
3870
3871                         //
3872                         // Restore the previous context
3873                         //
3874                         ec.LoopEnd = old_end;
3875                         ec.Switch = old_switch;
3876                 }
3877
3878                 protected override void CloneTo (CloneContext clonectx, Statement t)
3879                 {
3880                         Switch target = (Switch) t;
3881
3882                         target.Expr = Expr.Clone (clonectx);
3883                         target.Sections = new List<SwitchSection> ();
3884                         foreach (SwitchSection ss in Sections){
3885                                 target.Sections.Add (ss.Clone (clonectx));
3886                         }
3887                 }
3888         }
3889
3890         // A place where execution can restart in an iterator
3891         public abstract class ResumableStatement : Statement
3892         {
3893                 bool prepared;
3894                 protected Label resume_point;
3895
3896                 public Label PrepareForEmit (EmitContext ec)
3897                 {
3898                         if (!prepared) {
3899                                 prepared = true;
3900                                 resume_point = ec.DefineLabel ();
3901                         }
3902                         return resume_point;
3903                 }
3904
3905                 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3906                 {
3907                         return end;
3908                 }
3909                 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3910                 {
3911                 }
3912         }
3913
3914         // Base class for statements that are implemented in terms of try...finally
3915         public abstract class ExceptionStatement : ResumableStatement
3916         {
3917                 bool code_follows;
3918                 Iterator iter;
3919                 List<ResumableStatement> resume_points;
3920                 int first_resume_pc;
3921                 protected Statement stmt;
3922                 Label dispose_try_block;
3923                 bool prepared_for_dispose, emitted_dispose;
3924
3925                 protected ExceptionStatement (Statement stmt, Location loc)
3926                 {
3927                         this.stmt = stmt;
3928                         this.loc = loc;
3929                 }
3930
3931                 #region Properties
3932
3933                 public Statement Statement {
3934                         get {
3935                                 return stmt;
3936                         }
3937                 }
3938
3939                 #endregion
3940
3941                 protected abstract void EmitPreTryBody (EmitContext ec);
3942                 protected abstract void EmitTryBody (EmitContext ec);
3943                 protected abstract void EmitFinallyBody (EmitContext ec);
3944
3945                 protected sealed override void DoEmit (EmitContext ec)
3946                 {
3947                         EmitPreTryBody (ec);
3948
3949                         if (resume_points != null) {
3950                                 ec.EmitInt ((int) Iterator.State.Running);
3951                                 ec.Emit (OpCodes.Stloc, iter.CurrentPC);
3952                         }
3953
3954                         ec.BeginExceptionBlock ();
3955
3956                         if (resume_points != null) {
3957                                 ec.MarkLabel (resume_point);
3958
3959                                 // For normal control flow, we want to fall-through the Switch
3960                                 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3961                                 ec.Emit (OpCodes.Ldloc, iter.CurrentPC);
3962                                 ec.EmitInt (first_resume_pc);
3963                                 ec.Emit (OpCodes.Sub);
3964
3965                                 Label [] labels = new Label [resume_points.Count];
3966                                 for (int i = 0; i < resume_points.Count; ++i)
3967                                         labels [i] = resume_points [i].PrepareForEmit (ec);
3968                                 ec.Emit (OpCodes.Switch, labels);
3969                         }
3970
3971                         EmitTryBody (ec);
3972
3973                         ec.BeginFinallyBlock ();
3974
3975                         Label start_finally = ec.DefineLabel ();
3976                         if (resume_points != null) {
3977                                 ec.Emit (OpCodes.Ldloc, iter.SkipFinally);
3978                                 ec.Emit (OpCodes.Brfalse_S, start_finally);
3979                                 ec.Emit (OpCodes.Endfinally);
3980                         }
3981
3982                         ec.MarkLabel (start_finally);
3983                         EmitFinallyBody (ec);
3984
3985                         ec.EndExceptionBlock ();
3986                 }
3987
3988                 public void SomeCodeFollows ()
3989                 {
3990                         code_follows = true;
3991                 }
3992
3993                 public override bool Resolve (BlockContext ec)
3994                 {
3995                         // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3996                         // So, ensure there's some IL code after this statement.
3997                         if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3998                                 ec.NeedReturnLabel ();
3999
4000                         iter = ec.CurrentIterator;
4001                         return true;
4002                 }
4003
4004                 public void AddResumePoint (ResumableStatement stmt, int pc)
4005                 {
4006                         if (resume_points == null) {
4007                                 resume_points = new List<ResumableStatement> ();
4008                                 first_resume_pc = pc;
4009                         }
4010
4011                         if (pc != first_resume_pc + resume_points.Count)
4012                                 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4013
4014                         resume_points.Add (stmt);
4015                 }
4016
4017                 public override Label PrepareForDispose (EmitContext ec, Label end)
4018                 {
4019                         if (!prepared_for_dispose) {
4020                                 prepared_for_dispose = true;
4021                                 dispose_try_block = ec.DefineLabel ();
4022                         }
4023                         return dispose_try_block;
4024                 }
4025
4026                 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4027                 {
4028                         if (emitted_dispose)
4029                                 return;
4030
4031                         emitted_dispose = true;
4032
4033                         Label end_of_try = ec.DefineLabel ();
4034
4035                         // Ensure that the only way we can get into this code is through a dispatcher
4036                         if (have_dispatcher)
4037                                 ec.Emit (OpCodes.Br, end);
4038
4039                         ec.BeginExceptionBlock ();
4040
4041                         ec.MarkLabel (dispose_try_block);
4042
4043                         Label [] labels = null;
4044                         for (int i = 0; i < resume_points.Count; ++i) {
4045                                 ResumableStatement s = (ResumableStatement) resume_points [i];
4046                                 Label ret = s.PrepareForDispose (ec, end_of_try);
4047                                 if (ret.Equals (end_of_try) && labels == null)
4048                                         continue;
4049                                 if (labels == null) {
4050                                         labels = new Label [resume_points.Count];
4051                                         for (int j = 0; j < i; ++j)
4052                                                 labels [j] = end_of_try;
4053                                 }
4054                                 labels [i] = ret;
4055                         }
4056
4057                         if (labels != null) {
4058                                 int j;
4059                                 for (j = 1; j < labels.Length; ++j)
4060                                         if (!labels [0].Equals (labels [j]))
4061                                                 break;
4062                                 bool emit_dispatcher = j < labels.Length;
4063
4064                                 if (emit_dispatcher) {
4065                                         //SymbolWriter.StartIteratorDispatcher (ec.ig);
4066                                         ec.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4067                                         ec.EmitInt (first_resume_pc);
4068                                         ec.Emit (OpCodes.Sub);
4069                                         ec.Emit (OpCodes.Switch, labels);
4070                                         //SymbolWriter.EndIteratorDispatcher (ec.ig);
4071                                 }
4072
4073                                 foreach (ResumableStatement s in resume_points)
4074                                         s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4075                         }
4076
4077                         ec.MarkLabel (end_of_try);
4078
4079                         ec.BeginFinallyBlock ();
4080
4081                         EmitFinallyBody (ec);
4082
4083                         ec.EndExceptionBlock ();
4084                 }
4085         }
4086
4087         public class Lock : ExceptionStatement {
4088                 Expression expr;
4089                 TemporaryVariableReference expr_copy;
4090                 TemporaryVariableReference lock_taken;
4091                         
4092                 public Lock (Expression expr, Statement stmt, Location loc)
4093                         : base (stmt, loc)
4094                 {
4095                         this.expr = expr;
4096                 }
4097
4098                 public override bool Resolve (BlockContext ec)
4099                 {
4100                         expr = expr.Resolve (ec);
4101                         if (expr == null)
4102                                 return false;
4103
4104                         if (!TypeManager.IsReferenceType (expr.Type)){
4105                                 ec.Report.Error (185, loc,
4106                                         "`{0}' is not a reference type as required by the lock statement",
4107                                         expr.Type.GetSignatureForError ());
4108                         }
4109
4110                         if (expr.Type.IsGenericParameter) {
4111                                 expr = Convert.ImplicitTypeParameterConversion (expr, TypeManager.object_type);
4112                         }
4113
4114                         VariableReference lv = expr as VariableReference;
4115                         bool locked;
4116                         if (lv != null) {
4117                                 locked = lv.IsLockedByStatement;
4118                                 lv.IsLockedByStatement = true;
4119                         } else {
4120                                 lv = null;
4121                                 locked = false;
4122                         }
4123
4124                         ec.StartFlowBranching (this);
4125                         Statement.Resolve (ec);
4126                         ec.EndFlowBranching ();
4127
4128                         if (lv != null) {
4129                                 lv.IsLockedByStatement = locked;
4130                         }
4131
4132                         base.Resolve (ec);
4133
4134                         //
4135                         // Have to keep original lock value around to unlock same location
4136                         // in the case the original has changed or is null
4137                         //
4138                         expr_copy = TemporaryVariableReference.Create (TypeManager.object_type, ec.CurrentBlock.Parent, loc);
4139                         expr_copy.Resolve (ec);
4140
4141                         //
4142                         // Ensure Monitor methods are available
4143                         //
4144                         if (ResolvePredefinedMethods (ec) > 1) {
4145                                 lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
4146                                 lock_taken.Resolve (ec);
4147                         }
4148
4149                         return true;
4150                 }
4151                 
4152                 protected override void EmitPreTryBody (EmitContext ec)
4153                 {
4154                         expr_copy.EmitAssign (ec, expr);
4155
4156                         if (lock_taken != null) {
4157                                 //
4158                                 // Initialize ref variable
4159                                 //
4160                                 lock_taken.EmitAssign (ec, new BoolLiteral (false, loc));
4161                         } else {
4162                                 //
4163                                 // Monitor.Enter (expr_copy)
4164                                 //
4165                                 expr_copy.Emit (ec);
4166                                 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4167                         }
4168                 }
4169
4170                 protected override void EmitTryBody (EmitContext ec)
4171                 {
4172                         //
4173                         // Monitor.Enter (expr_copy, ref lock_taken)
4174                         //
4175                         if (lock_taken != null) {
4176                                 expr_copy.Emit (ec);
4177                                 lock_taken.LocalInfo.CreateBuilder (ec);
4178                                 lock_taken.AddressOf (ec, AddressOp.Load);
4179                                 ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4180                         }
4181
4182                         Statement.Emit (ec);
4183                 }
4184
4185                 protected override void EmitFinallyBody (EmitContext ec)
4186                 {
4187                         //
4188                         // if (lock_taken) Monitor.Exit (expr_copy)
4189                         //
4190                         Label skip = ec.DefineLabel ();
4191
4192                         if (lock_taken != null) {
4193                                 lock_taken.Emit (ec);
4194                                 ec.Emit (OpCodes.Brfalse_S, skip);
4195                         }
4196
4197                         expr_copy.Emit (ec);
4198                         ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4199                         ec.MarkLabel (skip);
4200                 }
4201
4202                 int ResolvePredefinedMethods (ResolveContext rc)
4203                 {
4204                         if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4205                                 TypeSpec monitor_type = TypeManager.CoreLookupType (rc.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
4206
4207                                 if (monitor_type == null)
4208                                         return 0;
4209
4210                                 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4211                                 var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
4212                                         new[] {
4213                                                         new ParameterData (null, Parameter.Modifier.NONE),
4214                                                         new ParameterData (null, Parameter.Modifier.REF)
4215                                                 },
4216                                         new[] {
4217                                                         TypeManager.object_type,
4218                                                         TypeManager.bool_type
4219                                                 }, false), null);
4220
4221                                 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
4222                                 if (TypeManager.void_monitor_enter_object == null) {
4223                                         TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4224                                                 monitor_type, "Enter", loc, TypeManager.object_type);
4225                                 }
4226
4227                                 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4228                                         monitor_type, "Exit", loc, TypeManager.object_type);
4229                         }
4230
4231                         return TypeManager.void_monitor_enter_object.Parameters.Count;
4232                 }
4233
4234                 protected override void CloneTo (CloneContext clonectx, Statement t)
4235                 {
4236                         Lock target = (Lock) t;
4237
4238                         target.expr = expr.Clone (clonectx);
4239                         target.stmt = Statement.Clone (clonectx);
4240                 }
4241         }
4242
4243         public class Unchecked : Statement {
4244                 public Block Block;
4245                 
4246                 public Unchecked (Block b, Location loc)
4247                 {
4248                         Block = b;
4249                         b.Unchecked = true;
4250                         this.loc = loc;
4251                 }
4252
4253                 public override bool Resolve (BlockContext ec)
4254                 {
4255                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4256                                 return Block.Resolve (ec);
4257                 }
4258                 
4259                 protected override void DoEmit (EmitContext ec)
4260                 {
4261                         using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4262                                 Block.Emit (ec);
4263                 }
4264
4265                 protected override void CloneTo (CloneContext clonectx, Statement t)
4266                 {
4267                         Unchecked target = (Unchecked) t;
4268
4269                         target.Block = clonectx.LookupBlock (Block);
4270                 }
4271         }
4272
4273         public class Checked : Statement {
4274                 public Block Block;
4275                 
4276                 public Checked (Block b, Location loc)
4277                 {
4278                         Block = b;
4279                         b.Unchecked = false;
4280                         this.loc = loc;
4281                 }
4282
4283                 public override bool Resolve (BlockContext ec)
4284                 {
4285                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4286                                 return Block.Resolve (ec);
4287                 }
4288
4289                 protected override void DoEmit (EmitContext ec)
4290                 {
4291                         using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4292                                 Block.Emit (ec);
4293                 }
4294
4295                 protected override void CloneTo (CloneContext clonectx, Statement t)
4296                 {
4297                         Checked target = (Checked) t;
4298
4299                         target.Block = clonectx.LookupBlock (Block);
4300                 }
4301         }
4302
4303         public class Unsafe : Statement {
4304                 public Block Block;
4305
4306                 public Unsafe (Block b, Location loc)
4307                 {
4308                         Block = b;
4309                         Block.Unsafe = true;
4310                         this.loc = loc;
4311                 }
4312
4313                 public override bool Resolve (BlockContext ec)
4314                 {
4315                         if (ec.CurrentIterator != null)
4316                                 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4317
4318                         using (ec.Set (ResolveContext.Options.UnsafeScope))
4319                                 return Block.Resolve (ec);
4320                 }
4321                 
4322                 protected override void DoEmit (EmitContext ec)
4323                 {
4324                         Block.Emit (ec);
4325                 }
4326
4327                 protected override void CloneTo (CloneContext clonectx, Statement t)
4328                 {
4329                         Unsafe target = (Unsafe) t;
4330
4331                         target.Block = clonectx.LookupBlock (Block);
4332                 }
4333         }
4334
4335         // 
4336         // Fixed statement
4337         //
4338         public class Fixed : Statement
4339         {
4340                 abstract class Emitter : ShimExpression
4341                 {
4342                         protected LocalVariable vi;
4343
4344                         protected Emitter (Expression expr, LocalVariable li)
4345                                 : base (expr)
4346                         {
4347                                 vi = li;
4348                         }
4349
4350                         public abstract void EmitExit (EmitContext ec);
4351                 }
4352
4353                 class ExpressionEmitter : Emitter {
4354                         public ExpressionEmitter (Expression converted, LocalVariable li) :
4355                                 base (converted, li)
4356                         {
4357                         }
4358
4359                         protected override Expression DoResolve (ResolveContext rc)
4360                         {
4361                                 throw new NotImplementedException ();
4362                         }
4363
4364                         public override void Emit (EmitContext ec) {
4365                                 //
4366                                 // Store pointer in pinned location
4367                                 //
4368                                 expr.Emit (ec);
4369                                 vi.EmitAssign (ec);
4370                         }
4371
4372                         public override void EmitExit (EmitContext ec)
4373                         {
4374                                 ec.Emit (OpCodes.Ldc_I4_0);
4375                                 ec.Emit (OpCodes.Conv_U);
4376                                 vi.EmitAssign (ec);
4377                         }
4378                 }
4379
4380                 class StringEmitter : Emitter
4381                 {
4382                         LocalVariable pinned_string;
4383
4384                         public StringEmitter (Expression expr, LocalVariable li, Location loc)
4385                                 : base (expr, li)
4386                         {
4387                         }
4388
4389                         protected override Expression DoResolve (ResolveContext rc)
4390                         {
4391                                 pinned_string = new LocalVariable (vi.Block, "$pinned",
4392                                         LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4393                                         vi.Location);
4394
4395                                 pinned_string.Type = TypeManager.string_type;
4396
4397                                 if (TypeManager.int_get_offset_to_string_data == null) {
4398                                         TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4399                                                 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4400                                 }
4401
4402                                 eclass = ExprClass.Variable;
4403                                 type = TypeManager.int32_type;
4404                                 return this;
4405                         }
4406
4407                         public override void Emit (EmitContext ec)
4408                         {
4409                                 pinned_string.CreateBuilder (ec);
4410
4411                                 expr.Emit (ec);
4412                                 pinned_string.EmitAssign (ec);
4413
4414                                 // TODO: Should use Binary::Add
4415                                 pinned_string.Emit (ec);
4416                                 ec.Emit (OpCodes.Conv_I);
4417
4418                                 PropertyExpr pe = new PropertyExpr (TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4419                                 //pe.InstanceExpression = pinned_string;
4420                                 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4421
4422                                 ec.Emit (OpCodes.Add);
4423                                 vi.EmitAssign (ec);
4424                         }
4425
4426                         public override void EmitExit (EmitContext ec)
4427                         {
4428                                 ec.Emit (OpCodes.Ldnull);
4429                                 pinned_string.EmitAssign (ec);
4430                         }
4431                 }
4432
4433                 public class VariableDeclaration : BlockVariableDeclaration
4434                 {
4435                         public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4436                                 : base (type, li)
4437                         {
4438                         }
4439
4440                         protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4441                         {
4442                                 if (!Variable.Type.IsPointer && li == Variable) {
4443                                         bc.Report.Error (209, TypeExpression.Location,
4444                                                 "The type of locals declared in a fixed statement must be a pointer type");
4445                                         return null;
4446                                 }
4447
4448                                 //
4449                                 // The rules for the possible declarators are pretty wise,
4450                                 // but the production on the grammar is more concise.
4451                                 //
4452                                 // So we have to enforce these rules here.
4453                                 //
4454                                 // We do not resolve before doing the case 1 test,
4455                                 // because the grammar is explicit in that the token &
4456                                 // is present, so we need to test for this particular case.
4457                                 //
4458
4459                                 if (initializer is Cast) {
4460                                         bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4461                                         return null;
4462                                 }
4463
4464                                 initializer = initializer.Resolve (bc);
4465
4466                                 if (initializer == null)
4467                                         return null;
4468
4469                                 //
4470                                 // Case 1: Array
4471                                 //
4472                                 if (initializer.Type.IsArray) {
4473                                         TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4474
4475                                         //
4476                                         // Provided that array_type is unmanaged,
4477                                         //
4478                                         if (!TypeManager.VerifyUnmanaged (bc.Compiler, array_type, loc))
4479                                                 return null;
4480
4481                                         //
4482                                         // and T* is implicitly convertible to the
4483                                         // pointer type given in the fixed statement.
4484                                         //
4485                                         ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4486
4487                                         Expression converted = Convert.ImplicitConversionRequired (
4488                                                 bc, array_ptr, li.Type, loc);
4489                                         if (converted == null)
4490                                                 return null;
4491
4492                                         //
4493                                         // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4494                                         //
4495                                         converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4496                                                 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4497                                                 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (0, loc), loc), loc)),
4498                                                         new NullPointer (loc),
4499                                                         converted, loc);
4500
4501                                         converted = converted.Resolve (bc);
4502
4503                                         return new ExpressionEmitter (converted, li);
4504                                 }
4505
4506                                 //
4507                                 // Case 2: string
4508                                 //
4509                                 if (initializer.Type == TypeManager.string_type) {
4510                                         return new StringEmitter (initializer, li, loc).Resolve (bc);
4511                                 }
4512
4513                                 // Case 3: fixed buffer
4514                                 if (initializer is FixedBufferPtr) {
4515                                         return new ExpressionEmitter (initializer, li);
4516                                 }
4517
4518                                 //
4519                                 // Case 4: & object.
4520                                 //
4521                                 bool already_fixed = true;
4522                                 Unary u = initializer as Unary;
4523                                 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4524                                         IVariableReference vr = u.Expr as IVariableReference;
4525                                         if (vr == null || !vr.IsFixed) {
4526                                                 already_fixed = false;
4527                                         }
4528                                 }
4529
4530                                 if (already_fixed) {
4531                                         bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4532                                 }
4533
4534                                 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4535                                 return new ExpressionEmitter (initializer, li);
4536                         }
4537                 }
4538
4539
4540                 VariableDeclaration decl;
4541                 Statement statement;
4542                 bool has_ret;
4543
4544                 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4545                 {
4546                         this.decl = decl;
4547                         statement = stmt;
4548                         loc = l;
4549                 }
4550
4551                 #region Properties
4552
4553                 public Statement Statement {
4554                         get {
4555                                 return statement;
4556                         }
4557                 }
4558
4559                 public BlockVariableDeclaration Variables {
4560                         get {
4561                                 return decl;
4562                         }
4563                 }
4564
4565                 #endregion
4566
4567                 public override bool Resolve (BlockContext ec)
4568                 {
4569                         using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4570                                 if (!decl.Resolve (ec))
4571                                         return false;
4572                         }
4573
4574                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4575                         bool ok = statement.Resolve (ec);
4576                         bool flow_unreachable = ec.EndFlowBranching ();
4577                         has_ret = flow_unreachable;
4578
4579                         return ok;
4580                 }
4581                 
4582                 protected override void DoEmit (EmitContext ec)
4583                 {
4584                         decl.Variable.CreateBuilder (ec);
4585                         decl.Initializer.Emit (ec);
4586                         if (decl.Declarators != null) {
4587                                 foreach (var d in decl.Declarators) {
4588                                         d.Variable.CreateBuilder (ec);
4589                                         d.Initializer.Emit (ec);
4590                                 }
4591                         }
4592
4593                         statement.Emit (ec);
4594
4595                         if (has_ret)
4596                                 return;
4597
4598                         //
4599                         // Clear the pinned variable
4600                         //
4601                         ((Emitter) decl.Initializer).EmitExit (ec);
4602                         if (decl.Declarators != null) {
4603                                 foreach (var d in decl.Declarators) {
4604                                         ((Emitter)d.Initializer).EmitExit (ec);
4605                                 }
4606                         }
4607                 }
4608
4609                 protected override void CloneTo (CloneContext clonectx, Statement t)
4610                 {
4611                         Fixed target = (Fixed) t;
4612
4613                         target.decl = (VariableDeclaration) decl.Clone (clonectx);
4614                         target.statement = statement.Clone (clonectx);
4615                 }
4616         }
4617
4618         public class Catch : Statement
4619         {
4620                 Block block;
4621                 LocalVariable li;
4622                 FullNamedExpression type_expr;
4623                 CompilerAssign assign;
4624                 TypeSpec type;
4625                 
4626                 public Catch (Block block, Location loc)
4627                 {
4628                         this.block = block;
4629                         this.loc = loc;
4630                 }
4631
4632                 #region Properties
4633
4634                 public Block Block {
4635                         get {
4636                                 return block;
4637                         }
4638                 }
4639
4640                 public TypeSpec CatchType {
4641                         get {
4642                                 return type;
4643                         }
4644                 }
4645
4646                 public bool IsGeneral {
4647                         get {
4648                                 return type_expr == null;
4649                         }
4650                 }
4651
4652                 public FullNamedExpression TypeExpression {
4653                         get {
4654                                 return type_expr;
4655                         }
4656                         set {
4657                                 type_expr = value;
4658                         }
4659                 }
4660
4661                 public LocalVariable Variable {
4662                         get {
4663                                 return li;
4664                         }
4665                         set {
4666                                 li = value;
4667                         }
4668                 }
4669
4670                 #endregion
4671
4672                 protected override void DoEmit (EmitContext ec)
4673                 {
4674                         if (IsGeneral)
4675                                 ec.BeginCatchBlock (TypeManager.object_type);
4676                         else
4677                                 ec.BeginCatchBlock (CatchType);
4678
4679                         if (li != null) {
4680                                 li.CreateBuilder (ec);
4681
4682                                 //
4683                                 // Special case hoisted catch variable, we have to use a temporary variable
4684                                 // to pass via anonymous storey initialization with the value still on top
4685                                 // of the stack
4686                                 //
4687                                 if (li.HoistedVariant != null) {
4688                                         LocalTemporary lt = new LocalTemporary (li.Type);
4689                                         SymbolWriter.OpenCompilerGeneratedBlock (ec);
4690                                         lt.Store (ec);
4691                                         SymbolWriter.CloseCompilerGeneratedBlock (ec);
4692
4693                                         // switch to assigning from the temporary variable and not from top of the stack
4694                                         assign.UpdateSource (lt);
4695                                 }
4696                         } else {
4697                                 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4698                                 ec.Emit (OpCodes.Pop);
4699                                 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4700                         }
4701
4702                         Block.Emit (ec);
4703                 }
4704
4705                 public override bool Resolve (BlockContext ec)
4706                 {
4707                         using (ec.With (ResolveContext.Options.CatchScope, true)) {
4708                                 if (type_expr != null) {
4709                                         TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4710                                         if (te == null)
4711                                                 return false;
4712
4713                                         type = te.Type;
4714                                         if (type != TypeManager.exception_type && !TypeSpec.IsBaseClass (type, TypeManager.exception_type, false)) {
4715                                                 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4716                                         } else if (li != null) {
4717                                                 li.Type = type;
4718                                                 li.PrepareForFlowAnalysis (ec);
4719
4720                                                 // source variable is at the top of the stack
4721                                                 Expression source = new EmptyExpression (li.Type);
4722                                                 if (li.Type.IsGenericParameter)
4723                                                         source = new UnboxCast (source, li.Type);
4724
4725                                                 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4726                                                 Block.AddScopeStatement (new StatementExpression (assign));
4727                                         }
4728                                 }
4729
4730                                 return Block.Resolve (ec);
4731                         }
4732                 }
4733
4734                 protected override void CloneTo (CloneContext clonectx, Statement t)
4735                 {
4736                         Catch target = (Catch) t;
4737
4738                         if (type_expr != null)
4739                                 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4740
4741                         target.block = clonectx.LookupBlock (block);
4742                 }
4743         }
4744
4745         public class TryFinally : ExceptionStatement {
4746                 Block fini;
4747
4748                 public TryFinally (Statement stmt, Block fini, Location loc)
4749                          : base (stmt, loc)
4750                 {
4751                         this.fini = fini;
4752                 }
4753
4754                 public override bool Resolve (BlockContext ec)
4755                 {
4756                         bool ok = true;
4757
4758                         ec.StartFlowBranching (this);
4759
4760                         if (!stmt.Resolve (ec))
4761                                 ok = false;
4762
4763                         if (ok)
4764                                 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4765                         using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4766                                 if (!fini.Resolve (ec))
4767                                         ok = false;
4768                         }
4769
4770                         ec.EndFlowBranching ();
4771
4772                         ok &= base.Resolve (ec);
4773
4774                         return ok;
4775                 }
4776
4777                 protected override void EmitPreTryBody (EmitContext ec)
4778                 {
4779                 }
4780
4781                 protected override void EmitTryBody (EmitContext ec)
4782                 {
4783                         stmt.Emit (ec);
4784                 }
4785
4786                 protected override void EmitFinallyBody (EmitContext ec)
4787                 {
4788                         fini.Emit (ec);
4789                 }
4790
4791                 protected override void CloneTo (CloneContext clonectx, Statement t)
4792                 {
4793                         TryFinally target = (TryFinally) t;
4794
4795                         target.stmt = (Statement) stmt.Clone (clonectx);
4796                         if (fini != null)
4797                                 target.fini = clonectx.LookupBlock (fini);
4798                 }
4799         }
4800
4801         public class TryCatch : Statement {
4802                 public Block Block;
4803                 public List<Catch> Specific;
4804                 public Catch General;
4805                 bool inside_try_finally, code_follows;
4806
4807                 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4808                 {
4809                         this.Block = block;
4810                         this.Specific = catch_clauses;
4811                         this.inside_try_finally = inside_try_finally;
4812
4813                         Catch c = catch_clauses [0];
4814                         if (c.IsGeneral) {
4815                                 this.General = c;                       
4816                                 catch_clauses.RemoveAt (0);
4817                         }
4818
4819                         loc = l;
4820                 }
4821
4822                 public override bool Resolve (BlockContext ec)
4823                 {
4824                         bool ok = true;
4825
4826                         ec.StartFlowBranching (this);
4827
4828                         if (!Block.Resolve (ec))
4829                                 ok = false;
4830
4831                         TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4832                         int last_index = 0;
4833                         foreach (Catch c in Specific){
4834                                 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4835
4836                                 if (!c.Resolve (ec)) {
4837                                         ok = false;
4838                                         continue;
4839                                 }
4840
4841                                 TypeSpec resolved_type = c.CatchType;
4842                                 for (int ii = 0; ii < last_index; ++ii) {
4843                                         if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4844                                                 ec.Report.Error (160, c.loc,
4845                                                         "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4846                                                         TypeManager.CSharpName (prev_catches [ii]));
4847                                                 ok = false;
4848                                         }
4849                                 }
4850
4851                                 prev_catches [last_index++] = resolved_type;
4852                         }
4853
4854                         if (General != null) {
4855                                 foreach (Catch c in Specific) {
4856                                         if (c.CatchType != TypeManager.exception_type)
4857                                                 continue;
4858
4859                                         if (!ec.CurrentMemberDefinition.Module.DeclaringAssembly.WrapNonExceptionThrows)
4860                                                 continue;
4861
4862                                         if (!ec.Compiler.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4863                                                 continue;
4864
4865                                         ec.Report.Warning (1058, 1, c.loc,
4866                                                 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4867                                 }
4868
4869                                 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4870
4871                                 if (!General.Resolve (ec))
4872                                         ok = false;
4873                         }
4874
4875                         ec.EndFlowBranching ();
4876
4877                         // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4878                         // So, ensure there's some IL code after this statement
4879                         if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4880                                 ec.NeedReturnLabel ();
4881
4882                         return ok;
4883                 }
4884
4885                 public void SomeCodeFollows ()
4886                 {
4887                         code_follows = true;
4888                 }
4889                 
4890                 protected override void DoEmit (EmitContext ec)
4891                 {
4892                         if (!inside_try_finally)
4893                                 ec.BeginExceptionBlock ();
4894
4895                         Block.Emit (ec);
4896
4897                         foreach (Catch c in Specific)
4898                                 c.Emit (ec);
4899
4900                         if (General != null)
4901                                 General.Emit (ec);
4902
4903                         if (!inside_try_finally)
4904                                 ec.EndExceptionBlock ();
4905                 }
4906
4907                 protected override void CloneTo (CloneContext clonectx, Statement t)
4908                 {
4909                         TryCatch target = (TryCatch) t;
4910
4911                         target.Block = clonectx.LookupBlock (Block);
4912                         if (General != null)
4913                                 target.General = (Catch) General.Clone (clonectx);
4914                         if (Specific != null){
4915                                 target.Specific = new List<Catch> ();
4916                                 foreach (Catch c in Specific)
4917                                         target.Specific.Add ((Catch) c.Clone (clonectx));
4918                         }
4919                 }
4920         }
4921
4922         public class Using : ExceptionStatement
4923         {
4924                 public class VariableDeclaration : BlockVariableDeclaration
4925                 {
4926                         Statement dispose_call;
4927
4928                         public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4929                                 : base (type, li)
4930                         {
4931                         }
4932
4933                         public VariableDeclaration (LocalVariable li, Location loc)
4934                                 : base (li)
4935                         {
4936                                 this.loc = loc;
4937                         }
4938
4939                         public VariableDeclaration (Expression expr)
4940                                 : base (null)
4941                         {
4942                                 loc = expr.Location;
4943                                 Initializer = expr;
4944                         }
4945
4946                         #region Properties
4947
4948                         public bool IsNested { get; private set; }
4949
4950                         #endregion
4951
4952                         public void EmitDispose (EmitContext ec)
4953                         {
4954                                 dispose_call.Emit (ec);
4955                         }
4956
4957                         public override bool Resolve (BlockContext bc)
4958                         {
4959                                 if (IsNested)
4960                                         return true;
4961
4962                                 return base.Resolve (bc);
4963                         }
4964
4965                         public Expression ResolveExpression (BlockContext bc)
4966                         {
4967                                 var e = Initializer.Resolve (bc);
4968                                 if (e == null)
4969                                         return null;
4970
4971                                 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
4972                                 Initializer = ResolveInitializer (bc, Variable, e);
4973                                 return e;
4974                         }
4975
4976                         protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4977                         {
4978                                 if (li.Type == InternalType.Dynamic) {
4979                                         initializer = initializer.Resolve (bc);
4980                                         if (initializer == null)
4981                                                 return null;
4982
4983                                         // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
4984                                         Arguments args = new Arguments (1);
4985                                         args.Add (new Argument (initializer));
4986                                         initializer = new DynamicConversion (TypeManager.idisposable_type, 0, args, initializer.Location).Resolve (bc);
4987                                         if (initializer == null)
4988                                                 return null;
4989
4990                                         var var = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
4991                                         dispose_call = CreateDisposeCall (bc, var);
4992                                         dispose_call.Resolve (bc);
4993
4994                                         return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
4995                                 }
4996
4997                                 if (li == Variable) {
4998                                         CheckIDiposableConversion (bc, li, initializer);
4999                                         dispose_call = CreateDisposeCall (bc, li);
5000                                         dispose_call.Resolve (bc);
5001                                 }
5002
5003                                 return base.ResolveInitializer (bc, li, initializer);
5004                         }
5005
5006                         protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5007                         {
5008                                 var type = li.Type;
5009
5010                                 if (type != TypeManager.idisposable_type && !type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5011                                         if (TypeManager.IsNullableType (type)) {
5012                                                 // it's handled in CreateDisposeCall
5013                                                 return;
5014                                         }
5015
5016                                         bc.Report.SymbolRelatedToPreviousError (type);
5017                                         var loc = type_expr == null ? initializer.Location : type_expr.Location;
5018                                         bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5019                                                 type.GetSignatureForError ());
5020
5021                                         return;
5022                                 }
5023                         }
5024
5025                         protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5026                         {
5027                                 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5028                                 var type = lv.Type;
5029                                 var loc = lv.Location;
5030
5031                                 if (TypeManager.void_dispose_void == null) {
5032                                         TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5033                                                 TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5034                                 }
5035
5036                                 var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5037                                 dispose_mg.InstanceExpression = TypeManager.IsNullableType (type) ?
5038                                         new Cast (new TypeExpression (TypeManager.idisposable_type, loc), lvr, loc).Resolve (bc) :
5039                                         lvr;
5040
5041                                 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5042
5043                                 // Add conditional call when disposing possible null variable
5044                                 if (!type.IsStruct || TypeManager.IsNullableType (type))
5045                                         dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5046
5047                                 return dispose;
5048                         }
5049
5050                         public Statement RewriteForDeclarators (BlockContext bc, Statement stmt)
5051                         {
5052                                 for (int i = declarators.Count - 1; i >= 0; --i) {
5053                                         var d = declarators [i];
5054                                         var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5055                                         vd.Initializer = d.Initializer;
5056                                         vd.IsNested = true;
5057                                         vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5058                                         vd.dispose_call.Resolve (bc);
5059
5060                                         stmt = new Using (vd, stmt, d.Variable.Location);
5061                                 }
5062
5063                                 declarators = null;
5064                                 return stmt;
5065                         }
5066                 }
5067
5068                 VariableDeclaration decl;
5069
5070                 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5071                         : base (stmt, loc)
5072                 {
5073                         this.decl = decl;
5074                 }
5075
5076                 public Using (Expression expr, Statement stmt, Location loc)
5077                         : base (stmt, loc)
5078                 {
5079                         this.decl = new VariableDeclaration (expr);
5080                 }
5081
5082                 #region Properties
5083
5084                 public Expression Expression {
5085                         get {
5086                                 return decl.Variable == null ? decl.Initializer : null;
5087                         }
5088                 }
5089
5090                 public BlockVariableDeclaration Variables {
5091                         get {
5092                                 return decl;
5093                         }
5094                 }
5095
5096                 #endregion
5097
5098                 protected override void EmitPreTryBody (EmitContext ec)
5099                 {
5100                         decl.Emit (ec);
5101                 }
5102
5103                 protected override void EmitTryBody (EmitContext ec)
5104                 {
5105                         stmt.Emit (ec);
5106                 }
5107
5108                 protected override void EmitFinallyBody (EmitContext ec)
5109                 {
5110                         decl.EmitDispose (ec);
5111                 }
5112
5113                 public override bool Resolve (BlockContext ec)
5114                 {
5115                         VariableReference vr;
5116                         bool vr_locked = false;
5117
5118                         using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5119                                 if (decl.Variable == null) {
5120                                         vr = decl.ResolveExpression (ec) as VariableReference;
5121                                         if (vr != null) {
5122                                                 vr_locked = vr.IsLockedByStatement;
5123                                                 vr.IsLockedByStatement = true;
5124                                         }
5125                                 } else {
5126                                         if (!decl.Resolve (ec))
5127                                                 return false;
5128
5129                                         if (decl.Declarators != null) {
5130                                                 stmt = decl.RewriteForDeclarators (ec, stmt);
5131                                         }
5132
5133                                         vr = null;
5134                                 }
5135                         }
5136
5137                         ec.StartFlowBranching (this);
5138
5139                         stmt.Resolve (ec);
5140
5141                         ec.EndFlowBranching ();
5142
5143                         if (vr != null)
5144                                 vr.IsLockedByStatement = vr_locked;
5145
5146                         base.Resolve (ec);
5147
5148                         return true;
5149                 }
5150
5151                 protected override void CloneTo (CloneContext clonectx, Statement t)
5152                 {
5153                         Using target = (Using) t;
5154
5155                         target.decl = (VariableDeclaration) decl.Clone (clonectx);
5156                         target.stmt = stmt.Clone (clonectx);
5157                 }
5158         }
5159
5160         /// <summary>
5161         ///   Implementation of the foreach C# statement
5162         /// </summary>
5163         public class Foreach : Statement {
5164
5165                 sealed class ArrayForeach : Statement
5166                 {
5167                         readonly Foreach for_each;
5168                         readonly Statement statement;
5169
5170                         Expression conv;
5171                         TemporaryVariableReference[] lengths;
5172                         Expression [] length_exprs;
5173                         StatementExpression[] counter;
5174                         TemporaryVariableReference[] variables;
5175
5176                         TemporaryVariableReference copy;
5177                         Expression access;
5178                         LocalVariableReference variable;
5179
5180                         public ArrayForeach (Foreach @foreach, int rank)
5181                         {
5182                                 for_each = @foreach;
5183                                 statement = for_each.statement;
5184                                 loc = @foreach.loc;
5185                                 variable = new LocalVariableReference (for_each.variable, loc);
5186
5187                                 counter = new StatementExpression[rank];
5188                                 variables = new TemporaryVariableReference[rank];
5189                                 length_exprs = new Expression [rank];
5190
5191                                 //
5192                                 // Only use temporary length variables when dealing with
5193                                 // multi-dimensional arrays
5194                                 //
5195                                 if (rank > 1)
5196                                         lengths = new TemporaryVariableReference [rank];
5197                         }
5198
5199                         protected override void CloneTo (CloneContext clonectx, Statement target)
5200                         {
5201                                 throw new NotImplementedException ();
5202                         }
5203
5204                         public override bool Resolve (BlockContext ec)
5205                         {
5206                                 Block variables_block = variable.local_info.Block;
5207                                 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5208                                 copy.Resolve (ec);
5209
5210                                 int rank = length_exprs.Length;
5211                                 Arguments list = new Arguments (rank);
5212                                 for (int i = 0; i < rank; i++) {
5213                                         var v = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5214                                         variables[i] = v;
5215                                         counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5216                                         counter[i].Resolve (ec);
5217
5218                                         if (rank == 1) {
5219                                                 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5220                                         } else {
5221                                                 lengths[i] = TemporaryVariableReference.Create (TypeManager.int32_type, variables_block, loc);
5222                                                 lengths[i].Resolve (ec);
5223
5224                                                 Arguments args = new Arguments (1);
5225                                                 args.Add (new Argument (new IntConstant (i, loc)));
5226                                                 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5227                                         }
5228
5229                                         list.Add (new Argument (v));
5230                                 }
5231
5232                                 access = new ElementAccess (copy, list, loc).Resolve (ec);
5233                                 if (access == null)
5234                                         return false;
5235
5236                                 Expression var_type = for_each.type;
5237                                 VarExpr ve = var_type as VarExpr;
5238                                 if (ve != null) {
5239                                         // Infer implicitly typed local variable from foreach array type
5240                                         var_type = new TypeExpression (access.Type, ve.Location);
5241                                 }
5242
5243                                 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5244                                 if (var_type == null)
5245                                         return false;
5246
5247                                 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5248                                 if (conv == null)
5249                                         return false;
5250
5251                                 bool ok = true;
5252
5253                                 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5254                                 ec.CurrentBranching.CreateSibling ();
5255
5256                                 variable.local_info.Type = conv.Type;
5257                                 variable.Resolve (ec);
5258
5259                                 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5260                                 if (!statement.Resolve (ec))
5261                                         ok = false;
5262                                 ec.EndFlowBranching ();
5263
5264                                 // There's no direct control flow from the end of the embedded statement to the end of the loop
5265                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
5266
5267                                 ec.EndFlowBranching ();
5268
5269                                 return ok;
5270                         }
5271
5272                         protected override void DoEmit (EmitContext ec)
5273                         {
5274                                 copy.EmitAssign (ec, for_each.expr);
5275
5276                                 int rank = length_exprs.Length;
5277                                 Label[] test = new Label [rank];
5278                                 Label[] loop = new Label [rank];
5279
5280                                 for (int i = 0; i < rank; i++) {
5281                                         test [i] = ec.DefineLabel ();
5282                                         loop [i] = ec.DefineLabel ();
5283
5284                                         if (lengths != null)
5285                                                 lengths [i].EmitAssign (ec, length_exprs [i]);
5286                                 }
5287
5288                                 IntConstant zero = new IntConstant (0, loc);
5289                                 for (int i = 0; i < rank; i++) {
5290                                         variables [i].EmitAssign (ec, zero);
5291
5292                                         ec.Emit (OpCodes.Br, test [i]);
5293                                         ec.MarkLabel (loop [i]);
5294                                 }
5295
5296                                 variable.local_info.CreateBuilder (ec);
5297                                 variable.EmitAssign (ec, conv, false, false);
5298
5299                                 statement.Emit (ec);
5300
5301                                 ec.MarkLabel (ec.LoopBegin);
5302
5303                                 for (int i = rank - 1; i >= 0; i--){
5304                                         counter [i].Emit (ec);
5305
5306                                         ec.MarkLabel (test [i]);
5307                                         variables [i].Emit (ec);
5308
5309                                         if (lengths != null)
5310                                                 lengths [i].Emit (ec);
5311                                         else
5312                                                 length_exprs [i].Emit (ec);
5313
5314                                         ec.Emit (OpCodes.Blt, loop [i]);
5315                                 }
5316
5317                                 ec.MarkLabel (ec.LoopEnd);
5318                         }
5319                 }
5320
5321                 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5322                 {
5323                         class Body : Statement
5324                         {
5325                                 TypeSpec type;
5326                                 LocalVariableReference variable;
5327                                 Expression current, conv;
5328                                 Statement statement;
5329
5330                                 public Body (TypeSpec type, LocalVariable variable,
5331                                                                    Expression current, Statement statement,
5332                                                                    Location loc)
5333                                 {
5334                                         this.type = type;
5335                                         this.variable = new LocalVariableReference (variable, loc);
5336                                         this.current = current;
5337                                         this.statement = statement;
5338                                         this.loc = loc;
5339                                 }
5340
5341                                 protected override void CloneTo (CloneContext clonectx, Statement target)
5342                                 {
5343                                         throw new NotImplementedException ();
5344                                 }
5345
5346                                 public override bool Resolve (BlockContext ec)
5347                                 {
5348                                         current = current.Resolve (ec);
5349                                         if (current == null)
5350                                                 return false;
5351
5352                                         conv = Convert.ExplicitConversion (ec, current, type, loc);
5353                                         if (conv == null)
5354                                                 return false;
5355
5356                                         variable.local_info.Type = conv.Type;
5357                                         variable.Resolve (ec);
5358
5359                                         if (!statement.Resolve (ec))
5360                                                 return false;
5361
5362                                         return true;
5363                                 }
5364
5365                                 protected override void DoEmit (EmitContext ec)
5366                                 {
5367                                         variable.local_info.CreateBuilder (ec);
5368                                         variable.EmitAssign (ec, conv, false, false);
5369
5370                                         statement.Emit (ec);
5371                                 }
5372                         }
5373
5374                         class RuntimeDispose : Using.VariableDeclaration
5375                         {
5376                                 public RuntimeDispose (LocalVariable lv, Location loc)
5377                                         : base (lv, loc)
5378                                 {
5379                                 }
5380
5381                                 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5382                                 {
5383                                         // Defered to runtime check
5384                                 }
5385
5386                                 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5387                                 {
5388                                         if (TypeManager.void_dispose_void == null) {
5389                                                 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5390                                                         TypeManager.idisposable_type, "Dispose", loc, TypeSpec.EmptyTypes);
5391                                         }
5392
5393                                         //
5394                                         // Fabricates code like
5395                                         //
5396                                         // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5397                                         //
5398
5399                                         var dispose_variable = LocalVariable.CreateCompilerGenerated (TypeManager.idisposable_type, bc.CurrentBlock, loc);
5400
5401                                         var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5402                                                 dispose_variable.CreateReferenceExpression (bc, loc),
5403                                                 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5404                                                 loc), new NullLiteral (loc), loc);
5405
5406                                         var dispose_mg = MethodGroupExpr.CreatePredefined (TypeManager.void_dispose_void, TypeManager.idisposable_type, loc);
5407                                         dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5408
5409                                         Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5410                                         return new If (idisaposable_test, dispose, loc);
5411                                 }
5412                         }
5413
5414                         LocalVariable variable;
5415                         Expression expr;
5416                         Statement statement;
5417                         Expression var_type;
5418                         ExpressionStatement init;
5419                         TemporaryVariableReference enumerator_variable;
5420                         bool ambiguous_getenumerator_name;
5421
5422                         public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5423                         {
5424                                 this.var_type = var_type;
5425                                 this.variable = var;
5426                                 this.expr = expr;
5427                                 statement = stmt;
5428                                 loc = l;
5429                         }
5430
5431                         protected override void CloneTo (CloneContext clonectx, Statement target)
5432                         {
5433                                 throw new NotImplementedException ();
5434                         }
5435
5436                         void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5437                         {
5438                                 rc.Report.SymbolRelatedToPreviousError (enumerator);
5439                                 rc.Report.Error (202, loc,
5440                                         "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5441                                                 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5442                         }
5443
5444                         MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5445                         {
5446                                 //
5447                                 // Option 1: Try to match by name GetEnumerator first
5448                                 //
5449                                 var mexpr = Expression.MemberLookup (rc, rc.CurrentType, expr.Type,
5450                                         "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc);               // TODO: What if CS0229 ?
5451
5452                                 var mg = mexpr as MethodGroupExpr;
5453                                 if (mg != null) {
5454                                         mg.InstanceExpression = expr;
5455                                         Arguments args = new Arguments (0);
5456                                         mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5457
5458                                         // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5459                                         if (ambiguous_getenumerator_name)
5460                                                 mg = null;
5461
5462                                         if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5463                                                 return mg;
5464                                         }
5465                                 }
5466
5467                                 //
5468                                 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5469                                 //
5470                                 TypeSpec iface_candidate = null;
5471                                 for (TypeSpec t = expr.Type; t != null && t != TypeManager.object_type; t = t.BaseType) {
5472                                         var ifaces = t.Interfaces;
5473                                         if (ifaces != null) {
5474                                                 foreach (var iface in ifaces) {
5475                                                         if (TypeManager.generic_ienumerable_type != null && iface.MemberDefinition == TypeManager.generic_ienumerable_type.MemberDefinition) {
5476                                                                 if (iface_candidate != null && iface_candidate != TypeManager.ienumerable_type) {
5477                                                                         rc.Report.SymbolRelatedToPreviousError (expr.Type);
5478                                                                         rc.Report.Error (1640, loc,
5479                                                                                 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5480                                                                                 expr.Type.GetSignatureForError (), TypeManager.generic_ienumerable_type.GetSignatureForError ());
5481
5482                                                                         return null;
5483                                                                 }
5484
5485                                                                 iface_candidate = iface;
5486                                                                 continue;
5487                                                         }
5488
5489                                                         if (iface == TypeManager.ienumerable_type && iface_candidate == null) {
5490                                                                 iface_candidate = iface;
5491                                                         }
5492                                                 }
5493                                         }
5494                                 }
5495
5496                                 if (iface_candidate == null) {
5497                                         rc.Report.Error (1579, loc,
5498                                                 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5499                                                 expr.Type.GetSignatureForError (), "GetEnumerator");
5500
5501                                         return null;
5502                                 }
5503
5504                                 var method = TypeManager.GetPredefinedMethod (iface_candidate, 
5505                                         MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null), loc);
5506
5507                                 if (method == null)
5508                                         return null;
5509
5510                                 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5511                                 mg.InstanceExpression = expr;
5512                                 return mg;
5513                         }
5514
5515                         MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5516                         {
5517                                 var ms = MemberCache.FindMember (enumerator.ReturnType,
5518                                         MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, TypeManager.bool_type),
5519                                         BindingRestriction.InstanceOnly) as MethodSpec;
5520
5521                                 if (ms == null || !ms.IsPublic) {
5522                                         Error_WrongEnumerator (rc, enumerator);
5523                                         return null;
5524                                 }
5525
5526                                 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5527                         }
5528
5529                         PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5530                         {
5531                                 var ps = MemberCache.FindMember (enumerator.ReturnType,
5532                                         MemberFilter.Property ("Current", null),
5533                                         BindingRestriction.InstanceOnly) as PropertySpec;
5534
5535                                 if (ps == null || !ps.IsPublic) {
5536                                         Error_WrongEnumerator (rc, enumerator);
5537                                         return null;
5538                                 }
5539
5540                                 return ps;
5541                         }
5542
5543                         public override bool Resolve (BlockContext ec)
5544                         {
5545                                 bool is_dynamic = expr.Type == InternalType.Dynamic;
5546
5547                                 if (is_dynamic) {
5548                                         expr = Convert.ImplicitConversionRequired (ec, expr, TypeManager.ienumerable_type, loc);
5549                                 } else if (TypeManager.IsNullableType (expr.Type)) {
5550                                         expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5551                                 }
5552
5553                                 var get_enumerator_mg = ResolveGetEnumerator (ec);
5554                                 if (get_enumerator_mg == null) {
5555                                         return false;
5556                                 }
5557
5558                                 var get_enumerator = get_enumerator_mg.BestCandidate;
5559                                 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5560                                 enumerator_variable.Resolve (ec);
5561
5562                                 // Prepare bool MoveNext ()
5563                                 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5564                                 if (move_next_mg == null) {
5565                                         return false;
5566                                 }
5567
5568                                 move_next_mg.InstanceExpression = enumerator_variable;
5569
5570                                 // Prepare ~T~ Current { get; }
5571                                 var current_prop = ResolveCurrent (ec, get_enumerator);
5572                                 if (current_prop == null) {
5573                                         return false;
5574                                 }
5575
5576                                 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5577                                 if (current_pe == null)
5578                                         return false;
5579
5580                                 VarExpr ve = var_type as VarExpr;
5581                                 if (ve != null) {
5582                                         if (is_dynamic) {
5583                                                 // Source type is dynamic, set element type to dynamic too
5584                                                 var_type = new TypeExpression (InternalType.Dynamic, var_type.Location);
5585                                         } else {
5586                                                 // Infer implicitly typed local variable from foreach enumerable type
5587                                                 var_type = new TypeExpression (current_pe.Type, var_type.Location);
5588                                         }
5589                                 } else if (is_dynamic) {
5590                                         // Explicit cast of dynamic collection elements has to be done at runtime
5591                                         current_pe = EmptyCast.Create (current_pe, InternalType.Dynamic);
5592                                 }
5593
5594                                 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5595                                 if (var_type == null)
5596                                         return false;
5597
5598                                 variable.Type = var_type.Type;
5599
5600                                 var init = new Invocation (get_enumerator_mg, null);
5601
5602                                 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5603                                         new Body (var_type.Type, variable, current_pe, statement, loc), loc);
5604
5605                                 var enum_type = enumerator_variable.Type;
5606
5607                                 //
5608                                 // Add Dispose method call when enumerator can be IDisposable
5609                                 //
5610                                 if (!enum_type.ImplementsInterface (TypeManager.idisposable_type, false)) {
5611                                         if (!enum_type.IsSealed && !TypeManager.IsValueType (enum_type)) {
5612                                                 //
5613                                                 // Runtime Dispose check
5614                                                 //
5615                                                 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5616                                                 vd.Initializer = init;
5617                                                 statement = new Using (vd, statement, loc);
5618                                         } else {
5619                                                 //
5620                                                 // No Dispose call needed
5621                                                 //
5622                                                 this.init = new SimpleAssign (enumerator_variable, init);
5623                                                 this.init.Resolve (ec);
5624                                         }
5625                                 } else {
5626                                         //
5627                                         // Static Dispose check
5628                                         //
5629                                         var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5630                                         vd.Initializer = init;
5631                                         statement = new Using (vd, statement, loc);
5632                                 }
5633
5634                                 return statement.Resolve (ec);
5635                         }
5636
5637                         protected override void DoEmit (EmitContext ec)
5638                         {
5639                                 enumerator_variable.LocalInfo.CreateBuilder (ec);
5640
5641                                 if (init != null)
5642                                         init.EmitStatement (ec);
5643
5644                                 statement.Emit (ec);
5645                         }
5646
5647                         #region IErrorHandler Members
5648
5649                         bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5650                         {
5651                                 ec.Report.SymbolRelatedToPreviousError (best);
5652                                 ec.Report.Warning (278, 2, loc,
5653                                         "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5654                                         expr.Type.GetSignatureForError (), "enumerable",
5655                                         best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5656
5657                                 ambiguous_getenumerator_name = true;
5658                                 return true;
5659                         }
5660
5661                         bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5662                         {
5663                                 return false;
5664                         }
5665
5666                         bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5667                         {
5668                                 return false;
5669                         }
5670
5671                         bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5672                         {
5673                                 return false;
5674                         }
5675
5676                         #endregion
5677                 }
5678
5679                 Expression type;
5680                 LocalVariable variable;
5681                 Expression expr;
5682                 Statement statement;
5683
5684                 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5685                 {
5686                         this.type = type;
5687                         this.variable = var;
5688                         this.expr = expr;
5689                         statement = stmt;
5690                         loc = l;
5691                 }
5692
5693                 public Statement Statement {
5694                         get { return statement; }
5695                 }
5696
5697                 public override bool Resolve (BlockContext ec)
5698                 {
5699                         expr = expr.Resolve (ec);
5700                         if (expr == null)
5701                                 return false;
5702
5703                         if (expr.IsNull) {
5704                                 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5705                                 return false;
5706                         }
5707
5708                         if (expr.Type == TypeManager.string_type) {
5709                                 statement = new ArrayForeach (this, 1);
5710                         } else if (expr.Type is ArrayContainer) {
5711                                 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5712                         } else {
5713                                 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5714                                         ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5715                                                 expr.ExprClassName);
5716                                         return false;
5717                                 }
5718
5719                                 statement = new CollectionForeach (type, variable, expr, statement, loc);
5720                         }
5721
5722                         return statement.Resolve (ec);
5723                 }
5724
5725                 protected override void DoEmit (EmitContext ec)
5726                 {
5727                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5728                         ec.LoopBegin = ec.DefineLabel ();
5729                         ec.LoopEnd = ec.DefineLabel ();
5730
5731                         statement.Emit (ec);
5732
5733                         ec.LoopBegin = old_begin;
5734                         ec.LoopEnd = old_end;
5735                 }
5736
5737                 protected override void CloneTo (CloneContext clonectx, Statement t)
5738                 {
5739                         Foreach target = (Foreach) t;
5740
5741                         target.type = type.Clone (clonectx);
5742                         target.expr = expr.Clone (clonectx);
5743                         target.statement = statement.Clone (clonectx);
5744                 }
5745         }
5746 }