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