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