Check whether parent struct field is assigned when assigning to child field. Fixes...
[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 {
1551                                 return (flags & Flags.AddressTaken) != 0;
1552                         }
1553                 }
1554
1555                 public Block Block {
1556                         get {
1557                                 return block;
1558                         }
1559                 }
1560
1561                 public Constant ConstantValue {
1562                         get {
1563                                 return const_value;
1564                         }
1565                         set {
1566                                 const_value = value;
1567                         }
1568                 }
1569
1570                 //
1571                 // Hoisted local variable variant
1572                 //
1573                 public HoistedVariable HoistedVariant {
1574                         get {
1575                                 return hoisted_variant;
1576                         }
1577                         set {
1578                                 hoisted_variant = value;
1579                         }
1580                 }
1581
1582                 public bool IsDeclared {
1583                         get {
1584                                 return type != null;
1585                         }
1586                 }
1587
1588                 public bool IsConstant {
1589                         get {
1590                                 return (flags & Flags.Constant) != 0;
1591                         }
1592                 }
1593
1594                 public bool IsLocked {
1595                         get {
1596                                 return (flags & Flags.IsLocked) != 0;
1597                         }
1598                         set {
1599                                 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1600                         }
1601                 }
1602
1603                 public bool IsThis {
1604                         get {
1605                                 return (flags & Flags.IsThis) != 0;
1606                         }
1607                 }
1608
1609                 public bool IsFixed {
1610                         get {
1611                                 return (flags & Flags.FixedVariable) != 0;
1612                         }
1613                 }
1614
1615                 public bool IsReadonly {
1616                         get {
1617                                 return (flags & Flags.ReadonlyMask) != 0;
1618                         }
1619                 }
1620
1621                 public Location Location {
1622                         get {
1623                                 return loc;
1624                         }
1625                 }
1626
1627                 public string Name {
1628                         get {
1629                                 return name;
1630                         }
1631                 }
1632
1633                 public TypeSpec Type {
1634                     get {
1635                                 return type;
1636                         }
1637                     set {
1638                                 type = value;
1639                         }
1640                 }
1641
1642                 #endregion
1643
1644                 public void CreateBuilder (EmitContext ec)
1645                 {
1646                         if ((flags & Flags.Used) == 0) {
1647                                 if (VariableInfo == null) {
1648                                         // Missing flow analysis or wrong variable flags
1649                                         throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1650                                 }
1651
1652                                 if (VariableInfo.IsEverAssigned)
1653                                         ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1654                                 else
1655                                         ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1656                         }
1657
1658                         if (HoistedVariant != null)
1659                                 return;
1660
1661                         if (builder != null) {
1662                                 if ((flags & Flags.CompilerGenerated) != 0)
1663                                         return;
1664
1665                                 // To avoid Used warning duplicates
1666                                 throw new InternalErrorException ("Already created variable `{0}'", name);
1667                         }
1668
1669                         //
1670                         // All fixed variabled are pinned, a slot has to be alocated
1671                         //
1672                         builder = ec.DeclareLocal (Type, IsFixed);
1673                         if (SymbolWriter.HasSymbolWriter)
1674                                 ec.DefineLocalVariable (name, builder);
1675                 }
1676
1677                 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1678                 {
1679                         LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1680                         li.Type = type;
1681                         return li;
1682                 }
1683
1684                 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1685                 {
1686                         if (IsConstant && const_value != null)
1687                                 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1688
1689                         return new LocalVariableReference (this, loc);
1690                 }
1691
1692                 public void Emit (EmitContext ec)
1693                 {
1694                         // TODO: Need something better for temporary variables
1695                         if ((flags & Flags.CompilerGenerated) != 0)
1696                                 CreateBuilder (ec);
1697
1698                         ec.Emit (OpCodes.Ldloc, builder);
1699                 }
1700
1701                 public void EmitAssign (EmitContext ec)
1702                 {
1703                         // TODO: Need something better for temporary variables
1704                         if ((flags & Flags.CompilerGenerated) != 0)
1705                                 CreateBuilder (ec);
1706
1707                         ec.Emit (OpCodes.Stloc, builder);
1708                 }
1709
1710                 public void EmitAddressOf (EmitContext ec)
1711                 {
1712                         ec.Emit (OpCodes.Ldloca, builder);
1713                 }
1714
1715                 public static string GetCompilerGeneratedName (Block block)
1716                 {
1717                         return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1718                 }
1719
1720                 public string GetReadOnlyContext ()
1721                 {
1722                         switch (flags & Flags.ReadonlyMask) {
1723                         case Flags.FixedVariable:
1724                                 return "fixed variable";
1725                         case Flags.ForeachVariable:
1726                                 return "foreach iteration variable";
1727                         case Flags.UsingVariable:
1728                                 return "using variable";
1729                         }
1730
1731                         throw new InternalErrorException ("Variable is not readonly");
1732                 }
1733
1734                 public bool IsThisAssigned (BlockContext ec, Block block)
1735                 {
1736                         if (VariableInfo == null)
1737                                 throw new Exception ();
1738
1739                         if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1740                                 return true;
1741
1742                         return VariableInfo.TypeInfo.IsFullyInitialized (ec, VariableInfo, block.StartLocation);
1743                 }
1744
1745                 public bool IsAssigned (BlockContext ec)
1746                 {
1747                         if (VariableInfo == null)
1748                                 throw new Exception ();
1749
1750                         return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1751                 }
1752
1753                 public void PrepareForFlowAnalysis (BlockContext bc)
1754                 {
1755                         //
1756                         // No need for definitely assigned check for these guys
1757                         //
1758                         if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1759                                 return;
1760
1761                         VariableInfo = new VariableInfo (this, bc.FlowOffset);
1762                         bc.FlowOffset += VariableInfo.Length;
1763                 }
1764
1765                 //
1766                 // Mark the variables as referenced in the user code
1767                 //
1768                 public void SetIsUsed ()
1769                 {
1770                         flags |= Flags.Used;
1771                 }
1772
1773                 public void SetHasAddressTaken ()
1774                 {
1775                         flags |= (Flags.AddressTaken | Flags.Used);
1776                 }
1777
1778                 public override string ToString ()
1779                 {
1780                         return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1781                 }
1782         }
1783
1784         /// <summary>
1785         ///   Block represents a C# block.
1786         /// </summary>
1787         ///
1788         /// <remarks>
1789         ///   This class is used in a number of places: either to represent
1790         ///   explicit blocks that the programmer places or implicit blocks.
1791         ///
1792         ///   Implicit blocks are used as labels or to introduce variable
1793         ///   declarations.
1794         ///
1795         ///   Top-level blocks derive from Block, and they are called ToplevelBlock
1796         ///   they contain extra information that is not necessary on normal blocks.
1797         /// </remarks>
1798         public class Block : Statement {
1799                 [Flags]
1800                 public enum Flags
1801                 {
1802                         Unchecked = 1,
1803                         HasRet = 8,
1804                         Unsafe = 16,
1805                         IsIterator = 32,
1806                         HasCapturedVariable = 64,
1807                         HasCapturedThis = 1 << 7,
1808                         IsExpressionTree = 1 << 8,
1809                         CompilerGenerated = 1 << 9,
1810                         IsAsync = 1 << 10
1811                 }
1812
1813                 public Block Parent;
1814                 public Location StartLocation;
1815                 public Location EndLocation;
1816
1817                 public ExplicitBlock Explicit;
1818                 public ParametersBlock ParametersBlock;
1819
1820                 protected Flags flags;
1821
1822                 //
1823                 // The statements in this block
1824                 //
1825                 protected List<Statement> statements;
1826
1827                 protected List<Statement> scope_initializers;
1828
1829                 int? resolving_init_idx;
1830
1831                 protected Block original;
1832
1833 #if DEBUG
1834                 static int id;
1835                 public int ID = id++;
1836
1837                 static int clone_id_counter;
1838                 int clone_id;
1839 #endif
1840
1841 //              int assignable_slots;
1842                 bool unreachable_shown;
1843                 bool unreachable;
1844                 
1845                 public Block (Block parent, Location start, Location end)
1846                         : this (parent, 0, start, end)
1847                 {
1848                 }
1849
1850                 public Block (Block parent, Flags flags, Location start, Location end)
1851                 {
1852                         if (parent != null) {
1853                                 // the appropriate constructors will fixup these fields
1854                                 ParametersBlock = parent.ParametersBlock;
1855                                 Explicit = parent.Explicit;
1856                         }
1857                         
1858                         this.Parent = parent;
1859                         this.flags = flags;
1860                         this.StartLocation = start;
1861                         this.EndLocation = end;
1862                         this.loc = start;
1863                         statements = new List<Statement> (4);
1864
1865                         this.original = this;
1866                 }
1867
1868                 #region Properties
1869
1870                 public bool HasRet {
1871                         get { return (flags & Flags.HasRet) != 0; }
1872                 }
1873
1874                 public Block Original {
1875                         get {
1876                                 return original;
1877                         }
1878                 }
1879
1880                 public bool IsCompilerGenerated {
1881                         get { return (flags & Flags.CompilerGenerated) != 0; }
1882                         set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
1883                 }
1884
1885                 public bool Unchecked {
1886                         get { return (flags & Flags.Unchecked) != 0; }
1887                         set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1888                 }
1889
1890                 public bool Unsafe {
1891                         get { return (flags & Flags.Unsafe) != 0; }
1892                         set { flags |= Flags.Unsafe; }
1893                 }
1894
1895                 #endregion
1896
1897                 public Block CreateSwitchBlock (Location start)
1898                 {
1899                         // FIXME: Only explicit block should be created
1900                         var new_block = new Block (this, start, start);
1901                         new_block.IsCompilerGenerated = true;
1902                         return new_block;
1903                 }
1904
1905                 public void SetEndLocation (Location loc)
1906                 {
1907                         EndLocation = loc;
1908                 }
1909
1910                 public void AddLabel (LabeledStatement target)
1911                 {
1912                         ParametersBlock.TopBlock.AddLabel (target.Name, target);
1913                 }
1914
1915                 public void AddLocalName (LocalVariable li)
1916                 {
1917                         AddLocalName (li.Name, li);
1918                 }
1919
1920                 public virtual void AddLocalName (string name, INamedBlockVariable li)
1921                 {
1922                         ParametersBlock.TopBlock.AddLocalName (name, li);
1923                 }
1924
1925                 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
1926                 {
1927                         if (reason == null) {
1928                                 Error_AlreadyDeclared (name, variable);
1929                                 return;
1930                         }
1931
1932                         ParametersBlock.TopBlock.Report.Error (136, variable.Location,
1933                                 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
1934                                 "to `{0}', which is already used in a `{1}' scope to denote something else",
1935                                 name, reason);
1936                 }
1937
1938                 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
1939                 {
1940                         var pi = variable as ParametersBlock.ParameterInfo;
1941                         if (pi != null) {
1942                                 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
1943                         } else {
1944                                 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
1945                                         "A local variable named `{0}' is already defined in this scope", name);
1946                         }
1947                 }
1948                                         
1949                 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
1950                 {
1951                         ParametersBlock.TopBlock.Report.Error (412, loc,
1952                                 "The type parameter name `{0}' is the same as local variable or parameter name",
1953                                 name);
1954                 }
1955
1956                 //
1957                 // It should be used by expressions which require to
1958                 // register a statement during resolve process.
1959                 //
1960                 public void AddScopeStatement (Statement s)
1961                 {
1962                         if (scope_initializers == null)
1963                                 scope_initializers = new List<Statement> ();
1964
1965                         //
1966                         // Simple recursive helper, when resolve scope initializer another
1967                         // new scope initializer can be added, this ensures it's initialized
1968                         // before existing one. For now this can happen with expression trees
1969                         // in base ctor initializer only
1970                         //
1971                         if (resolving_init_idx.HasValue) {
1972                                 scope_initializers.Insert (resolving_init_idx.Value, s);
1973                                 ++resolving_init_idx;
1974                         } else {
1975                                 scope_initializers.Add (s);
1976                         }
1977                 }
1978                 
1979                 public void AddStatement (Statement s)
1980                 {
1981                         statements.Add (s);
1982                 }
1983
1984                 public int AssignableSlots {
1985                         get {
1986                                 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
1987                                 return 4096;
1988 //                              return assignable_slots;
1989                         }
1990                 }
1991
1992                 public LabeledStatement LookupLabel (string name)
1993                 {
1994                         return ParametersBlock.TopBlock.GetLabel (name, this);
1995                 }
1996
1997                 public override bool Resolve (BlockContext ec)
1998                 {
1999                         Block prev_block = ec.CurrentBlock;
2000                         bool ok = true;
2001
2002                         ec.CurrentBlock = this;
2003                         ec.StartFlowBranching (this);
2004
2005                         //
2006                         // Compiler generated scope statements
2007                         //
2008                         if (scope_initializers != null) {
2009                                 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2010                                         scope_initializers[resolving_init_idx.Value].Resolve (ec);
2011                                 }
2012
2013                                 resolving_init_idx = null;
2014                         }
2015
2016                         //
2017                         // This flag is used to notate nested statements as unreachable from the beginning of this block.
2018                         // For the purposes of this resolution, it doesn't matter that the whole block is unreachable 
2019                         // from the beginning of the function.  The outer Resolve() that detected the unreachability is
2020                         // responsible for handling the situation.
2021                         //
2022                         int statement_count = statements.Count;
2023                         for (int ix = 0; ix < statement_count; ix++){
2024                                 Statement s = statements [ix];
2025
2026                                 //
2027                                 // Warn if we detect unreachable code.
2028                                 //
2029                                 if (unreachable) {
2030                                         if (s is EmptyStatement)
2031                                                 continue;
2032
2033                                         if (!unreachable_shown && !(s is LabeledStatement)) {
2034                                                 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2035                                                 unreachable_shown = true;
2036                                         }
2037
2038                                         Block c_block = s as Block;
2039                                         if (c_block != null)
2040                                                 c_block.unreachable = c_block.unreachable_shown = true;
2041                                 }
2042
2043                                 //
2044                                 // Note that we're not using ResolveUnreachable() for unreachable
2045                                 // statements here.  ResolveUnreachable() creates a temporary
2046                                 // flow branching and kills it afterwards.  This leads to problems
2047                                 // if you have two unreachable statements where the first one
2048                                 // assigns a variable and the second one tries to access it.
2049                                 //
2050
2051                                 if (!s.Resolve (ec)) {
2052                                         ok = false;
2053                                         if (ec.IsInProbingMode)
2054                                                 break;
2055
2056                                         statements [ix] = new EmptyStatement (s.loc);
2057                                         continue;
2058                                 }
2059
2060                                 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2061                                         statements [ix] = new EmptyStatement (s.loc);
2062
2063                                 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2064                                 if (unreachable && s is LabeledStatement)
2065                                         throw new InternalErrorException ("should not happen");
2066                         }
2067
2068                         while (ec.CurrentBranching is FlowBranchingLabeled)
2069                                 ec.EndFlowBranching ();
2070
2071                         bool flow_unreachable = ec.EndFlowBranching ();
2072
2073                         ec.CurrentBlock = prev_block;
2074
2075                         if (flow_unreachable)
2076                                 flags |= Flags.HasRet;
2077
2078                         // If we're a non-static `struct' constructor which doesn't have an
2079                         // initializer, then we must initialize all of the struct's fields.
2080                         if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2081                                 ok = false;
2082
2083                         return ok;
2084                 }
2085
2086                 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2087                 {
2088                         unreachable_shown = true;
2089                         unreachable = true;
2090
2091                         if (warn)
2092                                 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2093
2094                         ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2095                         bool ok = Resolve (ec);
2096                         ec.KillFlowBranching ();
2097
2098                         return ok;
2099                 }
2100                 
2101                 protected override void DoEmit (EmitContext ec)
2102                 {
2103                         for (int ix = 0; ix < statements.Count; ix++){
2104                                 statements [ix].Emit (ec);
2105                         }
2106                 }
2107
2108                 public override void Emit (EmitContext ec)
2109                 {
2110                         if (scope_initializers != null)
2111                                 EmitScopeInitializers (ec);
2112
2113                         ec.Mark (StartLocation);
2114                         DoEmit (ec);
2115
2116                         if (SymbolWriter.HasSymbolWriter)
2117                                 EmitSymbolInfo (ec);
2118                 }
2119
2120                 protected void EmitScopeInitializers (EmitContext ec)
2121                 {
2122                         SymbolWriter.OpenCompilerGeneratedBlock (ec);
2123
2124                         using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2125                                 foreach (Statement s in scope_initializers)
2126                                         s.Emit (ec);
2127                         }
2128
2129                         SymbolWriter.CloseCompilerGeneratedBlock (ec);
2130                 }
2131
2132                 protected virtual void EmitSymbolInfo (EmitContext ec)
2133                 {
2134                 }
2135
2136 #if DEBUG
2137                 public override string ToString ()
2138                 {
2139                         return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2140                 }
2141 #endif
2142
2143                 protected override void CloneTo (CloneContext clonectx, Statement t)
2144                 {
2145                         Block target = (Block) t;
2146 #if DEBUG
2147                         target.clone_id = clone_id_counter++;
2148 #endif
2149
2150                         clonectx.AddBlockMap (this, target);
2151                         if (original != this)
2152                                 clonectx.AddBlockMap (original, target);
2153
2154                         target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2155                         target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2156
2157                         if (Parent != null)
2158                                 target.Parent = clonectx.RemapBlockCopy (Parent);
2159
2160                         target.statements = new List<Statement> (statements.Count);
2161                         foreach (Statement s in statements)
2162                                 target.statements.Add (s.Clone (clonectx));
2163                 }
2164         }
2165
2166         public class ExplicitBlock : Block
2167         {
2168                 protected AnonymousMethodStorey am_storey;
2169
2170                 public ExplicitBlock (Block parent, Location start, Location end)
2171                         : this (parent, (Flags) 0, start, end)
2172                 {
2173                 }
2174
2175                 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2176                         : base (parent, flags, start, end)
2177                 {
2178                         this.Explicit = this;
2179                 }
2180
2181                 #region Properties
2182
2183                 public AnonymousMethodStorey AnonymousMethodStorey {
2184                         get {
2185                                 return am_storey;
2186                         }
2187                 }
2188
2189                 public bool HasCapturedThis {
2190                         set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2191                         get {
2192                                 return (flags & Flags.HasCapturedThis) != 0;
2193                         }
2194                 }
2195
2196                 public bool HasCapturedVariable {
2197                         set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2198                         get {
2199                                 return (flags & Flags.HasCapturedVariable) != 0;
2200                         }
2201                 }
2202
2203                 #endregion
2204
2205                 //
2206                 // Creates anonymous method storey in current block
2207                 //
2208                 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2209                 {
2210                         //
2211                         // An iterator has only 1 storey block
2212                         //
2213                         if (ec.CurrentAnonymousMethod.IsIterator)
2214                             return ec.CurrentAnonymousMethod.Storey;
2215
2216                         //
2217                         // When referencing a variable in iterator storey from children anonymous method
2218                         //
2219                         if (ParametersBlock.am_storey is IteratorStorey) {
2220                                 return ParametersBlock.am_storey;
2221                         }
2222
2223                         if (am_storey == null) {
2224                                 MemberBase mc = ec.MemberContext as MemberBase;
2225
2226                                 //
2227                                 // Creates anonymous method storey for this block
2228                                 //
2229                                 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2230                         }
2231
2232                         return am_storey;
2233                 }
2234
2235                 public override void Emit (EmitContext ec)
2236                 {
2237                         if (am_storey != null) {
2238                                 DefineAnonymousStorey (ec);
2239                                 am_storey.EmitStoreyInstantiation (ec, this);
2240                         }
2241
2242                         bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2243                         if (emit_debug_info)
2244                                 ec.BeginScope ();
2245
2246                         base.Emit (ec);
2247
2248                         if (emit_debug_info)
2249                                 ec.EndScope ();
2250                 }
2251
2252                 void DefineAnonymousStorey (EmitContext ec)
2253                 {
2254                         //
2255                         // Creates anonymous method storey
2256                         //
2257                         if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2258                                 //
2259                                 // Creates parent storey reference when hoisted this is accessible
2260                                 //
2261                                 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2262                                         ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2263
2264                                         //
2265                                         // Hoisted this exists in top-level parent storey only
2266                                         //
2267                                         while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2268                                                 parent = parent.Parent.Explicit;
2269
2270                                         am_storey.AddParentStoreyReference (ec, parent.am_storey);
2271                                 }
2272
2273                                 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2274
2275                                 // TODO MemberCache: Review
2276                                 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2277                         }
2278
2279                         am_storey.CreateType ();
2280                         am_storey.DefineType ();
2281                         am_storey.ResolveTypeParameters ();
2282
2283                         var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2284                         if (ref_blocks != null) {
2285                                 foreach (ExplicitBlock ref_block in ref_blocks) {
2286                                         for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2287                                                 if (b.am_storey != null) {
2288                                                         b.am_storey.AddParentStoreyReference (ec, am_storey);
2289
2290                                                         // Stop propagation inside same top block
2291                                                         if (b.ParametersBlock.Original == ParametersBlock.Original)
2292                                                                 break;
2293
2294                                                         b = b.ParametersBlock;
2295                                                 }
2296
2297                                                 b.HasCapturedVariable = true;
2298                                         }
2299                                 }
2300                         }
2301
2302                         am_storey.Define ();
2303                         am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2304                 }
2305
2306                 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2307                 {
2308                         tryBlock.statements = statements;
2309                         statements = new List<Statement> (1);
2310                         statements.Add (tf);
2311                 }
2312         }
2313
2314         //
2315         // ParametersBlock was introduced to support anonymous methods
2316         // and lambda expressions
2317         // 
2318         public class ParametersBlock : ExplicitBlock
2319         {
2320                 public class ParameterInfo : INamedBlockVariable
2321                 {
2322                         readonly ParametersBlock block;
2323                         readonly int index;
2324                         public VariableInfo VariableInfo;
2325                         bool is_locked;
2326
2327                         public ParameterInfo (ParametersBlock block, int index)
2328                         {
2329                                 this.block = block;
2330                                 this.index = index;
2331                         }
2332
2333                         #region Properties
2334
2335                         public Block Block {
2336                                 get {
2337                                         return block;
2338                                 }
2339                         }
2340
2341                         public bool IsDeclared {
2342                                 get {
2343                                         return true;
2344                                 }
2345                         }
2346
2347                         public bool IsLocked {
2348                                 get {
2349                                         return is_locked;
2350                                 }
2351                                 set {
2352                                         is_locked = value;
2353                                 }
2354                         }
2355
2356                         public Location Location {
2357                                 get {
2358                                         return Parameter.Location;
2359                                 }
2360                         }
2361
2362                         public Parameter Parameter {
2363                                 get {
2364                                         return block.Parameters [index];
2365                                 }
2366                         }
2367
2368                         public TypeSpec ParameterType {
2369                                 get {
2370                                         return Parameter.Type;
2371                                 }
2372                         }
2373
2374                         #endregion
2375
2376                         public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2377                         {
2378                                 return new ParameterReference (this, loc);
2379                         }
2380                 }
2381
2382                 // 
2383                 // Block is converted into an expression
2384                 //
2385                 sealed class BlockScopeExpression : Expression
2386                 {
2387                         Expression child;
2388                         readonly ParametersBlock block;
2389
2390                         public BlockScopeExpression (Expression child, ParametersBlock block)
2391                         {
2392                                 this.child = child;
2393                                 this.block = block;
2394                         }
2395
2396                         public override bool ContainsEmitWithAwait ()
2397                         {
2398                                 return child.ContainsEmitWithAwait ();
2399                         }
2400
2401                         public override Expression CreateExpressionTree (ResolveContext ec)
2402                         {
2403                                 throw new NotSupportedException ();
2404                         }
2405
2406                         protected override Expression DoResolve (ResolveContext ec)
2407                         {
2408                                 if (child == null)
2409                                         return null;
2410
2411                                 child = child.Resolve (ec);
2412                                 if (child == null)
2413                                         return null;
2414
2415                                 eclass = child.eclass;
2416                                 type = child.Type;
2417                                 return this;
2418                         }
2419
2420                         public override void Emit (EmitContext ec)
2421                         {
2422                                 block.EmitScopeInitializers (ec);
2423                                 child.Emit (ec);
2424                         }
2425                 }
2426
2427                 protected ParametersCompiled parameters;
2428                 protected ParameterInfo[] parameter_info;
2429                 bool resolved;
2430                 protected bool unreachable;
2431                 protected ToplevelBlock top_block;
2432
2433                 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2434                         : base (parent, 0, start, start)
2435                 {
2436                         if (parameters == null)
2437                                 throw new ArgumentNullException ("parameters");
2438
2439                         this.parameters = parameters;
2440                         ParametersBlock = this;
2441
2442                         this.top_block = parent.ParametersBlock.top_block;
2443                         ProcessParameters ();
2444                 }
2445
2446                 protected ParametersBlock (ParametersCompiled parameters, Location start)
2447                         : base (null, 0, start, start)
2448                 {
2449                         if (parameters == null)
2450                                 throw new ArgumentNullException ("parameters");
2451
2452                         this.parameters = parameters;
2453                         ParametersBlock = this;
2454                 }
2455
2456                 //
2457                 // It's supposed to be used by method body implementation of anonymous methods
2458                 //
2459                 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2460                         : base (null, 0, source.StartLocation, source.EndLocation)
2461                 {
2462                         this.parameters = parameters;
2463                         this.statements = source.statements;
2464                         this.scope_initializers = source.scope_initializers;
2465
2466                         this.resolved = true;
2467                         this.unreachable = source.unreachable;
2468                         this.am_storey = source.am_storey;
2469
2470                         ParametersBlock = this;
2471
2472                         //
2473                         // Overwrite original for comparison purposes when linking cross references
2474                         // between anonymous methods
2475                         //
2476                         original = source;
2477                 }
2478
2479                 #region Properties
2480
2481                 public bool IsAsync
2482                 {
2483                         get {
2484                                 return (flags & Flags.IsAsync) != 0;
2485                         }
2486                         set {
2487                                 flags = value ? flags | Flags.IsAsync : flags & ~Flags.IsAsync;
2488                         }
2489                 }
2490
2491                 //
2492                 // Block has been converted to expression tree
2493                 //
2494                 public bool IsExpressionTree {
2495                         get {
2496                                 return (flags & Flags.IsExpressionTree) != 0;
2497                         }
2498                 }
2499
2500                 //
2501                 // The parameters for the block.
2502                 //
2503                 public ParametersCompiled Parameters {
2504                         get {
2505                                 return parameters;
2506                         }
2507                 }
2508
2509                 public ToplevelBlock TopBlock {
2510                         get {
2511                                 return top_block;
2512                         }
2513                 }
2514
2515                 public bool Resolved {
2516                         get {
2517                                 return resolved;
2518                         }
2519                 }
2520
2521                 public int TemporaryLocalsCount { get; set; }
2522
2523                 #endregion
2524
2525                 // <summary>
2526                 //   Check whether all `out' parameters have been assigned.
2527                 // </summary>
2528                 public void CheckOutParameters (FlowBranching.UsageVector vector)
2529                 {
2530                         if (vector.IsUnreachable)
2531                                 return;
2532
2533                         int n = parameter_info == null ? 0 : parameter_info.Length;
2534
2535                         for (int i = 0; i < n; i++) {
2536                                 VariableInfo var = parameter_info[i].VariableInfo;
2537
2538                                 if (var == null)
2539                                         continue;
2540
2541                                 if (vector.IsAssigned (var, false))
2542                                         continue;
2543
2544                                 var p = parameter_info[i].Parameter;
2545                                 TopBlock.Report.Error (177, p.Location,
2546                                         "The out parameter `{0}' must be assigned to before control leaves the current method",
2547                                         p.Name);
2548                         }
2549                 }
2550
2551                 public override Expression CreateExpressionTree (ResolveContext ec)
2552                 {
2553                         if (statements.Count == 1) {
2554                                 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2555                                 if (scope_initializers != null)
2556                                         expr = new BlockScopeExpression (expr, this);
2557
2558                                 return expr;
2559                         }
2560
2561                         return base.CreateExpressionTree (ec);
2562                 }
2563
2564                 public ParameterInfo GetParameterInfo (Parameter p)
2565                 {
2566                         for (int i = 0; i < parameters.Count; ++i) {
2567                                 if (parameters[i] == p)
2568                                         return parameter_info[i];
2569                         }
2570
2571                         throw new ArgumentException ("Invalid parameter");
2572                 }
2573
2574                 public Expression GetParameterReference (int index, Location loc)
2575                 {
2576                         return new ParameterReference (parameter_info[index], loc);
2577                 }
2578
2579                 public Statement PerformClone ()
2580                 {
2581                         CloneContext clonectx = new CloneContext ();
2582                         return Clone (clonectx);
2583                 }
2584
2585                 protected void ProcessParameters ()
2586                 {
2587                         if (parameters.Count == 0)
2588                                 return;
2589
2590                         parameter_info = new ParameterInfo[parameters.Count];
2591                         for (int i = 0; i < parameter_info.Length; ++i) {
2592                                 var p = parameters.FixedParameters[i];
2593                                 if (p == null)
2594                                         continue;
2595
2596                                 // TODO: Should use Parameter only and more block there
2597                                 parameter_info[i] = new ParameterInfo (this, i);
2598                                 if (p.Name != null)
2599                                         AddLocalName (p.Name, parameter_info[i]);
2600                         }
2601                 }
2602
2603                 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2604                 {
2605                         if (resolved)
2606                                 return true;
2607
2608                         resolved = true;
2609
2610                         if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2611                                 flags |= Flags.IsExpressionTree;
2612
2613                         try {
2614                                 ResolveMeta (rc);
2615
2616                                 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2617                                         FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2618
2619                                         if (!Resolve (rc))
2620                                                 return false;
2621
2622                                         unreachable = top_level.End ();
2623                                 }
2624                         } catch (Exception e) {
2625                                 if (e is CompletionResult || rc.Report.IsDisabled)
2626                                         throw;
2627
2628                                 if (rc.CurrentBlock != null) {
2629                                         rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2630                                 } else {
2631                                         rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2632                                 }
2633
2634                                 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2635                                         throw;
2636                         }
2637
2638                         if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2639                                 if (rc.CurrentAnonymousMethod == null) {
2640                                         // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2641                                         if (md is StateMachineMethod) {
2642                                                 unreachable = true;
2643                                         } else {
2644                                                 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2645                                                 return false;
2646                                         }
2647                                 } else {
2648                                         //
2649                                         // If an asynchronous body of F is either an expression classified as nothing, or a 
2650                                         // statement block where no return statements have expressions, the inferred return type is Task
2651                                         //
2652                                         if (IsAsync) {
2653                                                 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2654                                                 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2655                                                         am.ReturnTypeInference = null;
2656                                                         am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2657                                                         return true;
2658                                                 }
2659                                         }
2660
2661                                         rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2662                                                           rc.CurrentAnonymousMethod.GetSignatureForError ());
2663                                         return false;
2664                                 }
2665                         }
2666
2667                         return true;
2668                 }
2669
2670                 void ResolveMeta (BlockContext ec)
2671                 {
2672                         int orig_count = parameters.Count;
2673
2674                         for (int i = 0; i < orig_count; ++i) {
2675                                 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2676
2677                                 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2678                                         continue;
2679
2680                                 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2681                                 parameter_info[i].VariableInfo = vi;
2682                                 ec.FlowOffset += vi.Length;
2683                         }
2684                 }
2685
2686                 public void WrapIntoIterator (IMethodData method, TypeContainer host, TypeSpec iterator_type, bool is_enumerable)
2687                 {
2688                         ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2689                         pb.EndLocation = EndLocation;
2690                         pb.statements = statements;
2691
2692                         var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2693                         am_storey = new IteratorStorey (iterator);
2694
2695                         statements = new List<Statement> (1);
2696                         AddStatement (new Return (iterator, iterator.Location));
2697                 }
2698
2699                 public void WrapIntoAsyncTask (IMemberContext context, TypeContainer host, TypeSpec returnType)
2700                 {
2701                         ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2702                         pb.EndLocation = EndLocation;
2703                         pb.statements = statements;
2704
2705                         var block_type = host.Module.Compiler.BuiltinTypes.Void;
2706                         var initializer = new AsyncInitializer (pb, host, block_type);
2707                         initializer.Type = block_type;
2708
2709                         am_storey = new AsyncTaskStorey (context, initializer, returnType);
2710
2711                         statements = new List<Statement> (1);
2712                         AddStatement (new StatementExpression (initializer));
2713                 }
2714         }
2715
2716         //
2717         //
2718         //
2719         public class ToplevelBlock : ParametersBlock
2720         {
2721                 LocalVariable this_variable;
2722                 CompilerContext compiler;
2723                 Dictionary<string, object> names;
2724                 Dictionary<string, object> labels;
2725
2726                 public HoistedVariable HoistedThisVariable;
2727
2728                 public Report Report {
2729                         get { return compiler.Report; }
2730                 }
2731
2732                 public ToplevelBlock (CompilerContext ctx, Location loc)
2733                         : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2734                 {
2735                 }
2736
2737                 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2738                         : base (parameters, start)
2739                 {
2740                         this.compiler = ctx;
2741                         top_block = this;
2742
2743                         ProcessParameters ();
2744                 }
2745
2746                 //
2747                 // Recreates a top level block from parameters block. Used for
2748                 // compiler generated methods where the original block comes from
2749                 // explicit child block. This works for already resolved blocks
2750                 // only to ensure we resolve them in the correct flow order
2751                 //
2752                 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2753                         : base (source, parameters)
2754                 {
2755                         this.compiler = source.TopBlock.compiler;
2756                         top_block = this;
2757                 }
2758
2759                 public bool IsIterator
2760                 {
2761                         get {
2762                                 return (flags & Flags.IsIterator) != 0;
2763                         }
2764                         set {
2765                                 flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator;
2766                         }
2767                 }
2768
2769                 public override void AddLocalName (string name, INamedBlockVariable li)
2770                 {
2771                         if (names == null)
2772                                 names = new Dictionary<string, object> ();
2773
2774                         object value;
2775                         if (!names.TryGetValue (name, out value)) {
2776                                 names.Add (name, li);
2777                                 return;
2778                         }
2779
2780                         INamedBlockVariable existing = value as INamedBlockVariable;
2781                         List<INamedBlockVariable> existing_list;
2782                         if (existing != null) {
2783                                 existing_list = new List<INamedBlockVariable> ();
2784                                 existing_list.Add (existing);
2785                                 names[name] = existing_list;
2786                         } else {
2787                                 existing_list = (List<INamedBlockVariable>) value;
2788                         }
2789
2790                         //
2791                         // A collision checking between local names
2792                         //
2793                         for (int i = 0; i < existing_list.Count; ++i) {
2794                                 existing = existing_list[i];
2795                                 Block b = existing.Block;
2796
2797                                 // Collision at same level
2798                                 if (li.Block == b) {
2799                                         li.Block.Error_AlreadyDeclared (name, li);
2800                                         break;
2801                                 }
2802
2803                                 // Collision with parent
2804                                 b = li.Block;
2805                                 while ((b = b.Parent) != null) {
2806                                         if (existing.Block == b) {
2807                                                 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
2808                                                 i = existing_list.Count;
2809                                                 break;
2810                                         }
2811                                 }
2812
2813                                 // Collision with with children
2814                                 b = existing.Block;
2815                                 while ((b = b.Parent) != null) {
2816                                         if (li.Block == b) {
2817                                                 li.Block.Error_AlreadyDeclared (name, li, "child");
2818                                                 i = existing_list.Count;
2819                                                 break;
2820                                         }
2821                                 }
2822                         }
2823
2824                         existing_list.Add (li);
2825                 }
2826
2827                 public void AddLabel (string name, LabeledStatement label)
2828                 {
2829                         if (labels == null)
2830                                 labels = new Dictionary<string, object> ();
2831
2832                         object value;
2833                         if (!labels.TryGetValue (name, out value)) {
2834                                 labels.Add (name, label);
2835                                 return;
2836                         }
2837
2838                         LabeledStatement existing = value as LabeledStatement;
2839                         List<LabeledStatement> existing_list;
2840                         if (existing != null) {
2841                                 existing_list = new List<LabeledStatement> ();
2842                                 existing_list.Add (existing);
2843                                 labels[name] = existing_list;
2844                         } else {
2845                                 existing_list = (List<LabeledStatement>) value;
2846                         }
2847
2848                         //
2849                         // A collision checking between labels
2850                         //
2851                         for (int i = 0; i < existing_list.Count; ++i) {
2852                                 existing = existing_list[i];
2853                                 Block b = existing.Block;
2854
2855                                 // Collision at same level
2856                                 if (label.Block == b) {
2857                                         Report.SymbolRelatedToPreviousError (existing.loc, name);
2858                                         Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
2859                                         break;
2860                                 }
2861
2862                                 // Collision with parent
2863                                 b = label.Block;
2864                                 while ((b = b.Parent) != null) {
2865                                         if (existing.Block == b) {
2866                                                 Report.Error (158, label.loc,
2867                                                         "The label `{0}' shadows another label by the same name in a contained scope", name);
2868                                                 i = existing_list.Count;
2869                                                 break;
2870                                         }
2871                                 }
2872
2873                                 // Collision with with children
2874                                 b = existing.Block;
2875                                 while ((b = b.Parent) != null) {
2876                                         if (label.Block == b) {
2877                                                 Report.Error (158, label.loc,
2878                                                         "The label `{0}' shadows another label by the same name in a contained scope", name);
2879                                                 i = existing_list.Count;
2880                                                 break;
2881                                         }
2882                                 }
2883                         }
2884
2885                         existing_list.Add (label);
2886                 }
2887
2888                 //
2889                 // Creates an arguments set from all parameters, useful for method proxy calls
2890                 //
2891                 public Arguments GetAllParametersArguments ()
2892                 {
2893                         int count = parameters.Count;
2894                         Arguments args = new Arguments (count);
2895                         for (int i = 0; i < count; ++i) {
2896                                 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
2897                                 args.Add (new Argument (arg_expr));
2898                         }
2899
2900                         return args;
2901                 }
2902
2903                 //
2904                 // Lookup inside a block, the returned value can represent 3 states
2905                 //
2906                 // true+variable: A local name was found and it's valid
2907                 // false+variable: A local name was found in a child block only
2908                 // false+null: No local name was found
2909                 //
2910                 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
2911                 {
2912                         if (names == null)
2913                                 return false;
2914
2915                         object value;
2916                         if (!names.TryGetValue (name, out value))
2917                                 return false;
2918
2919                         variable = value as INamedBlockVariable;
2920                         Block b = block;
2921                         if (variable != null) {
2922                                 do {
2923                                         if (variable.Block == b.Original)
2924                                                 return true;
2925
2926                                         b = b.Parent;
2927                                 } while (b != null);
2928
2929                                 b = variable.Block;
2930                                 do {
2931                                         if (block == b)
2932                                                 return false;
2933
2934                                         b = b.Parent;
2935                                 } while (b != null);
2936                         } else {
2937                                 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
2938                                 for (int i = 0; i < list.Count; ++i) {
2939                                         variable = list[i];
2940                                         do {
2941                                                 if (variable.Block == b.Original)
2942                                                         return true;
2943
2944                                                 b = b.Parent;
2945                                         } while (b != null);
2946
2947                                         b = variable.Block;
2948                                         do {
2949                                                 if (block == b)
2950                                                         return false;
2951
2952                                                 b = b.Parent;
2953                                         } while (b != null);
2954
2955                                         b = block;
2956                                 }
2957                         }
2958
2959                         variable = null;
2960                         return false;
2961                 }
2962
2963                 public LabeledStatement GetLabel (string name, Block block)
2964                 {
2965                         if (labels == null)
2966                                 return null;
2967
2968                         object value;
2969                         if (!labels.TryGetValue (name, out value)) {
2970                                 return null;
2971                         }
2972
2973                         var label = value as LabeledStatement;
2974                         Block b = block;
2975                         if (label != null) {
2976                                 if (label.Block == b.Original)
2977                                         return label;
2978
2979                                 // TODO: Temporary workaround for the switch block implicit label block
2980                                 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2981                                         return label;
2982                         } else {
2983                                 List<LabeledStatement> list = (List<LabeledStatement>) value;
2984                                 for (int i = 0; i < list.Count; ++i) {
2985                                         label = list[i];
2986                                         if (label.Block == b.Original)
2987                                                 return label;
2988
2989                                         // TODO: Temporary workaround for the switch block implicit label block
2990                                         if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
2991                                                 return label;
2992                                 }
2993                         }
2994                                 
2995                         return null;
2996                 }
2997
2998                 // <summary>
2999                 //   Returns the "this" instance variable of this block.
3000                 //   See AddThisVariable() for more information.
3001                 // </summary>
3002                 public LocalVariable ThisVariable {
3003                         get { return this_variable; }
3004                 }
3005
3006                 // <summary>
3007                 //   This is used by non-static `struct' constructors which do not have an
3008                 //   initializer - in this case, the constructor must initialize all of the
3009                 //   struct's fields.  To do this, we add a "this" variable and use the flow
3010                 //   analysis code to ensure that it's been fully initialized before control
3011                 //   leaves the constructor.
3012                 // </summary>
3013                 public void AddThisVariable (BlockContext bc)
3014                 {
3015                         if (this_variable != null)
3016                                 throw new InternalErrorException (StartLocation.ToString ());
3017
3018                         this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3019                         this_variable.Type = bc.CurrentType;
3020                         this_variable.PrepareForFlowAnalysis (bc);
3021                 }
3022
3023                 public bool IsThisAssigned (BlockContext ec)
3024                 {
3025                         return this_variable == null || this_variable.IsThisAssigned (ec, this);
3026                 }
3027
3028                 public override void Emit (EmitContext ec)
3029                 {
3030                         if (Report.Errors > 0)
3031                                 return;
3032
3033 #if PRODUCTION
3034                         try {
3035 #endif
3036
3037                         base.Emit (ec);
3038
3039                         ec.Mark (EndLocation);
3040
3041                         if (ec.HasReturnLabel)
3042                                 ec.MarkLabel (ec.ReturnLabel);
3043
3044                         if (ec.return_value != null) {
3045                                 ec.Emit (OpCodes.Ldloc, ec.return_value);
3046                                 ec.Emit (OpCodes.Ret);
3047                         } else {
3048                                 //
3049                                 // If `HasReturnLabel' is set, then we already emitted a
3050                                 // jump to the end of the method, so we must emit a `ret'
3051                                 // there.
3052                                 //
3053                                 // Unfortunately, System.Reflection.Emit automatically emits
3054                                 // a leave to the end of a finally block.  This is a problem
3055                                 // if no code is following the try/finally block since we may
3056                                 // jump to a point after the end of the method.
3057                                 // As a workaround, we're always creating a return label in
3058                                 // this case.
3059                                 //
3060
3061                                 if (ec.HasReturnLabel || !unreachable) {
3062                                         if (ec.ReturnType.Kind != MemberKind.Void)
3063                                                 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3064                                         ec.Emit (OpCodes.Ret);
3065                                 }
3066                         }
3067
3068 #if PRODUCTION
3069                         } catch (Exception e){
3070                                 Console.WriteLine ("Exception caught by the compiler while emitting:");
3071                                 Console.WriteLine ("   Block that caused the problem begin at: " + block.loc);
3072                                         
3073                                 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3074                                 throw;
3075                         }
3076 #endif
3077                 }
3078
3079                 protected override void EmitSymbolInfo (EmitContext ec)
3080                 {
3081                         AnonymousExpression ae = ec.CurrentAnonymousMethod;
3082                         if ((ae != null) && (ae.Storey != null))
3083                                 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3084
3085                         base.EmitSymbolInfo (ec);
3086                 }
3087         }
3088         
3089         public class SwitchLabel {
3090                 Expression label;
3091                 Constant converted;
3092                 readonly Location loc;
3093
3094                 Label? il_label;
3095
3096                 //
3097                 // if expr == null, then it is the default case.
3098                 //
3099                 public SwitchLabel (Expression expr, Location l)
3100                 {
3101                         label = expr;
3102                         loc = l;
3103                 }
3104
3105                 public bool IsDefault {
3106                         get {
3107                                 return label == null;
3108                         }
3109                 }
3110
3111                 public Expression Label {
3112                         get {
3113                                 return label;
3114                         }
3115                 }
3116
3117                 public Location Location {
3118                         get {
3119                                 return loc;
3120                         }
3121                 }
3122
3123                 public Constant Converted {
3124                         get {
3125                                 return converted;
3126                         }
3127                         set {
3128                                 converted = value;
3129                         }
3130                 }
3131
3132                 public Label GetILLabel (EmitContext ec)
3133                 {
3134                         if (il_label == null){
3135                                 il_label = ec.DefineLabel ();
3136                         }
3137
3138                         return il_label.Value;
3139                 }
3140
3141                 //
3142                 // Resolves the expression, reduces it to a literal if possible
3143                 // and then converts it to the requested type.
3144                 //
3145                 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3146                 {       
3147                         Expression e = label.Resolve (ec);
3148
3149                         if (e == null)
3150                                 return false;
3151
3152                         Constant c = e as Constant;
3153                         if (c == null){
3154                                 ec.Report.Error (150, loc, "A constant value is expected");
3155                                 return false;
3156                         }
3157
3158                         if (allow_nullable && c is NullLiteral) {
3159                                 converted = c;
3160                                 return true;
3161                         }
3162
3163                         converted = c.ImplicitConversionRequired (ec, required_type, loc);
3164                         return converted != null;
3165                 }
3166
3167                 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3168                 {
3169                         string label;
3170                         if (converted == null)
3171                                 label = "default";
3172                         else
3173                                 label = converted.GetValueAsLiteral ();
3174                         
3175                         ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3176                         ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3177                 }
3178
3179                 public SwitchLabel Clone (CloneContext clonectx)
3180                 {
3181                         if (label == null)
3182                                 return this;
3183
3184                         return new SwitchLabel (label.Clone (clonectx), loc);
3185                 }
3186         }
3187
3188         public class SwitchSection {
3189                 public readonly List<SwitchLabel> Labels;
3190                 public readonly Block Block;
3191                 
3192                 public SwitchSection (List<SwitchLabel> labels, Block block)
3193                 {
3194                         Labels = labels;
3195                         Block = block;
3196                 }
3197
3198                 public SwitchSection Clone (CloneContext clonectx)
3199                 {
3200                         var cloned_labels = new List<SwitchLabel> ();
3201
3202                         foreach (SwitchLabel sl in Labels)
3203                                 cloned_labels.Add (sl.Clone (clonectx));
3204                         
3205                         return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3206                 }
3207         }
3208         
3209         public class Switch : Statement
3210         {
3211                 // structure used to hold blocks of keys while calculating table switch
3212                 sealed class LabelsRange : IComparable<LabelsRange>
3213                 {
3214                         public readonly long min;
3215                         public long max;
3216                         public readonly List<long> label_values;
3217
3218                         public LabelsRange (long value)
3219                         {
3220                                 min = max = value;
3221                                 label_values = new List<long> ();
3222                                 label_values.Add (value);
3223                         }
3224
3225                         public LabelsRange (long min, long max, ICollection<long> values)
3226                         {
3227                                 this.min = min;
3228                                 this.max = max;
3229                                 this.label_values = new List<long> (values);
3230                         }
3231
3232                         public long Range {
3233                                 get {
3234                                         return max - min + 1;
3235                                 }
3236                         }
3237
3238                         public bool AddValue (long value)
3239                         {
3240                                 var gap = value - min + 1;
3241                                 // Ensure the range has > 50% occupancy
3242                                 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3243                                         return false;
3244
3245                                 max = value;
3246                                 label_values.Add (value);
3247                                 return true;
3248                         }
3249
3250                         public int CompareTo (LabelsRange other)
3251                         {
3252                                 int nLength = label_values.Count;
3253                                 int nLengthOther = other.label_values.Count;
3254                                 if (nLengthOther == nLength)
3255                                         return (int) (other.min - min);
3256
3257                                 return nLength - nLengthOther;
3258                         }
3259                 }
3260
3261                 public List<SwitchSection> Sections;
3262                 public Expression Expr;
3263
3264                 //
3265                 // Mapping of all labels to their SwitchLabels
3266                 //
3267                 Dictionary<long, SwitchLabel> labels;
3268                 Dictionary<string, SwitchLabel> string_labels;
3269
3270                 /// <summary>
3271                 ///   The governing switch type
3272                 /// </summary>
3273                 public TypeSpec SwitchType;
3274
3275                 //
3276                 // Computed
3277                 //
3278                 Label default_target;
3279                 Label null_target;
3280                 Expression new_expr;
3281                 bool is_constant;
3282
3283                 SwitchSection constant_section;
3284                 SwitchSection default_section;
3285                 SwitchLabel null_section;
3286
3287                 ExpressionStatement string_dictionary;
3288                 FieldExpr switch_cache_field;
3289                 static int unique_counter;
3290                 ExplicitBlock block;
3291
3292                 //
3293                 // Nullable Types support
3294                 //
3295                 Nullable.Unwrap unwrap;
3296
3297                 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3298                 {
3299                         Expr = e;
3300                         this.block = block;
3301                         Sections = sects;
3302                         loc = l;
3303                 }
3304
3305                 public ExplicitBlock Block {
3306                         get {
3307                                 return block;
3308                         }
3309                 }
3310
3311                 public Label DefaultLabel {
3312                         get {
3313                                 return default_target;
3314                         }
3315                 }
3316
3317                 public bool GotDefault {
3318                         get {
3319                                 return default_section != null;
3320                         }
3321                 }
3322
3323                 public bool IsNullable {
3324                         get {
3325                                 return unwrap != null;
3326                         }
3327                 }
3328
3329                 //
3330                 // Determines the governing type for a switch.  The returned
3331                 // expression might be the expression from the switch, or an
3332                 // expression that includes any potential conversions to
3333                 //
3334                 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3335                 {
3336                         switch (expr.Type.BuiltinType) {
3337                         case BuiltinTypeSpec.Type.Byte:
3338                         case BuiltinTypeSpec.Type.SByte:
3339                         case BuiltinTypeSpec.Type.UShort:
3340                         case BuiltinTypeSpec.Type.Short:
3341                         case BuiltinTypeSpec.Type.UInt:
3342                         case BuiltinTypeSpec.Type.Int:
3343                         case BuiltinTypeSpec.Type.ULong:
3344                         case BuiltinTypeSpec.Type.Long:
3345                         case BuiltinTypeSpec.Type.Char:
3346                         case BuiltinTypeSpec.Type.String:
3347                         case BuiltinTypeSpec.Type.Bool:
3348                                 return expr;
3349                         }
3350
3351                         if (expr.Type.IsEnum)
3352                                 return expr;
3353
3354                         //
3355                         // Try to find a *user* defined implicit conversion.
3356                         //
3357                         // If there is no implicit conversion, or if there are multiple
3358                         // conversions, we have to report an error
3359                         //
3360                         Expression converted = null;
3361                         foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3362                                 Expression e;
3363                                 
3364                                 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3365                                 if (e == null)
3366                                         continue;
3367
3368                                 //
3369                                 // Ignore over-worked ImplicitUserConversions that do
3370                                 // an implicit conversion in addition to the user conversion.
3371                                 // 
3372                                 if (!(e is UserCast))
3373                                         continue;
3374
3375                                 if (converted != null){
3376                                         ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3377                                         return null;
3378                                 }
3379
3380                                 converted = e;
3381                         }
3382                         return converted;
3383                 }
3384
3385                 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3386                 {
3387                         // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3388                         return new[] {
3389                                 types.SByte,
3390                                 types.Byte,
3391                                 types.Short,
3392                                 types.UShort,
3393                                 types.Int,
3394                                 types.UInt,
3395                                 types.Long,
3396                                 types.ULong,
3397                                 types.Char,
3398                                 types.String
3399                         };
3400                 }
3401
3402                 //
3403                 // Performs the basic sanity checks on the switch statement
3404                 // (looks for duplicate keys and non-constant expressions).
3405                 //
3406                 // It also returns a hashtable with the keys that we will later
3407                 // use to compute the switch tables
3408                 //
3409                 bool CheckSwitch (ResolveContext ec)
3410                 {
3411                         bool error = false;
3412                         if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3413                                 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3414                         else
3415                                 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3416                                 
3417                         foreach (SwitchSection ss in Sections){
3418                                 foreach (SwitchLabel sl in ss.Labels){
3419                                         if (sl.IsDefault){
3420                                                 if (default_section != null){
3421                                                         sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3422                                                         error = true;
3423                                                 }
3424                                                 default_section = ss;
3425                                                 continue;
3426                                         }
3427
3428                                         if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3429                                                 error = true;
3430                                                 continue;
3431                                         }
3432                                         
3433                                         try {
3434                                                 if (string_labels != null) {
3435                                                         string s = sl.Converted.GetValue () as string;
3436                                                         if (s == null)
3437                                                                 null_section = sl;
3438                                                         else
3439                                                                 string_labels.Add (s, sl);
3440                                                 } else {
3441                                                         if (sl.Converted is NullLiteral) {
3442                                                                 null_section = sl;
3443                                                         } else {
3444                                                                 labels.Add (sl.Converted.GetValueAsLong (), sl);
3445                                                         }
3446                                                 }
3447                                         } catch (ArgumentException) {
3448                                                 if (string_labels != null)
3449                                                         sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3450                                                 else
3451                                                         sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3452
3453                                                 error = true;
3454                                         }
3455                                 }
3456                         }
3457                         return !error;
3458                 }
3459                 
3460                 //
3461                 // This method emits code for a lookup-based switch statement (non-string)
3462                 // Basically it groups the cases into blocks that are at least half full,
3463                 // and then spits out individual lookup opcodes for each block.
3464                 // It emits the longest blocks first, and short blocks are just
3465                 // handled with direct compares.
3466                 //
3467                 void EmitTableSwitch (EmitContext ec, Expression val)
3468                 {
3469                         Label lbl_default = default_target;
3470
3471                         if (labels.Count > 0) {
3472                                 List<LabelsRange> ranges;
3473                                 if (string_labels != null) {
3474                                         // We have done all hard work for string already
3475                                         // setup single range only
3476                                         ranges = new List<LabelsRange> (1);
3477                                         ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3478                                 } else {
3479                                         var element_keys = new long[labels.Count];
3480                                         labels.Keys.CopyTo (element_keys, 0);
3481                                         Array.Sort (element_keys);
3482
3483                                         //
3484                                         // Build possible ranges of switch labes to reduce number
3485                                         // of comparisons
3486                                         //
3487                                         ranges = new List<LabelsRange> (element_keys.Length);
3488                                         var range = new LabelsRange (element_keys[0]);
3489                                         ranges.Add (range);
3490                                         for (int i = 1; i < element_keys.Length; ++i) {
3491                                                 var l = element_keys[i];
3492                                                 if (range.AddValue (l))
3493                                                         continue;
3494
3495                                                 range = new LabelsRange (l);
3496                                                 ranges.Add (range);
3497                                         }
3498
3499                                         // sort the blocks so we can tackle the largest ones first
3500                                         ranges.Sort ();
3501                                 }
3502
3503                                 TypeSpec compare_type = TypeManager.IsEnumType (SwitchType) ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3504
3505                                 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3506                                         LabelsRange kb = ranges[range_index];
3507                                         lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3508
3509                                         // Optimize small ranges using simple equality check
3510                                         if (kb.Range <= 2) {
3511                                                 foreach (var key in kb.label_values) {
3512                                                         SwitchLabel sl = labels[key];
3513                                                         if (sl.Converted.IsDefaultValue) {
3514                                                                 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3515                                                         } else {
3516                                                                 val.Emit (ec);
3517                                                                 sl.Converted.Emit (ec);
3518                                                                 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3519                                                         }
3520                                                 }
3521                                         } else {
3522                                                 // TODO: if all the keys in the block are the same and there are
3523                                                 //       no gaps/defaults then just use a range-check.
3524                                                 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3525                                                         // TODO: optimize constant/I4 cases
3526
3527                                                         // check block range (could be > 2^31)
3528                                                         val.Emit (ec);
3529                                                         ec.EmitLong (kb.min);
3530                                                         ec.Emit (OpCodes.Blt, lbl_default);
3531
3532                                                         val.Emit (ec);
3533                                                         ec.EmitLong (kb.max);
3534                                                         ec.Emit (OpCodes.Bgt, lbl_default);
3535
3536                                                         // normalize range
3537                                                         val.Emit (ec);
3538                                                         if (kb.min != 0) {
3539                                                                 ec.EmitLong (kb.min);
3540                                                                 ec.Emit (OpCodes.Sub);
3541                                                         }
3542
3543                                                         ec.Emit (OpCodes.Conv_I4);      // assumes < 2^31 labels!
3544                                                 } else {
3545                                                         // normalize range
3546                                                         val.Emit (ec);
3547                                                         int first = (int) kb.min;
3548                                                         if (first > 0) {
3549                                                                 ec.EmitInt (first);
3550                                                                 ec.Emit (OpCodes.Sub);
3551                                                         } else if (first < 0) {
3552                                                                 ec.EmitInt (-first);
3553                                                                 ec.Emit (OpCodes.Add);
3554                                                         }
3555                                                 }
3556
3557                                                 // first, build the list of labels for the switch
3558                                                 int iKey = 0;
3559                                                 long cJumps = kb.Range;
3560                                                 Label[] switch_labels = new Label[cJumps];
3561                                                 for (int iJump = 0; iJump < cJumps; iJump++) {
3562                                                         var key = kb.label_values[iKey];
3563                                                         if (key == kb.min + iJump) {
3564                                                                 switch_labels[iJump] = labels[key].GetILLabel (ec);
3565                                                                 iKey++;
3566                                                         } else {
3567                                                                 switch_labels[iJump] = lbl_default;
3568                                                         }
3569                                                 }
3570
3571                                                 // emit the switch opcode
3572                                                 ec.Emit (OpCodes.Switch, switch_labels);
3573                                         }
3574
3575                                         // mark the default for this block
3576                                         if (range_index != 0)
3577                                                 ec.MarkLabel (lbl_default);
3578                                 }
3579
3580                                 // the last default just goes to the end
3581                                 if (ranges.Count > 0)
3582                                         ec.Emit (OpCodes.Br, lbl_default);
3583                         }
3584
3585                         // now emit the code for the sections
3586                         bool found_default = false;
3587
3588                         foreach (SwitchSection ss in Sections) {
3589                                 foreach (SwitchLabel sl in ss.Labels) {
3590                                         if (sl.IsDefault) {
3591                                                 ec.MarkLabel (lbl_default);
3592                                                 found_default = true;
3593                                                 if (null_section == null)
3594                                                         ec.MarkLabel (null_target);
3595                                         } else if (sl.Converted.IsNull) {
3596                                                 ec.MarkLabel (null_target);
3597                                         }
3598
3599                                         ec.MarkLabel (sl.GetILLabel (ec));
3600                                 }
3601
3602                                 ss.Block.Emit (ec);
3603                         }
3604                         
3605                         if (!found_default) {
3606                                 ec.MarkLabel (lbl_default);
3607                                 if (null_section == null) {
3608                                         ec.MarkLabel (null_target);
3609                                 }
3610                         }
3611                 }
3612
3613                 SwitchLabel FindLabel (Constant value)
3614                 {
3615                         SwitchLabel sl = null;
3616
3617                         if (string_labels != null) {
3618                                 string s = value.GetValue () as string;
3619                                 if (s == null) {
3620                                         if (null_section != null)
3621                                                 sl = null_section;
3622                                         else if (default_section != null)
3623                                                 sl = default_section.Labels[0];
3624                                 } else {
3625                                         string_labels.TryGetValue (s, out sl);
3626                                 }
3627                         } else {
3628                                 if (value is NullLiteral) {
3629                                         sl = null_section;
3630                                 } else {
3631                                         labels.TryGetValue (value.GetValueAsLong (), out sl);
3632                                 }
3633                         }
3634
3635                         return sl;
3636                 }
3637
3638                 SwitchSection FindSection (SwitchLabel label)
3639                 {
3640                         foreach (SwitchSection ss in Sections){
3641                                 foreach (SwitchLabel sl in ss.Labels){
3642                                         if (label == sl)
3643                                                 return ss;
3644                                 }
3645                         }
3646
3647                         return null;
3648                 }
3649
3650                 public static void Reset ()
3651                 {
3652                         unique_counter = 0;
3653                 }
3654
3655                 public override bool Resolve (BlockContext ec)
3656                 {
3657                         Expr = Expr.Resolve (ec);
3658                         if (Expr == null)
3659                                 return false;
3660
3661                         new_expr = SwitchGoverningType (ec, Expr);
3662
3663                         if (new_expr == null && Expr.Type.IsNullableType) {
3664                                 unwrap = Nullable.Unwrap.Create (Expr, false);
3665                                 if (unwrap == null)
3666                                         return false;
3667
3668                                 new_expr = SwitchGoverningType (ec, unwrap);
3669                         }
3670
3671                         if (new_expr == null){
3672                                 ec.Report.Error (151, loc,
3673                                         "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3674                                         TypeManager.CSharpName (Expr.Type));
3675                                 return false;
3676                         }
3677
3678                         // Validate switch.
3679                         SwitchType = new_expr.Type;
3680
3681                         if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3682                                 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3683                                 return false;
3684                         }
3685
3686                         if (!CheckSwitch (ec))
3687                                 return false;
3688
3689                         Switch old_switch = ec.Switch;
3690                         ec.Switch = this;
3691                         ec.Switch.SwitchType = SwitchType;
3692
3693                         ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3694
3695                         var constant = new_expr as Constant;
3696                         if (constant != null) {
3697                                 is_constant = true;
3698                                 SwitchLabel label = FindLabel (constant);
3699                                 if (label != null)
3700                                         constant_section = FindSection (label);
3701
3702                                 if (constant_section == null)
3703                                         constant_section = default_section;
3704                         }
3705
3706                         bool first = true;
3707                         bool ok = true;
3708                         foreach (SwitchSection ss in Sections){
3709                                 if (!first)
3710                                         ec.CurrentBranching.CreateSibling (
3711                                                 null, FlowBranching.SiblingType.SwitchSection);
3712                                 else
3713                                         first = false;
3714
3715                                 if (is_constant && (ss != constant_section)) {
3716                                         // If we're a constant switch, we're only emitting
3717                                         // one single section - mark all the others as
3718                                         // unreachable.
3719                                         ec.CurrentBranching.CurrentUsageVector.Goto ();
3720                                         if (!ss.Block.ResolveUnreachable (ec, true)) {
3721                                                 ok = false;
3722                                         }
3723                                 } else {
3724                                         if (!ss.Block.Resolve (ec))
3725                                                 ok = false;
3726                                 }
3727                         }
3728
3729                         if (default_section == null)
3730                                 ec.CurrentBranching.CreateSibling (
3731                                         null, FlowBranching.SiblingType.SwitchSection);
3732
3733                         ec.EndFlowBranching ();
3734                         ec.Switch = old_switch;
3735
3736                         if (!ok)
3737                                 return false;
3738
3739                         if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && !is_constant) {
3740                                 // TODO: Optimize single case, and single+default case
3741                                 ResolveStringSwitchMap (ec);
3742                         }
3743
3744                         return true;
3745                 }
3746
3747                 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
3748                 {
3749                         var sl = FindLabel (value);
3750
3751                         if (sl == null) {
3752                                 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
3753                         }
3754
3755                         return sl;
3756                 }
3757
3758                 void ResolveStringSwitchMap (ResolveContext ec)
3759                 {
3760                         FullNamedExpression string_dictionary_type;
3761                         if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
3762                                 string_dictionary_type = new TypeExpression (
3763                                         ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
3764                                                 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
3765                                         loc);
3766                         } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
3767                                 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
3768                         } else {
3769                                 ec.Module.PredefinedTypes.Dictionary.Resolve ();
3770                                 return;
3771                         }
3772
3773                         var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
3774                         Field field = new Field (ctype, string_dictionary_type,
3775                                 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3776                                 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3777                         if (!field.Define ())
3778                                 return;
3779                         ctype.AddField (field);
3780
3781                         var init = new List<Expression> ();
3782                         int counter = 0;
3783                         labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
3784                         string value = null;
3785                         foreach (SwitchSection section in Sections) {
3786                                 bool contains_label = false;
3787                                 foreach (SwitchLabel sl in section.Labels) {
3788                                         if (sl.IsDefault || sl.Converted.IsNull)
3789                                                 continue;
3790
3791                                         if (!contains_label) {
3792                                                 labels.Add (counter, sl);
3793                                                 contains_label = true;
3794                                         }
3795
3796                                         value = (string) sl.Converted.GetValue ();
3797                                         var init_args = new List<Expression> (2);
3798                                         init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
3799
3800                                         sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
3801                                         init_args.Add (sl.Converted);
3802
3803                                         init.Add (new CollectionElementInitializer (init_args, loc));
3804                                 }
3805
3806                                 //
3807                                 // Don't add empty sections
3808                                 //
3809                                 if (contains_label)
3810                                         ++counter;
3811                         }
3812
3813                         Arguments args = new Arguments (1);
3814                         args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
3815                         Expression initializer = new NewInitialize (string_dictionary_type, args,
3816                                 new CollectionOrObjectInitializers (init, loc), loc);
3817
3818                         switch_cache_field = new FieldExpr (field, loc);
3819                         string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3820                 }
3821
3822                 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3823                 {
3824                         Label l_initialized = ec.DefineLabel ();
3825
3826                         //
3827                         // Skip initialization when value is null
3828                         //
3829                         value.EmitBranchable (ec, null_target, false);
3830
3831                         //
3832                         // Check if string dictionary is initialized and initialize
3833                         //
3834                         switch_cache_field.EmitBranchable (ec, l_initialized, true);
3835                         string_dictionary.EmitStatement (ec);
3836                         ec.MarkLabel (l_initialized);
3837
3838                         LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
3839
3840                         ResolveContext rc = new ResolveContext (ec.MemberContext);
3841
3842                         if (switch_cache_field.Type.IsGeneric) {
3843                                 Arguments get_value_args = new Arguments (2);
3844                                 get_value_args.Add (new Argument (value));
3845                                 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3846                                 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3847                                 if (get_item == null)
3848                                         return;
3849
3850                                 //
3851                                 // A value was not found, go to default case
3852                                 //
3853                                 get_item.EmitBranchable (ec, default_target, false);
3854                         } else {
3855                                 Arguments get_value_args = new Arguments (1);
3856                                 get_value_args.Add (new Argument (value));
3857
3858                                 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
3859                                 if (get_item == null)
3860                                         return;
3861
3862                                 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
3863                                 get_item_object.EmitAssign (ec, get_item, true, false);
3864                                 ec.Emit (OpCodes.Brfalse, default_target);
3865
3866                                 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3867                                         new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
3868
3869                                 get_item_int.EmitStatement (ec);
3870                                 get_item_object.Release (ec);
3871                         }
3872
3873                         EmitTableSwitch (ec, string_switch_variable);
3874                         string_switch_variable.Release (ec);
3875                 }
3876                 
3877                 protected override void DoEmit (EmitContext ec)
3878                 {
3879                         //
3880                         // Needed to emit anonymous storey initialization
3881                         // Otherwise it does not contain any statements for now
3882                         //
3883                         block.Emit (ec);
3884
3885                         default_target = ec.DefineLabel ();
3886                         null_target = ec.DefineLabel ();
3887
3888                         // Store variable for comparission purposes
3889                         // TODO: Don't duplicate non-captured VariableReference
3890                         LocalTemporary value;
3891                         if (IsNullable) {
3892                                 value = new LocalTemporary (SwitchType);
3893                                 unwrap.EmitCheck (ec);
3894                                 ec.Emit (OpCodes.Brfalse, null_target);
3895                                 new_expr.Emit (ec);
3896                                 value.Store (ec);
3897                         } else if (!is_constant) {
3898                                 value = new LocalTemporary (SwitchType);
3899                                 new_expr.Emit (ec);
3900                                 value.Store (ec);
3901                         } else
3902                                 value = null;
3903
3904                         //
3905                         // Setup the codegen context
3906                         //
3907                         Label old_end = ec.LoopEnd;
3908                         Switch old_switch = ec.Switch;
3909                         
3910                         ec.LoopEnd = ec.DefineLabel ();
3911                         ec.Switch = this;
3912
3913                         // Emit Code.
3914                         if (is_constant) {
3915                                 if (constant_section != null)
3916                                         constant_section.Block.Emit (ec);
3917                         } else if (string_dictionary != null) {
3918                                 DoEmitStringSwitch (value, ec);
3919                         } else {
3920                                 EmitTableSwitch (ec, value);
3921                         }
3922
3923                         if (value != null)
3924                                 value.Release (ec);
3925
3926                         // Restore context state. 
3927                         ec.MarkLabel (ec.LoopEnd);
3928
3929                         //
3930                         // Restore the previous context
3931                         //
3932                         ec.LoopEnd = old_end;
3933                         ec.Switch = old_switch;
3934                 }
3935
3936                 protected override void CloneTo (CloneContext clonectx, Statement t)
3937                 {
3938                         Switch target = (Switch) t;
3939
3940                         target.Expr = Expr.Clone (clonectx);
3941                         target.Sections = new List<SwitchSection> ();
3942                         foreach (SwitchSection ss in Sections){
3943                                 target.Sections.Add (ss.Clone (clonectx));
3944                         }
3945                 }
3946         }
3947
3948         // A place where execution can restart in an iterator
3949         public abstract class ResumableStatement : Statement
3950         {
3951                 bool prepared;
3952                 protected Label resume_point;
3953
3954                 public Label PrepareForEmit (EmitContext ec)
3955                 {
3956                         if (!prepared) {
3957                                 prepared = true;
3958                                 resume_point = ec.DefineLabel ();
3959                         }
3960                         return resume_point;
3961                 }
3962
3963                 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3964                 {
3965                         return end;
3966                 }
3967
3968                 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
3969                 {
3970                 }
3971         }
3972
3973         public abstract class TryFinallyBlock : ExceptionStatement
3974         {
3975                 protected Statement stmt;
3976                 Label dispose_try_block;
3977                 bool prepared_for_dispose, emitted_dispose;
3978
3979                 protected TryFinallyBlock (Statement stmt, Location loc)
3980                         : base (loc)
3981                 {
3982                         this.stmt = stmt;
3983                 }
3984
3985                 #region Properties
3986
3987                 public Statement Statement {
3988                         get {
3989                                 return stmt;
3990                         }
3991                 }
3992
3993                 #endregion
3994
3995                 protected abstract void EmitTryBody (EmitContext ec);
3996                 protected abstract void EmitFinallyBody (EmitContext ec);
3997
3998                 public override Label PrepareForDispose (EmitContext ec, Label end)
3999                 {
4000                         if (!prepared_for_dispose) {
4001                                 prepared_for_dispose = true;
4002                                 dispose_try_block = ec.DefineLabel ();
4003                         }
4004                         return dispose_try_block;
4005                 }
4006
4007                 protected sealed override void DoEmit (EmitContext ec)
4008                 {
4009                         EmitTryBodyPrepare (ec);
4010                         EmitTryBody (ec);
4011
4012                         ec.BeginFinallyBlock ();
4013
4014                         Label start_finally = ec.DefineLabel ();
4015                         if (resume_points != null) {
4016                                 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4017
4018                                 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4019                                 ec.Emit (OpCodes.Brfalse_S, start_finally);
4020                                 ec.Emit (OpCodes.Endfinally);
4021                         }
4022
4023                         ec.MarkLabel (start_finally);
4024                         EmitFinallyBody (ec);
4025
4026                         ec.EndExceptionBlock ();
4027                 }
4028
4029                 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4030                 {
4031                         if (emitted_dispose)
4032                                 return;
4033
4034                         emitted_dispose = true;
4035
4036                         Label end_of_try = ec.DefineLabel ();
4037
4038                         // Ensure that the only way we can get into this code is through a dispatcher
4039                         if (have_dispatcher)
4040                                 ec.Emit (OpCodes.Br, end);
4041
4042                         ec.BeginExceptionBlock ();
4043
4044                         ec.MarkLabel (dispose_try_block);
4045
4046                         Label[] labels = null;
4047                         for (int i = 0; i < resume_points.Count; ++i) {
4048                                 ResumableStatement s = resume_points[i];
4049                                 Label ret = s.PrepareForDispose (ec, end_of_try);
4050                                 if (ret.Equals (end_of_try) && labels == null)
4051                                         continue;
4052                                 if (labels == null) {
4053                                         labels = new Label[resume_points.Count];
4054                                         for (int j = 0; j < i; ++j)
4055                                                 labels[j] = end_of_try;
4056                                 }
4057                                 labels[i] = ret;
4058                         }
4059
4060                         if (labels != null) {
4061                                 int j;
4062                                 for (j = 1; j < labels.Length; ++j)
4063                                         if (!labels[0].Equals (labels[j]))
4064                                                 break;
4065                                 bool emit_dispatcher = j < labels.Length;
4066
4067                                 if (emit_dispatcher) {
4068                                         //SymbolWriter.StartIteratorDispatcher (ec.ig);
4069                                         ec.Emit (OpCodes.Ldloc, pc);
4070                                         ec.EmitInt (first_resume_pc);
4071                                         ec.Emit (OpCodes.Sub);
4072                                         ec.Emit (OpCodes.Switch, labels);
4073                                         //SymbolWriter.EndIteratorDispatcher (ec.ig);
4074                                 }
4075
4076                                 foreach (ResumableStatement s in resume_points)
4077                                         s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4078                         }
4079
4080                         ec.MarkLabel (end_of_try);
4081
4082                         ec.BeginFinallyBlock ();
4083
4084                         EmitFinallyBody (ec);
4085
4086                         ec.EndExceptionBlock ();
4087                 }
4088         }
4089
4090         //
4091         // Base class for blocks using exception handling
4092         //
4093         public abstract class ExceptionStatement : ResumableStatement
4094         {
4095 #if !STATIC
4096                 bool code_follows;
4097 #endif
4098                 protected List<ResumableStatement> resume_points;
4099                 protected int first_resume_pc;
4100
4101                 protected ExceptionStatement (Location loc)
4102                 {
4103                         this.loc = loc;
4104                 }
4105
4106                 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4107                 {
4108                         StateMachineInitializer state_machine = null;
4109                         if (resume_points != null) {
4110                                 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4111
4112                                 ec.EmitInt ((int) IteratorStorey.State.Running);
4113                                 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4114                         }
4115
4116                         ec.BeginExceptionBlock ();
4117
4118                         if (resume_points != null) {
4119                                 ec.MarkLabel (resume_point);
4120
4121                                 // For normal control flow, we want to fall-through the Switch
4122                                 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4123                                 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4124                                 ec.EmitInt (first_resume_pc);
4125                                 ec.Emit (OpCodes.Sub);
4126
4127                                 Label[] labels = new Label[resume_points.Count];
4128                                 for (int i = 0; i < resume_points.Count; ++i)
4129                                         labels[i] = resume_points[i].PrepareForEmit (ec);
4130                                 ec.Emit (OpCodes.Switch, labels);
4131                         }
4132                 }
4133
4134                 public void SomeCodeFollows ()
4135                 {
4136 #if !STATIC
4137                         code_follows = true;
4138 #endif
4139                 }
4140
4141                 public override bool Resolve (BlockContext ec)
4142                 {
4143 #if !STATIC
4144                         // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4145                         // So, ensure there's some IL code after this statement.
4146                         if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4147                                 ec.NeedReturnLabel ();
4148 #endif
4149                         return true;
4150                 }
4151
4152                 public void AddResumePoint (ResumableStatement stmt, int pc)
4153                 {
4154                         if (resume_points == null) {
4155                                 resume_points = new List<ResumableStatement> ();
4156                                 first_resume_pc = pc;
4157                         }
4158
4159                         if (pc != first_resume_pc + resume_points.Count)
4160                                 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4161
4162                         resume_points.Add (stmt);
4163                 }
4164
4165         }
4166
4167         public class Lock : TryFinallyBlock
4168         {
4169                 Expression expr;
4170                 TemporaryVariableReference expr_copy;
4171                 TemporaryVariableReference lock_taken;
4172                         
4173                 public Lock (Expression expr, Statement stmt, Location loc)
4174                         : base (stmt, loc)
4175                 {
4176                         this.expr = expr;
4177                 }
4178
4179                 public override bool Resolve (BlockContext ec)
4180                 {
4181                         expr = expr.Resolve (ec);
4182                         if (expr == null)
4183                                 return false;
4184
4185                         if (!TypeSpec.IsReferenceType (expr.Type)) {
4186                                 ec.Report.Error (185, loc,
4187                                         "`{0}' is not a reference type as required by the lock statement",
4188                                         expr.Type.GetSignatureForError ());
4189                         }
4190
4191                         if (expr.Type.IsGenericParameter) {
4192                                 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4193                         }
4194
4195                         VariableReference lv = expr as VariableReference;
4196                         bool locked;
4197                         if (lv != null) {
4198                                 locked = lv.IsLockedByStatement;
4199                                 lv.IsLockedByStatement = true;
4200                         } else {
4201                                 lv = null;
4202                                 locked = false;
4203                         }
4204
4205                         using (ec.Set (ResolveContext.Options.LockScope)) {
4206                                 ec.StartFlowBranching (this);
4207                                 Statement.Resolve (ec);
4208                                 ec.EndFlowBranching ();
4209                         }
4210
4211                         if (lv != null) {
4212                                 lv.IsLockedByStatement = locked;
4213                         }
4214
4215                         base.Resolve (ec);
4216
4217                         //
4218                         // Have to keep original lock value around to unlock same location
4219                         // in the case the original has changed or is null
4220                         //
4221                         expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4222                         expr_copy.Resolve (ec);
4223
4224                         //
4225                         // Ensure Monitor methods are available
4226                         //
4227                         if (ResolvePredefinedMethods (ec) > 1) {
4228                                 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4229                                 lock_taken.Resolve (ec);
4230                         }
4231
4232                         return true;
4233                 }
4234                 
4235                 protected override void EmitTryBodyPrepare (EmitContext ec)
4236                 {
4237                         expr_copy.EmitAssign (ec, expr);
4238
4239                         if (lock_taken != null) {
4240                                 //
4241                                 // Initialize ref variable
4242                                 //
4243                                 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4244                         } else {
4245                                 //
4246                                 // Monitor.Enter (expr_copy)
4247                                 //
4248                                 expr_copy.Emit (ec);
4249                                 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4250                         }
4251
4252                         base.EmitTryBodyPrepare (ec);
4253                 }
4254
4255                 protected override void EmitTryBody (EmitContext ec)
4256                 {
4257                         //
4258                         // Monitor.Enter (expr_copy, ref lock_taken)
4259                         //
4260                         if (lock_taken != null) {
4261                                 expr_copy.Emit (ec);
4262                                 lock_taken.LocalInfo.CreateBuilder (ec);
4263                                 lock_taken.AddressOf (ec, AddressOp.Load);
4264                                 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4265                         }
4266
4267                         Statement.Emit (ec);
4268                 }
4269
4270                 protected override void EmitFinallyBody (EmitContext ec)
4271                 {
4272                         //
4273                         // if (lock_taken) Monitor.Exit (expr_copy)
4274                         //
4275                         Label skip = ec.DefineLabel ();
4276
4277                         if (lock_taken != null) {
4278                                 lock_taken.Emit (ec);
4279                                 ec.Emit (OpCodes.Brfalse_S, skip);
4280                         }
4281
4282                         expr_copy.Emit (ec);
4283                         var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4284                         if (m != null)
4285                                 ec.Emit (OpCodes.Call, m);
4286
4287                         ec.MarkLabel (skip);
4288                 }
4289
4290                 int ResolvePredefinedMethods (ResolveContext rc)
4291                 {
4292                         // Try 4.0 Monitor.Enter (object, ref bool) overload first
4293                         var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4294                         if (m != null)
4295                                 return 4;
4296
4297                         m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4298                         if (m != null)
4299                                 return 1;
4300
4301                         rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4302                         return 0;
4303                 }
4304
4305                 protected override void CloneTo (CloneContext clonectx, Statement t)
4306                 {
4307                         Lock target = (Lock) t;
4308
4309                         target.expr = expr.Clone (clonectx);
4310                         target.stmt = Statement.Clone (clonectx);
4311                 }
4312         }
4313
4314         public class Unchecked : Statement {
4315                 public Block Block;
4316                 
4317                 public Unchecked (Block b, Location loc)
4318                 {
4319                         Block = b;
4320                         b.Unchecked = true;
4321                         this.loc = loc;
4322                 }
4323
4324                 public override bool Resolve (BlockContext ec)
4325                 {
4326                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4327                                 return Block.Resolve (ec);
4328                 }
4329                 
4330                 protected override void DoEmit (EmitContext ec)
4331                 {
4332                         using (ec.With (EmitContext.Options.CheckedScope, false))
4333                                 Block.Emit (ec);
4334                 }
4335
4336                 protected override void CloneTo (CloneContext clonectx, Statement t)
4337                 {
4338                         Unchecked target = (Unchecked) t;
4339
4340                         target.Block = clonectx.LookupBlock (Block);
4341                 }
4342         }
4343
4344         public class Checked : Statement {
4345                 public Block Block;
4346                 
4347                 public Checked (Block b, Location loc)
4348                 {
4349                         Block = b;
4350                         b.Unchecked = false;
4351                         this.loc = loc;
4352                 }
4353
4354                 public override bool Resolve (BlockContext ec)
4355                 {
4356                         using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4357                                 return Block.Resolve (ec);
4358                 }
4359
4360                 protected override void DoEmit (EmitContext ec)
4361                 {
4362                         using (ec.With (EmitContext.Options.CheckedScope, true))
4363                                 Block.Emit (ec);
4364                 }
4365
4366                 protected override void CloneTo (CloneContext clonectx, Statement t)
4367                 {
4368                         Checked target = (Checked) t;
4369
4370                         target.Block = clonectx.LookupBlock (Block);
4371                 }
4372         }
4373
4374         public class Unsafe : Statement {
4375                 public Block Block;
4376
4377                 public Unsafe (Block b, Location loc)
4378                 {
4379                         Block = b;
4380                         Block.Unsafe = true;
4381                         this.loc = loc;
4382                 }
4383
4384                 public override bool Resolve (BlockContext ec)
4385                 {
4386                         if (ec.CurrentIterator != null)
4387                                 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4388
4389                         using (ec.Set (ResolveContext.Options.UnsafeScope))
4390                                 return Block.Resolve (ec);
4391                 }
4392                 
4393                 protected override void DoEmit (EmitContext ec)
4394                 {
4395                         Block.Emit (ec);
4396                 }
4397
4398                 protected override void CloneTo (CloneContext clonectx, Statement t)
4399                 {
4400                         Unsafe target = (Unsafe) t;
4401
4402                         target.Block = clonectx.LookupBlock (Block);
4403                 }
4404         }
4405
4406         // 
4407         // Fixed statement
4408         //
4409         public class Fixed : Statement
4410         {
4411                 abstract class Emitter : ShimExpression
4412                 {
4413                         protected LocalVariable vi;
4414
4415                         protected Emitter (Expression expr, LocalVariable li)
4416                                 : base (expr)
4417                         {
4418                                 vi = li;
4419                         }
4420
4421                         public abstract void EmitExit (EmitContext ec);
4422                 }
4423
4424                 class ExpressionEmitter : Emitter {
4425                         public ExpressionEmitter (Expression converted, LocalVariable li) :
4426                                 base (converted, li)
4427                         {
4428                         }
4429
4430                         protected override Expression DoResolve (ResolveContext rc)
4431                         {
4432                                 throw new NotImplementedException ();
4433                         }
4434
4435                         public override void Emit (EmitContext ec) {
4436                                 //
4437                                 // Store pointer in pinned location
4438                                 //
4439                                 expr.Emit (ec);
4440                                 vi.EmitAssign (ec);
4441                         }
4442
4443                         public override void EmitExit (EmitContext ec)
4444                         {
4445                                 ec.EmitInt (0);
4446                                 ec.Emit (OpCodes.Conv_U);
4447                                 vi.EmitAssign (ec);
4448                         }
4449                 }
4450
4451                 class StringEmitter : Emitter
4452                 {
4453                         LocalVariable pinned_string;
4454
4455                         public StringEmitter (Expression expr, LocalVariable li, Location loc)
4456                                 : base (expr, li)
4457                         {
4458                         }
4459
4460                         protected override Expression DoResolve (ResolveContext rc)
4461                         {
4462                                 pinned_string = new LocalVariable (vi.Block, "$pinned",
4463                                         LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4464                                         vi.Location);
4465                                 pinned_string.Type = rc.BuiltinTypes.String;
4466
4467                                 eclass = ExprClass.Variable;
4468                                 type = rc.BuiltinTypes.Int;
4469                                 return this;
4470                         }
4471
4472                         public override void Emit (EmitContext ec)
4473                         {
4474                                 pinned_string.CreateBuilder (ec);
4475
4476                                 expr.Emit (ec);
4477                                 pinned_string.EmitAssign (ec);
4478
4479                                 // TODO: Should use Binary::Add
4480                                 pinned_string.Emit (ec);
4481                                 ec.Emit (OpCodes.Conv_I);
4482
4483                                 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4484                                 if (m == null)
4485                                         return;
4486
4487                                 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4488                                 //pe.InstanceExpression = pinned_string;
4489                                 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4490
4491                                 ec.Emit (OpCodes.Add);
4492                                 vi.EmitAssign (ec);
4493                         }
4494
4495                         public override void EmitExit (EmitContext ec)
4496                         {
4497                                 ec.EmitNull ();
4498                                 pinned_string.EmitAssign (ec);
4499                         }
4500                 }
4501
4502                 public class VariableDeclaration : BlockVariableDeclaration
4503                 {
4504                         public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4505                                 : base (type, li)
4506                         {
4507                         }
4508
4509                         protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4510                         {
4511                                 if (!Variable.Type.IsPointer && li == Variable) {
4512                                         bc.Report.Error (209, TypeExpression.Location,
4513                                                 "The type of locals declared in a fixed statement must be a pointer type");
4514                                         return null;
4515                                 }
4516
4517                                 //
4518                                 // The rules for the possible declarators are pretty wise,
4519                                 // but the production on the grammar is more concise.
4520                                 //
4521                                 // So we have to enforce these rules here.
4522                                 //
4523                                 // We do not resolve before doing the case 1 test,
4524                                 // because the grammar is explicit in that the token &
4525                                 // is present, so we need to test for this particular case.
4526                                 //
4527
4528                                 if (initializer is Cast) {
4529                                         bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4530                                         return null;
4531                                 }
4532
4533                                 initializer = initializer.Resolve (bc);
4534
4535                                 if (initializer == null)
4536                                         return null;
4537
4538                                 //
4539                                 // Case 1: Array
4540                                 //
4541                                 if (initializer.Type.IsArray) {
4542                                         TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4543
4544                                         //
4545                                         // Provided that array_type is unmanaged,
4546                                         //
4547                                         if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4548                                                 return null;
4549
4550                                         //
4551                                         // and T* is implicitly convertible to the
4552                                         // pointer type given in the fixed statement.
4553                                         //
4554                                         ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4555
4556                                         Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4557                                         if (converted == null)
4558                                                 return null;
4559
4560                                         //
4561                                         // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4562                                         //
4563                                         converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4564                                                 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4565                                                 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4566                                                         new NullLiteral (loc),
4567                                                         converted, loc);
4568
4569                                         converted = converted.Resolve (bc);
4570
4571                                         return new ExpressionEmitter (converted, li);
4572                                 }
4573
4574                                 //
4575                                 // Case 2: string
4576                                 //
4577                                 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4578                                         return new StringEmitter (initializer, li, loc).Resolve (bc);
4579                                 }
4580
4581                                 // Case 3: fixed buffer
4582                                 if (initializer is FixedBufferPtr) {
4583                                         return new ExpressionEmitter (initializer, li);
4584                                 }
4585
4586                                 //
4587                                 // Case 4: & object.
4588                                 //
4589                                 bool already_fixed = true;
4590                                 Unary u = initializer as Unary;
4591                                 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4592                                         IVariableReference vr = u.Expr as IVariableReference;
4593                                         if (vr == null || !vr.IsFixed) {
4594                                                 already_fixed = false;
4595                                         }
4596                                 }
4597
4598                                 if (already_fixed) {
4599                                         bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4600                                 }
4601
4602                                 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4603                                 return new ExpressionEmitter (initializer, li);
4604                         }
4605                 }
4606
4607
4608                 VariableDeclaration decl;
4609                 Statement statement;
4610                 bool has_ret;
4611
4612                 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4613                 {
4614                         this.decl = decl;
4615                         statement = stmt;
4616                         loc = l;
4617                 }
4618
4619                 #region Properties
4620
4621                 public Statement Statement {
4622                         get {
4623                                 return statement;
4624                         }
4625                 }
4626
4627                 public BlockVariableDeclaration Variables {
4628                         get {
4629                                 return decl;
4630                         }
4631                 }
4632
4633                 #endregion
4634
4635                 public override bool Resolve (BlockContext ec)
4636                 {
4637                         using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4638                                 if (!decl.Resolve (ec))
4639                                         return false;
4640                         }
4641
4642                         ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4643                         bool ok = statement.Resolve (ec);
4644                         bool flow_unreachable = ec.EndFlowBranching ();
4645                         has_ret = flow_unreachable;
4646
4647                         return ok;
4648                 }
4649                 
4650                 protected override void DoEmit (EmitContext ec)
4651                 {
4652                         decl.Variable.CreateBuilder (ec);
4653                         decl.Initializer.Emit (ec);
4654                         if (decl.Declarators != null) {
4655                                 foreach (var d in decl.Declarators) {
4656                                         d.Variable.CreateBuilder (ec);
4657                                         d.Initializer.Emit (ec);
4658                                 }
4659                         }
4660
4661                         statement.Emit (ec);
4662
4663                         if (has_ret)
4664                                 return;
4665
4666                         //
4667                         // Clear the pinned variable
4668                         //
4669                         ((Emitter) decl.Initializer).EmitExit (ec);
4670                         if (decl.Declarators != null) {
4671                                 foreach (var d in decl.Declarators) {
4672                                         ((Emitter)d.Initializer).EmitExit (ec);
4673                                 }
4674                         }
4675                 }
4676
4677                 protected override void CloneTo (CloneContext clonectx, Statement t)
4678                 {
4679                         Fixed target = (Fixed) t;
4680
4681                         target.decl = (VariableDeclaration) decl.Clone (clonectx);
4682                         target.statement = statement.Clone (clonectx);
4683                 }
4684         }
4685
4686         public class Catch : Statement
4687         {
4688                 Block block;
4689                 LocalVariable li;
4690                 FullNamedExpression type_expr;
4691                 CompilerAssign assign;
4692                 TypeSpec type;
4693                 
4694                 public Catch (Block block, Location loc)
4695                 {
4696                         this.block = block;
4697                         this.loc = loc;
4698                 }
4699
4700                 #region Properties
4701
4702                 public Block Block {
4703                         get {
4704                                 return block;
4705                         }
4706                 }
4707
4708                 public TypeSpec CatchType {
4709                         get {
4710                                 return type;
4711                         }
4712                 }
4713
4714                 public bool IsGeneral {
4715                         get {
4716                                 return type_expr == null;
4717                         }
4718                 }
4719
4720                 public FullNamedExpression TypeExpression {
4721                         get {
4722                                 return type_expr;
4723                         }
4724                         set {
4725                                 type_expr = value;
4726                         }
4727                 }
4728
4729                 public LocalVariable Variable {
4730                         get {
4731                                 return li;
4732                         }
4733                         set {
4734                                 li = value;
4735                         }
4736                 }
4737
4738                 #endregion
4739
4740                 protected override void DoEmit (EmitContext ec)
4741                 {
4742                         if (IsGeneral)
4743                                 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
4744                         else
4745                                 ec.BeginCatchBlock (CatchType);
4746
4747                         if (li != null) {
4748                                 li.CreateBuilder (ec);
4749
4750                                 //
4751                                 // Special case hoisted catch variable, we have to use a temporary variable
4752                                 // to pass via anonymous storey initialization with the value still on top
4753                                 // of the stack
4754                                 //
4755                                 if (li.HoistedVariant != null) {
4756                                         LocalTemporary lt = new LocalTemporary (li.Type);
4757                                         SymbolWriter.OpenCompilerGeneratedBlock (ec);
4758                                         lt.Store (ec);
4759                                         SymbolWriter.CloseCompilerGeneratedBlock (ec);
4760
4761                                         // switch to assigning from the temporary variable and not from top of the stack
4762                                         assign.UpdateSource (lt);
4763                                 }
4764                         } else {
4765                                 SymbolWriter.OpenCompilerGeneratedBlock (ec);
4766                                 ec.Emit (OpCodes.Pop);
4767                                 SymbolWriter.CloseCompilerGeneratedBlock (ec);
4768                         }
4769
4770                         Block.Emit (ec);
4771                 }
4772
4773                 public override bool Resolve (BlockContext ec)
4774                 {
4775                         using (ec.With (ResolveContext.Options.CatchScope, true)) {
4776                                 if (type_expr != null) {
4777                                         type = type_expr.ResolveAsType (ec);
4778                                         if (type == null)
4779                                                 return false;
4780
4781                                         if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
4782                                                 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
4783                                         } else if (li != null) {
4784                                                 li.Type = type;
4785                                                 li.PrepareForFlowAnalysis (ec);
4786
4787                                                 // source variable is at the top of the stack
4788                                                 Expression source = new EmptyExpression (li.Type);
4789                                                 if (li.Type.IsGenericParameter)
4790                                                         source = new UnboxCast (source, li.Type);
4791
4792                                                 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
4793                                                 Block.AddScopeStatement (new StatementExpression (assign));
4794                                         }
4795                                 }
4796
4797                                 return Block.Resolve (ec);
4798                         }
4799                 }
4800
4801                 protected override void CloneTo (CloneContext clonectx, Statement t)
4802                 {
4803                         Catch target = (Catch) t;
4804
4805                         if (type_expr != null)
4806                                 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
4807
4808                         target.block = clonectx.LookupBlock (block);
4809                 }
4810         }
4811
4812         public class TryFinally : TryFinallyBlock
4813         {
4814                 Block fini;
4815
4816                 public TryFinally (Statement stmt, Block fini, Location loc)
4817                          : base (stmt, loc)
4818                 {
4819                         this.fini = fini;
4820                 }
4821
4822                 public override bool Resolve (BlockContext ec)
4823                 {
4824                         bool ok = true;
4825
4826                         ec.StartFlowBranching (this);
4827
4828                         if (!stmt.Resolve (ec))
4829                                 ok = false;
4830
4831                         if (ok)
4832                                 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4833                         using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4834                                 if (!fini.Resolve (ec))
4835                                         ok = false;
4836                         }
4837
4838                         ec.EndFlowBranching ();
4839
4840                         ok &= base.Resolve (ec);
4841
4842                         return ok;
4843                 }
4844
4845                 protected override void EmitTryBody (EmitContext ec)
4846                 {
4847                         stmt.Emit (ec);
4848                 }
4849
4850                 protected override void EmitFinallyBody (EmitContext ec)
4851                 {
4852                         fini.Emit (ec);
4853                 }
4854
4855                 protected override void CloneTo (CloneContext clonectx, Statement t)
4856                 {
4857                         TryFinally target = (TryFinally) t;
4858
4859                         target.stmt = (Statement) stmt.Clone (clonectx);
4860                         if (fini != null)
4861                                 target.fini = clonectx.LookupBlock (fini);
4862                 }
4863         }
4864
4865         public class TryCatch : ExceptionStatement
4866         {
4867                 public Block Block;
4868                 public List<Catch> Specific;
4869                 public Catch General;
4870                 readonly bool inside_try_finally;
4871
4872                 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
4873                         : base (l)
4874                 {
4875                         this.Block = block;
4876                         this.Specific = catch_clauses;
4877                         this.inside_try_finally = inside_try_finally;
4878
4879                         Catch c = catch_clauses [0];
4880                         if (c.IsGeneral) {
4881                                 this.General = c;                       
4882                                 catch_clauses.RemoveAt (0);
4883                         }
4884                 }
4885
4886                 public bool IsTryCatchFinally {
4887                         get {
4888                                 return inside_try_finally;
4889                         }
4890                 }
4891
4892                 public override bool Resolve (BlockContext ec)
4893                 {
4894                         bool ok = true;
4895
4896                         ec.StartFlowBranching (this);
4897
4898                         if (!Block.Resolve (ec))
4899                                 ok = false;
4900
4901                         TypeSpec[] prev_catches = new TypeSpec [Specific.Count];
4902                         int last_index = 0;
4903                         foreach (Catch c in Specific){
4904                                 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4905
4906                                 if (!c.Resolve (ec)) {
4907                                         ok = false;
4908                                         continue;
4909                                 }
4910
4911                                 TypeSpec resolved_type = c.CatchType;
4912                                 for (int ii = 0; ii < last_index; ++ii) {
4913                                         if (resolved_type == prev_catches[ii] || TypeSpec.IsBaseClass (resolved_type, prev_catches[ii], true)) {
4914                                                 ec.Report.Error (160, c.loc,
4915                                                         "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4916                                                         TypeManager.CSharpName (prev_catches [ii]));
4917                                                 ok = false;
4918                                         }
4919                                 }
4920
4921                                 prev_catches [last_index++] = resolved_type;
4922                         }
4923
4924                         if (General != null) {
4925                                 foreach (Catch c in Specific) {
4926                                         if (c.CatchType.BuiltinType != BuiltinTypeSpec.Type.Exception)
4927                                                 continue;
4928
4929                                         if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
4930                                                 continue;
4931
4932                                         if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
4933                                                 continue;
4934
4935                                         ec.Report.Warning (1058, 1, c.loc,
4936                                                 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4937                                 }
4938
4939                                 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4940
4941                                 if (!General.Resolve (ec))
4942                                         ok = false;
4943                         }
4944
4945                         ec.EndFlowBranching ();
4946
4947                         return base.Resolve (ec) && ok;
4948                 }
4949
4950                 protected sealed override void DoEmit (EmitContext ec)
4951                 {
4952                         if (!inside_try_finally)
4953                                 EmitTryBodyPrepare (ec);
4954
4955                         Block.Emit (ec);
4956
4957                         foreach (Catch c in Specific)
4958                                 c.Emit (ec);
4959
4960                         if (General != null)
4961                                 General.Emit (ec);
4962
4963                         if (!inside_try_finally)
4964                                 ec.EndExceptionBlock ();
4965                 }
4966
4967                 protected override void CloneTo (CloneContext clonectx, Statement t)
4968                 {
4969                         TryCatch target = (TryCatch) t;
4970
4971                         target.Block = clonectx.LookupBlock (Block);
4972                         if (General != null)
4973                                 target.General = (Catch) General.Clone (clonectx);
4974                         if (Specific != null){
4975                                 target.Specific = new List<Catch> ();
4976                                 foreach (Catch c in Specific)
4977                                         target.Specific.Add ((Catch) c.Clone (clonectx));
4978                         }
4979                 }
4980         }
4981
4982         public class Using : TryFinallyBlock
4983         {
4984                 public class VariableDeclaration : BlockVariableDeclaration
4985                 {
4986                         Statement dispose_call;
4987
4988                         public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4989                                 : base (type, li)
4990                         {
4991                         }
4992
4993                         public VariableDeclaration (LocalVariable li, Location loc)
4994                                 : base (li)
4995                         {
4996                                 this.loc = loc;
4997                         }
4998
4999                         public VariableDeclaration (Expression expr)
5000                                 : base (null)
5001                         {
5002                                 loc = expr.Location;
5003                                 Initializer = expr;
5004                         }
5005
5006                         #region Properties
5007
5008                         public bool IsNested { get; private set; }
5009
5010                         #endregion
5011
5012                         public void EmitDispose (EmitContext ec)
5013                         {
5014                                 dispose_call.Emit (ec);
5015                         }
5016
5017                         public override bool Resolve (BlockContext bc)
5018                         {
5019                                 if (IsNested)
5020                                         return true;
5021
5022                                 return base.Resolve (bc, false);
5023                         }
5024
5025                         public Expression ResolveExpression (BlockContext bc)
5026                         {
5027                                 var e = Initializer.Resolve (bc);
5028                                 if (e == null)
5029                                         return null;
5030
5031                                 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5032                                 Initializer = ResolveInitializer (bc, Variable, e);
5033                                 return e;
5034                         }
5035
5036                         protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5037                         {
5038                                 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5039                                         initializer = initializer.Resolve (bc);
5040                                         if (initializer == null)
5041                                                 return null;
5042
5043                                         // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5044                                         Arguments args = new Arguments (1);
5045                                         args.Add (new Argument (initializer));
5046                                         initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5047                                         if (initializer == null)
5048                                                 return null;
5049
5050                                         var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5051                                         dispose_call = CreateDisposeCall (bc, var);
5052                                         dispose_call.Resolve (bc);
5053
5054                                         return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5055                                 }
5056
5057                                 if (li == Variable) {
5058                                         CheckIDiposableConversion (bc, li, initializer);
5059                                         dispose_call = CreateDisposeCall (bc, li);
5060                                         dispose_call.Resolve (bc);
5061                                 }
5062
5063                                 return base.ResolveInitializer (bc, li, initializer);
5064                         }
5065
5066                         protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5067                         {
5068                                 var type = li.Type;
5069
5070                                 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5071                                         if (type.IsNullableType) {
5072                                                 // it's handled in CreateDisposeCall
5073                                                 return;
5074                                         }
5075
5076                                         bc.Report.SymbolRelatedToPreviousError (type);
5077                                         var loc = type_expr == null ? initializer.Location : type_expr.Location;
5078                                         bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5079                                                 type.GetSignatureForError ());
5080
5081                                         return;
5082                                 }
5083                         }
5084
5085                         protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5086                         {
5087                                 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5088                                 var type = lv.Type;
5089                                 var loc = lv.Location;
5090
5091                                 var idt = bc.BuiltinTypes.IDisposable;
5092                                 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5093
5094                                 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5095                                 dispose_mg.InstanceExpression = type.IsNullableType ?
5096                                         new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5097                                         lvr;
5098
5099                                 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5100
5101                                 // Add conditional call when disposing possible null variable
5102                                 if (!type.IsStruct || type.IsNullableType)
5103                                         dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5104
5105                                 return dispose;
5106                         }
5107
5108                         public void ResolveDeclaratorInitializer (BlockContext bc)
5109                         {
5110                                 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5111                         }
5112
5113                         public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5114                         {
5115                                 for (int i = declarators.Count - 1; i >= 0; --i) {
5116                                         var d = declarators [i];
5117                                         var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5118                                         vd.Initializer = d.Initializer;
5119                                         vd.IsNested = true;
5120                                         vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5121                                         vd.dispose_call.Resolve (bc);
5122
5123                                         stmt = new Using (vd, stmt, d.Variable.Location);
5124                                 }
5125
5126                                 declarators = null;
5127                                 return stmt;
5128                         }
5129                 }
5130
5131                 VariableDeclaration decl;
5132
5133                 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5134                         : base (stmt, loc)
5135                 {
5136                         this.decl = decl;
5137                 }
5138
5139                 public Using (Expression expr, Statement stmt, Location loc)
5140                         : base (stmt, loc)
5141                 {
5142                         this.decl = new VariableDeclaration (expr);
5143                 }
5144
5145                 #region Properties
5146
5147                 public Expression Expression {
5148                         get {
5149                                 return decl.Variable == null ? decl.Initializer : null;
5150                         }
5151                 }
5152
5153                 public BlockVariableDeclaration Variables {
5154                         get {
5155                                 return decl;
5156                         }
5157                 }
5158
5159                 #endregion
5160
5161                 protected override void EmitTryBodyPrepare (EmitContext ec)
5162                 {
5163                         decl.Emit (ec);
5164                         base.EmitTryBodyPrepare (ec);
5165                 }
5166
5167                 protected override void EmitTryBody (EmitContext ec)
5168                 {
5169                         stmt.Emit (ec);
5170                 }
5171
5172                 protected override void EmitFinallyBody (EmitContext ec)
5173                 {
5174                         decl.EmitDispose (ec);
5175                 }
5176
5177                 public override bool Resolve (BlockContext ec)
5178                 {
5179                         VariableReference vr;
5180                         bool vr_locked = false;
5181
5182                         using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5183                                 if (decl.Variable == null) {
5184                                         vr = decl.ResolveExpression (ec) as VariableReference;
5185                                         if (vr != null) {
5186                                                 vr_locked = vr.IsLockedByStatement;
5187                                                 vr.IsLockedByStatement = true;
5188                                         }
5189                                 } else {
5190                                         if (decl.IsNested) {
5191                                                 decl.ResolveDeclaratorInitializer (ec);
5192                                         } else {
5193                                                 if (!decl.Resolve (ec))
5194                                                         return false;
5195
5196                                                 if (decl.Declarators != null) {
5197                                                         stmt = decl.RewriteUsingDeclarators (ec, stmt);
5198                                                 }
5199                                         }
5200
5201                                         vr = null;
5202                                 }
5203                         }
5204
5205                         ec.StartFlowBranching (this);
5206
5207                         stmt.Resolve (ec);
5208
5209                         ec.EndFlowBranching ();
5210
5211                         if (vr != null)
5212                                 vr.IsLockedByStatement = vr_locked;
5213
5214                         base.Resolve (ec);
5215
5216                         return true;
5217                 }
5218
5219                 protected override void CloneTo (CloneContext clonectx, Statement t)
5220                 {
5221                         Using target = (Using) t;
5222
5223                         target.decl = (VariableDeclaration) decl.Clone (clonectx);
5224                         target.stmt = stmt.Clone (clonectx);
5225                 }
5226         }
5227
5228         /// <summary>
5229         ///   Implementation of the foreach C# statement
5230         /// </summary>
5231         public class Foreach : Statement
5232         {
5233                 sealed class ArrayForeach : Statement
5234                 {
5235                         readonly Foreach for_each;
5236                         readonly Statement statement;
5237
5238                         Expression conv;
5239                         TemporaryVariableReference[] lengths;
5240                         Expression [] length_exprs;
5241                         StatementExpression[] counter;
5242                         TemporaryVariableReference[] variables;
5243
5244                         TemporaryVariableReference copy;
5245                         Expression access;
5246                         LocalVariableReference variable;
5247
5248                         public ArrayForeach (Foreach @foreach, int rank)
5249                         {
5250                                 for_each = @foreach;
5251                                 statement = for_each.statement;
5252                                 loc = @foreach.loc;
5253
5254                                 counter = new StatementExpression[rank];
5255                                 variables = new TemporaryVariableReference[rank];
5256                                 length_exprs = new Expression [rank];
5257
5258                                 //
5259                                 // Only use temporary length variables when dealing with
5260                                 // multi-dimensional arrays
5261                                 //
5262                                 if (rank > 1)
5263                                         lengths = new TemporaryVariableReference [rank];
5264                         }
5265
5266                         protected override void CloneTo (CloneContext clonectx, Statement target)
5267                         {
5268                                 throw new NotImplementedException ();
5269                         }
5270
5271                         public override bool Resolve (BlockContext ec)
5272                         {
5273                                 Block variables_block = for_each.variable.Block;
5274                                 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5275                                 copy.Resolve (ec);
5276
5277                                 int rank = length_exprs.Length;
5278                                 Arguments list = new Arguments (rank);
5279                                 for (int i = 0; i < rank; i++) {
5280                                         var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5281                                         variables[i] = v;
5282                                         counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5283                                         counter[i].Resolve (ec);
5284
5285                                         if (rank == 1) {
5286                                                 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5287                                         } else {
5288                                                 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5289                                                 lengths[i].Resolve (ec);
5290
5291                                                 Arguments args = new Arguments (1);
5292                                                 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5293                                                 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5294                                         }
5295
5296                                         list.Add (new Argument (v));
5297                                 }
5298
5299                                 access = new ElementAccess (copy, list, loc).Resolve (ec);
5300                                 if (access == null)
5301                                         return false;
5302
5303                                 TypeSpec var_type;
5304                                 if (for_each.type is VarExpr) {
5305                                         // Infer implicitly typed local variable from foreach array type
5306                                         var_type = access.Type;
5307                                 } else {
5308                                         var_type = for_each.type.ResolveAsType (ec);
5309                                 }
5310
5311                                 if (var_type == null)
5312                                         return false;
5313
5314                                 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5315                                 if (conv == null)
5316                                         return false;
5317
5318                                 bool ok = true;
5319
5320                                 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5321                                 ec.CurrentBranching.CreateSibling ();
5322
5323                                 for_each.variable.Type = conv.Type;
5324                                 variable = new LocalVariableReference (for_each.variable, loc);
5325                                 variable.Resolve (ec);
5326
5327                                 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5328                                 if (!statement.Resolve (ec))
5329                                         ok = false;
5330                                 ec.EndFlowBranching ();
5331
5332                                 // There's no direct control flow from the end of the embedded statement to the end of the loop
5333                                 ec.CurrentBranching.CurrentUsageVector.Goto ();
5334
5335                                 ec.EndFlowBranching ();
5336
5337                                 return ok;
5338                         }
5339
5340                         protected override void DoEmit (EmitContext ec)
5341                         {
5342                                 copy.EmitAssign (ec, for_each.expr);
5343
5344                                 int rank = length_exprs.Length;
5345                                 Label[] test = new Label [rank];
5346                                 Label[] loop = new Label [rank];
5347
5348                                 for (int i = 0; i < rank; i++) {
5349                                         test [i] = ec.DefineLabel ();
5350                                         loop [i] = ec.DefineLabel ();
5351
5352                                         if (lengths != null)
5353                                                 lengths [i].EmitAssign (ec, length_exprs [i]);
5354                                 }
5355
5356                                 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5357                                 for (int i = 0; i < rank; i++) {
5358                                         variables [i].EmitAssign (ec, zero);
5359
5360                                         ec.Emit (OpCodes.Br, test [i]);
5361                                         ec.MarkLabel (loop [i]);
5362                                 }
5363
5364                                 variable.local_info.CreateBuilder (ec);
5365                                 variable.EmitAssign (ec, conv, false, false);
5366
5367                                 statement.Emit (ec);
5368
5369                                 ec.MarkLabel (ec.LoopBegin);
5370
5371                                 for (int i = rank - 1; i >= 0; i--){
5372                                         counter [i].Emit (ec);
5373
5374                                         ec.MarkLabel (test [i]);
5375                                         variables [i].Emit (ec);
5376
5377                                         if (lengths != null)
5378                                                 lengths [i].Emit (ec);
5379                                         else
5380                                                 length_exprs [i].Emit (ec);
5381
5382                                         ec.Emit (OpCodes.Blt, loop [i]);
5383                                 }
5384
5385                                 ec.MarkLabel (ec.LoopEnd);
5386                         }
5387                 }
5388
5389                 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5390                 {
5391                         class Body : Statement
5392                         {
5393                                 TypeSpec type;
5394                                 LocalVariableReference variable;
5395                                 Expression current, conv;
5396                                 Statement statement;
5397
5398                                 public Body (TypeSpec type, LocalVariable variable,
5399                                                                    Expression current, Statement statement,
5400                                                                    Location loc)
5401                                 {
5402                                         this.type = type;
5403                                         this.variable = new LocalVariableReference (variable, loc);
5404                                         this.current = current;
5405                                         this.statement = statement;
5406                                         this.loc = loc;
5407                                 }
5408
5409                                 protected override void CloneTo (CloneContext clonectx, Statement target)
5410                                 {
5411                                         throw new NotImplementedException ();
5412                                 }
5413
5414                                 public override bool Resolve (BlockContext ec)
5415                                 {
5416                                         current = current.Resolve (ec);
5417                                         if (current == null)
5418                                                 return false;
5419
5420                                         conv = Convert.ExplicitConversion (ec, current, type, loc);
5421                                         if (conv == null)
5422                                                 return false;
5423
5424                                         variable.local_info.Type = conv.Type;
5425                                         variable.Resolve (ec);
5426
5427                                         if (!statement.Resolve (ec))
5428                                                 return false;
5429
5430                                         return true;
5431                                 }
5432
5433                                 protected override void DoEmit (EmitContext ec)
5434                                 {
5435                                         variable.local_info.CreateBuilder (ec);
5436                                         variable.EmitAssign (ec, conv, false, false);
5437
5438                                         statement.Emit (ec);
5439                                 }
5440                         }
5441
5442                         class RuntimeDispose : Using.VariableDeclaration
5443                         {
5444                                 public RuntimeDispose (LocalVariable lv, Location loc)
5445                                         : base (lv, loc)
5446                                 {
5447                                 }
5448
5449                                 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5450                                 {
5451                                         // Defered to runtime check
5452                                 }
5453
5454                                 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5455                                 {
5456                                         var idt = bc.BuiltinTypes.IDisposable;
5457
5458                                         //
5459                                         // Fabricates code like
5460                                         //
5461                                         // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5462                                         //
5463
5464                                         var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5465
5466                                         var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5467                                                 dispose_variable.CreateReferenceExpression (bc, loc),
5468                                                 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5469                                                 loc), new NullLiteral (loc), loc);
5470
5471                                         var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5472
5473                                         var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5474                                         dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5475
5476                                         Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5477                                         return new If (idisaposable_test, dispose, loc);
5478                                 }
5479                         }
5480
5481                         LocalVariable variable;
5482                         Expression expr;
5483                         Statement statement;
5484                         Expression var_type;
5485                         ExpressionStatement init;
5486                         TemporaryVariableReference enumerator_variable;
5487                         bool ambiguous_getenumerator_name;
5488
5489                         public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5490                         {
5491                                 this.var_type = var_type;
5492                                 this.variable = var;
5493                                 this.expr = expr;
5494                                 statement = stmt;
5495                                 loc = l;
5496                         }
5497
5498                         protected override void CloneTo (CloneContext clonectx, Statement target)
5499                         {
5500                                 throw new NotImplementedException ();
5501                         }
5502
5503                         void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5504                         {
5505                                 rc.Report.SymbolRelatedToPreviousError (enumerator);
5506                                 rc.Report.Error (202, loc,
5507                                         "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5508                                                 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5509                         }
5510
5511                         MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5512                         {
5513                                 //
5514                                 // Option 1: Try to match by name GetEnumerator first
5515                                 //
5516                                 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5517                                         "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc);               // TODO: What if CS0229 ?
5518
5519                                 var mg = mexpr as MethodGroupExpr;
5520                                 if (mg != null) {
5521                                         mg.InstanceExpression = expr;
5522                                         Arguments args = new Arguments (0);
5523                                         mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5524
5525                                         // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5526                                         if (ambiguous_getenumerator_name)
5527                                                 mg = null;
5528
5529                                         if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5530                                                 return mg;
5531                                         }
5532                                 }
5533
5534                                 //
5535                                 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5536                                 //
5537                                 var t = expr.Type;
5538                                 PredefinedMember<MethodSpec> iface_candidate = null;
5539                                 var ptypes = rc.Module.PredefinedTypes;
5540                                 var gen_ienumerable = ptypes.IEnumerableGeneric;
5541                                 if (!gen_ienumerable.Define ())
5542                                         gen_ienumerable = null;
5543
5544                                 do {
5545                                         var ifaces = t.Interfaces;
5546                                         if (ifaces != null) {
5547                                                 foreach (var iface in ifaces) {
5548                                                         if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5549                                                                 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5550                                                                         rc.Report.SymbolRelatedToPreviousError (expr.Type);
5551                                                                         rc.Report.Error (1640, loc,
5552                                                                                 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5553                                                                                 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5554
5555                                                                         return null;
5556                                                                 }
5557
5558                                                                 // TODO: Cache this somehow
5559                                                                 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5560                                                                         MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5561
5562                                                                 continue;
5563                                                         }
5564
5565                                                         if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5566                                                                 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5567                                                         }
5568                                                 }
5569                                         }
5570
5571                                         if (t.IsGenericParameter)
5572                                                 t = t.BaseType;
5573                                         else
5574                                                 t = null;
5575
5576                                 } while (t != null);
5577
5578                                 if (iface_candidate == null) {
5579                                         rc.Report.Error (1579, loc,
5580                                                 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5581                                                 expr.Type.GetSignatureForError (), "GetEnumerator");
5582
5583                                         return null;
5584                                 }
5585
5586                                 var method = iface_candidate.Resolve (loc);
5587                                 if (method == null)
5588                                         return null;
5589
5590                                 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5591                                 mg.InstanceExpression = expr;
5592                                 return mg;
5593                         }
5594
5595                         MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5596                         {
5597                                 var ms = MemberCache.FindMember (enumerator.ReturnType,
5598                                         MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5599                                         BindingRestriction.InstanceOnly) as MethodSpec;
5600
5601                                 if (ms == null || !ms.IsPublic) {
5602                                         Error_WrongEnumerator (rc, enumerator);
5603                                         return null;
5604                                 }
5605
5606                                 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5607                         }
5608
5609                         PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5610                         {
5611                                 var ps = MemberCache.FindMember (enumerator.ReturnType,
5612                                         MemberFilter.Property ("Current", null),
5613                                         BindingRestriction.InstanceOnly) as PropertySpec;
5614
5615                                 if (ps == null || !ps.IsPublic) {
5616                                         Error_WrongEnumerator (rc, enumerator);
5617                                         return null;
5618                                 }
5619
5620                                 return ps;
5621                         }
5622
5623                         public override bool Resolve (BlockContext ec)
5624                         {
5625                                 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5626
5627                                 if (is_dynamic) {
5628                                         expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5629                                 } else if (expr.Type.IsNullableType) {
5630                                         expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5631                                 }
5632
5633                                 var get_enumerator_mg = ResolveGetEnumerator (ec);
5634                                 if (get_enumerator_mg == null) {
5635                                         return false;
5636                                 }
5637
5638                                 var get_enumerator = get_enumerator_mg.BestCandidate;
5639                                 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
5640                                 enumerator_variable.Resolve (ec);
5641
5642                                 // Prepare bool MoveNext ()
5643                                 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
5644                                 if (move_next_mg == null) {
5645                                         return false;
5646                                 }
5647
5648                                 move_next_mg.InstanceExpression = enumerator_variable;
5649
5650                                 // Prepare ~T~ Current { get; }
5651                                 var current_prop = ResolveCurrent (ec, get_enumerator);
5652                                 if (current_prop == null) {
5653                                         return false;
5654                                 }
5655
5656                                 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
5657                                 if (current_pe == null)
5658                                         return false;
5659
5660                                 VarExpr ve = var_type as VarExpr;
5661
5662                                 if (ve != null) {
5663                                         if (is_dynamic) {
5664                                                 // Source type is dynamic, set element type to dynamic too
5665                                                 variable.Type = ec.BuiltinTypes.Dynamic;
5666                                         } else {
5667                                                 // Infer implicitly typed local variable from foreach enumerable type
5668                                                 variable.Type = current_pe.Type;
5669                                         }
5670                                 } else {
5671                                         if (is_dynamic) {
5672                                                 // Explicit cast of dynamic collection elements has to be done at runtime
5673                                                 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
5674                                         }
5675
5676                                         variable.Type = var_type.ResolveAsType (ec);
5677                                 }
5678
5679                                 if (variable.Type == null)
5680                                         return false;
5681
5682                                 var init = new Invocation (get_enumerator_mg, null);
5683
5684                                 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
5685                                         new Body (variable.Type, variable, current_pe, statement, loc), loc);
5686
5687                                 var enum_type = enumerator_variable.Type;
5688
5689                                 //
5690                                 // Add Dispose method call when enumerator can be IDisposable
5691                                 //
5692                                 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
5693                                         if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
5694                                                 //
5695                                                 // Runtime Dispose check
5696                                                 //
5697                                                 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
5698                                                 vd.Initializer = init;
5699                                                 statement = new Using (vd, statement, loc);
5700                                         } else {
5701                                                 //
5702                                                 // No Dispose call needed
5703                                                 //
5704                                                 this.init = new SimpleAssign (enumerator_variable, init);
5705                                                 this.init.Resolve (ec);
5706                                         }
5707                                 } else {
5708                                         //
5709                                         // Static Dispose check
5710                                         //
5711                                         var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
5712                                         vd.Initializer = init;
5713                                         statement = new Using (vd, statement, loc);
5714                                 }
5715
5716                                 return statement.Resolve (ec);
5717                         }
5718
5719                         protected override void DoEmit (EmitContext ec)
5720                         {
5721                                 enumerator_variable.LocalInfo.CreateBuilder (ec);
5722
5723                                 if (init != null)
5724                                         init.EmitStatement (ec);
5725
5726                                 statement.Emit (ec);
5727                         }
5728
5729                         #region IErrorHandler Members
5730
5731                         bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
5732                         {
5733                                 ec.Report.SymbolRelatedToPreviousError (best);
5734                                 ec.Report.Warning (278, 2, loc,
5735                                         "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5736                                         expr.Type.GetSignatureForError (), "enumerable",
5737                                         best.GetSignatureForError (), ambiguous.GetSignatureForError ());
5738
5739                                 ambiguous_getenumerator_name = true;
5740                                 return true;
5741                         }
5742
5743                         bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
5744                         {
5745                                 return false;
5746                         }
5747
5748                         bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
5749                         {
5750                                 return false;
5751                         }
5752
5753                         bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
5754                         {
5755                                 return false;
5756                         }
5757
5758                         #endregion
5759                 }
5760
5761                 Expression type;
5762                 LocalVariable variable;
5763                 Expression expr;
5764                 Statement statement;
5765
5766                 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
5767                 {
5768                         this.type = type;
5769                         this.variable = var;
5770                         this.expr = expr;
5771                         statement = stmt;
5772                         loc = l;
5773                 }
5774
5775                 public Statement Statement {
5776                         get { return statement; }
5777                 }
5778
5779                 public override bool Resolve (BlockContext ec)
5780                 {
5781                         expr = expr.Resolve (ec);
5782                         if (expr == null)
5783                                 return false;
5784
5785                         if (expr.IsNull) {
5786                                 ec.Report.Error (186, loc, "Use of null is not valid in this context");
5787                                 return false;
5788                         }
5789
5790                         if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5791                                 statement = new ArrayForeach (this, 1);
5792                         } else if (expr.Type is ArrayContainer) {
5793                                 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
5794                         } else {
5795                                 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5796                                         ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5797                                                 expr.ExprClassName);
5798                                         return false;
5799                                 }
5800
5801                                 statement = new CollectionForeach (type, variable, expr, statement, loc);
5802                         }
5803
5804                         return statement.Resolve (ec);
5805                 }
5806
5807                 protected override void DoEmit (EmitContext ec)
5808                 {
5809                         Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5810                         ec.LoopBegin = ec.DefineLabel ();
5811                         ec.LoopEnd = ec.DefineLabel ();
5812
5813                         statement.Emit (ec);
5814
5815                         ec.LoopBegin = old_begin;
5816                         ec.LoopEnd = old_end;
5817                 }
5818
5819                 protected override void CloneTo (CloneContext clonectx, Statement t)
5820                 {
5821                         Foreach target = (Foreach) t;
5822
5823                         target.type = type.Clone (clonectx);
5824                         target.expr = expr.Clone (clonectx);
5825                         target.statement = statement.Clone (clonectx);
5826                 }
5827         }
5828 }