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