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