7a379fc172aff0bd375aefb7cdbee386f7e4f0c4
[mono.git] / mcs / mcs / statement.cs
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Authors:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //   Martin Baulig (martin@ximian.com)
7 //   Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
12 //
13
14 using System;
15 using System.Collections.Generic;
16
17 #if STATIC
18 using IKVM.Reflection.Emit;
19 #else
20 using System.Reflection.Emit;
21 #endif
22
23 namespace Mono.CSharp {
24         
25         public abstract class Statement {
26                 public Location loc;
27                 protected bool reachable;
28
29                 public bool IsUnreachable {
30                         get {
31                                 return !reachable;
32                         }
33                 }
34                 
35                 /// <summary>
36                 ///   Resolves the statement, true means that all sub-statements
37                 ///   did resolve ok.
38                 ///  </summary>
39                 public virtual bool Resolve (BlockContext bc)
40                 {
41                         return true;
42                 }
43
44                 /// <summary>
45                 ///   Return value indicates whether all code paths emitted return.
46                 /// </summary>
47                 protected abstract void DoEmit (EmitContext ec);
48
49                 public virtual void Emit (EmitContext ec)
50                 {
51                         ec.Mark (loc);
52                         DoEmit (ec);
53
54                         if (ec.StatementEpilogue != null) {
55                                 ec.EmitEpilogue ();
56                         }
57                 }
58
59                 //
60                 // This routine must be overrided in derived classes and make copies
61                 // of all the data that might be modified if resolved
62                 // 
63                 protected abstract void CloneTo (CloneContext clonectx, Statement target);
64
65                 public Statement Clone (CloneContext clonectx)
66                 {
67                         Statement s = (Statement) this.MemberwiseClone ();
68                         CloneTo (clonectx, s);
69                         return s;
70                 }
71
72                 public virtual Expression CreateExpressionTree (ResolveContext ec)
73                 {
74                         ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
75                         return null;
76                 }
77                 
78                 public virtual object Accept (StructuralVisitor visitor)
79                 {
80                         return visitor.Visit (this);
81                 }
82
83                 //
84                 // Return value indicates whether statement has unreachable end
85                 //
86                 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
87
88                 public bool FlowAnalysis (FlowAnalysisContext fc)
89                 {
90                         if (reachable) {
91                                 fc.UnreachableReported = false;
92                                 var res = DoFlowAnalysis (fc);
93                                 return res;
94                         }
95
96                         //
97                         // Special handling cases
98                         //
99                         if (this is Block) {
100                                 return DoFlowAnalysis (fc);
101                         }
102
103                         if (this is EmptyStatement || loc.IsNull)
104                                 return true;
105
106                         if (fc.UnreachableReported)
107                                 return true;
108
109                         fc.Report.Warning (162, 2, loc, "Unreachable code detected");
110                         fc.UnreachableReported = true;
111                         return true;
112                 }
113
114                 public virtual Reachability MarkReachable (Reachability rc)
115                 {
116                         if (!rc.IsUnreachable)
117                                 reachable = true;
118
119                         return rc;
120                 }
121
122                 protected void CheckExitBoundaries (BlockContext bc, Block scope)
123                 {
124                         if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
125                                 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
126                                 return;
127                         }
128
129                         for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
130                                 if (b.IsFinallyBlock) {
131                                         Error_FinallyClauseExit (bc);
132                                         break;
133                                 }
134                         }
135                 }
136
137                 protected void Error_FinallyClauseExit (BlockContext bc)
138                 {
139                         bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
140                 }
141         }
142
143         public sealed class EmptyStatement : Statement
144         {
145                 public EmptyStatement (Location loc)
146                 {
147                         this.loc = loc;
148                 }
149
150                 public override bool Resolve (BlockContext ec)
151                 {
152                         return true;
153                 }
154
155                 public override void Emit (EmitContext ec)
156                 {
157                 }
158
159                 protected override void DoEmit (EmitContext ec)
160                 {
161                         throw new NotSupportedException ();
162                 }
163
164                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
165                 {
166                         return false;
167                 }
168
169                 protected override void CloneTo (CloneContext clonectx, Statement target)
170                 {
171                         // nothing needed.
172                 }
173                 
174                 public override object Accept (StructuralVisitor visitor)
175                 {
176                         return visitor.Visit (this);
177                 }
178         }
179
180         public class If : Statement {
181                 Expression expr;
182                 public Statement TrueStatement;
183                 public Statement FalseStatement;
184
185                 bool true_returns, false_returns;
186
187                 public If (Expression bool_expr, Statement true_statement, Location l)
188                         : this (bool_expr, true_statement, null, l)
189                 {
190                 }
191
192                 public If (Expression bool_expr,
193                            Statement true_statement,
194                            Statement false_statement,
195                            Location l)
196                 {
197                         this.expr = bool_expr;
198                         TrueStatement = true_statement;
199                         FalseStatement = false_statement;
200                         loc = l;
201                 }
202
203                 public Expression Expr {
204                         get {
205                                 return this.expr;
206                         }
207                 }
208                 
209                 public override bool Resolve (BlockContext ec)
210                 {
211                         expr = expr.Resolve (ec);
212
213                         var ok = TrueStatement.Resolve (ec);
214
215                         if (FalseStatement != null) {
216                                 ok &= FalseStatement.Resolve (ec);
217                         }
218
219                         return ok;
220                 }
221                 
222                 protected override void DoEmit (EmitContext ec)
223                 {
224                         Label false_target = ec.DefineLabel ();
225                         Label end;
226
227                         //
228                         // If we're a boolean constant, Resolve() already
229                         // eliminated dead code for us.
230                         //
231                         Constant c = expr as Constant;
232                         if (c != null){
233                                 c.EmitSideEffect (ec);
234
235                                 if (!c.IsDefaultValue)
236                                         TrueStatement.Emit (ec);
237                                 else if (FalseStatement != null)
238                                         FalseStatement.Emit (ec);
239
240                                 return;
241                         }                       
242                         
243                         expr.EmitBranchable (ec, false_target, false);
244                         
245                         TrueStatement.Emit (ec);
246
247                         if (FalseStatement != null){
248                                 bool branch_emitted = false;
249                                 
250                                 end = ec.DefineLabel ();
251                                 if (!true_returns){
252                                         ec.Emit (OpCodes.Br, end);
253                                         branch_emitted = true;
254                                 }
255
256                                 ec.MarkLabel (false_target);
257                                 FalseStatement.Emit (ec);
258
259                                 if (branch_emitted)
260                                         ec.MarkLabel (end);
261                         } else {
262                                 ec.MarkLabel (false_target);
263                         }
264                 }
265
266                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
267                 {
268                         expr.FlowAnalysisConditional (fc);
269
270                         var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
271
272                         fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
273                         var labels = fc.CopyLabelStack ();
274
275                         var res = TrueStatement.FlowAnalysis (fc);
276
277                         fc.SetLabelStack (labels);
278
279                         if (FalseStatement == null) {
280
281                                 var c = expr as Constant;
282                                 if (c != null && !c.IsDefaultValue)
283                                         return true_returns;
284
285                                 if (true_returns)
286                                         fc.DefiniteAssignment = da_false;
287                                 else
288                                         fc.DefiniteAssignment &= da_false;
289  
290                                 return false;
291                         }
292
293                         if (true_returns) {
294                                 fc.DefiniteAssignment = da_false;
295
296                                 res = FalseStatement.FlowAnalysis (fc);
297                                 fc.SetLabelStack (labels);
298                                 return res;
299                         }
300
301                         var da_true = fc.DefiniteAssignment;
302
303                         fc.DefiniteAssignment = da_false;
304
305                         res &= FalseStatement.FlowAnalysis (fc);
306
307                         fc.SetLabelStack (labels);
308
309                         if (!TrueStatement.IsUnreachable) {
310                                 if (false_returns || FalseStatement.IsUnreachable)
311                                         fc.DefiniteAssignment = da_true;
312                                 else
313                                         fc.DefiniteAssignment &= da_true;
314                         }
315
316                         return res;
317                 }
318
319                 public override Reachability MarkReachable (Reachability rc)
320                 {
321                         if (rc.IsUnreachable)
322                                 return rc;
323
324                         base.MarkReachable (rc);
325
326                         var c = expr as Constant;
327                         if (c != null) {
328                                 bool take = !c.IsDefaultValue;
329                                 if (take) {
330                                         rc = TrueStatement.MarkReachable (rc);
331                                 } else {
332                                         if (FalseStatement != null)
333                                                 rc = FalseStatement.MarkReachable (rc);
334                                 }
335
336                                 return rc;
337                         }
338
339                         var true_rc = TrueStatement.MarkReachable (rc);
340                         true_returns = true_rc.IsUnreachable;
341         
342                         if (FalseStatement == null)
343                                 return rc;
344
345                         var false_rc = FalseStatement.MarkReachable (rc);
346                         false_returns = false_rc.IsUnreachable;
347
348                         return true_rc & false_rc;
349                 }
350
351                 protected override void CloneTo (CloneContext clonectx, Statement t)
352                 {
353                         If target = (If) t;
354
355                         target.expr = expr.Clone (clonectx);
356                         target.TrueStatement = TrueStatement.Clone (clonectx);
357                         if (FalseStatement != null)
358                                 target.FalseStatement = FalseStatement.Clone (clonectx);
359                 }
360                 
361                 public override object Accept (StructuralVisitor visitor)
362                 {
363                         return visitor.Visit (this);
364                 }
365         }
366
367         public class Do : LoopStatement
368         {
369                 public Expression expr;
370                 bool iterator_reachable, end_reachable;
371
372                 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
373                         : base (statement)
374                 {
375                         expr = bool_expr;
376                         loc = doLocation;
377                         WhileLocation = whileLocation;
378                 }
379
380                 public Location WhileLocation {
381                         get; private set;
382                 }
383
384                 public override bool Resolve (BlockContext bc)
385                 {
386                         var ok = base.Resolve (bc);
387
388                         expr = expr.Resolve (bc);
389
390                         return ok;
391                 }
392                 
393                 protected override void DoEmit (EmitContext ec)
394                 {
395                         Label loop = ec.DefineLabel ();
396                         Label old_begin = ec.LoopBegin;
397                         Label old_end = ec.LoopEnd;
398                         
399                         ec.LoopBegin = ec.DefineLabel ();
400                         ec.LoopEnd = ec.DefineLabel ();
401                                 
402                         ec.MarkLabel (loop);
403                         Statement.Emit (ec);
404                         ec.MarkLabel (ec.LoopBegin);
405
406                         // Mark start of while condition
407                         ec.Mark (WhileLocation);
408
409                         //
410                         // Dead code elimination
411                         //
412                         if (expr is Constant) {
413                                 bool res = !((Constant) expr).IsDefaultValue;
414
415                                 expr.EmitSideEffect (ec);
416                                 if (res)
417                                         ec.Emit (OpCodes.Br, loop);
418                         } else {
419                                 expr.EmitBranchable (ec, loop, true);
420                         }
421                         
422                         ec.MarkLabel (ec.LoopEnd);
423
424                         ec.LoopBegin = old_begin;
425                         ec.LoopEnd = old_end;
426                 }
427
428                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
429                 {
430                         var res = Statement.FlowAnalysis (fc);
431
432                         expr.FlowAnalysisConditional (fc);
433
434                         fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
435
436                         if (res && !iterator_reachable)
437                                 return !end_reachable;
438
439                         if (!end_reachable) {
440                                 var c = expr as Constant;
441                                 if (c != null && !c.IsDefaultValue)
442                                         return true;
443                         }
444
445                         return false;
446                 }
447                 
448                 public override Reachability MarkReachable (Reachability rc)
449                 {
450                         base.MarkReachable (rc);
451                         
452                         var body_rc = Statement.MarkReachable (rc);
453
454                         if (body_rc.IsUnreachable && !iterator_reachable) {
455                                 expr = new UnreachableExpression (expr);
456                                 return end_reachable ? rc : Reachability.CreateUnreachable ();
457                         }
458
459                         if (!end_reachable) {
460                                 var c = expr as Constant;
461                                 if (c != null && !c.IsDefaultValue)
462                                         return Reachability.CreateUnreachable ();
463                         }
464
465                         return rc;
466                 }
467
468                 protected override void CloneTo (CloneContext clonectx, Statement t)
469                 {
470                         Do target = (Do) t;
471
472                         target.Statement = Statement.Clone (clonectx);
473                         target.expr = expr.Clone (clonectx);
474                 }
475                 
476                 public override object Accept (StructuralVisitor visitor)
477                 {
478                         return visitor.Visit (this);
479                 }
480
481                 public override void SetEndReachable ()
482                 {
483                         end_reachable = true;
484                 }
485
486                 public override void SetIteratorReachable ()
487                 {
488                         iterator_reachable = true;
489                 }
490         }
491
492         public class While : LoopStatement
493         {
494                 public Expression expr;
495                 bool empty, infinite, end_reachable;
496                 List<DefiniteAssignmentBitSet> end_reachable_das;
497
498                 public While (BooleanExpression bool_expr, Statement statement, Location l)
499                         : base (statement)
500                 {
501                         this.expr = bool_expr;
502                         loc = l;
503                 }
504
505                 public override bool Resolve (BlockContext bc)
506                 {
507                         bool ok = true;
508
509                         expr = expr.Resolve (bc);
510                         if (expr == null)
511                                 ok = false;
512
513                         var c = expr as Constant;
514                         if (c != null) {
515                                 empty = c.IsDefaultValue;
516                                 infinite = !empty;
517                         }
518
519                         ok &= base.Resolve (bc);
520                         return ok;
521                 }
522                 
523                 protected override void DoEmit (EmitContext ec)
524                 {
525                         if (empty) {
526                                 expr.EmitSideEffect (ec);
527                                 return;
528                         }
529
530                         Label old_begin = ec.LoopBegin;
531                         Label old_end = ec.LoopEnd;
532                         
533                         ec.LoopBegin = ec.DefineLabel ();
534                         ec.LoopEnd = ec.DefineLabel ();
535
536                         //
537                         // Inform whether we are infinite or not
538                         //
539                         if (expr is Constant) {
540                                 // expr is 'true', since the 'empty' case above handles the 'false' case
541                                 ec.MarkLabel (ec.LoopBegin);
542
543                                 if (ec.EmitAccurateDebugInfo)
544                                         ec.Emit (OpCodes.Nop);
545
546                                 expr.EmitSideEffect (ec);
547                                 Statement.Emit (ec);
548                                 ec.Emit (OpCodes.Br, ec.LoopBegin);
549                                         
550                                 //
551                                 // Inform that we are infinite (ie, `we return'), only
552                                 // if we do not `break' inside the code.
553                                 //
554                                 ec.MarkLabel (ec.LoopEnd);
555                         } else {
556                                 Label while_loop = ec.DefineLabel ();
557
558                                 ec.Emit (OpCodes.Br, ec.LoopBegin);
559                                 ec.MarkLabel (while_loop);
560
561                                 expr.EmitPrepare (ec);
562
563                                 Statement.Emit (ec);
564                         
565                                 ec.MarkLabel (ec.LoopBegin);
566
567                                 ec.Mark (loc);
568                                 expr.EmitBranchable (ec, while_loop, true);
569                                 
570                                 ec.MarkLabel (ec.LoopEnd);
571                         }       
572
573                         ec.LoopBegin = old_begin;
574                         ec.LoopEnd = old_end;
575                 }
576
577                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
578                 {
579                         expr.FlowAnalysisConditional (fc);
580
581                         var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
582
583                         fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
584
585                         Statement.FlowAnalysis (fc);
586
587                         //
588                         // Special case infinite while with breaks
589                         //
590                         if (end_reachable_das != null) {
591                                 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
592                                 end_reachable_das = null;
593                         }
594
595                         fc.DefiniteAssignment = da_false;
596
597                         if (infinite && !end_reachable)
598                                 return true;
599
600                         return false;
601                 }
602
603                 public override Reachability MarkReachable (Reachability rc)
604                 {
605                         if (rc.IsUnreachable)
606                                 return rc;
607
608                         base.MarkReachable (rc);
609
610                         //
611                         // Special case unreachable while body
612                         //
613                         if (empty) {
614                                 Statement.MarkReachable (Reachability.CreateUnreachable ());
615                                 return rc;
616                         }
617
618                         Statement.MarkReachable (rc);
619
620                         //
621                         // When infinite while end is unreachable via break anything what follows is unreachable too
622                         //
623                         if (infinite && !end_reachable)
624                                 return Reachability.CreateUnreachable ();
625
626                         return rc;
627                 }
628
629                 protected override void CloneTo (CloneContext clonectx, Statement t)
630                 {
631                         While target = (While) t;
632
633                         target.expr = expr.Clone (clonectx);
634                         target.Statement = Statement.Clone (clonectx);
635                 }
636                 
637                 public override object Accept (StructuralVisitor visitor)
638                 {
639                         return visitor.Visit (this);
640                 }
641
642                 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
643                 {
644                         if (!infinite)
645                                 return;
646
647                         if (end_reachable_das == null)
648                                 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
649
650                         end_reachable_das.Add (fc.DefiniteAssignment);
651                 }
652
653                 public override void SetEndReachable ()
654                 {
655                         end_reachable = true;
656                 }
657         }
658
659         public class For : LoopStatement
660         {
661                 bool infinite, empty, iterator_reachable, end_reachable;
662                 List<DefiniteAssignmentBitSet> end_reachable_das;
663                 
664                 public For (Location l)
665                         : base (null)
666                 {
667                         loc = l;
668                 }
669
670                 public Statement Initializer {
671                         get; set;
672                 }
673
674                 public Expression Condition {
675                         get; set;
676                 }
677
678                 public Statement Iterator {
679                         get; set;
680                 }
681
682                 public override bool Resolve (BlockContext bc)
683                 {
684                         Initializer.Resolve (bc);
685
686                         if (Condition != null) {
687                                 Condition = Condition.Resolve (bc);
688                                 var condition_constant = Condition as Constant;
689                                 if (condition_constant != null) {
690                                         if (condition_constant.IsDefaultValue) {
691                                                 empty = true;
692                                         } else {
693                                                 infinite = true;
694                                         }
695                                 }
696                         } else {
697                                 infinite = true;
698                         }
699
700                         return base.Resolve (bc) && Iterator.Resolve (bc);
701                 }
702
703                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
704                 {
705                         Initializer.FlowAnalysis (fc);
706
707                         DefiniteAssignmentBitSet da_false;
708                         if (Condition != null) {
709                                 Condition.FlowAnalysisConditional (fc);
710                                 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
711                                 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
712                         } else {
713                                 da_false = fc.BranchDefiniteAssignment ();
714                         }
715
716                         Statement.FlowAnalysis (fc);
717
718                         Iterator.FlowAnalysis (fc);
719
720                         //
721                         // Special case infinite for with breaks
722                         //
723                         if (end_reachable_das != null) {
724                                 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
725                                 end_reachable_das = null;
726                         }
727
728                         fc.DefiniteAssignment = da_false;
729
730                         if (infinite && !end_reachable)
731                                 return true;
732
733                         return false;
734                 }
735
736                 public override Reachability MarkReachable (Reachability rc)
737                 {
738                         base.MarkReachable (rc);
739
740                         Initializer.MarkReachable (rc);
741
742                         var body_rc = Statement.MarkReachable (rc);
743                         if (!body_rc.IsUnreachable || iterator_reachable) {
744                                 Iterator.MarkReachable (rc);
745                         }
746
747                         //
748                         // When infinite for end is unreachable via break anything what follows is unreachable too
749                         //
750                         if (infinite && !end_reachable) {
751                                 return Reachability.CreateUnreachable ();
752                         }
753
754                         return rc;
755                 }
756
757                 protected override void DoEmit (EmitContext ec)
758                 {
759                         if (Initializer != null)
760                                 Initializer.Emit (ec);
761
762                         if (empty) {
763                                 Condition.EmitSideEffect (ec);
764                                 return;
765                         }
766
767                         Label old_begin = ec.LoopBegin;
768                         Label old_end = ec.LoopEnd;
769                         Label loop = ec.DefineLabel ();
770                         Label test = ec.DefineLabel ();
771
772                         ec.LoopBegin = ec.DefineLabel ();
773                         ec.LoopEnd = ec.DefineLabel ();
774
775                         ec.Emit (OpCodes.Br, test);
776                         ec.MarkLabel (loop);
777                         Statement.Emit (ec);
778
779                         ec.MarkLabel (ec.LoopBegin);
780                         Iterator.Emit (ec);
781
782                         ec.MarkLabel (test);
783                         //
784                         // If test is null, there is no test, and we are just
785                         // an infinite loop
786                         //
787                         if (Condition != null) {
788                                 ec.Mark (Condition.Location);
789
790                                 //
791                                 // The Resolve code already catches the case for
792                                 // Test == Constant (false) so we know that
793                                 // this is true
794                                 //
795                                 if (Condition is Constant) {
796                                         Condition.EmitSideEffect (ec);
797                                         ec.Emit (OpCodes.Br, loop);
798                                 } else {
799                                         Condition.EmitBranchable (ec, loop, true);
800                                 }
801                                 
802                         } else
803                                 ec.Emit (OpCodes.Br, loop);
804                         ec.MarkLabel (ec.LoopEnd);
805
806                         ec.LoopBegin = old_begin;
807                         ec.LoopEnd = old_end;
808                 }
809
810                 protected override void CloneTo (CloneContext clonectx, Statement t)
811                 {
812                         For target = (For) t;
813
814                         if (Initializer != null)
815                                 target.Initializer = Initializer.Clone (clonectx);
816                         if (Condition != null)
817                                 target.Condition = Condition.Clone (clonectx);
818                         if (Iterator != null)
819                                 target.Iterator = Iterator.Clone (clonectx);
820                         target.Statement = Statement.Clone (clonectx);
821                 }
822
823                 public override object Accept (StructuralVisitor visitor)
824                 {
825                         return visitor.Visit (this);
826                 }
827
828                 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
829                 {
830                         if (!infinite)
831                                 return;
832
833                         if (end_reachable_das == null)
834                                 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
835
836                         end_reachable_das.Add (fc.DefiniteAssignment);
837                 }
838
839                 public override void SetEndReachable ()
840                 {
841                         end_reachable = true;
842                 }
843
844                 public override void SetIteratorReachable ()
845                 {
846                         iterator_reachable = true;
847                 }
848         }
849
850         public abstract class LoopStatement : Statement
851         {
852                 protected LoopStatement (Statement statement)
853                 {
854                         Statement = statement;
855                 }
856
857                 public Statement Statement { get; set; }
858
859                 public override bool Resolve (BlockContext bc)
860                 {
861                         var prev_loop = bc.EnclosingLoop;
862                         var prev_los = bc.EnclosingLoopOrSwitch;
863                         bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
864                         var ok = Statement.Resolve (bc);
865                         bc.EnclosingLoopOrSwitch = prev_los;
866                         bc.EnclosingLoop = prev_loop;
867
868                         return ok;
869                 }
870
871                 //
872                 // Needed by possibly infinite loops statements (for, while) and switch statment
873                 //
874                 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
875                 {
876                 }
877
878                 public virtual void SetEndReachable ()
879                 {
880                 }
881
882                 public virtual void SetIteratorReachable ()
883                 {
884                 }
885         }
886         
887         public class StatementExpression : Statement
888         {
889                 ExpressionStatement expr;
890                 
891                 public StatementExpression (ExpressionStatement expr)
892                 {
893                         this.expr = expr;
894                         loc = expr.StartLocation;
895                 }
896
897                 public StatementExpression (ExpressionStatement expr, Location loc)
898                 {
899                         this.expr = expr;
900                         this.loc = loc;
901                 }
902
903                 public ExpressionStatement Expr {
904                         get {
905                                 return this.expr;
906                         }
907                 }
908                 
909                 protected override void CloneTo (CloneContext clonectx, Statement t)
910                 {
911                         StatementExpression target = (StatementExpression) t;
912                         target.expr = (ExpressionStatement) expr.Clone (clonectx);
913                 }
914                 
915                 protected override void DoEmit (EmitContext ec)
916                 {
917                         expr.EmitStatement (ec);
918                 }
919
920                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
921                 {
922                         expr.FlowAnalysis (fc);
923                         return false;
924                 }
925
926                 public override Reachability MarkReachable (Reachability rc)
927                 {
928                         base.MarkReachable (rc);
929                         expr.MarkReachable (rc);
930                         return rc;
931                 }
932
933                 public override bool Resolve (BlockContext ec)
934                 {
935                         expr = expr.ResolveStatement (ec);
936                         return expr != null;
937                 }
938                 
939                 public override object Accept (StructuralVisitor visitor)
940                 {
941                         return visitor.Visit (this);
942                 }
943         }
944
945         public class StatementErrorExpression : Statement
946         {
947                 Expression expr;
948
949                 public StatementErrorExpression (Expression expr)
950                 {
951                         this.expr = expr;
952                         this.loc = expr.StartLocation;
953                 }
954
955                 public Expression Expr {
956                         get {
957                                 return expr;
958                         }
959                 }
960
961                 public override bool Resolve (BlockContext bc)
962                 {
963                         expr.Error_InvalidExpressionStatement (bc);
964                         return true;
965                 }
966
967                 protected override void DoEmit (EmitContext ec)
968                 {
969                         throw new NotSupportedException ();
970                 }
971
972                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
973                 {
974                         return false;
975                 }
976
977                 protected override void CloneTo (CloneContext clonectx, Statement target)
978                 {
979                         var t = (StatementErrorExpression) target;
980
981                         t.expr = expr.Clone (clonectx);
982                 }
983                 
984                 public override object Accept (StructuralVisitor visitor)
985                 {
986                         return visitor.Visit (this);
987                 }
988         }
989
990         //
991         // Simple version of statement list not requiring a block
992         //
993         public class StatementList : Statement
994         {
995                 List<Statement> statements;
996
997                 public StatementList (Statement first, Statement second)
998                 {
999                         statements = new List<Statement> { first, second };
1000                 }
1001
1002                 #region Properties
1003                 public IList<Statement> Statements {
1004                         get {
1005                                 return statements;
1006                         }
1007                 }
1008                 #endregion
1009
1010                 public void Add (Statement statement)
1011                 {
1012                         statements.Add (statement);
1013                 }
1014
1015                 public override bool Resolve (BlockContext ec)
1016                 {
1017                         foreach (var s in statements)
1018                                 s.Resolve (ec);
1019
1020                         return true;
1021                 }
1022
1023                 protected override void DoEmit (EmitContext ec)
1024                 {
1025                         foreach (var s in statements)
1026                                 s.Emit (ec);
1027                 }
1028
1029                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1030                 {
1031                         foreach (var s in statements)
1032                                 s.FlowAnalysis (fc);
1033
1034                         return false;
1035                 }
1036
1037                 public override Reachability MarkReachable (Reachability rc)
1038                 {
1039                         base.MarkReachable (rc);
1040
1041                         Reachability res = rc;
1042                         foreach (var s in statements)
1043                                 res = s.MarkReachable (rc);
1044
1045                         return res;
1046                 }
1047
1048                 protected override void CloneTo (CloneContext clonectx, Statement target)
1049                 {
1050                         StatementList t = (StatementList) target;
1051
1052                         t.statements = new List<Statement> (statements.Count);
1053                         foreach (Statement s in statements)
1054                                 t.statements.Add (s.Clone (clonectx));
1055                 }
1056                 
1057                 public override object Accept (StructuralVisitor visitor)
1058                 {
1059                         return visitor.Visit (this);
1060                 }
1061         }
1062
1063         //
1064         // For statements which require special handling when inside try or catch block
1065         //
1066         public abstract class ExitStatement : Statement
1067         {
1068                 protected bool unwind_protect;
1069
1070                 protected abstract bool DoResolve (BlockContext bc);
1071                 protected abstract bool IsLocalExit { get; }
1072
1073                 public override bool Resolve (BlockContext bc)
1074                 {
1075                         var res = DoResolve (bc);
1076
1077                         if (!IsLocalExit) {
1078                                 //
1079                                 // We are inside finally scope but is it the scope we are exiting
1080                                 //
1081                                 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1082
1083                                         for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1084                                                 if (b.IsFinallyBlock) {
1085                                                         Error_FinallyClauseExit (bc);
1086                                                         break;
1087                                                 }
1088
1089                                                 if (b is ParametersBlock)
1090                                                         break;
1091                                         }
1092                                 }
1093                         }
1094
1095                         unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1096                         return res;
1097                 }
1098
1099                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1100                 {
1101                         if (IsLocalExit)
1102                                 return true;
1103
1104                         if (fc.TryFinally != null) {
1105                             fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1106                         } else {
1107                             fc.ParametersBlock.CheckControlExit (fc);
1108                         }
1109
1110                         return true;
1111                 }
1112         }
1113
1114         /// <summary>
1115         ///   Implements the return statement
1116         /// </summary>
1117         public class Return : ExitStatement
1118         {
1119                 Expression expr;
1120                 bool expr_returns;
1121
1122                 public Return (Expression expr, Location l)
1123                 {
1124                         this.expr = expr;
1125                         loc = l;
1126                 }
1127
1128                 #region Properties
1129
1130                 public Expression Expr {
1131                         get {
1132                                 return expr;
1133                         }
1134                         protected set {
1135                                 expr = value;
1136                         }
1137                 }
1138
1139                 protected override bool IsLocalExit {
1140                         get {
1141                                 return false;
1142                         }
1143                 }
1144
1145                 #endregion
1146
1147                 protected override bool DoResolve (BlockContext ec)
1148                 {
1149                         var block_return_type = ec.ReturnType;
1150
1151                         if (expr == null) {
1152                                 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1153                                         return true;
1154
1155                                 //
1156                                 // Return must not be followed by an expression when
1157                                 // the method return type is Task
1158                                 //
1159                                 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1160                                         var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1161                                         if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1162                                                 //
1163                                                 // Extra trick not to emit ret/leave inside awaiter body
1164                                                 //
1165                                                 expr = EmptyExpression.Null;
1166                                                 return true;
1167                                         }
1168
1169                                         if (storey.ReturnType.IsGenericTask)
1170                                                 block_return_type = storey.ReturnType.TypeArguments[0];
1171                                 }
1172
1173                                 if (ec.CurrentIterator != null) {
1174                                         Error_ReturnFromIterator (ec);
1175                                 } else if (block_return_type != InternalType.ErrorType) {
1176                                         ec.Report.Error (126, loc,
1177                                                 "An object of a type convertible to `{0}' is required for the return statement",
1178                                                 block_return_type.GetSignatureForError ());
1179                                 }
1180
1181                                 return false;
1182                         }
1183
1184                         expr = expr.Resolve (ec);
1185
1186                         AnonymousExpression am = ec.CurrentAnonymousMethod;
1187                         if (am == null) {
1188                                 if (block_return_type.Kind == MemberKind.Void) {
1189                                         ec.Report.Error (127, loc,
1190                                                 "`{0}': A return keyword must not be followed by any expression when method returns void",
1191                                                 ec.GetSignatureForError ());
1192
1193                                         return false;
1194                                 }
1195                         } else {
1196                                 if (am.IsIterator) {
1197                                         Error_ReturnFromIterator (ec);
1198                                         return false;
1199                                 }
1200
1201                                 var async_block = am as AsyncInitializer;
1202                                 if (async_block != null) {
1203                                         if (expr != null) {
1204                                                 var storey = (AsyncTaskStorey) am.Storey;
1205                                                 var async_type = storey.ReturnType;
1206
1207                                                 if (async_type == null && async_block.ReturnTypeInference != null) {
1208                                                         if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1209                                                                 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1210                                                         else
1211                                                                 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1212                                                         return true;
1213                                                 }
1214
1215                                                 if (async_type.Kind == MemberKind.Void) {
1216                                                         ec.Report.Error (8030, loc,
1217                                                                 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1218                                                         return false;
1219                                                 }
1220
1221                                                 if (!async_type.IsGenericTask) {
1222                                                         if (this is ContextualReturn)
1223                                                                 return true;
1224
1225                                                         if (async_block.DelegateType != null) {
1226                                                                 ec.Report.Error (8031, loc,
1227                                                                         "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1228                                                         } else {
1229                                                                 ec.Report.Error (1997, loc,
1230                                                                         "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1231                                                                         ec.GetSignatureForError ());
1232                                                         }
1233                                                         return false;
1234                                                 }
1235
1236                                                 //
1237                                                 // The return type is actually Task<T> type argument
1238                                                 //
1239                                                 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1240                                                         ec.Report.Error (4016, loc,
1241                                                                 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1242                                                                 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1243                                                 } else {
1244                                                         block_return_type = async_type.TypeArguments[0];
1245                                                 }
1246                                         }
1247                                 } else {
1248                                         if (block_return_type.Kind == MemberKind.Void) {
1249                                                 ec.Report.Error (8030, loc,
1250                                                         "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1251                                                 return false;
1252                                         }
1253
1254                                         var l = am as AnonymousMethodBody;
1255                                         if (l != null && expr != null) {
1256                                                 if (l.ReturnTypeInference != null) {
1257                                                         l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1258                                                         return true;
1259                                                 }
1260
1261                                                 //
1262                                                 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1263                                                 // unexpected debugging experience
1264                                                 //
1265                                                 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1266                                                         l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1267                                                 }
1268                                         }
1269                                 }
1270                         }
1271
1272                         if (expr == null)
1273                                 return false;
1274
1275                         if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1276                                 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1277
1278                                 if (expr == null) {
1279                                         if (am != null && block_return_type == ec.ReturnType) {
1280                                                 ec.Report.Error (1662, loc,
1281                                                         "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",
1282                                                         am.ContainerType, am.GetSignatureForError ());
1283                                         }
1284                                         return false;
1285                                 }
1286                         }
1287
1288                         return true;                    
1289                 }
1290                 
1291                 protected override void DoEmit (EmitContext ec)
1292                 {
1293                         if (expr != null) {
1294
1295                                 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1296                                 if (async_body != null) {
1297                                         var storey = (AsyncTaskStorey)async_body.Storey;
1298                                         Label exit_label = async_body.BodyEnd;
1299
1300                                         //
1301                                         // It's null for await without async
1302                                         //
1303                                         if (storey.HoistedReturnValue != null) {
1304                                                 //
1305                                                 // Special case hoisted return value (happens in try/finally scenario)
1306                                                 //
1307                                                 if (ec.TryFinallyUnwind != null) {
1308                                                         exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1309                                                 }
1310
1311                                                 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1312                                                 async_return.EmitAssign (ec, expr, false, false);
1313                                                 ec.EmitEpilogue ();
1314                                         } else {
1315                                                 expr.Emit (ec);
1316
1317                                                 if (ec.TryFinallyUnwind != null)
1318                                                         exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1319                                         }
1320
1321                                         ec.Emit (OpCodes.Leave, exit_label);
1322                                         return;
1323                                 }
1324
1325                                 expr.Emit (ec);
1326                                 ec.EmitEpilogue ();
1327
1328                                 if (unwind_protect || ec.EmitAccurateDebugInfo)
1329                                         ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1330                         }
1331
1332                         if (unwind_protect) {
1333                                 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1334                         } else if (ec.EmitAccurateDebugInfo) {
1335                                 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1336                         } else {
1337                                 ec.Emit (OpCodes.Ret);
1338                         }
1339                 }
1340
1341                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1342                 {
1343                         if (expr != null)
1344                                 expr.FlowAnalysis (fc);
1345
1346                         if (!expr_returns)
1347                                 base.DoFlowAnalysis (fc);
1348                         
1349                         return true;
1350                 }
1351
1352                 void Error_ReturnFromIterator (ResolveContext rc)
1353                 {
1354                         rc.Report.Error (1622, loc,
1355                                 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1356                 }
1357
1358                 public override Reachability MarkReachable (Reachability rc)
1359                 {
1360                         base.MarkReachable (rc);
1361
1362                         if (Expr != null) {
1363                                 rc = Expr.MarkReachable (rc);
1364                                 expr_returns = rc.IsUnreachable;
1365                         }
1366
1367                         return Reachability.CreateUnreachable ();
1368                 }
1369
1370                 protected override void CloneTo (CloneContext clonectx, Statement t)
1371                 {
1372                         Return target = (Return) t;
1373                         // It's null for simple return;
1374                         if (expr != null)
1375                                 target.expr = expr.Clone (clonectx);
1376                 }
1377
1378                 public override object Accept (StructuralVisitor visitor)
1379                 {
1380                         return visitor.Visit (this);
1381                 }
1382         }
1383
1384         public class Goto : ExitStatement
1385         {
1386                 string target;
1387                 LabeledStatement label;
1388                 TryFinally try_finally;
1389
1390                 public Goto (string label, Location l)
1391                 {
1392                         loc = l;
1393                         target = label;
1394                 }
1395
1396                 public string Target {
1397                         get { return target; }
1398                 }
1399
1400                 protected override bool IsLocalExit {
1401                         get {
1402                                 return true;
1403                         }
1404                 }
1405
1406                 protected override bool DoResolve (BlockContext bc)
1407                 {
1408                         label = bc.CurrentBlock.LookupLabel (target);
1409                         if (label == null) {
1410                                 Error_UnknownLabel (bc, target, loc);
1411                                 return false;
1412                         }
1413
1414                         try_finally = bc.CurrentTryBlock as TryFinally;
1415
1416                         CheckExitBoundaries (bc, label.Block);
1417
1418                         return true;
1419                 }
1420
1421                 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1422                 {
1423                         bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1424                                 label);
1425                 }
1426
1427                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1428                 {
1429                         // Goto to unreachable label
1430                         if (label == null)
1431                                 return true;
1432
1433                         if (fc.AddReachedLabel (label))
1434                                 return true;
1435
1436                         label.Block.ScanGotoJump (label, fc);
1437                         return true;
1438                 }
1439
1440                 public override Reachability MarkReachable (Reachability rc)
1441                 {
1442                         if (rc.IsUnreachable)
1443                                 return rc;
1444
1445                         base.MarkReachable (rc);
1446
1447                         if (try_finally != null) {
1448                                 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1449                                         label.AddGotoReference (rc);
1450                                 } else {
1451                                         label = null;
1452                                 }
1453                         } else {
1454                                 label.AddGotoReference (rc);
1455                         }
1456
1457                         return Reachability.CreateUnreachable ();
1458                 }
1459
1460                 protected override void CloneTo (CloneContext clonectx, Statement target)
1461                 {
1462                         // Nothing to clone
1463                 }
1464
1465                 protected override void DoEmit (EmitContext ec)
1466                 {
1467                         // This should only happen for goto from try block to unrechable label
1468                         if (label == null)
1469                                 return;
1470
1471                         Label l = label.LabelTarget (ec);
1472
1473                         if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1474                                 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1475                                 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1476                         }
1477
1478                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1479                 }
1480
1481                 bool IsLeavingFinally (Block labelBlock)
1482                 {
1483                         var b = try_finally.Statement as Block;
1484                         while (b != null) {
1485                                 if (b == labelBlock)
1486                                         return true;
1487
1488                                 b = b.Parent;
1489                         }
1490
1491                         return false;
1492                 }
1493                 
1494                 public override object Accept (StructuralVisitor visitor)
1495                 {
1496                         return visitor.Visit (this);
1497                 }
1498         }
1499
1500         public class LabeledStatement : Statement {
1501                 string name;
1502                 bool defined;
1503                 bool referenced;
1504                 Label label;
1505                 Block block;
1506                 
1507                 public LabeledStatement (string name, Block block, Location l)
1508                 {
1509                         this.name = name;
1510                         this.block = block;
1511                         this.loc = l;
1512                 }
1513
1514                 public Label LabelTarget (EmitContext ec)
1515                 {
1516                         if (defined)
1517                                 return label;
1518
1519                         label = ec.DefineLabel ();
1520                         defined = true;
1521                         return label;
1522                 }
1523
1524                 public Block Block {
1525                         get {
1526                                 return block;
1527                         }
1528                 }
1529
1530                 public string Name {
1531                         get { return name; }
1532                 }
1533
1534                 protected override void CloneTo (CloneContext clonectx, Statement target)
1535                 {
1536                         var t = (LabeledStatement) target;
1537
1538                         t.block = clonectx.RemapBlockCopy (block);
1539                 }
1540
1541                 public override bool Resolve (BlockContext bc)
1542                 {
1543                         return true;
1544                 }
1545
1546                 protected override void DoEmit (EmitContext ec)
1547                 {
1548                         LabelTarget (ec);
1549                         ec.MarkLabel (label);
1550                 }
1551
1552                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1553                 {
1554                         if (!referenced) {
1555                                 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1556                         }
1557
1558                         return false;
1559                 }
1560
1561                 public override Reachability MarkReachable (Reachability rc)
1562                 {
1563                         base.MarkReachable (rc);
1564
1565                         if (referenced)
1566                                 rc = new Reachability ();
1567
1568                         return rc;
1569                 }
1570
1571                 public void AddGotoReference (Reachability rc)
1572                 {
1573                         if (referenced)
1574                                 return;
1575
1576                         referenced = true;
1577                         MarkReachable (rc);
1578
1579                         block.ScanGotoJump (this);
1580                 }
1581
1582                 public override object Accept (StructuralVisitor visitor)
1583                 {
1584                         return visitor.Visit (this);
1585                 }
1586         }
1587         
1588
1589         /// <summary>
1590         ///   `goto default' statement
1591         /// </summary>
1592         public class GotoDefault : SwitchGoto
1593         {               
1594                 public GotoDefault (Location l)
1595                         : base (l)
1596                 {
1597                 }
1598
1599                 public override bool Resolve (BlockContext bc)
1600                 {
1601                         if (bc.Switch == null) {
1602                                 Error_GotoCaseRequiresSwitchBlock (bc);
1603                                 return false;
1604                         }
1605
1606                         bc.Switch.RegisterGotoCase (null, null);
1607                         base.Resolve (bc);
1608
1609                         return true;
1610                 }
1611
1612                 protected override void DoEmit (EmitContext ec)
1613                 {
1614                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1615                 }
1616
1617                 public override Reachability MarkReachable (Reachability rc)
1618                 {
1619                         if (!rc.IsUnreachable) {
1620                                 var label = switch_statement.DefaultLabel;
1621                                 if (label.IsUnreachable) {
1622                                         label.MarkReachable (rc);
1623                                         switch_statement.Block.ScanGotoJump (label);
1624                                 }
1625                         }
1626
1627                         return base.MarkReachable (rc);
1628                 }
1629
1630                 public override object Accept (StructuralVisitor visitor)
1631                 {
1632                         return visitor.Visit (this);
1633                 }
1634         }
1635
1636         /// <summary>
1637         ///   `goto case' statement
1638         /// </summary>
1639         public class GotoCase : SwitchGoto
1640         {
1641                 Expression expr;
1642                 
1643                 public GotoCase (Expression e, Location l)
1644                         : base (l)
1645                 {
1646                         expr = e;
1647                 }
1648
1649                 public Expression Expr {
1650                         get {
1651                                 return expr;
1652                         }
1653                 }
1654
1655                 public SwitchLabel Label { get; set; }
1656
1657                 public override bool Resolve (BlockContext ec)
1658                 {
1659                         if (ec.Switch == null) {
1660                                 Error_GotoCaseRequiresSwitchBlock (ec);
1661                                 return false;
1662                         }
1663
1664                         Constant c = expr.ResolveLabelConstant (ec);
1665                         if (c == null) {
1666                                 return false;
1667                         }
1668
1669                         Constant res;
1670                         if (ec.Switch.IsNullable && c is NullLiteral) {
1671                                 res = c;
1672                         } else {
1673                                 TypeSpec type = ec.Switch.SwitchType;
1674                                 res = c.Reduce (ec, type);
1675                                 if (res == null) {
1676                                         c.Error_ValueCannotBeConverted (ec, type, true);
1677                                         return false;
1678                                 }
1679
1680                                 if (!Convert.ImplicitStandardConversionExists (c, type))
1681                                         ec.Report.Warning (469, 2, loc,
1682                                                 "The `goto case' value is not implicitly convertible to type `{0}'",
1683                                                 type.GetSignatureForError ());
1684
1685                         }
1686
1687                         ec.Switch.RegisterGotoCase (this, res);
1688                         base.Resolve (ec);
1689                         expr = res;
1690
1691                         return true;
1692                 }
1693
1694                 protected override void DoEmit (EmitContext ec)
1695                 {
1696                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1697                 }
1698
1699                 protected override void CloneTo (CloneContext clonectx, Statement t)
1700                 {
1701                         GotoCase target = (GotoCase) t;
1702
1703                         target.expr = expr.Clone (clonectx);
1704                 }
1705
1706                 public override Reachability MarkReachable (Reachability rc)
1707                 {
1708                         if (!rc.IsUnreachable) {
1709                                 var label = switch_statement.FindLabel ((Constant) expr);
1710                                 if (label.IsUnreachable) {
1711                                         label.MarkReachable (rc);
1712                                         switch_statement.Block.ScanGotoJump (label);
1713                                 }
1714                         }
1715
1716                         return base.MarkReachable (rc);
1717                 }
1718                 
1719                 public override object Accept (StructuralVisitor visitor)
1720                 {
1721                         return visitor.Visit (this);
1722                 }
1723         }
1724
1725         public abstract class SwitchGoto : Statement
1726         {
1727                 protected bool unwind_protect;
1728                 protected Switch switch_statement;
1729
1730                 protected SwitchGoto (Location loc)
1731                 {
1732                         this.loc = loc;
1733                 }
1734
1735                 protected override void CloneTo (CloneContext clonectx, Statement target)
1736                 {
1737                         // Nothing to clone
1738                 }
1739
1740                 public override bool Resolve (BlockContext bc)
1741                 {
1742                         CheckExitBoundaries (bc, bc.Switch.Block);
1743
1744                         unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1745                         switch_statement = bc.Switch;
1746
1747                         return true;
1748                 }
1749
1750                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1751                 {
1752                         return true;
1753                 }
1754
1755                 public override Reachability MarkReachable (Reachability rc)
1756                 {
1757                         base.MarkReachable (rc);
1758                         return Reachability.CreateUnreachable ();
1759                 }
1760
1761                 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1762                 {
1763                         bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1764                 }
1765         }
1766         
1767         public class Throw : Statement {
1768                 Expression expr;
1769                 
1770                 public Throw (Expression expr, Location l)
1771                 {
1772                         this.expr = expr;
1773                         loc = l;
1774                 }
1775
1776                 public Expression Expr {
1777                         get {
1778                                 return this.expr;
1779                         }
1780                 }
1781
1782                 public static Expression ConvertType (ResolveContext rc, Expression expr)
1783                 {
1784                         var et = rc.BuiltinTypes.Exception;
1785                         if (Convert.ImplicitConversionExists (rc, expr, et))
1786                                 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1787                         else {
1788                                 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1789                                 expr = EmptyCast.Create (expr, et);
1790                         }
1791
1792                         return expr;
1793                 }
1794
1795                 public override bool Resolve (BlockContext ec)
1796                 {
1797                         if (expr == null) {
1798                                 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1799                                         ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1800                                 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1801                                         for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1802                                                 if (b.IsFinallyBlock) {
1803                                                         ec.Report.Error (724, loc,
1804                                                                 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1805                                                         break;
1806                                                 }
1807                                         }
1808                                 }
1809
1810                                 return true;
1811                         }
1812
1813                         expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1814
1815                         if (expr == null)
1816                                 return false;
1817
1818                         expr = ConvertType (ec, expr);
1819
1820                         return true;
1821                 }
1822                         
1823                 protected override void DoEmit (EmitContext ec)
1824                 {
1825                         if (expr == null) {
1826                                 var atv = ec.AsyncThrowVariable;
1827                                 if (atv != null) {
1828                                         if (atv.HoistedVariant != null) {
1829                                                 atv.HoistedVariant.Emit (ec);
1830                                         } else {
1831                                                 atv.Emit (ec);
1832                                         }
1833
1834                                         ec.Emit (OpCodes.Throw);
1835                                 } else {
1836                                         ec.Emit (OpCodes.Rethrow);
1837                                 }
1838                         } else {
1839                                 expr.Emit (ec);
1840
1841                                 ec.Emit (OpCodes.Throw);
1842                         }
1843                 }
1844
1845                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1846                 {
1847                         if (expr != null)
1848                                 expr.FlowAnalysis (fc);
1849
1850                         return true;
1851                 }
1852
1853                 public override Reachability MarkReachable (Reachability rc)
1854                 {
1855                         base.MarkReachable (rc);
1856                         return Reachability.CreateUnreachable ();
1857                 }
1858
1859                 protected override void CloneTo (CloneContext clonectx, Statement t)
1860                 {
1861                         Throw target = (Throw) t;
1862
1863                         if (expr != null)
1864                                 target.expr = expr.Clone (clonectx);
1865                 }
1866                 
1867                 public override object Accept (StructuralVisitor visitor)
1868                 {
1869                         return visitor.Visit (this);
1870                 }
1871         }
1872
1873         public class Break : LocalExitStatement
1874         {               
1875                 public Break (Location l)
1876                         : base (l)
1877                 {
1878                 }
1879                 
1880                 public override object Accept (StructuralVisitor visitor)
1881                 {
1882                         return visitor.Visit (this);
1883                 }
1884
1885                 protected override void DoEmit (EmitContext ec)
1886                 {
1887                         var l = ec.LoopEnd;
1888
1889                         if (ec.TryFinallyUnwind != null) {
1890                                 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1891                                 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1892                         }
1893
1894                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1895                 }
1896
1897                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1898                 {
1899                         enclosing_loop.AddEndDefiniteAssignment (fc);
1900                         return true;
1901                 }
1902
1903                 protected override bool DoResolve (BlockContext bc)
1904                 {
1905                         enclosing_loop = bc.EnclosingLoopOrSwitch;
1906                         return base.DoResolve (bc);
1907                 }
1908
1909                 public override Reachability MarkReachable (Reachability rc)
1910                 {
1911                         base.MarkReachable (rc);
1912
1913                         if (!rc.IsUnreachable)
1914                                 enclosing_loop.SetEndReachable ();
1915
1916                         return Reachability.CreateUnreachable ();
1917                 }
1918         }
1919
1920         public class Continue : LocalExitStatement
1921         {               
1922                 public Continue (Location l)
1923                         : base (l)
1924                 {
1925                 }
1926
1927                 public override object Accept (StructuralVisitor visitor)
1928                 {
1929                         return visitor.Visit (this);
1930                 }
1931
1932
1933                 protected override void DoEmit (EmitContext ec)
1934                 {
1935                         var l = ec.LoopBegin;
1936
1937                         if (ec.TryFinallyUnwind != null) {
1938                                 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1939                                 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1940                         }
1941
1942                         ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1943                 }
1944
1945                 protected override bool DoResolve (BlockContext bc)
1946                 {
1947                         enclosing_loop = bc.EnclosingLoop;
1948                         return base.DoResolve (bc);
1949                 }
1950
1951                 public override Reachability MarkReachable (Reachability rc)
1952                 {
1953                         base.MarkReachable (rc);
1954
1955                         if (!rc.IsUnreachable)
1956                                 enclosing_loop.SetIteratorReachable ();
1957
1958                         return Reachability.CreateUnreachable ();
1959                 }
1960         }
1961
1962         public abstract class LocalExitStatement : ExitStatement
1963         {
1964                 protected LoopStatement enclosing_loop;
1965
1966                 protected LocalExitStatement (Location loc)
1967                 {
1968                         this.loc = loc;
1969                 }
1970
1971                 protected override bool IsLocalExit {
1972                         get {
1973                                 return true;
1974                         }
1975                 }
1976
1977                 protected override void CloneTo (CloneContext clonectx, Statement t)
1978                 {
1979                         // nothing needed.
1980                 }
1981
1982                 protected override bool DoResolve (BlockContext bc)
1983                 {
1984                         if (enclosing_loop == null) {
1985                                 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1986                                 return false;
1987                         }
1988
1989                         var block = enclosing_loop.Statement as Block;
1990
1991                         // Don't need to do extra checks for simple statements loops
1992                         if (block != null) {
1993                                 CheckExitBoundaries (bc, block);
1994                         }
1995
1996                         return true;
1997                 }
1998         }
1999
2000         public interface ILocalVariable
2001         {
2002                 void Emit (EmitContext ec);
2003                 void EmitAssign (EmitContext ec);
2004                 void EmitAddressOf (EmitContext ec);
2005         }
2006
2007         public interface INamedBlockVariable
2008         {
2009                 Block Block { get; }
2010                 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2011                 bool IsDeclared { get; }
2012                 bool IsParameter { get; }
2013                 Location Location { get; }
2014         }
2015
2016         public class BlockVariableDeclarator
2017         {
2018                 LocalVariable li;
2019                 Expression initializer;
2020
2021                 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2022                 {
2023                         if (li.Type != null)
2024                                 throw new ArgumentException ("Expected null variable type");
2025
2026                         this.li = li;
2027                         this.initializer = initializer;
2028                 }
2029
2030                 #region Properties
2031
2032                 public LocalVariable Variable {
2033                         get {
2034                                 return li;
2035                         }
2036                 }
2037
2038                 public Expression Initializer {
2039                         get {
2040                                 return initializer;
2041                         }
2042                         set {
2043                                 initializer = value;
2044                         }
2045                 }
2046
2047                 #endregion
2048
2049                 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2050                 {
2051                         var t = (BlockVariableDeclarator) MemberwiseClone ();
2052                         if (initializer != null)
2053                                 t.initializer = initializer.Clone (cloneCtx);
2054
2055                         return t;
2056                 }
2057         }
2058
2059         public class BlockVariable : Statement
2060         {
2061                 Expression initializer;
2062                 protected FullNamedExpression type_expr;
2063                 protected LocalVariable li;
2064                 protected List<BlockVariableDeclarator> declarators;
2065                 TypeSpec type;
2066
2067                 public BlockVariable (FullNamedExpression type, LocalVariable li)
2068                 {
2069                         this.type_expr = type;
2070                         this.li = li;
2071                         this.loc = type_expr.Location;
2072                 }
2073
2074                 protected BlockVariable (LocalVariable li)
2075                 {
2076                         this.li = li;
2077                 }
2078
2079                 #region Properties
2080
2081                 public List<BlockVariableDeclarator> Declarators {
2082                         get {
2083                                 return declarators;
2084                         }
2085                 }
2086
2087                 public Expression Initializer {
2088                         get {
2089                                 return initializer;
2090                         }
2091                         set {
2092                                 initializer = value;
2093                         }
2094                 }
2095
2096                 public FullNamedExpression TypeExpression {
2097                         get {
2098                                 return type_expr;
2099                         }
2100                 }
2101
2102                 public LocalVariable Variable {
2103                         get {
2104                                 return li;
2105                         }
2106                 }
2107
2108                 #endregion
2109
2110                 public void AddDeclarator (BlockVariableDeclarator decl)
2111                 {
2112                         if (declarators == null)
2113                                 declarators = new List<BlockVariableDeclarator> ();
2114
2115                         declarators.Add (decl);
2116                 }
2117
2118                 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2119                 {
2120                         if (bc.Report.Errors != 0)
2121                                 return;
2122
2123                         var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2124
2125                         Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2126                                 new MemberName (li.Name, li.Location), null);
2127
2128                         container.AddField (f);
2129                         f.Define ();
2130
2131                         li.HoistedVariant = new HoistedEvaluatorVariable (f);
2132                         li.SetIsUsed ();
2133                 }
2134
2135                 public override bool Resolve (BlockContext bc)
2136                 {
2137                         return Resolve (bc, true);
2138                 }
2139
2140                 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2141                 {
2142                         if (type == null && !li.IsCompilerGenerated) {
2143                                 var vexpr = type_expr as VarExpr;
2144
2145                                 //
2146                                 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2147                                 // same name exists or as a keyword when no type was found
2148                                 //
2149                                 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2150                                         if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2151                                                 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2152
2153                                         if (li.IsFixed) {
2154                                                 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2155                                                 return false;
2156                                         }
2157
2158                                         if (li.IsConstant) {
2159                                                 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2160                                                 return false;
2161                                         }
2162
2163                                         if (Initializer == null) {
2164                                                 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2165                                                 return false;
2166                                         }
2167
2168                                         if (declarators != null) {
2169                                                 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2170                                                 declarators = null;
2171                                         }
2172
2173                                         Initializer = Initializer.Resolve (bc);
2174                                         if (Initializer != null) {
2175                                                 ((VarExpr) type_expr).InferType (bc, Initializer);
2176                                                 type = type_expr.Type;
2177                                         } else {
2178                                                 // Set error type to indicate the var was placed correctly but could
2179                                                 // not be infered
2180                                                 //
2181                                                 // var a = missing ();
2182                                                 //
2183                                                 type = InternalType.ErrorType;
2184                                         }
2185                                 }
2186
2187                                 if (type == null) {
2188                                         type = type_expr.ResolveAsType (bc);
2189                                         if (type == null)
2190                                                 return false;
2191
2192                                         if (li.IsConstant && !type.IsConstantCompatible) {
2193                                                 Const.Error_InvalidConstantType (type, loc, bc.Report);
2194                                         }
2195                                 }
2196
2197                                 if (type.IsStatic)
2198                                         FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2199
2200                                 li.Type = type;
2201                         }
2202
2203                         bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2204                         if (eval_global) {
2205                                 CreateEvaluatorVariable (bc, li);
2206                         } else if (type != InternalType.ErrorType) {
2207                                 li.PrepareAssignmentAnalysis (bc);
2208                         }
2209
2210                         if (initializer != null) {
2211                                 initializer = ResolveInitializer (bc, li, initializer);
2212                                 // li.Variable.DefinitelyAssigned 
2213                         }
2214
2215                         if (declarators != null) {
2216                                 foreach (var d in declarators) {
2217                                         d.Variable.Type = li.Type;
2218                                         if (eval_global) {
2219                                                 CreateEvaluatorVariable (bc, d.Variable);
2220                                         } else if (type != InternalType.ErrorType) {
2221                                                 d.Variable.PrepareAssignmentAnalysis (bc);
2222                                         }
2223
2224                                         if (d.Initializer != null && resolveDeclaratorInitializers) {
2225                                                 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2226                                                 // d.Variable.DefinitelyAssigned 
2227                                         } 
2228                                 }
2229                         }
2230
2231                         return true;
2232                 }
2233
2234                 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2235                 {
2236                         var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2237                         return a.ResolveStatement (bc);
2238                 }
2239
2240                 protected override void DoEmit (EmitContext ec)
2241                 {
2242                         li.CreateBuilder (ec);
2243
2244                         if (Initializer != null && !IsUnreachable)
2245                                 ((ExpressionStatement) Initializer).EmitStatement (ec);
2246
2247                         if (declarators != null) {
2248                                 foreach (var d in declarators) {
2249                                         d.Variable.CreateBuilder (ec);
2250                                         if (d.Initializer != null && !IsUnreachable) {
2251                                                 ec.Mark (d.Variable.Location);
2252                                                 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2253                                         }
2254                                 }
2255                         }
2256                 }
2257
2258                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2259                 {
2260                         if (Initializer != null)
2261                                 Initializer.FlowAnalysis (fc);
2262
2263                         if (declarators != null) {
2264                                 foreach (var d in declarators) {
2265                                         if (d.Initializer != null)
2266                                                 d.Initializer.FlowAnalysis (fc);
2267                                 }
2268                         }
2269
2270                         return false;
2271                 }
2272
2273                 public override Reachability MarkReachable (Reachability rc)
2274                 {
2275                         base.MarkReachable (rc);
2276                         return initializer == null ? rc : initializer.MarkReachable (rc);
2277                 }
2278
2279                 protected override void CloneTo (CloneContext clonectx, Statement target)
2280                 {
2281                         BlockVariable t = (BlockVariable) target;
2282
2283                         if (type_expr != null)
2284                                 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2285
2286                         if (initializer != null)
2287                                 t.initializer = initializer.Clone (clonectx);
2288
2289                         if (declarators != null) {
2290                                 t.declarators = null;
2291                                 foreach (var d in declarators)
2292                                         t.AddDeclarator (d.Clone (clonectx));
2293                         }
2294                 }
2295
2296                 public override object Accept (StructuralVisitor visitor)
2297                 {
2298                         return visitor.Visit (this);
2299                 }
2300         }
2301
2302         public class BlockConstant : BlockVariable
2303         {
2304                 public BlockConstant (FullNamedExpression type, LocalVariable li)
2305                         : base (type, li)
2306                 {
2307                 }
2308
2309                 public override void Emit (EmitContext ec)
2310                 {
2311                         if (!Variable.IsUsed)
2312                                 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2313                         
2314                         // Nothing to emit, not even sequence point
2315                 }
2316
2317                 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2318                 {
2319                         initializer = initializer.Resolve (bc);
2320                         if (initializer == null)
2321                                 return null;
2322
2323                         var c = initializer as Constant;
2324                         if (c == null) {
2325                                 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2326                                 return null;
2327                         }
2328
2329                         c = c.ConvertImplicitly (li.Type);
2330                         if (c == null) {
2331                                 if (TypeSpec.IsReferenceType (li.Type))
2332                                         initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2333                                 else
2334                                         initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2335
2336                                 return null;
2337                         }
2338
2339                         li.ConstantValue = c;
2340                         return initializer;
2341                 }
2342                 
2343                 public override object Accept (StructuralVisitor visitor)
2344                 {
2345                         return visitor.Visit (this);
2346                 }
2347         }
2348
2349         //
2350         // The information about a user-perceived local variable
2351         //
2352         public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2353         {
2354                 [Flags]
2355                 public enum Flags
2356                 {
2357                         Used = 1,
2358                         IsThis = 1 << 1,
2359                         AddressTaken = 1 << 2,
2360                         CompilerGenerated = 1 << 3,
2361                         Constant = 1 << 4,
2362                         ForeachVariable = 1 << 5,
2363                         FixedVariable = 1 << 6,
2364                         UsingVariable = 1 << 7,
2365                         IsLocked = 1 << 8,
2366                         SymbolFileHidden = 1 << 9,
2367
2368                         ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2369                 }
2370
2371                 TypeSpec type;
2372                 readonly string name;
2373                 readonly Location loc;
2374                 readonly Block block;
2375                 Flags flags;
2376                 Constant const_value;
2377
2378                 public VariableInfo VariableInfo;
2379                 HoistedVariable hoisted_variant;
2380
2381                 LocalBuilder builder;
2382
2383                 public LocalVariable (Block block, string name, Location loc)
2384                 {
2385                         this.block = block;
2386                         this.name = name;
2387                         this.loc = loc;
2388                 }
2389
2390                 public LocalVariable (Block block, string name, Flags flags, Location loc)
2391                         : this (block, name, loc)
2392                 {
2393                         this.flags = flags;
2394                 }
2395
2396                 //
2397                 // Used by variable declarators
2398                 //
2399                 public LocalVariable (LocalVariable li, string name, Location loc)
2400                         : this (li.block, name, li.flags, loc)
2401                 {
2402                 }
2403
2404                 #region Properties
2405
2406                 public bool AddressTaken {
2407                         get {
2408                                 return (flags & Flags.AddressTaken) != 0;
2409                         }
2410                 }
2411
2412                 public Block Block {
2413                         get {
2414                                 return block;
2415                         }
2416                 }
2417
2418                 public Constant ConstantValue {
2419                         get {
2420                                 return const_value;
2421                         }
2422                         set {
2423                                 const_value = value;
2424                         }
2425                 }
2426
2427                 //
2428                 // Hoisted local variable variant
2429                 //
2430                 public HoistedVariable HoistedVariant {
2431                         get {
2432                                 return hoisted_variant;
2433                         }
2434                         set {
2435                                 hoisted_variant = value;
2436                         }
2437                 }
2438
2439                 public bool Created {
2440                         get {
2441                                 return builder != null;
2442                         }
2443                 }
2444
2445                 public bool IsDeclared {
2446                         get {
2447                                 return type != null;
2448                         }
2449                 }
2450
2451                 public bool IsCompilerGenerated {
2452                         get {
2453                                 return (flags & Flags.CompilerGenerated) != 0;
2454                         }
2455                 }
2456
2457                 public bool IsConstant {
2458                         get {
2459                                 return (flags & Flags.Constant) != 0;
2460                         }
2461                 }
2462
2463                 public bool IsLocked {
2464                         get {
2465                                 return (flags & Flags.IsLocked) != 0;
2466                         }
2467                         set {
2468                                 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2469                         }
2470                 }
2471
2472                 public bool IsThis {
2473                         get {
2474                                 return (flags & Flags.IsThis) != 0;
2475                         }
2476                 }
2477
2478                 public bool IsUsed {
2479                         get {
2480                                 return (flags & Flags.Used) != 0;
2481                         }
2482                 }
2483
2484                 public bool IsFixed {
2485                         get {
2486                                 return (flags & Flags.FixedVariable) != 0;
2487                         }
2488                         set {
2489                                 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2490                         }
2491                 }
2492
2493                 bool INamedBlockVariable.IsParameter {
2494                         get {
2495                                 return false;
2496                         }
2497                 }
2498
2499                 public bool IsReadonly {
2500                         get {
2501                                 return (flags & Flags.ReadonlyMask) != 0;
2502                         }
2503                 }
2504
2505                 public Location Location {
2506                         get {
2507                                 return loc;
2508                         }
2509                 }
2510
2511                 public string Name {
2512                         get {
2513                                 return name;
2514                         }
2515                 }
2516
2517                 public TypeSpec Type {
2518                     get {
2519                                 return type;
2520                         }
2521                     set {
2522                                 type = value;
2523                         }
2524                 }
2525
2526                 #endregion
2527
2528                 public void CreateBuilder (EmitContext ec)
2529                 {
2530                         if ((flags & Flags.Used) == 0) {
2531                                 if (VariableInfo == null) {
2532                                         // Missing flow analysis or wrong variable flags
2533                                         throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2534                                 }
2535
2536                                 if (VariableInfo.IsEverAssigned)
2537                                         ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2538                                 else
2539                                         ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2540                         }
2541
2542                         if (HoistedVariant != null)
2543                                 return;
2544
2545                         if (builder != null) {
2546                                 if ((flags & Flags.CompilerGenerated) != 0)
2547                                         return;
2548
2549                                 // To avoid Used warning duplicates
2550                                 throw new InternalErrorException ("Already created variable `{0}'", name);
2551                         }
2552
2553                         //
2554                         // All fixed variabled are pinned, a slot has to be alocated
2555                         //
2556                         builder = ec.DeclareLocal (Type, IsFixed);
2557                         if ((flags & Flags.SymbolFileHidden) == 0)
2558                                 ec.DefineLocalVariable (name, builder);
2559                 }
2560
2561                 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2562                 {
2563                         LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2564                         if (!writeToSymbolFile)
2565                                 li.flags |= Flags.SymbolFileHidden;
2566                         
2567                         li.Type = type;
2568                         return li;
2569                 }
2570
2571                 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2572                 {
2573                         if (IsConstant && const_value != null) {
2574                                 SetIsUsed ();
2575                                 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2576                         }
2577
2578                         return new LocalVariableReference (this, loc);
2579                 }
2580
2581                 public void Emit (EmitContext ec)
2582                 {
2583                         // TODO: Need something better for temporary variables
2584                         if ((flags & Flags.CompilerGenerated) != 0)
2585                                 CreateBuilder (ec);
2586
2587                         ec.Emit (OpCodes.Ldloc, builder);
2588                 }
2589
2590                 public void EmitAssign (EmitContext ec)
2591                 {
2592                         // TODO: Need something better for temporary variables
2593                         if ((flags & Flags.CompilerGenerated) != 0)
2594                                 CreateBuilder (ec);
2595
2596                         ec.Emit (OpCodes.Stloc, builder);
2597                 }
2598
2599                 public void EmitAddressOf (EmitContext ec)
2600                 {
2601                         // TODO: Need something better for temporary variables
2602                         if ((flags & Flags.CompilerGenerated) != 0)
2603                                 CreateBuilder (ec);
2604
2605                         ec.Emit (OpCodes.Ldloca, builder);
2606                 }
2607
2608                 public static string GetCompilerGeneratedName (Block block)
2609                 {
2610                         // HACK: Debugger depends on the name semantics
2611                         return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2612                 }
2613
2614                 public string GetReadOnlyContext ()
2615                 {
2616                         switch (flags & Flags.ReadonlyMask) {
2617                         case Flags.FixedVariable:
2618                                 return "fixed variable";
2619                         case Flags.ForeachVariable:
2620                                 return "foreach iteration variable";
2621                         case Flags.UsingVariable:
2622                                 return "using variable";
2623                         }
2624
2625                         throw new InternalErrorException ("Variable is not readonly");
2626                 }
2627
2628                 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2629                 {
2630                         if (VariableInfo == null)
2631                                 throw new Exception ();
2632
2633                         if (IsAssigned (fc))
2634                                 return true;
2635
2636                         return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2637                 }
2638
2639                 public bool IsAssigned (FlowAnalysisContext fc)
2640                 {
2641                         return fc.IsDefinitelyAssigned (VariableInfo);
2642                 }
2643
2644                 public void PrepareAssignmentAnalysis (BlockContext bc)
2645                 {
2646                         //
2647                         // No need to run assignment analysis for these guys
2648                         //
2649                         if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2650                                 return;
2651
2652                         VariableInfo = VariableInfo.Create (bc, this);
2653                 }
2654
2655                 //
2656                 // Mark the variables as referenced in the user code
2657                 //
2658                 public void SetIsUsed ()
2659                 {
2660                         flags |= Flags.Used;
2661                 }
2662
2663                 public void SetHasAddressTaken ()
2664                 {
2665                         flags |= (Flags.AddressTaken | Flags.Used);
2666                 }
2667
2668                 public override string ToString ()
2669                 {
2670                         return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2671                 }
2672         }
2673
2674         /// <summary>
2675         ///   Block represents a C# block.
2676         /// </summary>
2677         ///
2678         /// <remarks>
2679         ///   This class is used in a number of places: either to represent
2680         ///   explicit blocks that the programmer places or implicit blocks.
2681         ///
2682         ///   Implicit blocks are used as labels or to introduce variable
2683         ///   declarations.
2684         ///
2685         ///   Top-level blocks derive from Block, and they are called ToplevelBlock
2686         ///   they contain extra information that is not necessary on normal blocks.
2687         /// </remarks>
2688         public class Block : Statement {
2689                 [Flags]
2690                 public enum Flags
2691                 {
2692                         Unchecked = 1,
2693                         ReachableEnd = 8,
2694                         Unsafe = 16,
2695                         HasCapturedVariable = 64,
2696                         HasCapturedThis = 1 << 7,
2697                         IsExpressionTree = 1 << 8,
2698                         CompilerGenerated = 1 << 9,
2699                         HasAsyncModifier = 1 << 10,
2700                         Resolved = 1 << 11,
2701                         YieldBlock = 1 << 12,
2702                         AwaitBlock = 1 << 13,
2703                         FinallyBlock = 1 << 14,
2704                         CatchBlock = 1 << 15,
2705                         HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2706                         Iterator = 1 << 20,
2707                         NoFlowAnalysis = 1 << 21,
2708                         InitializationEmitted = 1 << 22
2709                 }
2710
2711                 public Block Parent;
2712                 public Location StartLocation;
2713                 public Location EndLocation;
2714
2715                 public ExplicitBlock Explicit;
2716                 public ParametersBlock ParametersBlock;
2717
2718                 protected Flags flags;
2719
2720                 //
2721                 // The statements in this block
2722                 //
2723                 protected List<Statement> statements;
2724
2725                 protected List<Statement> scope_initializers;
2726
2727                 int? resolving_init_idx;
2728
2729                 Block original;
2730
2731 #if DEBUG
2732                 static int id;
2733                 public int ID = id++;
2734
2735                 static int clone_id_counter;
2736                 int clone_id;
2737 #endif
2738
2739 //              int assignable_slots;
2740
2741                 public Block (Block parent, Location start, Location end)
2742                         : this (parent, 0, start, end)
2743                 {
2744                 }
2745
2746                 public Block (Block parent, Flags flags, Location start, Location end)
2747                 {
2748                         if (parent != null) {
2749                                 // the appropriate constructors will fixup these fields
2750                                 ParametersBlock = parent.ParametersBlock;
2751                                 Explicit = parent.Explicit;
2752                         }
2753                         
2754                         this.Parent = parent;
2755                         this.flags = flags;
2756                         this.StartLocation = start;
2757                         this.EndLocation = end;
2758                         this.loc = start;
2759                         statements = new List<Statement> (4);
2760
2761                         this.original = this;
2762                 }
2763
2764                 #region Properties
2765
2766                 public Block Original {
2767                         get {
2768                                 return original;
2769                         }
2770                         protected set {
2771                                 original = value;
2772                         }
2773                 }
2774
2775                 public bool IsCompilerGenerated {
2776                         get { return (flags & Flags.CompilerGenerated) != 0; }
2777                         set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2778                 }
2779
2780
2781                 public bool IsCatchBlock {
2782                         get {
2783                                 return (flags & Flags.CatchBlock) != 0;
2784                         }
2785                 }
2786
2787                 public bool IsFinallyBlock {
2788                         get {
2789                                 return (flags & Flags.FinallyBlock) != 0;
2790                         }
2791                 }
2792
2793                 public bool Unchecked {
2794                         get { return (flags & Flags.Unchecked) != 0; }
2795                         set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2796                 }
2797
2798                 public bool Unsafe {
2799                         get { return (flags & Flags.Unsafe) != 0; }
2800                         set { flags |= Flags.Unsafe; }
2801                 }
2802
2803                 public List<Statement> Statements {
2804                         get { return statements; }
2805                 }
2806
2807                 #endregion
2808
2809                 public void SetEndLocation (Location loc)
2810                 {
2811                         EndLocation = loc;
2812                 }
2813
2814                 public void AddLabel (LabeledStatement target)
2815                 {
2816                         ParametersBlock.TopBlock.AddLabel (target.Name, target);
2817                 }
2818
2819                 public void AddLocalName (LocalVariable li)
2820                 {
2821                         AddLocalName (li.Name, li);
2822                 }
2823
2824                 public void AddLocalName (string name, INamedBlockVariable li)
2825                 {
2826                         ParametersBlock.TopBlock.AddLocalName (name, li, false);
2827                 }
2828
2829                 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2830                 {
2831                         if (reason == null) {
2832                                 Error_AlreadyDeclared (name, variable);
2833                                 return;
2834                         }
2835
2836                         ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2837                                 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2838                                 "to `{0}', which is already used in a `{1}' scope to denote something else",
2839                                 name, reason);
2840                 }
2841
2842                 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2843                 {
2844                         var pi = variable as ParametersBlock.ParameterInfo;
2845                         if (pi != null) {
2846                                 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2847                         } else {
2848                                 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2849                                         "A local variable named `{0}' is already defined in this scope", name);
2850                         }
2851                 }
2852                                         
2853                 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2854                 {
2855                         ParametersBlock.TopBlock.Report.Error (412, loc,
2856                                 "The type parameter name `{0}' is the same as local variable or parameter name",
2857                                 name);
2858                 }
2859
2860                 //
2861                 // It should be used by expressions which require to
2862                 // register a statement during resolve process.
2863                 //
2864                 public void AddScopeStatement (Statement s)
2865                 {
2866                         if (scope_initializers == null)
2867                                 scope_initializers = new List<Statement> ();
2868
2869                         //
2870                         // Simple recursive helper, when resolve scope initializer another
2871                         // new scope initializer can be added, this ensures it's initialized
2872                         // before existing one. For now this can happen with expression trees
2873                         // in base ctor initializer only
2874                         //
2875                         if (resolving_init_idx.HasValue) {
2876                                 scope_initializers.Insert (resolving_init_idx.Value, s);
2877                                 ++resolving_init_idx;
2878                         } else {
2879                                 scope_initializers.Add (s);
2880                         }
2881                 }
2882
2883                 public void InsertStatement (int index, Statement s)
2884                 {
2885                         statements.Insert (index, s);
2886                 }
2887                 
2888                 public void AddStatement (Statement s)
2889                 {
2890                         statements.Add (s);
2891                 }
2892
2893                 public LabeledStatement LookupLabel (string name)
2894                 {
2895                         return ParametersBlock.GetLabel (name, this);
2896                 }
2897
2898                 public override Reachability MarkReachable (Reachability rc)
2899                 {
2900                         if (rc.IsUnreachable)
2901                                 return rc;
2902
2903                         MarkReachableScope (rc);
2904
2905                         foreach (var s in statements) {
2906                                 rc = s.MarkReachable (rc);
2907                                 if (rc.IsUnreachable) {
2908                                         if ((flags & Flags.ReachableEnd) != 0)
2909                                                 return new Reachability ();
2910
2911                                         return rc;
2912                                 }
2913                         }
2914
2915                         flags |= Flags.ReachableEnd;
2916
2917                         return rc;
2918                 }
2919
2920                 public void MarkReachableScope (Reachability rc)
2921                 {
2922                         base.MarkReachable (rc);
2923
2924                         if (scope_initializers != null) {
2925                                 foreach (var si in scope_initializers)
2926                                         si.MarkReachable (rc);
2927                         }
2928                 }
2929
2930                 public override bool Resolve (BlockContext bc)
2931                 {
2932                         if ((flags & Flags.Resolved) != 0)
2933                                 return true;
2934
2935                         Block prev_block = bc.CurrentBlock;
2936                         bc.CurrentBlock = this;
2937
2938                         //
2939                         // Compiler generated scope statements
2940                         //
2941                         if (scope_initializers != null) {
2942                                 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2943                                         scope_initializers[resolving_init_idx.Value].Resolve (bc);
2944                                 }
2945
2946                                 resolving_init_idx = null;
2947                         }
2948
2949                         bool ok = true;
2950                         int statement_count = statements.Count;
2951                         for (int ix = 0; ix < statement_count; ix++){
2952                                 Statement s = statements [ix];
2953
2954                                 if (!s.Resolve (bc)) {
2955                                         ok = false;
2956                                         statements [ix] = new EmptyStatement (s.loc);
2957                                         continue;
2958                                 }
2959                         }
2960
2961                         bc.CurrentBlock = prev_block;
2962
2963                         flags |= Flags.Resolved;
2964                         return ok;
2965                 }
2966
2967                 protected override void DoEmit (EmitContext ec)
2968                 {
2969                         for (int ix = 0; ix < statements.Count; ix++){
2970                                 statements [ix].Emit (ec);
2971                         }
2972                 }
2973
2974                 public override void Emit (EmitContext ec)
2975                 {
2976                         if (scope_initializers != null)
2977                                 EmitScopeInitializers (ec);
2978
2979                         DoEmit (ec);
2980                 }
2981
2982                 protected void EmitScopeInitializers (EmitContext ec)
2983                 {
2984                         foreach (Statement s in scope_initializers)
2985                                 s.Emit (ec);
2986                 }
2987
2988                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2989                 {
2990                         if (scope_initializers != null) {
2991                                 foreach (var si in scope_initializers)
2992                                         si.FlowAnalysis (fc);
2993                         }
2994
2995                         return DoFlowAnalysis (fc, 0);  
2996                 }
2997
2998                 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2999                 {
3000                         bool end_unreachable = !reachable;
3001                         bool goto_flow_analysis = startIndex != 0;
3002                         for (; startIndex < statements.Count; ++startIndex) {
3003                                 var s = statements[startIndex];
3004
3005                                 end_unreachable = s.FlowAnalysis (fc);
3006                                 if (s.IsUnreachable) {
3007                                         statements [startIndex] = RewriteUnreachableStatement (s);
3008                                         continue;
3009                                 }
3010
3011                                 //
3012                                 // Statement end reachability is needed mostly due to goto support. Consider
3013                                 //
3014                                 // if (cond) {
3015                                 //    goto X;
3016                                 // } else {
3017                                 //    goto Y;
3018                                 // }
3019                                 // X:
3020                                 //
3021                                 // X label is reachable only via goto not as another statement after if. We need
3022                                 // this for flow-analysis only to carry variable info correctly.
3023                                 //
3024                                 if (end_unreachable) {
3025                                         bool after_goto_case = goto_flow_analysis && s is GotoCase;
3026
3027                                         var f = s as TryFinally;
3028                                         if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3029                                                 //
3030                                                 // Special case for try-finally with unreachable code after
3031                                                 // finally block. Try block has to include leave opcode but there is
3032                                                 // no label to leave to after unreachable finally block closing
3033                                                 // brace. This sentinel ensures there is always IL instruction to
3034                                                 // leave to even if we know it'll never be reached.
3035                                                 //
3036                                                 statements.Insert (startIndex + 1, new SentinelStatement ());
3037                                         } else {
3038                                                 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3039                                                         s = statements [startIndex];
3040                                                         if (s is SwitchLabel) {
3041                                                                 if (!after_goto_case)
3042                                                                         s.FlowAnalysis (fc);
3043
3044                                                                 break;
3045                                                         }
3046
3047                                                         if (s.IsUnreachable) {
3048                                                                 s.FlowAnalysis (fc);
3049                                                                 statements [startIndex] = RewriteUnreachableStatement (s);
3050                                                         }
3051                                                 }
3052                                         }
3053
3054                                         //
3055                                         // Idea is to stop after goto case because goto case will always have at least same
3056                                         // variable assigned as switch case label. This saves a lot for complex goto case tests
3057                                         //
3058                                         if (after_goto_case)
3059                                                 break;
3060
3061                                         continue;
3062                                 }
3063
3064                                 var lb = s as LabeledStatement;
3065                                 if (lb != null && fc.AddReachedLabel (lb))
3066                                         break;
3067                         }
3068
3069                         //
3070                         // The condition should be true unless there is forward jumping goto
3071                         // 
3072                         // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3073                         //      Debug.Fail ();
3074
3075                         return !Explicit.HasReachableClosingBrace;
3076                 }
3077
3078                 static Statement RewriteUnreachableStatement (Statement s)
3079                 {
3080                         // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3081                         // analysis. Even csc report unreachable warning for it but it's actually used hence
3082                         // we try to emulate this behaviour
3083                         //
3084                         // Consider:
3085                         //      goto L;
3086                         //      int v;
3087                         // L:
3088                         //      v = 1;
3089
3090                         if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3091                                 return s;
3092
3093                         return new EmptyStatement (s.loc);
3094                 }
3095
3096                 public void ScanGotoJump (Statement label)
3097                 {
3098                         int i;
3099                         for (i = 0; i < statements.Count; ++i) {
3100                                 if (statements[i] == label)
3101                                         break;
3102                         }
3103
3104                         var rc = new Reachability ();
3105                         for (++i; i < statements.Count; ++i) {
3106                                 var s = statements[i];
3107                                 rc = s.MarkReachable (rc);
3108                                 if (rc.IsUnreachable)
3109                                         return;
3110                         }
3111
3112                         flags |= Flags.ReachableEnd;
3113                 }
3114
3115                 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3116                 {
3117                         int i;
3118                         for (i = 0; i < statements.Count; ++i) {
3119                                 if (statements[i] == label)
3120                                         break;
3121                         }
3122
3123                         DoFlowAnalysis (fc, ++i);
3124                 }
3125
3126 #if DEBUG
3127                 public override string ToString ()
3128                 {
3129                         return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3130                 }
3131 #endif
3132
3133                 protected override void CloneTo (CloneContext clonectx, Statement t)
3134                 {
3135                         Block target = (Block) t;
3136 #if DEBUG
3137                         target.clone_id = ++clone_id_counter;
3138 #endif
3139
3140                         clonectx.AddBlockMap (this, target);
3141                         if (original != this)
3142                                 clonectx.AddBlockMap (original, target);
3143
3144                         target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3145                         target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3146
3147                         if (Parent != null)
3148                                 target.Parent = clonectx.RemapBlockCopy (Parent);
3149
3150                         target.statements = new List<Statement> (statements.Count);
3151                         foreach (Statement s in statements)
3152                                 target.statements.Add (s.Clone (clonectx));
3153                 }
3154
3155                 public override object Accept (StructuralVisitor visitor)
3156                 {
3157                         return visitor.Visit (this);
3158                 }
3159         }
3160
3161         public class ExplicitBlock : Block
3162         {
3163                 protected AnonymousMethodStorey am_storey;
3164                 int debug_scope_index;
3165
3166                 public ExplicitBlock (Block parent, Location start, Location end)
3167                         : this (parent, (Flags) 0, start, end)
3168                 {
3169                 }
3170
3171                 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3172                         : base (parent, flags, start, end)
3173                 {
3174                         this.Explicit = this;
3175                 }
3176
3177                 #region Properties
3178
3179                 public AnonymousMethodStorey AnonymousMethodStorey {
3180                         get {
3181                                 return am_storey;
3182                         }
3183                 }
3184
3185                 public bool HasAwait {
3186                         get {
3187                                 return (flags & Flags.AwaitBlock) != 0;
3188                         }
3189                 }
3190
3191                 public bool HasCapturedThis {
3192                         set {
3193                                 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3194                         }
3195                         get {
3196                                 return (flags & Flags.HasCapturedThis) != 0;
3197                         }
3198                 }
3199
3200                 //
3201                 // Used to indicate that the block has reference to parent
3202                 // block and cannot be made static when defining anonymous method
3203                 //
3204                 public bool HasCapturedVariable {
3205                         set {
3206                                 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3207                         }
3208                         get {
3209                                 return (flags & Flags.HasCapturedVariable) != 0;
3210                         }
3211                 }
3212
3213                 public bool HasReachableClosingBrace {
3214                     get {
3215                         return (flags & Flags.ReachableEnd) != 0;
3216                     }
3217                         set {
3218                                 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3219                         }
3220                 }
3221
3222                 public bool HasYield {
3223                         get {
3224                                 return (flags & Flags.YieldBlock) != 0;
3225                         }
3226                 }
3227
3228                 #endregion
3229
3230                 //
3231                 // Creates anonymous method storey in current block
3232                 //
3233                 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3234                 {
3235                         //
3236                         // Return same story for iterator and async blocks unless we are
3237                         // in nested anonymous method
3238                         //
3239                         if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3240                                 return ec.CurrentAnonymousMethod.Storey;
3241
3242                         if (am_storey == null) {
3243                                 MemberBase mc = ec.MemberContext as MemberBase;
3244
3245                                 //
3246                                 // Creates anonymous method storey for this block
3247                                 //
3248                                 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3249                         }
3250
3251                         return am_storey;
3252                 }
3253
3254                 public void EmitScopeInitialization (EmitContext ec)
3255                 {
3256                         if ((flags & Flags.InitializationEmitted) != 0)
3257                                 return;
3258
3259                         if (am_storey != null) {
3260                                 DefineStoreyContainer (ec, am_storey);
3261                                 am_storey.EmitStoreyInstantiation (ec, this);
3262                         }
3263
3264                         if (scope_initializers != null)
3265                                 EmitScopeInitializers (ec);
3266
3267                         flags |= Flags.InitializationEmitted;
3268                 }
3269
3270                 public override void Emit (EmitContext ec)
3271                 {
3272                         // TODO: It's needed only when scope has variable (normal or lifted)
3273                         var scopeIndex = GetDebugSymbolScopeIndex ();
3274                         if (scopeIndex > 0) {
3275                                 ec.BeginScope (scopeIndex);
3276                         }
3277
3278                         EmitScopeInitialization (ec);
3279
3280                         if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3281                                 ec.Emit (OpCodes.Nop);
3282                         }
3283
3284                         DoEmit (ec);
3285
3286                         if (scopeIndex > 0)
3287                                 ec.EndScope ();
3288
3289                         if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3290                                 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3291                                 ec.Emit (OpCodes.Nop);
3292                         }
3293                 }
3294
3295                 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3296                 {
3297                         if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3298                                 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3299                                 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3300                         }
3301
3302                         //
3303                         // Creates anonymous method storey
3304                         //
3305                         storey.CreateContainer ();
3306                         storey.DefineContainer ();
3307                         storey.ExpandBaseInterfaces ();
3308
3309                         if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3310
3311                                 //
3312                                 // Only first storey in path will hold this reference. All children blocks will
3313                                 // reference it indirectly using $ref field
3314                                 //
3315                                 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3316                                         if (b.Parent != null) {
3317                                                 var s = b.Parent.Explicit.AnonymousMethodStorey;
3318                                                 if (s != null) {
3319                                                         storey.HoistedThis = s.HoistedThis;
3320                                                         break;
3321                                                 }
3322                                         }
3323
3324                                         if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3325                                                 if (storey.HoistedThis == null)
3326                                                         storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3327
3328                                                 if (storey.HoistedThis != null)
3329                                                         break;
3330                                         }
3331                                 }
3332
3333                                 //
3334                                 // We are the first storey on path and 'this' has to be hoisted
3335                                 //
3336                                 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3337                                         foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3338                                                 //
3339                                                 // ThisReferencesFromChildrenBlock holds all reference even if they
3340                                                 // are not on this path. It saves some memory otherwise it'd have to
3341                                                 // be in every explicit block. We run this check to see if the reference
3342                                                 // is valid for this storey
3343                                                 //
3344                                                 Block block_on_path = ref_block;
3345                                                 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3346
3347                                                 if (block_on_path == null)
3348                                                         continue;
3349
3350                                                 if (storey.HoistedThis == null) {
3351                                                         storey.AddCapturedThisField (ec, null);
3352                                                 }
3353
3354                                                 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3355                                                         ParametersBlock pb;
3356                                                         AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3357
3358                                                         if (b_storey != null) {
3359                                                                 //
3360                                                                 // Don't add storey cross reference for `this' when the storey ends up not
3361                                                                 // beeing attached to any parent
3362                                                                 //
3363                                                                 if (b.ParametersBlock.StateMachine == null) {
3364                                                                         AnonymousMethodStorey s = null;
3365                                                                         for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3366                                                                                 s = ab.Explicit.AnonymousMethodStorey;
3367                                                                                 if (s != null)
3368                                                                                         break;
3369                                                                         }
3370
3371                                                                         // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3372                                                                         if (s == null) {
3373                                                                                 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3374                                                                                 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3375                                                                                 break;
3376                                                                         }
3377
3378                                                                 }
3379
3380                                                                 //
3381                                                                 // Stop propagation inside same top block
3382                                                                 //
3383                                                                 if (b.ParametersBlock == ParametersBlock.Original) {
3384                                                                         b_storey.AddParentStoreyReference (ec, storey);
3385 //                                                                      b_storey.HoistedThis = storey.HoistedThis;
3386                                                                         break;
3387                                                                 }
3388
3389                                                                 b = pb = b.ParametersBlock;
3390                                                         } else {
3391                                                                 pb = b as ParametersBlock;
3392                                                         }
3393
3394                                                         if (pb != null && pb.StateMachine != null) {
3395                                                                 if (pb.StateMachine == storey)
3396                                                                         break;
3397
3398                                                                 //
3399                                                                 // If we are state machine with no parent. We can hook into parent without additional
3400                                                                 // reference and capture this directly
3401                                                                 //
3402                                                                 ExplicitBlock parent_storey_block = pb;
3403                                                                 while (parent_storey_block.Parent != null) {
3404                                                                         parent_storey_block = parent_storey_block.Parent.Explicit;
3405                                                                         if (parent_storey_block.AnonymousMethodStorey != null) {
3406                                                                                 break;
3407                                                                         }
3408                                                                 }
3409
3410                                                                 if (parent_storey_block.AnonymousMethodStorey == null) {
3411                                                                         if (pb.StateMachine.HoistedThis == null) {
3412                                                                                 pb.StateMachine.AddCapturedThisField (ec, null);
3413                                                                                 b.HasCapturedThis = true;
3414                                                                         }
3415
3416                                                                         continue;
3417                                                                 }
3418
3419                                                                 var parent_this_block = pb;
3420                                                                 while (parent_this_block.Parent != null) {
3421                                                                         parent_this_block = parent_this_block.Parent.ParametersBlock;
3422                                                                         if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3423                                                                                 break;
3424                                                                         }
3425                                                                 }
3426
3427                                                                 //
3428                                                                 // Add reference to closest storey which holds captured this
3429                                                                 //
3430                                                                 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3431                                                         }
3432
3433                                                         //
3434                                                         // Add parent storey reference only when this is not captured directly
3435                                                         //
3436                                                         if (b_storey != null) {
3437                                                                 b_storey.AddParentStoreyReference (ec, storey);
3438                                                                 b_storey.HoistedThis = storey.HoistedThis;
3439                                                         }
3440                                                 }
3441                                         }
3442                                 }
3443                         }
3444
3445                         var ref_blocks = storey.ReferencesFromChildrenBlock;
3446                         if (ref_blocks != null) {
3447                                 foreach (ExplicitBlock ref_block in ref_blocks) {
3448                                         for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3449                                                 if (b.AnonymousMethodStorey != null) {
3450                                                         b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3451
3452                                                         //
3453                                                         // Stop propagation inside same top block
3454                                                         //
3455                                                         if (b.ParametersBlock == ParametersBlock.Original)
3456                                                                 break;
3457
3458                                                         b = b.ParametersBlock;
3459                                                 }
3460
3461                                                 var pb = b as ParametersBlock;
3462                                                 if (pb != null && pb.StateMachine != null) {
3463                                                         if (pb.StateMachine == storey)
3464                                                                 break;
3465
3466                                                         pb.StateMachine.AddParentStoreyReference (ec, storey);
3467                                                 }
3468
3469                                                 b.HasCapturedVariable = true;
3470                                         }
3471                                 }
3472                         }
3473
3474                         storey.Define ();
3475                         storey.PrepareEmit ();
3476                         storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3477                 }
3478
3479                 public void DisableDebugScopeIndex ()
3480                 {
3481                         debug_scope_index = -1;
3482                 }
3483
3484                 public virtual int GetDebugSymbolScopeIndex ()
3485                 {
3486                         if (debug_scope_index == 0)
3487                                 debug_scope_index = ++ParametersBlock.debug_scope_index;
3488
3489                         return debug_scope_index;
3490                 }
3491
3492                 public void RegisterAsyncAwait ()
3493                 {
3494                         var block = this;
3495                         while ((block.flags & Flags.AwaitBlock) == 0) {
3496                                 block.flags |= Flags.AwaitBlock;
3497
3498                                 if (block is ParametersBlock)
3499                                         return;
3500
3501                                 block = block.Parent.Explicit;
3502                         }
3503                 }
3504
3505                 public void RegisterIteratorYield ()
3506                 {
3507                         ParametersBlock.TopBlock.IsIterator = true;
3508
3509                         var block = this;
3510                         while ((block.flags & Flags.YieldBlock) == 0) {
3511                                 block.flags |= Flags.YieldBlock;
3512
3513                                 if (block.Parent == null)
3514                                         return;
3515
3516                                 block = block.Parent.Explicit;
3517                         }
3518                 }
3519
3520                 public void SetCatchBlock ()
3521                 {
3522                         flags |= Flags.CatchBlock;
3523                 }
3524
3525                 public void SetFinallyBlock ()
3526                 {
3527                         flags |= Flags.FinallyBlock;
3528                 }
3529
3530                 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3531                 {
3532                         tryBlock.statements = statements;
3533                         statements = new List<Statement> (1);
3534                         statements.Add (tf);
3535                 }
3536         }
3537
3538         //
3539         // ParametersBlock was introduced to support anonymous methods
3540         // and lambda expressions
3541         // 
3542         public class ParametersBlock : ExplicitBlock
3543         {
3544                 public class ParameterInfo : INamedBlockVariable
3545                 {
3546                         readonly ParametersBlock block;
3547                         readonly int index;
3548                         public VariableInfo VariableInfo;
3549                         bool is_locked;
3550
3551                         public ParameterInfo (ParametersBlock block, int index)
3552                         {
3553                                 this.block = block;
3554                                 this.index = index;
3555                         }
3556
3557                         #region Properties
3558
3559                         public ParametersBlock Block {
3560                                 get {
3561                                         return block;
3562                                 }
3563                         }
3564
3565                         Block INamedBlockVariable.Block {
3566                                 get {
3567                                         return block;
3568                                 }
3569                         }
3570
3571                         public bool IsDeclared {
3572                                 get {
3573                                         return true;
3574                                 }
3575                         }
3576
3577                         public bool IsParameter {
3578                                 get {
3579                                         return true;
3580                                 }
3581                         }
3582
3583                         public bool IsLocked {
3584                                 get {
3585                                         return is_locked;
3586                                 }
3587                                 set {
3588                                         is_locked = value;
3589                                 }
3590                         }
3591
3592                         public Location Location {
3593                                 get {
3594                                         return Parameter.Location;
3595                                 }
3596                         }
3597
3598                         public Parameter Parameter {
3599                                 get {
3600                                         return block.Parameters [index];
3601                                 }
3602                         }
3603
3604                         public TypeSpec ParameterType {
3605                                 get {
3606                                         return Parameter.Type;
3607                                 }
3608                         }
3609
3610                         #endregion
3611
3612                         public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3613                         {
3614                                 return new ParameterReference (this, loc);
3615                         }
3616                 }
3617
3618                 // 
3619                 // Block is converted into an expression
3620                 //
3621                 sealed class BlockScopeExpression : Expression
3622                 {
3623                         Expression child;
3624                         readonly ParametersBlock block;
3625
3626                         public BlockScopeExpression (Expression child, ParametersBlock block)
3627                         {
3628                                 this.child = child;
3629                                 this.block = block;
3630                         }
3631
3632                         public override bool ContainsEmitWithAwait ()
3633                         {
3634                                 return child.ContainsEmitWithAwait ();
3635                         }
3636
3637                         public override Expression CreateExpressionTree (ResolveContext ec)
3638                         {
3639                                 throw new NotSupportedException ();
3640                         }
3641
3642                         protected override Expression DoResolve (ResolveContext ec)
3643                         {
3644                                 if (child == null)
3645                                         return null;
3646
3647                                 child = child.Resolve (ec);
3648                                 if (child == null)
3649                                         return null;
3650
3651                                 eclass = child.eclass;
3652                                 type = child.Type;
3653                                 return this;
3654                         }
3655
3656                         public override void Emit (EmitContext ec)
3657                         {
3658                                 block.EmitScopeInitializers (ec);
3659                                 child.Emit (ec);
3660                         }
3661                 }
3662
3663                 protected ParametersCompiled parameters;
3664                 protected ParameterInfo[] parameter_info;
3665                 protected bool resolved;
3666                 protected ToplevelBlock top_block;
3667                 protected StateMachine state_machine;
3668                 protected Dictionary<string, object> labels;
3669
3670                 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3671                         : base (parent, 0, start, start)
3672                 {
3673                         if (parameters == null)
3674                                 throw new ArgumentNullException ("parameters");
3675
3676                         this.parameters = parameters;
3677                         ParametersBlock = this;
3678
3679                         this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3680
3681                         this.top_block = parent.ParametersBlock.top_block;
3682                         ProcessParameters ();
3683                 }
3684
3685                 protected ParametersBlock (ParametersCompiled parameters, Location start)
3686                         : base (null, 0, start, start)
3687                 {
3688                         if (parameters == null)
3689                                 throw new ArgumentNullException ("parameters");
3690
3691                         this.parameters = parameters;
3692                         ParametersBlock = this;
3693                 }
3694
3695                 //
3696                 // It's supposed to be used by method body implementation of anonymous methods
3697                 //
3698                 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3699                         : base (null, 0, source.StartLocation, source.EndLocation)
3700                 {
3701                         this.parameters = parameters;
3702                         this.statements = source.statements;
3703                         this.scope_initializers = source.scope_initializers;
3704
3705                         this.resolved = true;
3706                         this.reachable = source.reachable;
3707                         this.am_storey = source.am_storey;
3708                         this.state_machine = source.state_machine;
3709                         this.flags = source.flags & Flags.ReachableEnd;
3710
3711                         ParametersBlock = this;
3712
3713                         //
3714                         // Overwrite original for comparison purposes when linking cross references
3715                         // between anonymous methods
3716                         //
3717                         Original = source.Original;
3718                 }
3719
3720                 #region Properties
3721
3722                 public bool HasReferenceToStoreyForInstanceLambdas {
3723                         get {
3724                                 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3725                         }
3726                         set {
3727                                 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3728                         }
3729                 }
3730
3731                 public bool IsAsync {
3732                         get {
3733                                 return (flags & Flags.HasAsyncModifier) != 0;
3734                         }
3735                         set {
3736                                 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3737                         }
3738                 }
3739
3740                 //
3741                 // Block has been converted to expression tree
3742                 //
3743                 public bool IsExpressionTree {
3744                         get {
3745                                 return (flags & Flags.IsExpressionTree) != 0;
3746                         }
3747                 }
3748
3749                 //
3750                 // The parameters for the block.
3751                 //
3752                 public ParametersCompiled Parameters {
3753                         get {
3754                                 return parameters;
3755                         }
3756                 }
3757
3758                 public StateMachine StateMachine {
3759                         get {
3760                                 return state_machine;
3761                         }
3762                 }
3763
3764                 public ToplevelBlock TopBlock {
3765                         get {
3766                                 return top_block;
3767                         }
3768                         set {
3769                                 top_block = value;
3770                         }
3771                 }
3772
3773                 public bool Resolved {
3774                         get {
3775                                 return (flags & Flags.Resolved) != 0;
3776                         }
3777                 }
3778
3779                 public int TemporaryLocalsCount { get; set; }
3780
3781                 #endregion
3782
3783                 //
3784                 // Checks whether all `out' parameters have been assigned.
3785                 //
3786                 public void CheckControlExit (FlowAnalysisContext fc)
3787                 {
3788                         CheckControlExit (fc, fc.DefiniteAssignment);
3789                 }
3790
3791                 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3792                 {
3793                         if (parameter_info == null)
3794                                 return;
3795
3796                         foreach (var p in parameter_info) {
3797                                 if (p.VariableInfo == null)
3798                                         continue;
3799
3800                                 if (p.VariableInfo.IsAssigned (dat))
3801                                         continue;
3802
3803                                 fc.Report.Error (177, p.Location,
3804                                         "The out parameter `{0}' must be assigned to before control leaves the current method",
3805                                         p.Parameter.Name);
3806                         }                                       
3807                 }
3808
3809                 protected override void CloneTo (CloneContext clonectx, Statement t)
3810                 {
3811                         base.CloneTo (clonectx, t);
3812
3813                         var target = (ParametersBlock) t;
3814
3815                         //
3816                         // Clone label statements as well as they contain block reference
3817                         //
3818                         var pb = this;
3819                         while (true) {
3820                                 if (pb.labels != null) {
3821                                         target.labels = new Dictionary<string, object> ();
3822
3823                                         foreach (var entry in pb.labels) {
3824                                                 var list = entry.Value as List<LabeledStatement>;
3825
3826                                                 if (list != null) {
3827                                                         var list_clone = new List<LabeledStatement> ();
3828                                                         foreach (var lentry in list) {
3829                                                                 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3830                                                         }
3831
3832                                                         target.labels.Add (entry.Key, list_clone);
3833                                                 } else {
3834                                                         var labeled = (LabeledStatement) entry.Value;
3835                                                         target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3836                                                 }
3837                                         }
3838
3839                                         break;
3840                                 }
3841
3842                                 if (pb.Parent == null)
3843                                         break;
3844
3845                                 pb = pb.Parent.ParametersBlock;
3846                         }
3847                 }
3848
3849                 public override Expression CreateExpressionTree (ResolveContext ec)
3850                 {
3851                         if (statements.Count == 1) {
3852                                 Expression expr = statements[0].CreateExpressionTree (ec);
3853                                 if (scope_initializers != null)
3854                                         expr = new BlockScopeExpression (expr, this);
3855
3856                                 return expr;
3857                         }
3858
3859                         return base.CreateExpressionTree (ec);
3860                 }
3861
3862                 public override void Emit (EmitContext ec)
3863                 {
3864                         if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3865                                 DefineStoreyContainer (ec, state_machine);
3866                                 state_machine.EmitStoreyInstantiation (ec, this);
3867                         }
3868
3869                         base.Emit (ec);
3870                 }
3871
3872                 public void EmitEmbedded (EmitContext ec)
3873                 {
3874                         if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3875                                 DefineStoreyContainer (ec, state_machine);
3876                                 state_machine.EmitStoreyInstantiation (ec, this);
3877                         }
3878
3879                         base.Emit (ec);
3880                 }
3881
3882                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3883                 {
3884                         var res = base.DoFlowAnalysis (fc);
3885
3886                         if (HasReachableClosingBrace)
3887                                 CheckControlExit (fc);
3888
3889                         return res;
3890                 }
3891
3892                 public override int GetDebugSymbolScopeIndex ()
3893                 {
3894                         return 0;
3895                 }
3896
3897                 public LabeledStatement GetLabel (string name, Block block)
3898                 {
3899                         //
3900                         // Cloned parameters blocks can have their own cloned version of top-level labels
3901                         //
3902                         if (labels == null) {
3903                                 if (Parent != null)
3904                                         return Parent.ParametersBlock.GetLabel (name, block);
3905
3906                                 return null;
3907                         }
3908
3909                         object value;
3910                         if (!labels.TryGetValue (name, out value)) {
3911                                 return null;
3912                         }
3913
3914                         var label = value as LabeledStatement;
3915                         Block b = block;
3916                         if (label != null) {
3917                                 if (IsLabelVisible (label, b))
3918                                         return label;
3919
3920                         } else {
3921                                 List<LabeledStatement> list = (List<LabeledStatement>) value;
3922                                 for (int i = 0; i < list.Count; ++i) {
3923                                         label = list[i];
3924                                         if (IsLabelVisible (label, b))
3925                                                 return label;
3926                                 }
3927                         }
3928
3929                         return null;
3930                 }
3931
3932                 static bool IsLabelVisible (LabeledStatement label, Block b)
3933                 {
3934                         do {
3935                                 if (label.Block == b)
3936                                         return true;
3937                                 b = b.Parent;
3938                         } while (b != null);
3939
3940                         return false;
3941                 }
3942
3943                 public ParameterInfo GetParameterInfo (Parameter p)
3944                 {
3945                         for (int i = 0; i < parameters.Count; ++i) {
3946                                 if (parameters[i] == p)
3947                                         return parameter_info[i];
3948                         }
3949
3950                         throw new ArgumentException ("Invalid parameter");
3951                 }
3952
3953                 public ParameterReference GetParameterReference (int index, Location loc)
3954                 {
3955                         return new ParameterReference (parameter_info[index], loc);
3956                 }
3957
3958                 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3959                 {
3960                         undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3961
3962                         CloneContext clonectx = new CloneContext ();
3963                         return Clone (clonectx);
3964                 }
3965
3966                 protected void ProcessParameters ()
3967                 {
3968                         if (parameters.Count == 0)
3969                                 return;
3970
3971                         parameter_info = new ParameterInfo[parameters.Count];
3972                         for (int i = 0; i < parameter_info.Length; ++i) {
3973                                 var p = parameters.FixedParameters[i];
3974                                 if (p == null)
3975                                         continue;
3976
3977                                 // TODO: Should use Parameter only and more block there
3978                                 parameter_info[i] = new ParameterInfo (this, i);
3979                                 if (p.Name != null)
3980                                         AddLocalName (p.Name, parameter_info[i]);
3981                         }
3982                 }
3983
3984                 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3985                 {
3986                         var src = stmt.Block;
3987
3988                         //
3989                         // Cannot remap label block if the label was not yet cloned which
3990                         // can happen in case of anonymous method inside anoynymous method
3991                         // with a label. But in this case we don't care because goto cannot
3992                         // jump of out anonymous method
3993                         //
3994                         if (src.ParametersBlock != this)
3995                                 return stmt;
3996
3997                         var src_stmts = src.Statements;
3998                         for (int i = 0; i < src_stmts.Count; ++i) {
3999                                 if (src_stmts[i] == stmt)
4000                                         return (LabeledStatement) dst.Statements[i];
4001                         }
4002
4003                         throw new InternalErrorException ("Should never be reached");
4004                 }
4005
4006                 public override bool Resolve (BlockContext bc)
4007                 {
4008                         // TODO: if ((flags & Flags.Resolved) != 0)
4009
4010                         if (resolved)
4011                                 return true;
4012
4013                         resolved = true;
4014
4015                         if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4016                                 flags |= Flags.IsExpressionTree;
4017
4018                         try {
4019                                 PrepareAssignmentAnalysis (bc);
4020
4021                                 if (!base.Resolve (bc))
4022                                         return false;
4023
4024                         } catch (Exception e) {
4025                                 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4026                                         throw;
4027
4028                                 if (bc.CurrentBlock != null) {
4029                                         bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4030                                 } else {
4031                                         bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4032                                 }
4033                         }
4034
4035                         //
4036                         // If an asynchronous body of F is either an expression classified as nothing, or a 
4037                         // statement block where no return statements have expressions, the inferred return type is Task
4038                         //
4039                         if (IsAsync) {
4040                                 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4041                                 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4042                                         am.ReturnTypeInference = null;
4043                                         am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4044                                         return true;
4045                                 }
4046                         }
4047
4048                         return true;
4049                 }
4050
4051                 void PrepareAssignmentAnalysis (BlockContext bc)
4052                 {
4053                         for (int i = 0; i < parameters.Count; ++i) {
4054                                 var par = parameters.FixedParameters[i];
4055
4056                                 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4057                                         continue;
4058
4059                                 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4060                         }
4061                 }
4062
4063                 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4064                 {
4065                         var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4066                         var stateMachine = new IteratorStorey (iterator);
4067
4068                         state_machine = stateMachine;
4069                         iterator.SetStateMachine (stateMachine);
4070
4071                         var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4072                         tlb.Original = this;
4073                         tlb.state_machine = stateMachine;
4074                         tlb.AddStatement (new Return (iterator, iterator.Location));
4075                         return tlb;
4076                 }
4077
4078                 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4079                 {
4080                         for (int i = 0; i < parameters.Count; i++) {
4081                                 Parameter p = parameters[i];
4082                                 Parameter.Modifier mod = p.ModFlags;
4083                                 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4084                                         host.Compiler.Report.Error (1988, p.Location,
4085                                                 "Async methods cannot have ref or out parameters");
4086                                         return this;
4087                                 }
4088
4089                                 if (p is ArglistParameter) {
4090                                         host.Compiler.Report.Error (4006, p.Location,
4091                                                 "__arglist is not allowed in parameter list of async methods");
4092                                         return this;
4093                                 }
4094
4095                                 if (parameters.Types[i].IsPointer) {
4096                                         host.Compiler.Report.Error (4005, p.Location,
4097                                                 "Async methods cannot have unsafe parameters");
4098                                         return this;
4099                                 }
4100                         }
4101
4102                         if (!HasAwait) {
4103                                 host.Compiler.Report.Warning (1998, 1, loc,
4104                                         "Async block lacks `await' operator and will run synchronously");
4105                         }
4106
4107                         var block_type = host.Module.Compiler.BuiltinTypes.Void;
4108                         var initializer = new AsyncInitializer (this, host, block_type);
4109                         initializer.Type = block_type;
4110                         initializer.DelegateType = delegateType;
4111
4112                         var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4113
4114                         state_machine = stateMachine;
4115                         initializer.SetStateMachine (stateMachine);
4116
4117                         const Flags flags = Flags.CompilerGenerated;
4118
4119                         var b = this is ToplevelBlock ?
4120                                 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4121                                 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4122
4123                         b.Original = this;
4124                         b.state_machine = stateMachine;
4125                         b.AddStatement (new AsyncInitializerStatement (initializer));
4126                         return b;
4127                 }
4128         }
4129
4130         //
4131         //
4132         //
4133         public class ToplevelBlock : ParametersBlock
4134         {
4135                 LocalVariable this_variable;
4136                 CompilerContext compiler;
4137                 Dictionary<string, object> names;
4138
4139                 List<ExplicitBlock> this_references;
4140
4141                 public ToplevelBlock (CompilerContext ctx, Location loc)
4142                         : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4143                 {
4144                 }
4145
4146                 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4147                         : base (parameters, start)
4148                 {
4149                         this.compiler = ctx;
4150                         this.flags = flags;
4151                         top_block = this;
4152
4153                         ProcessParameters ();
4154                 }
4155
4156                 //
4157                 // Recreates a top level block from parameters block. Used for
4158                 // compiler generated methods where the original block comes from
4159                 // explicit child block. This works for already resolved blocks
4160                 // only to ensure we resolve them in the correct flow order
4161                 //
4162                 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4163                         : base (source, parameters)
4164                 {
4165                         this.compiler = source.TopBlock.compiler;
4166                         top_block = this;
4167                 }
4168
4169                 public bool IsIterator {
4170                         get {
4171                                 return (flags & Flags.Iterator) != 0;
4172                         }
4173                         set {
4174                                 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4175                         }
4176                 }
4177
4178                 public Report Report {
4179                         get {
4180                                 return compiler.Report;
4181                         }
4182                 }
4183
4184                 //
4185                 // Used by anonymous blocks to track references of `this' variable
4186                 //
4187                 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4188                         get {
4189                                 return this_references;
4190                         }
4191                 }
4192
4193                 //
4194                 // Returns the "this" instance variable of this block.
4195                 // See AddThisVariable() for more information.
4196                 //
4197                 public LocalVariable ThisVariable {
4198                         get {
4199                                 return this_variable;
4200                         }
4201                 }
4202
4203                 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4204                 {
4205                         if (names == null)
4206                                 names = new Dictionary<string, object> ();
4207
4208                         object value;
4209                         if (!names.TryGetValue (name, out value)) {
4210                                 names.Add (name, li);
4211                                 return;
4212                         }
4213
4214                         INamedBlockVariable existing = value as INamedBlockVariable;
4215                         List<INamedBlockVariable> existing_list;
4216                         if (existing != null) {
4217                                 existing_list = new List<INamedBlockVariable> ();
4218                                 existing_list.Add (existing);
4219                                 names[name] = existing_list;
4220                         } else {
4221                                 existing_list = (List<INamedBlockVariable>) value;
4222                         }
4223
4224                         //
4225                         // A collision checking between local names
4226                         //
4227                         var variable_block = li.Block.Explicit;
4228                         for (int i = 0; i < existing_list.Count; ++i) {
4229                                 existing = existing_list[i];
4230                                 Block b = existing.Block.Explicit;
4231
4232                                 // Collision at same level
4233                                 if (variable_block == b) {
4234                                         li.Block.Error_AlreadyDeclared (name, li);
4235                                         break;
4236                                 }
4237
4238                                 // Collision with parent
4239                                 Block parent = variable_block;
4240                                 while ((parent = parent.Parent) != null) {
4241                                         if (parent == b) {
4242                                                 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4243                                                 i = existing_list.Count;
4244                                                 break;
4245                                         }
4246                                 }
4247
4248                                 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4249                                         // Collision with children
4250                                         while ((b = b.Parent) != null) {
4251                                                 if (variable_block == b) {
4252                                                         li.Block.Error_AlreadyDeclared (name, li, "child");
4253                                                         i = existing_list.Count;
4254                                                         break;
4255                                                 }
4256                                         }
4257                                 }
4258                         }
4259
4260                         existing_list.Add (li);
4261                 }
4262
4263                 public void AddLabel (string name, LabeledStatement label)
4264                 {
4265                         if (labels == null)
4266                                 labels = new Dictionary<string, object> ();
4267
4268                         object value;
4269                         if (!labels.TryGetValue (name, out value)) {
4270                                 labels.Add (name, label);
4271                                 return;
4272                         }
4273
4274                         LabeledStatement existing = value as LabeledStatement;
4275                         List<LabeledStatement> existing_list;
4276                         if (existing != null) {
4277                                 existing_list = new List<LabeledStatement> ();
4278                                 existing_list.Add (existing);
4279                                 labels[name] = existing_list;
4280                         } else {
4281                                 existing_list = (List<LabeledStatement>) value;
4282                         }
4283
4284                         //
4285                         // A collision checking between labels
4286                         //
4287                         for (int i = 0; i < existing_list.Count; ++i) {
4288                                 existing = existing_list[i];
4289                                 Block b = existing.Block;
4290
4291                                 // Collision at same level
4292                                 if (label.Block == b) {
4293                                         Report.SymbolRelatedToPreviousError (existing.loc, name);
4294                                         Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4295                                         break;
4296                                 }
4297
4298                                 // Collision with parent
4299                                 b = label.Block;
4300                                 while ((b = b.Parent) != null) {
4301                                         if (existing.Block == b) {
4302                                                 Report.Error (158, label.loc,
4303                                                         "The label `{0}' shadows another label by the same name in a contained scope", name);
4304                                                 i = existing_list.Count;
4305                                                 break;
4306                                         }
4307                                 }
4308
4309                                 // Collision with with children
4310                                 b = existing.Block;
4311                                 while ((b = b.Parent) != null) {
4312                                         if (label.Block == b) {
4313                                                 Report.Error (158, label.loc,
4314                                                         "The label `{0}' shadows another label by the same name in a contained scope", name);
4315                                                 i = existing_list.Count;
4316                                                 break;
4317                                         }
4318                                 }
4319                         }
4320
4321                         existing_list.Add (label);
4322                 }
4323
4324                 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4325                 {
4326                         if (this_references == null)
4327                                 this_references = new List<ExplicitBlock> ();
4328
4329                         if (!this_references.Contains (block))
4330                                 this_references.Add (block);
4331                 }
4332
4333                 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4334                 {
4335                         this_references.Remove (block);
4336                 }
4337
4338                 //
4339                 // Creates an arguments set from all parameters, useful for method proxy calls
4340                 //
4341                 public Arguments GetAllParametersArguments ()
4342                 {
4343                         int count = parameters.Count;
4344                         Arguments args = new Arguments (count);
4345                         for (int i = 0; i < count; ++i) {
4346                                 var pi = parameter_info[i];
4347                                 var arg_expr = GetParameterReference (i, pi.Location);
4348
4349                                 Argument.AType atype_modifier;
4350                                 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4351                                 case Parameter.Modifier.REF:
4352                                         atype_modifier = Argument.AType.Ref;
4353                                         break;
4354                                 case Parameter.Modifier.OUT:
4355                                         atype_modifier = Argument.AType.Out;
4356                                         break;
4357                                 default:
4358                                         atype_modifier = 0;
4359                                         break;
4360                                 }
4361
4362                                 args.Add (new Argument (arg_expr, atype_modifier));
4363                         }
4364
4365                         return args;
4366                 }
4367
4368                 //
4369                 // Lookup inside a block, the returned value can represent 3 states
4370                 //
4371                 // true+variable: A local name was found and it's valid
4372                 // false+variable: A local name was found in a child block only
4373                 // false+null: No local name was found
4374                 //
4375                 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4376                 {
4377                         if (names == null)
4378                                 return false;
4379
4380                         object value;
4381                         if (!names.TryGetValue (name, out value))
4382                                 return false;
4383
4384                         variable = value as INamedBlockVariable;
4385                         Block b = block;
4386                         if (variable != null) {
4387                                 do {
4388                                         if (variable.Block == b.Original)
4389                                                 return true;
4390
4391                                         b = b.Parent;
4392                                 } while (b != null);
4393
4394                                 b = variable.Block;
4395                                 do {
4396                                         if (block == b)
4397                                                 return false;
4398
4399                                         b = b.Parent;
4400                                 } while (b != null);
4401                         } else {
4402                                 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4403                                 for (int i = 0; i < list.Count; ++i) {
4404                                         variable = list[i];
4405                                         do {
4406                                                 if (variable.Block == b.Original)
4407                                                         return true;
4408
4409                                                 b = b.Parent;
4410                                         } while (b != null);
4411
4412                                         b = variable.Block;
4413                                         do {
4414                                                 if (block == b)
4415                                                         return false;
4416
4417                                                 b = b.Parent;
4418                                         } while (b != null);
4419
4420                                         b = block;
4421                                 }
4422                         }
4423
4424                         variable = null;
4425                         return false;
4426                 }
4427
4428                 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4429                 {
4430                         if (block.names != null) {
4431                                 foreach (var n in block.names) {
4432                                         var variable = n.Value as INamedBlockVariable;
4433                                         if (variable != null) {
4434                                                 if (variable.Block.ParametersBlock == pb)
4435                                                         AddLocalName (n.Key, variable, false);
4436                                                 continue;
4437                                         }
4438
4439                                         foreach (var v in (List<INamedBlockVariable>) n.Value)
4440                                                 if (v.Block.ParametersBlock == pb)
4441                                                         AddLocalName (n.Key, v, false);
4442                                 }
4443                         }
4444                 }
4445
4446                 // <summary>
4447                 //   This is used by non-static `struct' constructors which do not have an
4448                 //   initializer - in this case, the constructor must initialize all of the
4449                 //   struct's fields.  To do this, we add a "this" variable and use the flow
4450                 //   analysis code to ensure that it's been fully initialized before control
4451                 //   leaves the constructor.
4452                 // </summary>
4453                 public void AddThisVariable (BlockContext bc)
4454                 {
4455                         if (this_variable != null)
4456                                 throw new InternalErrorException (StartLocation.ToString ());
4457
4458                         this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4459                         this_variable.Type = bc.CurrentType;
4460                         this_variable.PrepareAssignmentAnalysis (bc);
4461                 }
4462
4463                 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4464                 {
4465                         //
4466                         // If we're a non-static struct constructor which doesn't have an
4467                         // initializer, then we must initialize all of the struct's fields.
4468                         //
4469                         if (this_variable != null)
4470                                 this_variable.IsThisAssigned (fc, this);
4471
4472                         base.CheckControlExit (fc, dat);
4473                 }
4474
4475                 public HashSet<LocalVariable> GetUndeclaredVariables ()
4476                 {
4477                         if (names == null)
4478                                 return null;
4479
4480                         HashSet<LocalVariable> variables = null;
4481
4482                         foreach (var entry in names) {
4483                                 var complex = entry.Value as List<INamedBlockVariable>;
4484                                 if (complex != null) {
4485                                         foreach (var centry in complex) {
4486                                                 if (IsUndeclaredVariable (centry)) {
4487                                                         if (variables == null)
4488                                                                 variables = new HashSet<LocalVariable> ();
4489
4490                                                         variables.Add ((LocalVariable) centry);
4491                                                 }
4492                                         }
4493                                 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4494                                         if (variables == null)
4495                                                 variables = new HashSet<LocalVariable> ();
4496
4497                                         variables.Add ((LocalVariable)entry.Value);                                     
4498                                 }
4499                         }
4500
4501                         return variables;
4502                 }
4503
4504                 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4505                 {
4506                         var lv = namedBlockVariable as LocalVariable;
4507                         return lv != null && !lv.IsDeclared;
4508                 }
4509
4510                 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4511                 {
4512                         if (names == null)
4513                                 return;
4514                         
4515                         foreach (var entry in names) {
4516                                 var complex = entry.Value as List<INamedBlockVariable>;
4517                                 if (complex != null) {
4518                                         foreach (var centry in complex) {
4519                                                 var lv = centry as LocalVariable;
4520                                                 if (lv != null && undeclaredVariables.Contains (lv)) {
4521                                                         lv.Type = null;
4522                                                 }
4523                                         }
4524                                 } else {
4525                                         var lv = entry.Value as LocalVariable;
4526                                         if (lv != null && undeclaredVariables.Contains (lv))
4527                                                 lv.Type = null;
4528                                 }
4529                         }
4530                 }
4531
4532                 public override void Emit (EmitContext ec)
4533                 {
4534                         if (Report.Errors > 0)
4535                                 return;
4536
4537                         try {
4538                         if (IsCompilerGenerated) {
4539                                 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4540                                         base.Emit (ec);
4541                                 }
4542                         } else {
4543                                 base.Emit (ec);
4544                         }
4545
4546                         //
4547                         // If `HasReturnLabel' is set, then we already emitted a
4548                         // jump to the end of the method, so we must emit a `ret'
4549                         // there.
4550                         //
4551                         // Unfortunately, System.Reflection.Emit automatically emits
4552                         // a leave to the end of a finally block.  This is a problem
4553                         // if no code is following the try/finally block since we may
4554                         // jump to a point after the end of the method.
4555                         // As a workaround, we're always creating a return label in
4556                         // this case.
4557                         //
4558                         if (ec.HasReturnLabel || HasReachableClosingBrace) {
4559                                 if (ec.HasReturnLabel)
4560                                         ec.MarkLabel (ec.ReturnLabel);
4561
4562                                 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4563                                         ec.Mark (EndLocation);
4564
4565                                 if (ec.ReturnType.Kind != MemberKind.Void)
4566                                         ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4567
4568                                 ec.Emit (OpCodes.Ret);
4569                         }
4570
4571                         } catch (Exception e) {
4572                                 throw new InternalErrorException (e, StartLocation);
4573                         }
4574                 }
4575
4576                 public bool Resolve (BlockContext bc, IMethodData md)
4577                 {
4578                         if (resolved)
4579                                 return true;
4580
4581                         var errors = bc.Report.Errors;
4582
4583                         base.Resolve (bc);
4584
4585                         if (bc.Report.Errors > errors)
4586                                 return false;
4587
4588                         MarkReachable (new Reachability ());
4589
4590                         if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4591                                 // TODO: var md = bc.CurrentMemberDefinition;
4592                                 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4593                         }
4594
4595                         if ((flags & Flags.NoFlowAnalysis) != 0)
4596                                 return true;
4597
4598                         var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4599                         try {
4600                                 FlowAnalysis (fc);
4601                         } catch (Exception e) {
4602                                 throw new InternalErrorException (e, StartLocation);
4603                         }
4604
4605                         return true;
4606                 }
4607         }
4608         
4609         public class SwitchLabel : Statement
4610         {
4611                 Constant converted;
4612                 Expression label;
4613
4614                 Label? il_label;
4615
4616                 //
4617                 // if expr == null, then it is the default case.
4618                 //
4619                 public SwitchLabel (Expression expr, Location l)
4620                 {
4621                         label = expr;
4622                         loc = l;
4623                 }
4624
4625                 public bool IsDefault {
4626                         get {
4627                                 return label == null;
4628                         }
4629                 }
4630
4631                 public Expression Label {
4632                         get {
4633                                 return label;
4634                         }
4635                 }
4636
4637                 public Location Location {
4638                         get {
4639                                 return loc;
4640                         }
4641                 }
4642
4643                 public Constant Converted {
4644                         get {
4645                                 return converted;
4646                         }
4647                         set {
4648                                 converted = value; 
4649                         }
4650                 }
4651
4652                 public bool PatternMatching { get; set; }
4653
4654                 public bool SectionStart { get; set; }
4655
4656                 public Label GetILLabel (EmitContext ec)
4657                 {
4658                         if (il_label == null){
4659                                 il_label = ec.DefineLabel ();
4660                         }
4661
4662                         return il_label.Value;
4663                 }
4664
4665                 protected override void DoEmit (EmitContext ec)
4666                 {
4667                         ec.MarkLabel (GetILLabel (ec));
4668                 }
4669
4670                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4671                 {
4672                         if (!SectionStart)
4673                                 return false;
4674
4675                         fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4676                         return false;
4677                 }
4678
4679                 public override bool Resolve (BlockContext bc)
4680                 {
4681                         if (ResolveAndReduce (bc))
4682                                 bc.Switch.RegisterLabel (bc, this);
4683
4684                         return true;
4685                 }
4686
4687                 //
4688                 // Resolves the expression, reduces it to a literal if possible
4689                 // and then converts it to the requested type.
4690                 //
4691                 bool ResolveAndReduce (BlockContext bc)
4692                 {
4693                         if (IsDefault)
4694                                 return true;
4695
4696                         var switch_statement = bc.Switch;
4697
4698                         if (PatternMatching) {
4699                                 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4700                                 return label != null;
4701                         }
4702
4703                         var c = label.ResolveLabelConstant (bc);
4704                         if (c == null)
4705                                 return false;
4706
4707                         if (switch_statement.IsNullable && c is NullLiteral) {
4708                                 converted = c;
4709                                 return true;
4710                         }
4711
4712                         if (switch_statement.IsPatternMatching) {
4713                                 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4714                                 return true;
4715                         }
4716
4717                         converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4718                         return converted != null;
4719                 }
4720
4721                 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4722                 {
4723                         ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4724                         ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4725                 }
4726
4727                 protected override void CloneTo (CloneContext clonectx, Statement target)
4728                 {
4729                         var t = (SwitchLabel) target;
4730                         if (label != null)
4731                                 t.label = label.Clone (clonectx);
4732                 }
4733
4734                 public override object Accept (StructuralVisitor visitor)
4735                 {
4736                         return visitor.Visit (this);
4737                 }
4738
4739                 public string GetSignatureForError ()
4740                 {
4741                         string label;
4742                         if (converted == null)
4743                                 label = "default";
4744                         else
4745                                 label = converted.GetValueAsLiteral ();
4746
4747                         return string.Format ("case {0}:", label);
4748                 }
4749         }
4750
4751         public class Switch : LoopStatement
4752         {
4753                 // structure used to hold blocks of keys while calculating table switch
4754                 sealed class LabelsRange : IComparable<LabelsRange>
4755                 {
4756                         public readonly long min;
4757                         public long max;
4758                         public readonly List<long> label_values;
4759
4760                         public LabelsRange (long value)
4761                         {
4762                                 min = max = value;
4763                                 label_values = new List<long> ();
4764                                 label_values.Add (value);
4765                         }
4766
4767                         public LabelsRange (long min, long max, ICollection<long> values)
4768                         {
4769                                 this.min = min;
4770                                 this.max = max;
4771                                 this.label_values = new List<long> (values);
4772                         }
4773
4774                         public long Range {
4775                                 get {
4776                                         return max - min + 1;
4777                                 }
4778                         }
4779
4780                         public bool AddValue (long value)
4781                         {
4782                                 var gap = value - min + 1;
4783                                 // Ensure the range has > 50% occupancy
4784                                 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4785                                         return false;
4786
4787                                 max = value;
4788                                 label_values.Add (value);
4789                                 return true;
4790                         }
4791
4792                         public int CompareTo (LabelsRange other)
4793                         {
4794                                 int nLength = label_values.Count;
4795                                 int nLengthOther = other.label_values.Count;
4796                                 if (nLengthOther == nLength)
4797                                         return (int) (other.min - min);
4798
4799                                 return nLength - nLengthOther;
4800                         }
4801                 }
4802
4803                 sealed class DispatchStatement : Statement
4804                 {
4805                         readonly Switch body;
4806
4807                         public DispatchStatement (Switch body)
4808                         {
4809                                 this.body = body;
4810                         }
4811
4812                         protected override void CloneTo (CloneContext clonectx, Statement target)
4813                         {
4814                                 throw new NotImplementedException ();
4815                         }
4816
4817                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4818                         {
4819                                 return false;
4820                         }
4821
4822                         protected override void DoEmit (EmitContext ec)
4823                         {
4824                                 body.EmitDispatch (ec);
4825                         }
4826                 }
4827
4828                 class MissingBreak : Statement
4829                 {
4830                         readonly SwitchLabel label;
4831
4832                         public MissingBreak (SwitchLabel sl)
4833                         {
4834                                 this.label = sl;
4835                                 this.loc = sl.loc;
4836                         }
4837
4838                         public bool FallOut { get; set; }
4839
4840                         protected override void DoEmit (EmitContext ec)
4841                         {
4842                         }
4843
4844                         protected override void CloneTo (CloneContext clonectx, Statement target)
4845                         {
4846                         }
4847
4848                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4849                         {
4850                                 if (FallOut) {
4851                                         fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4852                                                 label.GetSignatureForError ());
4853                                 } else {
4854                                         fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4855                                                 label.GetSignatureForError ());
4856                                 }
4857                                 return true;
4858                         }
4859                 }
4860
4861                 public Expression Expr;
4862
4863                 //
4864                 // Mapping of all labels to their SwitchLabels
4865                 //
4866                 Dictionary<long, SwitchLabel> labels;
4867                 Dictionary<string, SwitchLabel> string_labels;
4868                 List<SwitchLabel> case_labels;
4869
4870                 List<Tuple<GotoCase, Constant>> goto_cases;
4871                 List<DefiniteAssignmentBitSet> end_reachable_das;
4872
4873                 /// <summary>
4874                 ///   The governing switch type
4875                 /// </summary>
4876                 public TypeSpec SwitchType;
4877
4878                 Expression new_expr;
4879
4880                 SwitchLabel case_null;
4881                 SwitchLabel case_default;
4882
4883                 Label defaultLabel, nullLabel;
4884                 VariableReference value;
4885                 ExpressionStatement string_dictionary;
4886                 FieldExpr switch_cache_field;
4887                 ExplicitBlock block;
4888                 bool end_reachable;
4889
4890                 //
4891                 // Nullable Types support
4892                 //
4893                 Nullable.Unwrap unwrap;
4894
4895                 public Switch (Expression e, ExplicitBlock block, Location l)
4896                         : base (block)
4897                 {
4898                         Expr = e;
4899                         this.block = block;
4900                         loc = l;
4901                 }
4902
4903                 public SwitchLabel ActiveLabel { get; set; }
4904
4905                 public ExplicitBlock Block {
4906                         get {
4907                                 return block;
4908                         }
4909                 }
4910
4911                 public SwitchLabel DefaultLabel {
4912                         get {
4913                                 return case_default;
4914                         }
4915                 }
4916
4917                 public bool IsNullable {
4918                         get {
4919                                 return unwrap != null;
4920                         }
4921                 }
4922
4923                 public bool IsPatternMatching {
4924                         get {
4925                                 return new_expr == null && SwitchType != null;
4926                         }
4927                 }
4928
4929                 public List<SwitchLabel> RegisteredLabels {
4930                         get {
4931                                 return case_labels;
4932                         }
4933                 }
4934
4935                 public VariableReference ExpressionValue {
4936                         get {
4937                                 return value;
4938                         }
4939                 }
4940
4941                 //
4942                 // Determines the governing type for a switch.  The returned
4943                 // expression might be the expression from the switch, or an
4944                 // expression that includes any potential conversions to
4945                 //
4946                 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4947                 {
4948                         switch (expr.Type.BuiltinType) {
4949                         case BuiltinTypeSpec.Type.Byte:
4950                         case BuiltinTypeSpec.Type.SByte:
4951                         case BuiltinTypeSpec.Type.UShort:
4952                         case BuiltinTypeSpec.Type.Short:
4953                         case BuiltinTypeSpec.Type.UInt:
4954                         case BuiltinTypeSpec.Type.Int:
4955                         case BuiltinTypeSpec.Type.ULong:
4956                         case BuiltinTypeSpec.Type.Long:
4957                         case BuiltinTypeSpec.Type.Char:
4958                         case BuiltinTypeSpec.Type.String:
4959                         case BuiltinTypeSpec.Type.Bool:
4960                                 return expr;
4961                         }
4962
4963                         if (expr.Type.IsEnum)
4964                                 return expr;
4965
4966                         //
4967                         // Try to find a *user* defined implicit conversion.
4968                         //
4969                         // If there is no implicit conversion, or if there are multiple
4970                         // conversions, we have to report an error
4971                         //
4972                         Expression converted = null;
4973                         foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4974
4975                                 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4976                                         break;
4977
4978                                 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4979                                         Convert.UserConversionRestriction.ProbingOnly;
4980
4981                                 if (unwrapExpr)
4982                                         restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4983
4984                                 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4985                                 if (e == null)
4986                                         continue;
4987
4988                                 //
4989                                 // Ignore over-worked ImplicitUserConversions that do
4990                                 // an implicit conversion in addition to the user conversion.
4991                                 // 
4992                                 var uc = e as UserCast;
4993                                 if (uc == null)
4994                                         continue;
4995
4996                                 if (converted != null){
4997 //                                      rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4998                                         return null;
4999                                 }
5000
5001                                 converted = e;
5002                         }
5003                         return converted;
5004                 }
5005
5006                 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
5007                 {
5008                         var types = module.Compiler.BuiltinTypes;
5009
5010                         // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5011                         TypeSpec[] stypes = new[] {
5012                                 types.SByte,
5013                                 types.Byte,
5014                                 types.Short,
5015                                 types.UShort,
5016                                 types.Int,
5017                                 types.UInt,
5018                                 types.Long,
5019                                 types.ULong,
5020                                 types.Char,
5021                                 types.String
5022                         };
5023
5024                         if (nullable != null) {
5025
5026                                 Array.Resize (ref stypes, stypes.Length + 9);
5027
5028                                 for (int i = 0; i < 9; ++i) {
5029                                         stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5030                                 }
5031                         }
5032
5033                         return stypes;
5034                 }
5035
5036                 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5037                 {
5038                         case_labels.Add (sl);
5039
5040                         if (sl.IsDefault) {
5041                                 if (case_default != null) {
5042                                         sl.Error_AlreadyOccurs (rc, case_default);
5043                                 } else {
5044                                         case_default = sl;
5045                                 }
5046
5047                                 return;
5048                         }
5049
5050                         if (sl.Converted == null)
5051                                 return;
5052
5053                         try {
5054                                 if (string_labels != null) {
5055                                         string string_value = sl.Converted.GetValue () as string;
5056                                         if (string_value == null)
5057                                                 case_null = sl;
5058                                         else
5059                                                 string_labels.Add (string_value, sl);
5060                                 } else {
5061                                         if (sl.Converted.IsNull) {
5062                                                 case_null = sl;
5063                                         } else {
5064                                                 labels.Add (sl.Converted.GetValueAsLong (), sl);
5065                                         }
5066                                 }
5067                         } catch (ArgumentException) {
5068                                 if (string_labels != null)
5069                                         sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5070                                 else
5071                                         sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5072                         }
5073                 }
5074                 
5075                 //
5076                 // This method emits code for a lookup-based switch statement (non-string)
5077                 // Basically it groups the cases into blocks that are at least half full,
5078                 // and then spits out individual lookup opcodes for each block.
5079                 // It emits the longest blocks first, and short blocks are just
5080                 // handled with direct compares.
5081                 //
5082                 void EmitTableSwitch (EmitContext ec, Expression val)
5083                 {
5084                         if (labels != null && labels.Count > 0) {
5085                                 List<LabelsRange> ranges;
5086                                 if (string_labels != null) {
5087                                         // We have done all hard work for string already
5088                                         // setup single range only
5089                                         ranges = new List<LabelsRange> (1);
5090                                         ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5091                                 } else {
5092                                         var element_keys = new long[labels.Count];
5093                                         labels.Keys.CopyTo (element_keys, 0);
5094                                         Array.Sort (element_keys);
5095
5096                                         //
5097                                         // Build possible ranges of switch labes to reduce number
5098                                         // of comparisons
5099                                         //
5100                                         ranges = new List<LabelsRange> (element_keys.Length);
5101                                         var range = new LabelsRange (element_keys[0]);
5102                                         ranges.Add (range);
5103                                         for (int i = 1; i < element_keys.Length; ++i) {
5104                                                 var l = element_keys[i];
5105                                                 if (range.AddValue (l))
5106                                                         continue;
5107
5108                                                 range = new LabelsRange (l);
5109                                                 ranges.Add (range);
5110                                         }
5111
5112                                         // sort the blocks so we can tackle the largest ones first
5113                                         ranges.Sort ();
5114                                 }
5115
5116                                 Label lbl_default = defaultLabel;
5117                                 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5118
5119                                 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5120                                         LabelsRange kb = ranges[range_index];
5121                                         lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5122
5123                                         // Optimize small ranges using simple equality check
5124                                         if (kb.Range <= 2) {
5125                                                 foreach (var key in kb.label_values) {
5126                                                         SwitchLabel sl = labels[key];
5127                                                         if (sl == case_default || sl == case_null)
5128                                                                 continue;
5129
5130                                                         if (sl.Converted.IsZeroInteger) {
5131                                                                 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5132                                                         } else {
5133                                                                 val.Emit (ec);
5134                                                                 sl.Converted.Emit (ec);
5135                                                                 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5136                                                         }
5137                                                 }
5138                                         } else {
5139                                                 // TODO: if all the keys in the block are the same and there are
5140                                                 //       no gaps/defaults then just use a range-check.
5141                                                 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5142                                                         // TODO: optimize constant/I4 cases
5143
5144                                                         // check block range (could be > 2^31)
5145                                                         val.Emit (ec);
5146                                                         ec.EmitLong (kb.min);
5147                                                         ec.Emit (OpCodes.Blt, lbl_default);
5148
5149                                                         val.Emit (ec);
5150                                                         ec.EmitLong (kb.max);
5151                                                         ec.Emit (OpCodes.Bgt, lbl_default);
5152
5153                                                         // normalize range
5154                                                         val.Emit (ec);
5155                                                         if (kb.min != 0) {
5156                                                                 ec.EmitLong (kb.min);
5157                                                                 ec.Emit (OpCodes.Sub);
5158                                                         }
5159
5160                                                         ec.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
5161                                                 } else {
5162                                                         // normalize range
5163                                                         val.Emit (ec);
5164                                                         int first = (int) kb.min;
5165                                                         if (first > 0) {
5166                                                                 ec.EmitInt (first);
5167                                                                 ec.Emit (OpCodes.Sub);
5168                                                         } else if (first < 0) {
5169                                                                 ec.EmitInt (-first);
5170                                                                 ec.Emit (OpCodes.Add);
5171                                                         }
5172                                                 }
5173
5174                                                 // first, build the list of labels for the switch
5175                                                 int iKey = 0;
5176                                                 long cJumps = kb.Range;
5177                                                 Label[] switch_labels = new Label[cJumps];
5178                                                 for (int iJump = 0; iJump < cJumps; iJump++) {
5179                                                         var key = kb.label_values[iKey];
5180                                                         if (key == kb.min + iJump) {
5181                                                                 switch_labels[iJump] = labels[key].GetILLabel (ec);
5182                                                                 iKey++;
5183                                                         } else {
5184                                                                 switch_labels[iJump] = lbl_default;
5185                                                         }
5186                                                 }
5187
5188                                                 // emit the switch opcode
5189                                                 ec.Emit (OpCodes.Switch, switch_labels);
5190                                         }
5191
5192                                         // mark the default for this block
5193                                         if (range_index != 0)
5194                                                 ec.MarkLabel (lbl_default);
5195                                 }
5196
5197                                 // the last default just goes to the end
5198                                 if (ranges.Count > 0)
5199                                         ec.Emit (OpCodes.Br, lbl_default);
5200                         }
5201                 }
5202                 
5203                 public SwitchLabel FindLabel (Constant value)
5204                 {
5205                         SwitchLabel sl = null;
5206
5207                         if (string_labels != null) {
5208                                 string s = value.GetValue () as string;
5209                                 if (s == null) {
5210                                         if (case_null != null)
5211                                                 sl = case_null;
5212                                         else if (case_default != null)
5213                                                 sl = case_default;
5214                                 } else {
5215                                         string_labels.TryGetValue (s, out sl);
5216                                 }
5217                         } else {
5218                                 if (value is NullLiteral) {
5219                                         sl = case_null;
5220                                 } else {
5221                                         labels.TryGetValue (value.GetValueAsLong (), out sl);
5222                                 }
5223                         }
5224
5225                         if (sl == null || sl.SectionStart)
5226                                 return sl;
5227
5228                         //
5229                         // Always return section start, it simplifies handling of switch labels
5230                         //
5231                         for (int idx = case_labels.IndexOf (sl); ; --idx) {
5232                                 var cs = case_labels [idx];
5233                                 if (cs.SectionStart)
5234                                         return cs;
5235                         }
5236                 }
5237
5238                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5239                 {
5240                         Expr.FlowAnalysis (fc);
5241
5242                         var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5243                         var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5244                         fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5245
5246                         block.FlowAnalysis (fc);
5247
5248                         fc.SwitchInitialDefinitiveAssignment = prev_switch;
5249
5250                         if (end_reachable_das != null) {
5251                                 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5252                                 InitialDefinitiveAssignment |= sections_das;
5253                                 end_reachable_das = null;
5254                         }
5255
5256                         fc.DefiniteAssignment = InitialDefinitiveAssignment;
5257
5258                         return case_default != null && !end_reachable;
5259                 }
5260
5261                 public override bool Resolve (BlockContext ec)
5262                 {
5263                         Expr = Expr.Resolve (ec);
5264                         if (Expr == null)
5265                                 return false;
5266
5267                         //
5268                         // LAMESPEC: User conversion from non-nullable governing type has a priority
5269                         //
5270                         new_expr = SwitchGoverningType (ec, Expr, false);
5271
5272                         if (new_expr == null) {
5273                                 if (Expr.Type.IsNullableType) {
5274                                         unwrap = Nullable.Unwrap.Create (Expr, false);
5275                                         if (unwrap == null)
5276                                                 return false;
5277
5278                                         //
5279                                         // Unwrap + user conversion using non-nullable type is not allowed but user operator
5280                                         // involving nullable Expr and nullable governing type is
5281                                         //
5282                                         new_expr = SwitchGoverningType (ec, unwrap, true);
5283                                 }
5284                         }
5285
5286                         Expression switch_expr;
5287                         if (new_expr == null) {
5288                                 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5289                                         if (Expr.Type != InternalType.ErrorType) {
5290                                                 ec.Report.Error (151, loc,
5291                                                         "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5292                                                         Expr.Type.GetSignatureForError ());
5293                                         }
5294
5295                                         return false;
5296                                 }
5297
5298                                 switch_expr = Expr;
5299                                 SwitchType = Expr.Type;
5300                         } else {
5301                                 switch_expr = new_expr;
5302                                 SwitchType = new_expr.Type;
5303                                 if (SwitchType.IsNullableType) {
5304                                         new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5305                                         SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5306                                 }
5307
5308                                 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5309                                         ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5310                                         return false;
5311                                 }
5312
5313                                 if (block.Statements.Count == 0)
5314                                         return true;
5315
5316                                 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5317                                         string_labels = new Dictionary<string, SwitchLabel> ();
5318                                 } else {
5319                                         labels = new Dictionary<long, SwitchLabel> ();
5320                                 }
5321                         }
5322
5323                         var constant = switch_expr as Constant;
5324
5325                         //
5326                         // Don't need extra variable for constant switch or switch with
5327                         // only default case
5328                         //
5329                         if (constant == null) {
5330                                 //
5331                                 // Store switch expression for comparison purposes
5332                                 //
5333                                 value = switch_expr as VariableReference;
5334                                 if (value == null && !HasOnlyDefaultSection ()) {
5335                                         var current_block = ec.CurrentBlock;
5336                                         ec.CurrentBlock = Block;
5337                                         // Create temporary variable inside switch scope
5338                                         value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5339                                         value.Resolve (ec);
5340                                         ec.CurrentBlock = current_block;
5341                                 }
5342                         }
5343
5344                         case_labels = new List<SwitchLabel> ();
5345
5346                         Switch old_switch = ec.Switch;
5347                         ec.Switch = this;
5348                         var parent_los = ec.EnclosingLoopOrSwitch;
5349                         ec.EnclosingLoopOrSwitch = this;
5350
5351                         var ok = Statement.Resolve (ec);
5352
5353                         ec.EnclosingLoopOrSwitch = parent_los;
5354                         ec.Switch = old_switch;
5355
5356                         //
5357                         // Check if all goto cases are valid. Needs to be done after switch
5358                         // is resolved because goto can jump forward in the scope.
5359                         //
5360                         if (goto_cases != null) {
5361                                 foreach (var gc in goto_cases) {
5362                                         if (gc.Item1 == null) {
5363                                                 if (DefaultLabel == null) {
5364                                                         Goto.Error_UnknownLabel (ec, "default", loc);
5365                                                 }
5366
5367                                                 continue;
5368                                         }
5369
5370                                         var sl = FindLabel (gc.Item2);
5371                                         if (sl == null) {
5372                                                 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5373                                         } else {
5374                                                 gc.Item1.Label = sl;
5375                                         }
5376                                 }
5377                         }
5378
5379                         if (!ok)
5380                                 return false;
5381
5382                         if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5383                                 ResolveStringSwitchMap (ec);
5384                         }
5385
5386                         //
5387                         // Anonymous storey initialization has to happen before
5388                         // any generated switch dispatch
5389                         //
5390                         block.InsertStatement (0, new DispatchStatement (this));
5391
5392                         return true;
5393                 }
5394
5395                 bool HasOnlyDefaultSection ()
5396                 {
5397                         for (int i = 0; i < block.Statements.Count; ++i) {
5398                                 var s = block.Statements[i] as SwitchLabel;
5399
5400                                 if (s == null || s.IsDefault)
5401                                         continue;
5402
5403                                 return false;
5404                         }
5405
5406                         return true;
5407                 }
5408
5409                 public override Reachability MarkReachable (Reachability rc)
5410                 {
5411                         if (rc.IsUnreachable)
5412                                 return rc;
5413
5414                         base.MarkReachable (rc);
5415
5416                         block.MarkReachableScope (rc);
5417
5418                         if (block.Statements.Count == 0)
5419                                 return rc;
5420
5421                         SwitchLabel constant_label = null;
5422                         var constant = new_expr as Constant;
5423
5424                         if (constant != null) {
5425                                 constant_label = FindLabel (constant) ?? case_default;
5426                                 if (constant_label == null) {
5427                                         block.Statements.RemoveAt (0);
5428                                         return rc;
5429                                 }
5430                         }
5431
5432                         var section_rc = new Reachability ();
5433                         SwitchLabel prev_label = null;
5434
5435                         for (int i = 0; i < block.Statements.Count; ++i) {
5436                                 var s = block.Statements[i];
5437                                 var sl = s as SwitchLabel;
5438
5439                                 if (sl != null && sl.SectionStart) {
5440                                         //
5441                                         // Section is marked already via goto case
5442                                         //
5443                                         if (!sl.IsUnreachable) {
5444                                                 section_rc = new Reachability ();
5445                                                 continue;
5446                                         }
5447
5448                                         if (section_rc.IsUnreachable) {
5449                                                 //
5450                                                 // Common case. Previous label section end is unreachable as
5451                                                 // it ends with break, return, etc. For next section revert
5452                                                 // to reachable again unless we have constant switch block
5453                                                 //
5454                                                 section_rc = constant_label != null && constant_label != sl ?
5455                                                         Reachability.CreateUnreachable () :
5456                                                         new Reachability ();
5457                                         } else if (prev_label != null) {
5458                                                 //
5459                                                 // Error case as control cannot fall through from one case label
5460                                                 //
5461                                                 sl.SectionStart = false;
5462                                                 s = new MissingBreak (prev_label);
5463                                                 s.MarkReachable (rc);
5464                                                 block.Statements.Insert (i - 1, s);
5465                                                 ++i;
5466                                         } else if (constant_label != null && constant_label != sl) {
5467                                                 //
5468                                                 // Special case for the first unreachable label in constant
5469                                                 // switch block
5470                                                 //
5471                                                 section_rc = Reachability.CreateUnreachable ();
5472                                         }
5473
5474                                         prev_label = sl;
5475                                 }
5476
5477                                 section_rc = s.MarkReachable (section_rc);
5478                         }
5479
5480                         if (!section_rc.IsUnreachable && prev_label != null) {
5481                                 prev_label.SectionStart = false;
5482                                 var s = new MissingBreak (prev_label) {
5483                                         FallOut = true
5484                                 };
5485
5486                                 s.MarkReachable (rc);
5487                                 block.Statements.Add (s);
5488                         }
5489
5490                         //
5491                         // Reachability can affect parent only when all possible paths are handled but
5492                         // we still need to run reachability check on switch body to check for fall-through
5493                         //
5494                         if (case_default == null && constant_label == null)
5495                                 return rc;
5496
5497                         //
5498                         // We have at least one local exit from the switch
5499                         //
5500                         if (end_reachable)
5501                                 return rc;
5502
5503                         return Reachability.CreateUnreachable ();
5504                 }
5505
5506                 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5507                 {
5508                         if (goto_cases == null)
5509                                 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5510
5511                         goto_cases.Add (Tuple.Create (gotoCase, value));
5512                 }
5513
5514                 //
5515                 // Converts string switch into string hashtable
5516                 //
5517                 void ResolveStringSwitchMap (ResolveContext ec)
5518                 {
5519                         FullNamedExpression string_dictionary_type;
5520                         if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5521                                 string_dictionary_type = new TypeExpression (
5522                                         ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5523                                                 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5524                                         loc);
5525                         } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5526                                 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5527                         } else {
5528                                 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5529                                 return;
5530                         }
5531
5532                         var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5533                         Field field = new Field (ctype, string_dictionary_type,
5534                                 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5535                                 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5536                         if (!field.Define ())
5537                                 return;
5538                         ctype.AddField (field);
5539
5540                         var init = new List<Expression> ();
5541                         int counter = -1;
5542                         labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5543                         string value = null;
5544
5545                         foreach (SwitchLabel sl in case_labels) {
5546
5547                                 if (sl.SectionStart)
5548                                         labels.Add (++counter, sl);
5549
5550                                 if (sl == case_default || sl == case_null)
5551                                         continue;
5552
5553                                 value = (string) sl.Converted.GetValue ();
5554                                 var init_args = new List<Expression> (2);
5555                                 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5556
5557                                 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5558                                 init_args.Add (sl.Converted);
5559
5560                                 init.Add (new CollectionElementInitializer (init_args, loc));
5561                         }
5562         
5563                         Arguments args = new Arguments (1);
5564                         args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5565                         Expression initializer = new NewInitialize (string_dictionary_type, args,
5566                                 new CollectionOrObjectInitializers (init, loc), loc);
5567
5568                         switch_cache_field = new FieldExpr (field, loc);
5569                         string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5570                 }
5571
5572                 void DoEmitStringSwitch (EmitContext ec)
5573                 {
5574                         Label l_initialized = ec.DefineLabel ();
5575
5576                         //
5577                         // Skip initialization when value is null
5578                         //
5579                         value.EmitBranchable (ec, nullLabel, false);
5580
5581                         //
5582                         // Check if string dictionary is initialized and initialize
5583                         //
5584                         switch_cache_field.EmitBranchable (ec, l_initialized, true);
5585                         using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5586                                 string_dictionary.EmitStatement (ec);
5587                         }
5588                         ec.MarkLabel (l_initialized);
5589
5590                         LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5591
5592                         ResolveContext rc = new ResolveContext (ec.MemberContext);
5593
5594                         if (switch_cache_field.Type.IsGeneric) {
5595                                 Arguments get_value_args = new Arguments (2);
5596                                 get_value_args.Add (new Argument (value));
5597                                 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5598                                 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5599                                 if (get_item == null)
5600                                         return;
5601
5602                                 //
5603                                 // A value was not found, go to default case
5604                                 //
5605                                 get_item.EmitBranchable (ec, defaultLabel, false);
5606                         } else {
5607                                 Arguments get_value_args = new Arguments (1);
5608                                 get_value_args.Add (new Argument (value));
5609
5610                                 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5611                                 if (get_item == null)
5612                                         return;
5613
5614                                 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5615                                 get_item_object.EmitAssign (ec, get_item, true, false);
5616                                 ec.Emit (OpCodes.Brfalse, defaultLabel);
5617
5618                                 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5619                                         new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5620
5621                                 get_item_int.EmitStatement (ec);
5622                                 get_item_object.Release (ec);
5623                         }
5624
5625                         EmitTableSwitch (ec, string_switch_variable);
5626                         string_switch_variable.Release (ec);
5627                 }
5628
5629                 //
5630                 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5631                 //
5632                 void EmitShortSwitch (EmitContext ec)
5633                 {
5634                         MethodSpec equal_method = null;
5635                         if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5636                                 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5637                         }
5638
5639                         if (equal_method != null) {
5640                                 value.EmitBranchable (ec, nullLabel, false);
5641                         }
5642
5643                         for (int i = 0; i < case_labels.Count; ++i) {
5644                                 var label = case_labels [i];
5645                                 if (label == case_default || label == case_null)
5646                                         continue;
5647
5648                                 var constant = label.Converted;
5649
5650                                 if (constant == null) {
5651                                         label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5652                                         continue;
5653                                 }
5654
5655                                 if (equal_method != null) {
5656                                         value.Emit (ec);
5657                                         constant.Emit (ec);
5658
5659                                         var call = new CallEmitter ();
5660                                         call.EmitPredefined (ec, equal_method, new Arguments (0));
5661                                         ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5662                                         continue;
5663                                 }
5664
5665                                 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5666                                         value.EmitBranchable (ec, label.GetILLabel (ec), false);
5667                                         continue;
5668                                 }
5669
5670                                 value.Emit (ec);
5671                                 constant.Emit (ec);
5672                                 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5673                         }
5674
5675                         ec.Emit (OpCodes.Br, defaultLabel);
5676                 }
5677
5678                 void EmitDispatch (EmitContext ec)
5679                 {
5680                         if (IsPatternMatching) {
5681                                 EmitShortSwitch (ec);
5682                                 return;
5683                         }
5684
5685                         if (value == null) {
5686                                 //
5687                                 // Constant switch, we've already done the work if there is only 1 label
5688                                 // referenced
5689                                 //
5690                                 int reachable = 0;
5691                                 foreach (var sl in case_labels) {
5692                                         if (sl.IsUnreachable)
5693                                                 continue;
5694
5695                                         if (reachable++ > 0) {
5696                                                 var constant = (Constant) new_expr;
5697                                                 var constant_label = FindLabel (constant) ?? case_default;
5698
5699                                                 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5700                                                 break;
5701                                         }
5702                                 }
5703
5704                                 return;
5705                         }
5706
5707                         if (string_dictionary != null) {
5708                                 DoEmitStringSwitch (ec);
5709                         } else if (case_labels.Count < 4 || string_labels != null) {
5710                                 EmitShortSwitch (ec);
5711                         } else {
5712                                 EmitTableSwitch (ec, value);
5713                         }
5714                 }
5715
5716                 protected override void DoEmit (EmitContext ec)
5717                 {
5718                         //
5719                         // Setup the codegen context
5720                         //
5721                         Label old_end = ec.LoopEnd;
5722                         Switch old_switch = ec.Switch;
5723
5724                         ec.LoopEnd = ec.DefineLabel ();
5725                         ec.Switch = this;
5726
5727                         defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5728                         nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5729
5730                         if (value != null) {
5731                                 ec.Mark (loc);
5732
5733                                 var switch_expr = new_expr ?? Expr;
5734                                 if (IsNullable) {
5735                                         unwrap.EmitCheck (ec);
5736                                         ec.Emit (OpCodes.Brfalse, nullLabel);
5737                                         value.EmitAssign (ec, switch_expr, false, false);
5738                                 } else if (switch_expr != value) {
5739                                         value.EmitAssign (ec, switch_expr, false, false);
5740                                 }
5741
5742
5743                                 //
5744                                 // Next statement is compiler generated we don't need extra
5745                                 // nop when we can use the statement for sequence point
5746                                 //
5747                                 ec.Mark (block.StartLocation);
5748                                 block.IsCompilerGenerated = true;
5749                         } else {
5750                                 new_expr.EmitSideEffect (ec);
5751                         }
5752
5753                         block.Emit (ec);
5754
5755                         // Restore context state. 
5756                         ec.MarkLabel (ec.LoopEnd);
5757
5758                         //
5759                         // Restore the previous context
5760                         //
5761                         ec.LoopEnd = old_end;
5762                         ec.Switch = old_switch;
5763                 }
5764
5765                 protected override void CloneTo (CloneContext clonectx, Statement t)
5766                 {
5767                         Switch target = (Switch) t;
5768
5769                         target.Expr = Expr.Clone (clonectx);
5770                         target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5771                 }
5772                 
5773                 public override object Accept (StructuralVisitor visitor)
5774                 {
5775                         return visitor.Visit (this);
5776                 }
5777
5778                 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5779                 {
5780                         if (case_default == null && !(new_expr is Constant))
5781                                 return;
5782
5783                         if (end_reachable_das == null)
5784                                 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5785
5786                         end_reachable_das.Add (fc.DefiniteAssignment);
5787                 }
5788
5789                 public override void SetEndReachable ()
5790                 {
5791                         end_reachable = true;
5792                 }
5793         }
5794
5795         // A place where execution can restart in a state machine
5796         public abstract class ResumableStatement : Statement
5797         {
5798                 bool prepared;
5799                 protected Label resume_point;
5800
5801                 public Label PrepareForEmit (EmitContext ec)
5802                 {
5803                         if (!prepared) {
5804                                 prepared = true;
5805                                 resume_point = ec.DefineLabel ();
5806                         }
5807                         return resume_point;
5808                 }
5809
5810                 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5811                 {
5812                         return end;
5813                 }
5814
5815                 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5816                 {
5817                 }
5818         }
5819
5820         public abstract class TryFinallyBlock : ExceptionStatement
5821         {
5822                 protected Statement stmt;
5823                 Label dispose_try_block;
5824                 bool prepared_for_dispose, emitted_dispose;
5825                 Method finally_host;
5826
5827                 protected TryFinallyBlock (Statement stmt, Location loc)
5828                         : base (loc)
5829                 {
5830                         this.stmt = stmt;
5831                 }
5832
5833                 #region Properties
5834
5835                 public Statement Statement {
5836                         get {
5837                                 return stmt;
5838                         }
5839                 }
5840
5841                 #endregion
5842
5843                 protected abstract void EmitTryBody (EmitContext ec);
5844                 public abstract void EmitFinallyBody (EmitContext ec);
5845
5846                 public override Label PrepareForDispose (EmitContext ec, Label end)
5847                 {
5848                         if (!prepared_for_dispose) {
5849                                 prepared_for_dispose = true;
5850                                 dispose_try_block = ec.DefineLabel ();
5851                         }
5852                         return dispose_try_block;
5853                 }
5854
5855                 protected sealed override void DoEmit (EmitContext ec)
5856                 {
5857                         EmitTryBodyPrepare (ec);
5858                         EmitTryBody (ec);
5859
5860                         bool beginFinally = EmitBeginFinallyBlock (ec);
5861
5862                         Label start_finally = ec.DefineLabel ();
5863                         if (resume_points != null && beginFinally) {
5864                                 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5865
5866                                 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5867                                 ec.Emit (OpCodes.Brfalse_S, start_finally);
5868                                 ec.Emit (OpCodes.Endfinally);
5869                         }
5870
5871                         ec.MarkLabel (start_finally);
5872
5873                         if (finally_host != null) {
5874                                 finally_host.Define ();
5875                                 finally_host.PrepareEmit ();
5876                                 finally_host.Emit ();
5877
5878                                 // Now it's safe to add, to close it properly and emit sequence points
5879                                 finally_host.Parent.AddMember (finally_host);
5880
5881                                 var ce = new CallEmitter ();
5882                                 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5883                                 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5884                         } else {
5885                                 EmitFinallyBody (ec);
5886                         }
5887
5888                         if (beginFinally)
5889                                 ec.EndExceptionBlock ();
5890                 }
5891
5892                 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5893                 {
5894                         if (emitted_dispose)
5895                                 return;
5896
5897                         emitted_dispose = true;
5898
5899                         Label end_of_try = ec.DefineLabel ();
5900
5901                         // Ensure that the only way we can get into this code is through a dispatcher
5902                         if (have_dispatcher)
5903                                 ec.Emit (OpCodes.Br, end);
5904
5905                         ec.BeginExceptionBlock ();
5906
5907                         ec.MarkLabel (dispose_try_block);
5908
5909                         Label[] labels = null;
5910                         for (int i = 0; i < resume_points.Count; ++i) {
5911                                 ResumableStatement s = resume_points[i];
5912                                 Label ret = s.PrepareForDispose (ec, end_of_try);
5913                                 if (ret.Equals (end_of_try) && labels == null)
5914                                         continue;
5915                                 if (labels == null) {
5916                                         labels = new Label[resume_points.Count];
5917                                         for (int j = 0; j < i; ++j)
5918                                                 labels[j] = end_of_try;
5919                                 }
5920                                 labels[i] = ret;
5921                         }
5922
5923                         if (labels != null) {
5924                                 int j;
5925                                 for (j = 1; j < labels.Length; ++j)
5926                                         if (!labels[0].Equals (labels[j]))
5927                                                 break;
5928                                 bool emit_dispatcher = j < labels.Length;
5929
5930                                 if (emit_dispatcher) {
5931                                         ec.Emit (OpCodes.Ldloc, pc);
5932                                         ec.EmitInt (first_resume_pc);
5933                                         ec.Emit (OpCodes.Sub);
5934                                         ec.Emit (OpCodes.Switch, labels);
5935                                 }
5936
5937                                 foreach (ResumableStatement s in resume_points)
5938                                         s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5939                         }
5940
5941                         ec.MarkLabel (end_of_try);
5942
5943                         ec.BeginFinallyBlock ();
5944
5945                         if (finally_host != null) {
5946                                 var ce = new CallEmitter ();
5947                                 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5948                                 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5949                         } else {
5950                                 EmitFinallyBody (ec);
5951                         }
5952
5953                         ec.EndExceptionBlock ();
5954                 }
5955
5956                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5957                 {
5958                         var res = stmt.FlowAnalysis (fc);
5959                         parent_try_block = null;
5960                         return res;
5961                 }
5962
5963                 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5964                 {
5965                         ec.BeginFinallyBlock ();
5966                         return true;
5967                 }
5968
5969                 public override Reachability MarkReachable (Reachability rc)
5970                 {
5971                         base.MarkReachable (rc);
5972                         return Statement.MarkReachable (rc);
5973                 }
5974
5975                 public override bool Resolve (BlockContext bc)
5976                 {
5977                         bool ok;
5978
5979                         parent_try_block = bc.CurrentTryBlock;
5980                         bc.CurrentTryBlock = this;
5981
5982                         if (stmt is TryCatch) {
5983                                 ok = stmt.Resolve (bc);
5984                         } else {
5985                                 using (bc.Set (ResolveContext.Options.TryScope)) {
5986                                         ok = stmt.Resolve (bc);
5987                                 }
5988                         }
5989
5990                         bc.CurrentTryBlock = parent_try_block;
5991
5992                         //
5993                         // Finally block inside iterator is called from MoveNext and
5994                         // Dispose methods that means we need to lift the block into
5995                         // newly created host method to emit the body only once. The
5996                         // original block then simply calls the newly generated method.
5997                         //
5998                         if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5999                                 var b = stmt as Block;
6000                                 if (b != null && b.Explicit.HasYield) {
6001                                         finally_host = bc.CurrentIterator.CreateFinallyHost (this);
6002                                 }
6003                         }
6004
6005                         return base.Resolve (bc) && ok;
6006                 }
6007         }
6008
6009         //
6010         // Base class for blocks using exception handling
6011         //
6012         public abstract class ExceptionStatement : ResumableStatement
6013         {
6014                 protected List<ResumableStatement> resume_points;
6015                 protected int first_resume_pc;
6016                 protected ExceptionStatement parent_try_block;
6017                 protected int first_catch_resume_pc = -1;
6018
6019                 protected ExceptionStatement (Location loc)
6020                 {
6021                         this.loc = loc;
6022                 }
6023
6024                 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6025                 {
6026                         StateMachineInitializer state_machine = null;
6027                         if (resume_points != null) {
6028                                 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6029
6030                                 ec.EmitInt ((int) IteratorStorey.State.Running);
6031                                 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6032                         }
6033
6034                         //
6035                         // The resume points in catch section when this is try-catch-finally
6036                         //
6037                         if (IsRewrittenTryCatchFinally ()) {
6038                                 ec.BeginExceptionBlock ();
6039
6040                                 if (first_catch_resume_pc >= 0) {
6041
6042                                         ec.MarkLabel (resume_point);
6043
6044                                         // For normal control flow, we want to fall-through the Switch
6045                                         // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6046                                         ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6047                                         ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6048                                         ec.Emit (OpCodes.Sub);
6049
6050                                         var labels = new Label [resume_points.Count - first_catch_resume_pc];
6051                                         for (int i = 0; i < labels.Length; ++i)
6052                                                 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6053                                         ec.Emit (OpCodes.Switch, labels);
6054                                 }
6055                         }
6056
6057                         ec.BeginExceptionBlock ();
6058
6059                         //
6060                         // The resume points for try section
6061                         //
6062                         if (resume_points != null && first_catch_resume_pc != 0) {
6063                                 if (first_catch_resume_pc < 0)
6064                                         ec.MarkLabel (resume_point);
6065
6066                                 // For normal control flow, we want to fall-through the Switch
6067                                 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6068                                 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6069                                 ec.EmitInt (first_resume_pc);
6070                                 ec.Emit (OpCodes.Sub);
6071
6072                                 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6073                                 for (int i = 0; i < labels.Length; ++i)
6074                                         labels[i] = resume_points[i].PrepareForEmit (ec);
6075                                 ec.Emit (OpCodes.Switch, labels);
6076                         }
6077                 }
6078
6079                 bool IsRewrittenTryCatchFinally ()
6080                 {
6081                         var tf = this as TryFinally;
6082                         if (tf == null)
6083                                 return false;
6084
6085                         var tc = tf.Statement as TryCatch;
6086                         if (tc == null)
6087                                 return false;
6088
6089                         return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6090                 }
6091
6092                 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6093                 {
6094                         if (parent_try_block != null) {
6095                                 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6096                         } else {
6097                                 pc = stateMachine.AddResumePoint (this);
6098                         }
6099
6100                         if (resume_points == null) {
6101                                 resume_points = new List<ResumableStatement> ();
6102                                 first_resume_pc = pc;
6103                         }
6104
6105                         if (pc != first_resume_pc + resume_points.Count)
6106                                 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6107
6108                         var tf = this as TryFinally;
6109                         if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6110                                 first_catch_resume_pc = resume_points.Count;
6111                         }
6112
6113                         resume_points.Add (stmt);
6114                         return pc;
6115                 }
6116         }
6117
6118         public class Lock : TryFinallyBlock
6119         {
6120                 Expression expr;
6121                 TemporaryVariableReference expr_copy;
6122                 TemporaryVariableReference lock_taken;
6123                         
6124                 public Lock (Expression expr, Statement stmt, Location loc)
6125                         : base (stmt, loc)
6126                 {
6127                         this.expr = expr;
6128                 }
6129
6130                 public Expression Expr {
6131                         get {
6132                                 return this.expr;
6133                         }
6134                 }
6135
6136                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6137                 {
6138                         expr.FlowAnalysis (fc);
6139                         return base.DoFlowAnalysis (fc);
6140                 }
6141
6142                 public override bool Resolve (BlockContext ec)
6143                 {
6144                         expr = expr.Resolve (ec);
6145                         if (expr == null)
6146                                 return false;
6147
6148                         if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6149                                 ec.Report.Error (185, loc,
6150                                         "`{0}' is not a reference type as required by the lock statement",
6151                                         expr.Type.GetSignatureForError ());
6152                         }
6153
6154                         if (expr.Type.IsGenericParameter) {
6155                                 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6156                         }
6157
6158                         VariableReference lv = expr as VariableReference;
6159                         bool locked;
6160                         if (lv != null) {
6161                                 locked = lv.IsLockedByStatement;
6162                                 lv.IsLockedByStatement = true;
6163                         } else {
6164                                 lv = null;
6165                                 locked = false;
6166                         }
6167
6168                         //
6169                         // Have to keep original lock value around to unlock same location
6170                         // in the case of original value has changed or is null
6171                         //
6172                         expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6173                         expr_copy.Resolve (ec);
6174
6175                         //
6176                         // Ensure Monitor methods are available
6177                         //
6178                         if (ResolvePredefinedMethods (ec) > 1) {
6179                                 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6180                                 lock_taken.Resolve (ec);
6181                         }
6182
6183                         using (ec.Set (ResolveContext.Options.LockScope)) {
6184                                 base.Resolve (ec);
6185                         }
6186
6187                         if (lv != null) {
6188                                 lv.IsLockedByStatement = locked;
6189                         }
6190
6191                         return true;
6192                 }
6193                 
6194                 protected override void EmitTryBodyPrepare (EmitContext ec)
6195                 {
6196                         expr_copy.EmitAssign (ec, expr);
6197
6198                         if (lock_taken != null) {
6199                                 //
6200                                 // Initialize ref variable
6201                                 //
6202                                 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6203                         } else {
6204                                 //
6205                                 // Monitor.Enter (expr_copy)
6206                                 //
6207                                 expr_copy.Emit (ec);
6208                                 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6209                         }
6210
6211                         base.EmitTryBodyPrepare (ec);
6212                 }
6213
6214                 protected override void EmitTryBody (EmitContext ec)
6215                 {
6216                         //
6217                         // Monitor.Enter (expr_copy, ref lock_taken)
6218                         //
6219                         if (lock_taken != null) {
6220                                 expr_copy.Emit (ec);
6221                                 lock_taken.LocalInfo.CreateBuilder (ec);
6222                                 lock_taken.AddressOf (ec, AddressOp.Load);
6223                                 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6224                         }
6225
6226                         Statement.Emit (ec);
6227                 }
6228
6229                 public override void EmitFinallyBody (EmitContext ec)
6230                 {
6231                         //
6232                         // if (lock_taken) Monitor.Exit (expr_copy)
6233                         //
6234                         Label skip = ec.DefineLabel ();
6235
6236                         if (lock_taken != null) {
6237                                 lock_taken.Emit (ec);
6238                                 ec.Emit (OpCodes.Brfalse_S, skip);
6239                         }
6240
6241                         expr_copy.Emit (ec);
6242                         var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6243                         if (m != null)
6244                                 ec.Emit (OpCodes.Call, m);
6245
6246                         ec.MarkLabel (skip);
6247                 }
6248
6249                 int ResolvePredefinedMethods (ResolveContext rc)
6250                 {
6251                         // Try 4.0 Monitor.Enter (object, ref bool) overload first
6252                         var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6253                         if (m != null)
6254                                 return 4;
6255
6256                         m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6257                         if (m != null)
6258                                 return 1;
6259
6260                         rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6261                         return 0;
6262                 }
6263
6264                 protected override void CloneTo (CloneContext clonectx, Statement t)
6265                 {
6266                         Lock target = (Lock) t;
6267
6268                         target.expr = expr.Clone (clonectx);
6269                         target.stmt = Statement.Clone (clonectx);
6270                 }
6271                 
6272                 public override object Accept (StructuralVisitor visitor)
6273                 {
6274                         return visitor.Visit (this);
6275                 }
6276
6277         }
6278
6279         public class Unchecked : Statement {
6280                 public Block Block;
6281                 
6282                 public Unchecked (Block b, Location loc)
6283                 {
6284                         Block = b;
6285                         b.Unchecked = true;
6286                         this.loc = loc;
6287                 }
6288
6289                 public override bool Resolve (BlockContext ec)
6290                 {
6291                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6292                                 return Block.Resolve (ec);
6293                 }
6294                 
6295                 protected override void DoEmit (EmitContext ec)
6296                 {
6297                         using (ec.With (EmitContext.Options.CheckedScope, false))
6298                                 Block.Emit (ec);
6299                 }
6300
6301                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6302                 {
6303                         return Block.FlowAnalysis (fc);
6304                 }
6305
6306                 public override Reachability MarkReachable (Reachability rc)
6307                 {
6308                         base.MarkReachable (rc);
6309                         return Block.MarkReachable (rc);
6310                 }
6311
6312                 protected override void CloneTo (CloneContext clonectx, Statement t)
6313                 {
6314                         Unchecked target = (Unchecked) t;
6315
6316                         target.Block = clonectx.LookupBlock (Block);
6317                 }
6318                 
6319                 public override object Accept (StructuralVisitor visitor)
6320                 {
6321                         return visitor.Visit (this);
6322                 }
6323         }
6324
6325         public class Checked : Statement {
6326                 public Block Block;
6327                 
6328                 public Checked (Block b, Location loc)
6329                 {
6330                         Block = b;
6331                         b.Unchecked = false;
6332                         this.loc = loc;
6333                 }
6334
6335                 public override bool Resolve (BlockContext ec)
6336                 {
6337                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6338                                 return Block.Resolve (ec);
6339                 }
6340
6341                 protected override void DoEmit (EmitContext ec)
6342                 {
6343                         using (ec.With (EmitContext.Options.CheckedScope, true))
6344                                 Block.Emit (ec);
6345                 }
6346
6347                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6348                 {
6349                         return Block.FlowAnalysis (fc);
6350                 }
6351
6352                 public override Reachability MarkReachable (Reachability rc)
6353                 {
6354                         base.MarkReachable (rc);
6355                         return Block.MarkReachable (rc);
6356                 }
6357
6358                 protected override void CloneTo (CloneContext clonectx, Statement t)
6359                 {
6360                         Checked target = (Checked) t;
6361
6362                         target.Block = clonectx.LookupBlock (Block);
6363                 }
6364                 
6365                 public override object Accept (StructuralVisitor visitor)
6366                 {
6367                         return visitor.Visit (this);
6368                 }
6369         }
6370
6371         public class Unsafe : Statement {
6372                 public Block Block;
6373
6374                 public Unsafe (Block b, Location loc)
6375                 {
6376                         Block = b;
6377                         Block.Unsafe = true;
6378                         this.loc = loc;
6379                 }
6380
6381                 public override bool Resolve (BlockContext ec)
6382                 {
6383                         if (ec.CurrentIterator != null)
6384                                 Expression.UnsafeInsideIteratorError (ec, loc);
6385
6386                         using (ec.Set (ResolveContext.Options.UnsafeScope))
6387                                 return Block.Resolve (ec);
6388                 }
6389                 
6390                 protected override void DoEmit (EmitContext ec)
6391                 {
6392                         Block.Emit (ec);
6393                 }
6394
6395                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6396                 {
6397                         return Block.FlowAnalysis (fc);
6398                 }
6399
6400                 public override Reachability MarkReachable (Reachability rc)
6401                 {
6402                         base.MarkReachable (rc);
6403                         return Block.MarkReachable (rc);
6404                 }
6405
6406                 protected override void CloneTo (CloneContext clonectx, Statement t)
6407                 {
6408                         Unsafe target = (Unsafe) t;
6409
6410                         target.Block = clonectx.LookupBlock (Block);
6411                 }
6412                 
6413                 public override object Accept (StructuralVisitor visitor)
6414                 {
6415                         return visitor.Visit (this);
6416                 }
6417         }
6418
6419         // 
6420         // Fixed statement
6421         //
6422         public class Fixed : Statement
6423         {
6424                 abstract class Emitter : ShimExpression
6425                 {
6426                         protected LocalVariable vi;
6427
6428                         protected Emitter (Expression expr, LocalVariable li)
6429                                 : base (expr)
6430                         {
6431                                 vi = li;
6432                         }
6433
6434                         public abstract void EmitExit (EmitContext ec);
6435
6436                         public override void FlowAnalysis (FlowAnalysisContext fc)
6437                         {
6438                                 expr.FlowAnalysis (fc);
6439                         }
6440                 }
6441
6442                 sealed class ExpressionEmitter : Emitter {
6443                         public ExpressionEmitter (Expression converted, LocalVariable li)
6444                                 : base (converted, li)
6445                         {
6446                         }
6447
6448                         protected override Expression DoResolve (ResolveContext rc)
6449                         {
6450                                 throw new NotImplementedException ();
6451                         }
6452
6453                         public override void Emit (EmitContext ec) {
6454                                 //
6455                                 // Store pointer in pinned location
6456                                 //
6457                                 expr.Emit (ec);
6458                                 vi.EmitAssign (ec);
6459                         }
6460
6461                         public override void EmitExit (EmitContext ec)
6462                         {
6463                                 ec.EmitInt (0);
6464                                 ec.Emit (OpCodes.Conv_U);
6465                                 vi.EmitAssign (ec);
6466                         }
6467                 }
6468
6469                 class StringEmitter : Emitter
6470                 {
6471                         LocalVariable pinned_string;
6472
6473                         public StringEmitter (Expression expr, LocalVariable li)
6474                                 : base (expr, li)
6475                         {
6476                         }
6477
6478                         protected override Expression DoResolve (ResolveContext rc)
6479                         {
6480                                 pinned_string = new LocalVariable (vi.Block, "$pinned",
6481                                         LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6482                                         vi.Location);
6483                                 pinned_string.Type = rc.BuiltinTypes.String;
6484                                 vi.IsFixed = false;
6485
6486                                 eclass = ExprClass.Variable;
6487                                 type = rc.BuiltinTypes.Int;
6488                                 return this;
6489                         }
6490
6491                         public override void Emit (EmitContext ec)
6492                         {
6493                                 pinned_string.CreateBuilder (ec);
6494
6495                                 expr.Emit (ec);
6496                                 pinned_string.EmitAssign (ec);
6497
6498                                 // TODO: Should use Binary::Add
6499                                 pinned_string.Emit (ec);
6500                                 ec.Emit (OpCodes.Conv_I);
6501
6502                                 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6503                                 if (m == null)
6504                                         return;
6505
6506                                 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6507                                 //pe.InstanceExpression = pinned_string;
6508                                 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6509
6510                                 ec.Emit (OpCodes.Add);
6511                                 vi.EmitAssign (ec);
6512                         }
6513
6514                         public override void EmitExit (EmitContext ec)
6515                         {
6516                                 ec.EmitNull ();
6517                                 pinned_string.EmitAssign (ec);
6518                         }
6519                 }
6520
6521                 public class VariableDeclaration : BlockVariable
6522                 {
6523                         public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6524                                 : base (type, li)
6525                         {
6526                         }
6527
6528                         protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6529                         {
6530                                 if (!Variable.Type.IsPointer && li == Variable) {
6531                                         bc.Report.Error (209, TypeExpression.Location,
6532                                                 "The type of locals declared in a fixed statement must be a pointer type");
6533                                         return null;
6534                                 }
6535
6536                                 var res = initializer.Resolve (bc);
6537                                 if (res == null)
6538                                         return null;
6539
6540                                 //
6541                                 // Case 1: Array
6542                                 //
6543                                 var ac = res.Type as ArrayContainer;
6544                                 if (ac != null) {
6545                                         TypeSpec array_type = ac.Element;
6546
6547                                         //
6548                                         // Provided that array_type is unmanaged,
6549                                         //
6550                                         if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6551                                                 return null;
6552
6553                                         Expression res_init;
6554                                         if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6555                                                 res_init = res;
6556                                         } else {
6557                                                 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6558                                                 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6559                                                 res = expr_variable.CreateReferenceExpression (bc, loc);
6560                                         }
6561
6562                                         //
6563                                         // and T* is implicitly convertible to the
6564                                         // pointer type given in the fixed statement.
6565                                         //
6566                                         ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6567
6568                                         Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6569                                         if (converted == null)
6570                                                 return null;
6571
6572                                         //
6573                                         // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6574                                         //
6575                                         converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6576                                                 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6577                                                 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6578                                                         new NullLiteral (loc),
6579                                                         converted, loc);
6580
6581                                         converted = converted.Resolve (bc);
6582
6583                                         return new ExpressionEmitter (converted, li);
6584                                 }
6585
6586                                 //
6587                                 // Case 2: string
6588                                 //
6589                                 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6590                                         return new StringEmitter (res, li).Resolve (bc);
6591                                 }
6592
6593                                 // Case 3: fixed buffer
6594                                 if (res is FixedBufferPtr) {
6595                                         return new ExpressionEmitter (res, li);
6596                                 }
6597
6598                                 bool already_fixed = true;
6599
6600                                 //
6601                                 // Case 4: & object.
6602                                 //
6603                                 Unary u = res as Unary;
6604                                 if (u != null) {
6605                                         if (u.Oper == Unary.Operator.AddressOf) {
6606                                                 IVariableReference vr = u.Expr as IVariableReference;
6607                                                 if (vr == null || !vr.IsFixed) {
6608                                                         already_fixed = false;
6609                                                 }
6610                                         }
6611                                 } else if (initializer is Cast) {
6612                                         bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6613                                         return null;
6614                                 }
6615
6616                                 if (already_fixed) {
6617                                         bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6618                                 }
6619
6620                                 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6621                                 return new ExpressionEmitter (res, li);
6622                         }
6623                 }
6624
6625
6626                 VariableDeclaration decl;
6627                 Statement statement;
6628                 bool has_ret;
6629
6630                 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6631                 {
6632                         this.decl = decl;
6633                         statement = stmt;
6634                         loc = l;
6635                 }
6636
6637                 #region Properties
6638
6639                 public Statement Statement {
6640                         get {
6641                                 return statement;
6642                         }
6643                 }
6644
6645                 public BlockVariable Variables {
6646                         get {
6647                                 return decl;
6648                         }
6649                 }
6650
6651                 #endregion
6652
6653                 public override bool Resolve (BlockContext bc)
6654                 {
6655                         using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6656                                 if (!decl.Resolve (bc))
6657                                         return false;
6658                         }
6659
6660                         return statement.Resolve (bc);
6661                 }
6662
6663                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6664                 {
6665                         decl.FlowAnalysis (fc);
6666                         return statement.FlowAnalysis (fc);
6667                 }
6668                 
6669                 protected override void DoEmit (EmitContext ec)
6670                 {
6671                         decl.Variable.CreateBuilder (ec);
6672                         decl.Initializer.Emit (ec);
6673                         if (decl.Declarators != null) {
6674                                 foreach (var d in decl.Declarators) {
6675                                         d.Variable.CreateBuilder (ec);
6676                                         d.Initializer.Emit (ec);
6677                                 }
6678                         }
6679
6680                         statement.Emit (ec);
6681
6682                         if (has_ret)
6683                                 return;
6684
6685                         //
6686                         // Clear the pinned variable
6687                         //
6688                         ((Emitter) decl.Initializer).EmitExit (ec);
6689                         if (decl.Declarators != null) {
6690                                 foreach (var d in decl.Declarators) {
6691                                         ((Emitter)d.Initializer).EmitExit (ec);
6692                                 }
6693                         }
6694                 }
6695
6696                 public override Reachability MarkReachable (Reachability rc)
6697                 {
6698                         base.MarkReachable (rc);
6699
6700                         decl.MarkReachable (rc);
6701
6702                         rc = statement.MarkReachable (rc);
6703
6704                         // TODO: What if there is local exit?
6705                         has_ret = rc.IsUnreachable;
6706                         return rc;
6707                 }
6708
6709                 protected override void CloneTo (CloneContext clonectx, Statement t)
6710                 {
6711                         Fixed target = (Fixed) t;
6712
6713                         target.decl = (VariableDeclaration) decl.Clone (clonectx);
6714                         target.statement = statement.Clone (clonectx);
6715                 }
6716                 
6717                 public override object Accept (StructuralVisitor visitor)
6718                 {
6719                         return visitor.Visit (this);
6720                 }
6721         }
6722
6723         public class Catch : Statement
6724         {
6725                 class CatchVariableStore : Statement
6726                 {
6727                         readonly Catch ctch;
6728
6729                         public CatchVariableStore (Catch ctch)
6730                         {
6731                                 this.ctch = ctch;
6732                         }
6733
6734                         protected override void CloneTo (CloneContext clonectx, Statement target)
6735                         {
6736                         }
6737
6738                         protected override void DoEmit (EmitContext ec)
6739                         {
6740                                 // Emits catch variable debug information inside correct block
6741                                 ctch.EmitCatchVariableStore (ec);
6742                         }
6743
6744                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6745                         {
6746                                 return true;
6747                         }
6748                 }
6749
6750                 class FilterStatement : Statement
6751                 {
6752                         readonly Catch ctch;
6753
6754                         public FilterStatement (Catch ctch)
6755                         {
6756                                 this.ctch = ctch;
6757                         }
6758
6759                         protected override void CloneTo (CloneContext clonectx, Statement target)
6760                         {
6761                         }
6762
6763                         protected override void DoEmit (EmitContext ec)
6764                         {
6765                                 if (ctch.li != null) {
6766                                         if (ctch.hoisted_temp != null)
6767                                                 ctch.hoisted_temp.Emit (ec);
6768                                         else
6769                                                 ctch.li.Emit (ec);
6770
6771                                         if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6772                                                 ec.Emit (OpCodes.Box, ctch.type);
6773                                 }
6774
6775                                 var expr_start = ec.DefineLabel ();
6776                                 var end = ec.DefineLabel ();
6777
6778                                 ec.Emit (OpCodes.Brtrue_S, expr_start);
6779                                 ec.EmitInt (0);
6780                                 ec.Emit (OpCodes.Br, end);
6781                                 ec.MarkLabel (expr_start);
6782
6783                                 ctch.Filter.Emit (ec);
6784
6785                                 ec.MarkLabel (end);
6786                                 ec.Emit (OpCodes.Endfilter);
6787                                 ec.BeginFilterHandler ();
6788                                 ec.Emit (OpCodes.Pop);
6789                         }
6790
6791                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6792                         {
6793                                 ctch.Filter.FlowAnalysis (fc);
6794                                 return true;
6795                         }
6796
6797                         public override bool Resolve (BlockContext bc)
6798                         {
6799                                 ctch.Filter = ctch.Filter.Resolve (bc);
6800
6801                                 if (ctch.Filter != null) {
6802                                         if (ctch.Filter.ContainsEmitWithAwait ()) {
6803                                                 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6804                                         }
6805
6806                                         var c = ctch.Filter as Constant;
6807                                         if (c != null && !c.IsDefaultValue) {
6808                                                 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6809                                         }
6810                                 }
6811
6812                                 return true;
6813                         }
6814                 }
6815
6816                 ExplicitBlock block;
6817                 LocalVariable li;
6818                 FullNamedExpression type_expr;
6819                 CompilerAssign assign;
6820                 TypeSpec type;
6821                 LocalTemporary hoisted_temp;
6822
6823                 public Catch (ExplicitBlock block, Location loc)
6824                 {
6825                         this.block = block;
6826                         this.loc = loc;
6827                 }
6828
6829                 #region Properties
6830
6831                 public ExplicitBlock Block {
6832                         get {
6833                                 return block;
6834                         }
6835                 }
6836
6837                 public TypeSpec CatchType {
6838                         get {
6839                                 return type;
6840                         }
6841                 }
6842
6843                 public Expression Filter {
6844                         get; set;
6845                 }
6846
6847                 public bool IsGeneral {
6848                         get {
6849                                 return type_expr == null;
6850                         }
6851                 }
6852
6853                 public FullNamedExpression TypeExpression {
6854                         get {
6855                                 return type_expr;
6856                         }
6857                         set {
6858                                 type_expr = value;
6859                         }
6860                 }
6861
6862                 public LocalVariable Variable {
6863                         get {
6864                                 return li;
6865                         }
6866                         set {
6867                                 li = value;
6868                         }
6869                 }
6870
6871                 #endregion
6872
6873                 protected override void DoEmit (EmitContext ec)
6874                 {
6875                         if (Filter != null) {
6876                                 ec.BeginExceptionFilterBlock ();
6877                                 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6878
6879                                 if (Block.HasAwait) {
6880                                         Block.EmitScopeInitialization (ec);
6881                                 } else {
6882                                         Block.Emit (ec);
6883                                 }
6884
6885                                 return;
6886                         }
6887
6888                         if (IsGeneral)
6889                                 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6890                         else
6891                                 ec.BeginCatchBlock (CatchType);
6892
6893                         if (li == null)
6894                                 ec.Emit (OpCodes.Pop);
6895
6896                         if (Block.HasAwait) {
6897                                 if (li != null)
6898                                         EmitCatchVariableStore (ec);
6899                         } else {
6900                                 Block.Emit (ec);
6901                         }
6902                 }
6903
6904                 void EmitCatchVariableStore (EmitContext ec)
6905                 {
6906                         li.CreateBuilder (ec);
6907
6908                         //
6909                         // For hoisted catch variable we have to use a temporary local variable
6910                         // for captured variable initialization during storey setup because variable
6911                         // needs to be on the stack after storey instance for stfld operation
6912                         //
6913                         if (li.HoistedVariant != null) {
6914                                 hoisted_temp = new LocalTemporary (li.Type);
6915                                 hoisted_temp.Store (ec);
6916
6917                                 // switch to assignment from temporary variable and not from top of the stack
6918                                 assign.UpdateSource (hoisted_temp);
6919                         }
6920                 }
6921
6922                 public override bool Resolve (BlockContext bc)
6923                 {
6924                         using (bc.Set (ResolveContext.Options.CatchScope)) {
6925                                 if (type_expr == null) {
6926                                         if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6927                                                 if (!block.HasAwait || Filter != null)
6928                                                         block.AddScopeStatement (new CatchVariableStore (this));
6929
6930                                                 Expression source = new EmptyExpression (li.Type);
6931                                                 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6932                                                 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6933                                         }
6934                                 } else {
6935                                         type = type_expr.ResolveAsType (bc);
6936                                         if (type == null)
6937                                                 return false;
6938
6939                                         if (li == null)
6940                                                 CreateExceptionVariable (type);
6941
6942                                         if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6943                                                 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6944                                         } else if (li != null) {
6945                                                 li.Type = type;
6946                                                 li.PrepareAssignmentAnalysis (bc);
6947
6948                                                 // source variable is at the top of the stack
6949                                                 Expression source = new EmptyExpression (li.Type);
6950                                                 if (li.Type.IsGenericParameter)
6951                                                         source = new UnboxCast (source, li.Type);
6952
6953                                                 if (!block.HasAwait || Filter != null)
6954                                                         block.AddScopeStatement (new CatchVariableStore (this));
6955
6956                                                 //
6957                                                 // Uses Location.Null to hide from symbol file
6958                                                 //
6959                                                 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6960                                                 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6961                                         }
6962                                 }
6963
6964                                 if (Filter != null) {
6965                                         Block.AddScopeStatement (new FilterStatement (this));
6966                                 }
6967
6968                                 Block.SetCatchBlock ();
6969                                 return Block.Resolve (bc);
6970                         }
6971                 }
6972
6973                 bool CreateExceptionVariable (TypeSpec type)
6974                 {
6975                         if (!Block.HasAwait)
6976                                 return false;
6977
6978                         // TODO: Scan the block for rethrow expression
6979                         //if (!Block.HasRethrow)
6980                         //      return;
6981
6982                         li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6983                         return true;
6984                 }
6985
6986                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6987                 {
6988                         if (li != null && !li.IsCompilerGenerated) {
6989                                 fc.SetVariableAssigned (li.VariableInfo, true);
6990                         }
6991
6992                         return block.FlowAnalysis (fc);
6993                 }
6994
6995                 public override Reachability MarkReachable (Reachability rc)
6996                 {
6997                         base.MarkReachable (rc);
6998
6999                         var c = Filter as Constant;
7000                         if (c != null && c.IsDefaultValue)
7001                                 return Reachability.CreateUnreachable ();
7002
7003                         return block.MarkReachable (rc);
7004                 }
7005
7006                 protected override void CloneTo (CloneContext clonectx, Statement t)
7007                 {
7008                         Catch target = (Catch) t;
7009
7010                         if (type_expr != null)
7011                                 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7012
7013                         if (Filter != null)
7014                                 target.Filter = Filter.Clone (clonectx);
7015
7016                         target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7017                 }
7018         }
7019
7020         public class TryFinally : TryFinallyBlock
7021         {
7022                 ExplicitBlock fini;
7023                 List<DefiniteAssignmentBitSet> try_exit_dat;
7024                 List<Tuple<Label, bool>> redirected_jumps;
7025                 Label? start_fin_label;
7026
7027                 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7028                          : base (stmt, loc)
7029                 {
7030                         this.fini = fini;
7031                 }
7032
7033                 public ExplicitBlock FinallyBlock {
7034                         get {
7035                                 return fini;
7036                         }
7037                 }
7038
7039                 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7040                 {
7041                         if (try_exit_dat == null)
7042                                 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7043
7044                         try_exit_dat.Add (vector);
7045                 }
7046
7047                 public override bool Resolve (BlockContext bc)
7048                 {
7049                         bool ok = base.Resolve (bc);
7050
7051                         fini.SetFinallyBlock ();
7052                         using (bc.Set (ResolveContext.Options.FinallyScope)) {
7053                                 ok &= fini.Resolve (bc);
7054                         }
7055
7056                         return ok;
7057                 }
7058
7059                 protected override void EmitTryBody (EmitContext ec)
7060                 {
7061                         if (fini.HasAwait) {
7062                                 if (ec.TryFinallyUnwind == null)
7063                                         ec.TryFinallyUnwind = new List<TryFinally> ();
7064
7065                                 ec.TryFinallyUnwind.Add (this);
7066                                 stmt.Emit (ec);
7067
7068                                 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7069                                         ec.EndExceptionBlock ();
7070
7071                                 ec.TryFinallyUnwind.Remove (this);
7072
7073                                 if (start_fin_label != null)
7074                                         ec.MarkLabel (start_fin_label.Value);
7075
7076                                 return;
7077                         }
7078
7079                         stmt.Emit (ec);
7080                 }
7081
7082                 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7083                 {
7084                         if (fini.HasAwait)
7085                                 return false;
7086
7087                         return base.EmitBeginFinallyBlock (ec);
7088                 }
7089
7090                 public override void EmitFinallyBody (EmitContext ec)
7091                 {
7092                         if (!fini.HasAwait) {
7093                                 fini.Emit (ec);
7094                                 return;
7095                         }
7096
7097                         //
7098                         // Emits catch block like
7099                         //
7100                         // catch (object temp) {
7101                         //      this.exception_field = temp;
7102                         // }
7103                         //
7104                         var type = ec.BuiltinTypes.Object;
7105                         ec.BeginCatchBlock (type);
7106
7107                         var temp = ec.GetTemporaryLocal (type);
7108                         ec.Emit (OpCodes.Stloc, temp);
7109
7110                         var exception_field = ec.GetTemporaryField (type);
7111                         exception_field.AutomaticallyReuse = false;
7112                         ec.EmitThis ();
7113                         ec.Emit (OpCodes.Ldloc, temp);
7114                         exception_field.EmitAssignFromStack (ec);
7115
7116                         ec.EndExceptionBlock ();
7117
7118                         ec.FreeTemporaryLocal (temp, type);
7119
7120                         fini.Emit (ec);
7121
7122                         //
7123                         // Emits exception rethrow
7124                         //
7125                         // if (this.exception_field != null)
7126                         //      throw this.exception_field;
7127                         //
7128                         exception_field.Emit (ec);
7129                         var skip_throw = ec.DefineLabel ();
7130                         ec.Emit (OpCodes.Brfalse_S, skip_throw);
7131                         exception_field.Emit (ec);
7132                         ec.Emit (OpCodes.Throw);
7133                         ec.MarkLabel (skip_throw);
7134
7135                         exception_field.PrepareCleanup (ec);
7136
7137                         EmitUnwindFinallyTable (ec);
7138                 }
7139
7140                 bool IsParentBlock (Block block)
7141                 {
7142                         for (Block b = fini; b != null; b = b.Parent) {
7143                                 if (b == block)
7144                                         return true;
7145                         }
7146
7147                         return false;
7148                 }
7149
7150                 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7151                 {
7152                         int idx;
7153                         if (labelBlock != null) {
7154                                 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7155                                         var fin = ec.TryFinallyUnwind [idx - 1];
7156                                         if (!fin.IsParentBlock (labelBlock))
7157                                                 break;
7158                                 }
7159                         } else {
7160                                 idx = 0;
7161                         }
7162
7163                         bool set_return_state = true;
7164
7165                         for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7166                                 var fin = ec.TryFinallyUnwind [idx];
7167                                 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7168                                         break;
7169
7170                                 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7171                                 set_return_state = false;
7172
7173                                 if (fin.start_fin_label == null) {
7174                                         fin.start_fin_label = ec.DefineLabel ();
7175                                 }
7176
7177                                 label = fin.start_fin_label.Value;
7178                         }
7179
7180                         return label;
7181                 }
7182
7183                 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7184                 {
7185                         return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7186                 }
7187
7188                 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7189                 {
7190                         if (redirected_jumps == null) {
7191                                 redirected_jumps = new List<Tuple<Label, bool>> ();
7192
7193                                 // Add fallthrough label
7194                                 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7195
7196                                 if (setReturnState)
7197                                         initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7198                         }
7199
7200                         int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7201                         if (index < 0) {
7202                                 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7203                                 index = redirected_jumps.Count - 1;
7204                         }
7205
7206                         //
7207                         // Indicates we have captured exit jump
7208                         //
7209                         if (setReturnState) {
7210                                 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7211                                 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7212                         }
7213                 }
7214
7215                 //
7216                 // Emits state table of jumps outside of try block and reload of return
7217                 // value when try block returns value
7218                 //
7219                 void EmitUnwindFinallyTable (EmitContext ec)
7220                 {
7221                         if (redirected_jumps == null)
7222                                 return;
7223
7224                         var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7225                         initializer.HoistedReturnState.EmitLoad (ec);
7226
7227                         var jumps_table = new Label [redirected_jumps.Count];
7228                         List<Tuple<Label, Label>> leave_redirect = null;
7229                         for (int i = 0; i < jumps_table.Length; ++i) {
7230                                 var val = redirected_jumps [i];
7231
7232                                 if (val.Item2) {
7233                                         if (leave_redirect == null)
7234                                                 leave_redirect = new List<Tuple<Label, Label>> ();
7235                                         var label = ec.DefineLabel ();
7236                                         leave_redirect.Add (Tuple.Create (label, val.Item1));
7237                                         jumps_table [i] = label;
7238                                 } else {
7239                                         jumps_table [i] = val.Item1;
7240                                 }
7241                         }
7242
7243                         ec.Emit (OpCodes.Switch, jumps_table);
7244
7245                         if (leave_redirect != null) {
7246                                 foreach (var entry in leave_redirect) {
7247                                         ec.MarkLabel (entry.Item1);
7248                                         ec.Emit (OpCodes.Leave, entry.Item2);
7249                                 }
7250                         }
7251
7252                         // Mark fallthrough label
7253                         ec.MarkLabel (jumps_table [0]);
7254                 }
7255
7256                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7257                 {
7258                         var da = fc.BranchDefiniteAssignment ();
7259
7260                         var tf = fc.TryFinally;
7261                         fc.TryFinally = this;
7262
7263                         var res_stmt = Statement.FlowAnalysis (fc);
7264
7265                         fc.TryFinally = tf;
7266
7267                         var try_da = fc.DefiniteAssignment;
7268                         fc.DefiniteAssignment = da;
7269
7270                         var res_fin = fini.FlowAnalysis (fc);
7271
7272                         if (try_exit_dat != null) {
7273                                 //
7274                                 // try block has global exit but we need to run definite assignment check
7275                                 // for parameter block out parameter after finally block because it's always
7276                                 // executed before exit
7277                                 //
7278                                 foreach (var try_da_part in try_exit_dat)
7279                                         fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7280
7281                                 try_exit_dat = null;
7282                         }
7283
7284                         fc.DefiniteAssignment |= try_da;
7285                         return res_stmt | res_fin;
7286                 }
7287
7288                 public override Reachability MarkReachable (Reachability rc)
7289                 {
7290                         //
7291                         // Mark finally block first for any exit statement in try block
7292                         // to know whether the code which follows finally is reachable
7293                         //
7294                         return fini.MarkReachable (rc) | base.MarkReachable (rc);
7295                 }
7296
7297                 protected override void CloneTo (CloneContext clonectx, Statement t)
7298                 {
7299                         TryFinally target = (TryFinally) t;
7300
7301                         target.stmt = stmt.Clone (clonectx);
7302                         if (fini != null)
7303                                 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7304                 }
7305                 
7306                 public override object Accept (StructuralVisitor visitor)
7307                 {
7308                         return visitor.Visit (this);
7309                 }
7310         }
7311
7312         public class TryCatch : ExceptionStatement
7313         {
7314                 public Block Block;
7315                 List<Catch> clauses;
7316                 readonly bool inside_try_finally;
7317                 List<Catch> catch_sm;
7318
7319                 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7320                         : base (l)
7321                 {
7322                         this.Block = block;
7323                         this.clauses = catch_clauses;
7324                         this.inside_try_finally = inside_try_finally;
7325                 }
7326
7327                 public List<Catch> Clauses {
7328                         get {
7329                                 return clauses;
7330                         }
7331                 }
7332
7333                 public bool HasClauseWithAwait {
7334                         get {
7335                                 return catch_sm != null;
7336                         }
7337                 }
7338
7339                 public bool IsTryCatchFinally {
7340                         get {
7341                                 return inside_try_finally;
7342                         }
7343                 }
7344
7345                 public override bool Resolve (BlockContext bc)
7346                 {
7347                         bool ok;
7348
7349                         using (bc.Set (ResolveContext.Options.TryScope)) {
7350
7351                                 parent_try_block = bc.CurrentTryBlock;
7352
7353                                 if (IsTryCatchFinally) {
7354                                         ok = Block.Resolve (bc);
7355                                 } else {
7356                                         using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7357                                                 bc.CurrentTryBlock = this;
7358                                                 ok = Block.Resolve (bc);
7359                                                 bc.CurrentTryBlock = parent_try_block;
7360                                         }
7361                                 }
7362                         }
7363
7364                         var prev_catch = bc.CurrentTryCatch;
7365                         bc.CurrentTryCatch = this;
7366
7367                         for (int i = 0; i < clauses.Count; ++i) {
7368                                 var c = clauses[i];
7369
7370                                 ok &= c.Resolve (bc);
7371
7372                                 if (c.Block.HasAwait) {
7373                                         if (catch_sm == null)
7374                                                 catch_sm = new List<Catch> ();
7375
7376                                         catch_sm.Add (c);
7377                                 }
7378
7379                                 if (c.Filter != null)
7380                                         continue;
7381
7382                                 TypeSpec resolved_type = c.CatchType;
7383                                 if (resolved_type == null)
7384                                         continue;
7385
7386                                 for (int ii = 0; ii < clauses.Count; ++ii) {
7387                                         if (ii == i)
7388                                                 continue;
7389
7390                                         if (clauses[ii].Filter != null)
7391                                                 continue;
7392
7393                                         if (clauses[ii].IsGeneral) {
7394                                                 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7395                                                         continue;
7396
7397                                                 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7398                                                         continue;
7399
7400                                                 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7401                                                         continue;
7402
7403                                                 bc.Report.Warning (1058, 1, c.loc,
7404                                                         "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7405
7406                                                 continue;
7407                                         }
7408
7409                                         if (ii >= i)
7410                                                 continue;
7411
7412                                         var ct = clauses[ii].CatchType;
7413                                         if (ct == null)
7414                                                 continue;
7415
7416                                         if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7417                                                 bc.Report.Error (160, c.loc,
7418                                                         "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7419                                                         ct.GetSignatureForError ());
7420                                                 ok = false;
7421                                         }
7422                                 }
7423                         }
7424
7425                         bc.CurrentTryCatch = prev_catch;
7426
7427                         return base.Resolve (bc) && ok;
7428                 }
7429
7430                 protected sealed override void DoEmit (EmitContext ec)
7431                 {
7432                         if (!inside_try_finally)
7433                                 EmitTryBodyPrepare (ec);
7434
7435                         Block.Emit (ec);
7436
7437                         LocalBuilder state_variable = null;
7438                         foreach (Catch c in clauses) {
7439                                 c.Emit (ec);
7440
7441                                 if (catch_sm != null) {
7442                                         if (state_variable == null) {
7443                                                 //
7444                                                 // Cannot reuse temp variable because non-catch path assumes the value is 0
7445                                                 // which may not be true for reused local variable
7446                                                 //
7447                                                 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7448                                         }
7449
7450                                         var index = catch_sm.IndexOf (c);
7451                                         if (index < 0)
7452                                                 continue;
7453
7454                                         ec.EmitInt (index + 1);
7455                                         ec.Emit (OpCodes.Stloc, state_variable);
7456                                 }
7457                         }
7458
7459                         if (state_variable == null) {
7460                                 if (!inside_try_finally)
7461                                         ec.EndExceptionBlock ();
7462                         } else {
7463                                 ec.EndExceptionBlock ();
7464
7465                                 ec.Emit (OpCodes.Ldloc, state_variable);
7466
7467                                 var labels = new Label [catch_sm.Count + 1];
7468                                 for (int i = 0; i < labels.Length; ++i) {
7469                                         labels [i] = ec.DefineLabel ();
7470                                 }
7471
7472                                 var end = ec.DefineLabel ();
7473                                 ec.Emit (OpCodes.Switch, labels);
7474
7475                                 // 0 value is default label
7476                                 ec.MarkLabel (labels [0]);
7477                                 ec.Emit (OpCodes.Br, end);
7478
7479                                 var atv = ec.AsyncThrowVariable;
7480                                 Catch c = null;
7481                                 for (int i = 0; i < catch_sm.Count; ++i) {
7482                                         if (c != null && c.Block.HasReachableClosingBrace)
7483                                                 ec.Emit (OpCodes.Br, end);
7484
7485                                         ec.MarkLabel (labels [i + 1]);
7486
7487                                         ec.EmitInt (0);
7488                                         ec.Emit (OpCodes.Stloc, state_variable);
7489
7490                                         c = catch_sm [i];
7491                                         ec.AsyncThrowVariable = c.Variable;
7492                                         c.Block.Emit (ec);
7493                                 }
7494                                 ec.AsyncThrowVariable = atv;
7495
7496                                 ec.MarkLabel (end);
7497                         }
7498                 }
7499
7500                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7501                 {
7502                         var start_fc = fc.BranchDefiniteAssignment ();
7503                         var res = Block.FlowAnalysis (fc);
7504
7505                         DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7506
7507                         foreach (var c in clauses) {
7508                                 fc.BranchDefiniteAssignment (start_fc);
7509                                 if (!c.FlowAnalysis (fc)) {
7510                                         if (try_fc == null)
7511                                                 try_fc = fc.DefiniteAssignment;
7512                                         else
7513                                                 try_fc &= fc.DefiniteAssignment;
7514
7515                                         res = false;
7516                                 }
7517                         }
7518
7519                         fc.DefiniteAssignment = try_fc ?? start_fc;
7520                         parent_try_block = null;
7521                         return res;
7522                 }
7523
7524                 public override Reachability MarkReachable (Reachability rc)
7525                 {
7526                         if (rc.IsUnreachable)
7527                                 return rc;
7528
7529                         base.MarkReachable (rc);
7530
7531                         var tc_rc = Block.MarkReachable (rc);
7532
7533                         foreach (var c in clauses)
7534                                 tc_rc &= c.MarkReachable (rc);
7535
7536                         return tc_rc;
7537                 }
7538
7539                 protected override void CloneTo (CloneContext clonectx, Statement t)
7540                 {
7541                         TryCatch target = (TryCatch) t;
7542
7543                         target.Block = clonectx.LookupBlock (Block);
7544                         if (clauses != null){
7545                                 target.clauses = new List<Catch> ();
7546                                 foreach (Catch c in clauses)
7547                                         target.clauses.Add ((Catch) c.Clone (clonectx));
7548                         }
7549                 }
7550
7551                 public override object Accept (StructuralVisitor visitor)
7552                 {
7553                         return visitor.Visit (this);
7554                 }
7555         }
7556
7557         public class Using : TryFinallyBlock
7558         {
7559                 public class VariableDeclaration : BlockVariable
7560                 {
7561                         Statement dispose_call;
7562
7563                         public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7564                                 : base (type, li)
7565                         {
7566                         }
7567
7568                         public VariableDeclaration (LocalVariable li, Location loc)
7569                                 : base (li)
7570                         {
7571                                 reachable = true;
7572                                 this.loc = loc;
7573                         }
7574
7575                         public VariableDeclaration (Expression expr)
7576                                 : base (null)
7577                         {
7578                                 loc = expr.Location;
7579                                 Initializer = expr;
7580                         }
7581
7582                         #region Properties
7583
7584                         public bool IsNested { get; private set; }
7585
7586                         #endregion
7587
7588                         public void EmitDispose (EmitContext ec)
7589                         {
7590                                 dispose_call.Emit (ec);
7591                         }
7592
7593                         public override bool Resolve (BlockContext bc)
7594                         {
7595                                 if (IsNested)
7596                                         return true;
7597
7598                                 return base.Resolve (bc, false);
7599                         }
7600
7601                         public Expression ResolveExpression (BlockContext bc)
7602                         {
7603                                 var e = Initializer.Resolve (bc);
7604                                 if (e == null)
7605                                         return null;
7606
7607                                 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7608                                 Initializer = ResolveInitializer (bc, Variable, e);
7609                                 return e;
7610                         }
7611
7612                         protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7613                         {
7614                                 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7615                                         initializer = initializer.Resolve (bc);
7616                                         if (initializer == null)
7617                                                 return null;
7618
7619                                         // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7620                                         Arguments args = new Arguments (1);
7621                                         args.Add (new Argument (initializer));
7622                                         initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7623                                         if (initializer == null)
7624                                                 return null;
7625
7626                                         var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7627                                         dispose_call = CreateDisposeCall (bc, var);
7628                                         dispose_call.Resolve (bc);
7629
7630                                         return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7631                                 }
7632
7633                                 if (li == Variable) {
7634                                         CheckIDiposableConversion (bc, li, initializer);
7635                                         dispose_call = CreateDisposeCall (bc, li);
7636                                         dispose_call.Resolve (bc);
7637                                 }
7638
7639                                 return base.ResolveInitializer (bc, li, initializer);
7640                         }
7641
7642                         protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7643                         {
7644                                 var type = li.Type;
7645
7646                                 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7647                                         if (type.IsNullableType) {
7648                                                 // it's handled in CreateDisposeCall
7649                                                 return;
7650                                         }
7651
7652                                         if (type != InternalType.ErrorType) {
7653                                                 bc.Report.SymbolRelatedToPreviousError (type);
7654                                                 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7655                                                 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7656                                                         type.GetSignatureForError ());
7657                                         }
7658
7659                                         return;
7660                                 }
7661                         }
7662
7663                         static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7664                         {
7665                                 var target = bc.BuiltinTypes.IDisposable;
7666                                 var tp = type as TypeParameterSpec;
7667                                 if (tp != null)
7668                                         return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7669
7670                                 return type.ImplementsInterface (target, false);
7671                         }
7672
7673                         protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7674                         {
7675                                 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7676                                 var type = lv.Type;
7677                                 var loc = lv.Location;
7678
7679                                 var idt = bc.BuiltinTypes.IDisposable;
7680                                 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7681
7682                                 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7683                                 dispose_mg.InstanceExpression = type.IsNullableType ?
7684                                         new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7685                                         lvr;
7686
7687                                 //
7688                                 // Hide it from symbol file via null location
7689                                 //
7690                                 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7691
7692                                 // Add conditional call when disposing possible null variable
7693                                 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7694                                         dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7695
7696                                 return dispose;
7697                         }
7698
7699                         public void ResolveDeclaratorInitializer (BlockContext bc)
7700                         {
7701                                 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7702                         }
7703
7704                         public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7705                         {
7706                                 for (int i = declarators.Count - 1; i >= 0; --i) {
7707                                         var d = declarators [i];
7708                                         var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7709                                         vd.Initializer = d.Initializer;
7710                                         vd.IsNested = true;
7711                                         vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7712                                         vd.dispose_call.Resolve (bc);
7713
7714                                         stmt = new Using (vd, stmt, d.Variable.Location);
7715                                 }
7716
7717                                 declarators = null;
7718                                 return stmt;
7719                         }       
7720
7721                         public override object Accept (StructuralVisitor visitor)
7722                         {
7723                                 return visitor.Visit (this);
7724                         }       
7725                 }
7726
7727                 VariableDeclaration decl;
7728
7729                 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7730                         : base (stmt, loc)
7731                 {
7732                         this.decl = decl;
7733                 }
7734
7735                 public Using (Expression expr, Statement stmt, Location loc)
7736                         : base (stmt, loc)
7737                 {
7738                         this.decl = new VariableDeclaration (expr);
7739                 }
7740
7741                 #region Properties
7742
7743                 public Expression Expr {
7744                         get {
7745                                 return decl.Variable == null ? decl.Initializer : null;
7746                         }
7747                 }
7748
7749                 public BlockVariable Variables {
7750                         get {
7751                                 return decl;
7752                         }
7753                 }
7754
7755                 #endregion
7756
7757                 public override void Emit (EmitContext ec)
7758                 {
7759                         //
7760                         // Don't emit sequence point it will be set on variable declaration
7761                         //
7762                         DoEmit (ec);
7763                 }
7764
7765                 protected override void EmitTryBodyPrepare (EmitContext ec)
7766                 {
7767                         decl.Emit (ec);
7768                         base.EmitTryBodyPrepare (ec);
7769                 }
7770
7771                 protected override void EmitTryBody (EmitContext ec)
7772                 {
7773                         stmt.Emit (ec);
7774                 }
7775
7776                 public override void EmitFinallyBody (EmitContext ec)
7777                 {
7778                         decl.EmitDispose (ec);
7779                 }
7780
7781                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7782                 {
7783                         decl.FlowAnalysis (fc);
7784                         return stmt.FlowAnalysis (fc);
7785                 }
7786
7787                 public override Reachability MarkReachable (Reachability rc)
7788                 {
7789                         decl.MarkReachable (rc);
7790                         return base.MarkReachable (rc);
7791                 }
7792
7793                 public override bool Resolve (BlockContext ec)
7794                 {
7795                         VariableReference vr;
7796                         bool vr_locked = false;
7797
7798                         using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7799                                 if (decl.Variable == null) {
7800                                         vr = decl.ResolveExpression (ec) as VariableReference;
7801                                         if (vr != null) {
7802                                                 vr_locked = vr.IsLockedByStatement;
7803                                                 vr.IsLockedByStatement = true;
7804                                         }
7805                                 } else {
7806                                         if (decl.IsNested) {
7807                                                 decl.ResolveDeclaratorInitializer (ec);
7808                                         } else {
7809                                                 if (!decl.Resolve (ec))
7810                                                         return false;
7811
7812                                                 if (decl.Declarators != null) {
7813                                                         stmt = decl.RewriteUsingDeclarators (ec, stmt);
7814                                                 }
7815                                         }
7816
7817                                         vr = null;
7818                                 }
7819                         }
7820
7821                         var ok = base.Resolve (ec);
7822
7823                         if (vr != null)
7824                                 vr.IsLockedByStatement = vr_locked;
7825
7826                         return ok;
7827                 }
7828
7829                 protected override void CloneTo (CloneContext clonectx, Statement t)
7830                 {
7831                         Using target = (Using) t;
7832
7833                         target.decl = (VariableDeclaration) decl.Clone (clonectx);
7834                         target.stmt = stmt.Clone (clonectx);
7835                 }
7836
7837                 public override object Accept (StructuralVisitor visitor)
7838                 {
7839                         return visitor.Visit (this);
7840                 }
7841         }
7842
7843         /// <summary>
7844         ///   Implementation of the foreach C# statement
7845         /// </summary>
7846         public class Foreach : LoopStatement
7847         {
7848                 abstract class IteratorStatement : Statement
7849                 {
7850                         protected readonly Foreach for_each;
7851
7852                         protected IteratorStatement (Foreach @foreach)
7853                         {
7854                                 this.for_each = @foreach;
7855                                 this.loc = @foreach.expr.Location;
7856                         }
7857
7858                         protected override void CloneTo (CloneContext clonectx, Statement target)
7859                         {
7860                                 throw new NotImplementedException ();
7861                         }
7862
7863                         public override void Emit (EmitContext ec)
7864                         {
7865                                 if (ec.EmitAccurateDebugInfo) {
7866                                         ec.Emit (OpCodes.Nop);
7867                                 }
7868
7869                                 base.Emit (ec);
7870                         }
7871
7872                         protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7873                         {
7874                                 throw new NotImplementedException ();
7875                         }
7876                 }
7877
7878                 sealed class ArrayForeach : IteratorStatement
7879                 {
7880                         TemporaryVariableReference[] lengths;
7881                         Expression [] length_exprs;
7882                         StatementExpression[] counter;
7883                         TemporaryVariableReference[] variables;
7884
7885                         TemporaryVariableReference copy;
7886
7887                         public ArrayForeach (Foreach @foreach, int rank)
7888                                 : base (@foreach)
7889                         {
7890                                 counter = new StatementExpression[rank];
7891                                 variables = new TemporaryVariableReference[rank];
7892                                 length_exprs = new Expression [rank];
7893
7894                                 //
7895                                 // Only use temporary length variables when dealing with
7896                                 // multi-dimensional arrays
7897                                 //
7898                                 if (rank > 1)
7899                                         lengths = new TemporaryVariableReference [rank];
7900                         }
7901
7902                         public override bool Resolve (BlockContext ec)
7903                         {
7904                                 Block variables_block = for_each.variable.Block;
7905                                 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7906                                 copy.Resolve (ec);
7907
7908                                 int rank = length_exprs.Length;
7909                                 Arguments list = new Arguments (rank);
7910                                 for (int i = 0; i < rank; i++) {
7911                                         var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7912                                         variables[i] = v;
7913                                         counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7914                                         counter[i].Resolve (ec);
7915
7916                                         if (rank == 1) {
7917                                                 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7918                                         } else {
7919                                                 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7920                                                 lengths[i].Resolve (ec);
7921
7922                                                 Arguments args = new Arguments (1);
7923                                                 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7924                                                 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7925                                         }
7926
7927                                         list.Add (new Argument (v));
7928                                 }
7929
7930                                 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7931                                 if (access == null)
7932                                         return false;
7933
7934                                 TypeSpec var_type;
7935                                 if (for_each.type is VarExpr) {
7936                                         // Infer implicitly typed local variable from foreach array type
7937                                         var_type = access.Type;
7938                                 } else {
7939                                         var_type = for_each.type.ResolveAsType (ec);
7940
7941                                         if (var_type == null)
7942                                                 return false;
7943
7944                                         access = Convert.ExplicitConversion (ec, access, var_type, loc);
7945                                         if (access == null)
7946                                                 return false;
7947                                 }
7948
7949                                 for_each.variable.Type = var_type;
7950
7951                                 var prev_block = ec.CurrentBlock;
7952                                 ec.CurrentBlock = variables_block;
7953                                 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7954                                 ec.CurrentBlock = prev_block;
7955
7956                                 if (variable_ref == null)
7957                                         return false;
7958
7959                                 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7960
7961                                 return for_each.body.Resolve (ec);
7962                         }
7963
7964                         protected override void DoEmit (EmitContext ec)
7965                         {
7966                                 copy.EmitAssign (ec, for_each.expr);
7967
7968                                 int rank = length_exprs.Length;
7969                                 Label[] test = new Label [rank];
7970                                 Label[] loop = new Label [rank];
7971
7972                                 for (int i = 0; i < rank; i++) {
7973                                         test [i] = ec.DefineLabel ();
7974                                         loop [i] = ec.DefineLabel ();
7975
7976                                         if (lengths != null)
7977                                                 lengths [i].EmitAssign (ec, length_exprs [i]);
7978                                 }
7979
7980                                 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7981                                 for (int i = 0; i < rank; i++) {
7982                                         variables [i].EmitAssign (ec, zero);
7983
7984                                         ec.Emit (OpCodes.Br, test [i]);
7985                                         ec.MarkLabel (loop [i]);
7986                                 }
7987
7988                                 for_each.body.Emit (ec);
7989
7990                                 ec.MarkLabel (ec.LoopBegin);
7991                                 ec.Mark (for_each.expr.Location);
7992
7993                                 for (int i = rank - 1; i >= 0; i--){
7994                                         counter [i].Emit (ec);
7995
7996                                         ec.MarkLabel (test [i]);
7997                                         variables [i].Emit (ec);
7998
7999                                         if (lengths != null)
8000                                                 lengths [i].Emit (ec);
8001                                         else
8002                                                 length_exprs [i].Emit (ec);
8003
8004                                         ec.Emit (OpCodes.Blt, loop [i]);
8005                                 }
8006
8007                                 ec.MarkLabel (ec.LoopEnd);
8008                         }
8009                 }
8010
8011                 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8012                 {
8013                         class RuntimeDispose : Using.VariableDeclaration
8014                         {
8015                                 public RuntimeDispose (LocalVariable lv, Location loc)
8016                                         : base (lv, loc)
8017                                 {
8018                                         reachable = true;
8019                                 }
8020
8021                                 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8022                                 {
8023                                         // Defered to runtime check
8024                                 }
8025
8026                                 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8027                                 {
8028                                         var idt = bc.BuiltinTypes.IDisposable;
8029
8030                                         //
8031                                         // Fabricates code like
8032                                         //
8033                                         // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8034                                         //
8035
8036                                         var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8037
8038                                         var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8039                                                 dispose_variable.CreateReferenceExpression (bc, loc),
8040                                                 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8041                                                 loc), new NullLiteral (loc));
8042
8043                                         var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8044
8045                                         var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8046                                         dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8047
8048                                         Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8049                                         return new If (idisaposable_test, dispose, loc);
8050                                 }
8051                         }
8052
8053                         LocalVariable variable;
8054                         Expression expr;
8055                         Statement statement;
8056                         ExpressionStatement init;
8057                         TemporaryVariableReference enumerator_variable;
8058                         bool ambiguous_getenumerator_name;
8059
8060                         public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8061                                 : base (@foreach)
8062                         {
8063                                 this.variable = var;
8064                                 this.expr = expr;
8065                         }
8066
8067                         void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8068                         {
8069                                 rc.Report.SymbolRelatedToPreviousError (enumerator);
8070                                 rc.Report.Error (202, loc,
8071                                         "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8072                                                 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8073                         }
8074
8075                         MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8076                         {
8077                                 //
8078                                 // Option 1: Try to match by name GetEnumerator first
8079                                 //
8080                                 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8081                                         "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc);               // TODO: What if CS0229 ?
8082
8083                                 var mg = mexpr as MethodGroupExpr;
8084                                 if (mg != null) {
8085                                         mg.InstanceExpression = expr;
8086                                         Arguments args = new Arguments (0);
8087                                         mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8088
8089                                         // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8090                                         if (ambiguous_getenumerator_name)
8091                                                 mg = null;
8092
8093                                         if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8094                                                 return mg;
8095                                         }
8096                                 }
8097
8098                                 //
8099                                 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8100                                 //
8101                                 var t = expr.Type;
8102                                 PredefinedMember<MethodSpec> iface_candidate = null;
8103                                 var ptypes = rc.Module.PredefinedTypes;
8104                                 var gen_ienumerable = ptypes.IEnumerableGeneric;
8105                                 if (!gen_ienumerable.Define ())
8106                                         gen_ienumerable = null;
8107
8108                                 var ifaces = t.Interfaces;
8109                                 if (ifaces != null) {
8110                                         foreach (var iface in ifaces) {
8111                                                 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8112                                                         if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8113                                                                 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8114                                                                 rc.Report.Error (1640, loc,
8115                                                                         "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8116                                                                         expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8117
8118                                                                 return null;
8119                                                         }
8120
8121                                                         // TODO: Cache this somehow
8122                                                         iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8123                                                                 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8124
8125                                                         continue;
8126                                                 }
8127
8128                                                 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8129                                                         iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8130                                                 }
8131                                         }
8132                                 }
8133
8134                                 if (iface_candidate == null) {
8135                                         if (expr.Type != InternalType.ErrorType) {
8136                                                 rc.Report.Error (1579, loc,
8137                                                         "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8138                                                         expr.Type.GetSignatureForError (), "GetEnumerator");
8139                                         }
8140
8141                                         return null;
8142                                 }
8143
8144                                 var method = iface_candidate.Resolve (loc);
8145                                 if (method == null)
8146                                         return null;
8147
8148                                 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8149                                 mg.InstanceExpression = expr;
8150                                 return mg;
8151                         }
8152
8153                         MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8154                         {
8155                                 var ms = MemberCache.FindMember (enumerator.ReturnType,
8156                                         MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8157                                         BindingRestriction.InstanceOnly) as MethodSpec;
8158
8159                                 if (ms == null || !ms.IsPublic) {
8160                                         Error_WrongEnumerator (rc, enumerator);
8161                                         return null;
8162                                 }
8163
8164                                 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8165                         }
8166
8167                         PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8168                         {
8169                                 var ps = MemberCache.FindMember (enumerator.ReturnType,
8170                                         MemberFilter.Property ("Current", null),
8171                                         BindingRestriction.InstanceOnly) as PropertySpec;
8172
8173                                 if (ps == null || !ps.IsPublic) {
8174                                         Error_WrongEnumerator (rc, enumerator);
8175                                         return null;
8176                                 }
8177
8178                                 return ps;
8179                         }
8180
8181                         public override bool Resolve (BlockContext ec)
8182                         {
8183                                 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8184
8185                                 if (is_dynamic) {
8186                                         expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8187                                 } else if (expr.Type.IsNullableType) {
8188                                         expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8189                                 }
8190
8191                                 var get_enumerator_mg = ResolveGetEnumerator (ec);
8192                                 if (get_enumerator_mg == null) {
8193                                         return false;
8194                                 }
8195
8196                                 var get_enumerator = get_enumerator_mg.BestCandidate;
8197                                 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8198                                 enumerator_variable.Resolve (ec);
8199
8200                                 // Prepare bool MoveNext ()
8201                                 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8202                                 if (move_next_mg == null) {
8203                                         return false;
8204                                 }
8205
8206                                 move_next_mg.InstanceExpression = enumerator_variable;
8207
8208                                 // Prepare ~T~ Current { get; }
8209                                 var current_prop = ResolveCurrent (ec, get_enumerator);
8210                                 if (current_prop == null) {
8211                                         return false;
8212                                 }
8213
8214                                 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8215                                 if (current_pe == null)
8216                                         return false;
8217
8218                                 VarExpr ve = for_each.type as VarExpr;
8219
8220                                 if (ve != null) {
8221                                         if (is_dynamic) {
8222                                                 // Source type is dynamic, set element type to dynamic too
8223                                                 variable.Type = ec.BuiltinTypes.Dynamic;
8224                                         } else {
8225                                                 // Infer implicitly typed local variable from foreach enumerable type
8226                                                 variable.Type = current_pe.Type;
8227                                         }
8228                                 } else {
8229                                         if (is_dynamic) {
8230                                                 // Explicit cast of dynamic collection elements has to be done at runtime
8231                                                 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8232                                         }
8233
8234                                         variable.Type = for_each.type.ResolveAsType (ec);
8235
8236                                         if (variable.Type == null)
8237                                                 return false;
8238
8239                                         current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8240                                         if (current_pe == null)
8241                                                 return false;
8242                                 }
8243
8244                                 var prev_block = ec.CurrentBlock;
8245                                 ec.CurrentBlock = for_each.variable.Block;
8246                                 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8247                                 ec.CurrentBlock = prev_block;
8248                                 if (variable_ref == null)
8249                                         return false;
8250
8251                                 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8252
8253                                 var init = new Invocation.Predefined (get_enumerator_mg, null);
8254
8255                                 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8256                                          for_each.body, Location.Null);
8257
8258                                 var enum_type = enumerator_variable.Type;
8259
8260                                 //
8261                                 // Add Dispose method call when enumerator can be IDisposable
8262                                 //
8263                                 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8264                                         if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8265                                                 //
8266                                                 // Runtime Dispose check
8267                                                 //
8268                                                 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8269                                                 vd.Initializer = init;
8270                                                 statement = new Using (vd, statement, Location.Null);
8271                                         } else {
8272                                                 //
8273                                                 // No Dispose call needed
8274                                                 //
8275                                                 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8276                                                 this.init.Resolve (ec);
8277                                         }
8278                                 } else {
8279                                         //
8280                                         // Static Dispose check
8281                                         //
8282                                         var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8283                                         vd.Initializer = init;
8284                                         statement = new Using (vd, statement, Location.Null);
8285                                 }
8286
8287                                 return statement.Resolve (ec);
8288                         }
8289
8290                         protected override void DoEmit (EmitContext ec)
8291                         {
8292                                 enumerator_variable.LocalInfo.CreateBuilder (ec);
8293
8294                                 if (init != null)
8295                                         init.EmitStatement (ec);
8296
8297                                 statement.Emit (ec);
8298                         }
8299
8300                         #region IErrorHandler Members
8301
8302                         bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8303                         {
8304                                 ec.Report.SymbolRelatedToPreviousError (best);
8305                                 ec.Report.Warning (278, 2, expr.Location,
8306                                         "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8307                                         expr.Type.GetSignatureForError (), "enumerable",
8308                                         best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8309
8310                                 ambiguous_getenumerator_name = true;
8311                                 return true;
8312                         }
8313
8314                         bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8315                         {
8316                                 return false;
8317                         }
8318
8319                         bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8320                         {
8321                                 return false;
8322                         }
8323
8324                         bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8325                         {
8326                                 return false;
8327                         }
8328
8329                         #endregion
8330                 }
8331
8332                 Expression type;
8333                 LocalVariable variable;
8334                 Expression expr;
8335                 Block body;
8336
8337                 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8338                         : base (stmt)
8339                 {
8340                         this.type = type;
8341                         this.variable = var;
8342                         this.expr = expr;
8343                         this.body = body;
8344                         loc = l;
8345                 }
8346
8347                 public Expression Expr {
8348                         get { return expr; }
8349                 }
8350
8351                 public Expression TypeExpression {
8352                         get { return type; }
8353                 }
8354
8355                 public LocalVariable Variable {
8356                         get { return variable; }
8357                 }
8358
8359                 public override Reachability MarkReachable (Reachability rc)
8360                 {
8361                         base.MarkReachable (rc);
8362
8363                         body.MarkReachable (rc);
8364
8365                         return rc;
8366                 }
8367
8368                 public override bool Resolve (BlockContext ec)
8369                 {
8370                         expr = expr.Resolve (ec);
8371                         if (expr == null)
8372                                 return false;
8373
8374                         if (expr.IsNull) {
8375                                 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8376                                 return false;
8377                         }
8378
8379                         body.AddStatement (Statement);
8380
8381                         if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8382                                 Statement = new ArrayForeach (this, 1);
8383                         } else if (expr.Type is ArrayContainer) {
8384                                 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8385                         } else {
8386                                 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8387                                         ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8388                                                 expr.ExprClassName);
8389                                         return false;
8390                                 }
8391
8392                                 Statement = new CollectionForeach (this, variable, expr);
8393                         }
8394
8395                         return base.Resolve (ec);
8396                 }
8397
8398                 protected override void DoEmit (EmitContext ec)
8399                 {
8400                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8401                         ec.LoopBegin = ec.DefineLabel ();
8402                         ec.LoopEnd = ec.DefineLabel ();
8403
8404                         ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8405                         body.Explicit.DisableDebugScopeIndex ();
8406
8407                         variable.CreateBuilder (ec);
8408
8409                         Statement.Emit (ec);
8410
8411                         ec.EndScope ();
8412
8413                         ec.LoopBegin = old_begin;
8414                         ec.LoopEnd = old_end;
8415                 }
8416
8417                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8418                 {
8419                         expr.FlowAnalysis (fc);
8420
8421                         var da = fc.BranchDefiniteAssignment ();
8422                         body.FlowAnalysis (fc);
8423                         fc.DefiniteAssignment = da;
8424                         return false;
8425                 }
8426
8427                 protected override void CloneTo (CloneContext clonectx, Statement t)
8428                 {
8429                         Foreach target = (Foreach) t;
8430
8431                         target.type = type.Clone (clonectx);
8432                         target.expr = expr.Clone (clonectx);
8433                         target.body = (Block) body.Clone (clonectx);
8434                         target.Statement = Statement.Clone (clonectx);
8435                 }
8436                 
8437                 public override object Accept (StructuralVisitor visitor)
8438                 {
8439                         return visitor.Visit (this);
8440                 }
8441         }
8442
8443         class SentinelStatement: Statement
8444         {
8445                 protected override void CloneTo (CloneContext clonectx, Statement target)
8446                 {
8447                 }
8448
8449                 protected override void DoEmit (EmitContext ec)
8450                 {
8451                         var l = ec.DefineLabel ();
8452                         ec.MarkLabel (l);
8453                         ec.Emit (OpCodes.Br_S, l);
8454                 }
8455
8456                 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8457                 {
8458                         throw new NotImplementedException ();
8459                 }
8460         }
8461 }