Initial separation of state machine and anonymous method story to allow both of them...
[mono-project.git] / mcs / mcs / statement.cs
blob2a85bee28c629127aa0f690b5127a0279bdb9f20
1 //
2 // statement.cs: Statement representation for the IL tree.
3 //
4 // Authors:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
14 using System;
15 using System.Collections.Generic;
17 #if STATIC
18 using IKVM.Reflection.Emit;
19 #else
20 using System.Reflection.Emit;
21 #endif
23 namespace Mono.CSharp {
25 public abstract class Statement {
26 public Location loc;
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)
34 return true;
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)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
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.
52 if (warn)
53 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56 bool ok = Resolve (ec);
57 ec.KillFlowBranching ();
59 return ok;
62 /// <summary>
63 /// Return value indicates whether all code paths emitted return.
64 /// </summary>
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
69 ec.Mark (loc);
70 DoEmit (ec);
72 if (ec.StatementEpilogue != null) {
73 ec.EmitEpilogue ();
78 // This routine must be overrided in derived classes and make copies
79 // of all the data that might be modified if resolved
80 //
81 protected abstract void CloneTo (CloneContext clonectx, Statement target);
83 public Statement Clone (CloneContext clonectx)
85 Statement s = (Statement) this.MemberwiseClone ();
86 CloneTo (clonectx, s);
87 return s;
90 public virtual Expression CreateExpressionTree (ResolveContext ec)
92 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
93 return null;
96 public virtual object Accept (StructuralVisitor visitor)
98 return visitor.Visit (this);
102 public sealed class EmptyStatement : Statement
104 public EmptyStatement (Location loc)
106 this.loc = loc;
109 public override bool Resolve (BlockContext ec)
111 return true;
114 public override bool ResolveUnreachable (BlockContext ec, bool warn)
116 return true;
119 public override void Emit (EmitContext ec)
123 protected override void DoEmit (EmitContext ec)
125 throw new NotSupportedException ();
128 protected override void CloneTo (CloneContext clonectx, Statement target)
130 // nothing needed.
133 public override object Accept (StructuralVisitor visitor)
135 return visitor.Visit (this);
139 public class If : Statement {
140 Expression expr;
141 public Statement TrueStatement;
142 public Statement FalseStatement;
144 bool is_true_ret;
146 public If (Expression bool_expr, Statement true_statement, Location l)
147 : this (bool_expr, true_statement, null, l)
151 public If (Expression bool_expr,
152 Statement true_statement,
153 Statement false_statement,
154 Location l)
156 this.expr = bool_expr;
157 TrueStatement = true_statement;
158 FalseStatement = false_statement;
159 loc = l;
162 public Expression Expr {
163 get {
164 return this.expr;
168 public override bool Resolve (BlockContext ec)
170 bool ok = true;
172 expr = expr.Resolve (ec);
173 if (expr == null) {
174 ok = false;
175 } else {
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
182 if (take) {
183 if (!TrueStatement.Resolve (ec))
184 return false;
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
188 return false;
189 FalseStatement = null;
190 } else {
191 if (!TrueStatement.ResolveUnreachable (ec, true))
192 return false;
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
197 return false;
200 return true;
204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206 ok &= TrueStatement.Resolve (ec);
208 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210 ec.CurrentBranching.CreateSibling ();
212 if (FalseStatement != null)
213 ok &= FalseStatement.Resolve (ec);
215 ec.EndFlowBranching ();
217 return ok;
220 protected override void DoEmit (EmitContext ec)
222 Label false_target = ec.DefineLabel ();
223 Label end;
226 // If we're a boolean constant, Resolve() already
227 // eliminated dead code for us.
229 Constant c = expr as Constant;
230 if (c != null){
231 c.EmitSideEffect (ec);
233 if (!c.IsDefaultValue)
234 TrueStatement.Emit (ec);
235 else if (FalseStatement != null)
236 FalseStatement.Emit (ec);
238 return;
241 expr.EmitBranchable (ec, false_target, false);
243 TrueStatement.Emit (ec);
245 if (FalseStatement != null){
246 bool branch_emitted = false;
248 end = ec.DefineLabel ();
249 if (!is_true_ret){
250 ec.Emit (OpCodes.Br, end);
251 branch_emitted = true;
254 ec.MarkLabel (false_target);
255 FalseStatement.Emit (ec);
257 if (branch_emitted)
258 ec.MarkLabel (end);
259 } else {
260 ec.MarkLabel (false_target);
264 protected override void CloneTo (CloneContext clonectx, Statement t)
266 If target = (If) t;
268 target.expr = expr.Clone (clonectx);
269 target.TrueStatement = TrueStatement.Clone (clonectx);
270 if (FalseStatement != null)
271 target.FalseStatement = FalseStatement.Clone (clonectx);
274 public override object Accept (StructuralVisitor visitor)
276 return visitor.Visit (this);
280 public class Do : Statement {
281 public Expression expr;
282 public Statement EmbeddedStatement;
284 public Do (Statement statement, BooleanExpression bool_expr, Location l)
286 expr = bool_expr;
287 EmbeddedStatement = statement;
288 loc = l;
291 public override bool Resolve (BlockContext ec)
293 bool ok = true;
295 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
297 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
299 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
300 if (!EmbeddedStatement.Resolve (ec))
301 ok = false;
302 ec.EndFlowBranching ();
304 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
305 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
307 expr = expr.Resolve (ec);
308 if (expr == null)
309 ok = false;
310 else if (expr is Constant){
311 bool infinite = !((Constant) expr).IsDefaultValue;
312 if (infinite)
313 ec.CurrentBranching.CurrentUsageVector.Goto ();
316 ec.EndFlowBranching ();
318 return ok;
321 protected override void DoEmit (EmitContext ec)
323 Label loop = ec.DefineLabel ();
324 Label old_begin = ec.LoopBegin;
325 Label old_end = ec.LoopEnd;
327 ec.LoopBegin = ec.DefineLabel ();
328 ec.LoopEnd = ec.DefineLabel ();
330 ec.MarkLabel (loop);
331 EmbeddedStatement.Emit (ec);
332 ec.MarkLabel (ec.LoopBegin);
334 // Mark start of while condition
335 ec.Mark (expr.Location);
338 // Dead code elimination
340 if (expr is Constant) {
341 bool res = !((Constant) expr).IsDefaultValue;
343 expr.EmitSideEffect (ec);
344 if (res)
345 ec.Emit (OpCodes.Br, loop);
346 } else {
347 expr.EmitBranchable (ec, loop, true);
350 ec.MarkLabel (ec.LoopEnd);
352 ec.LoopBegin = old_begin;
353 ec.LoopEnd = old_end;
356 protected override void CloneTo (CloneContext clonectx, Statement t)
358 Do target = (Do) t;
360 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
361 target.expr = expr.Clone (clonectx);
364 public override object Accept (StructuralVisitor visitor)
366 return visitor.Visit (this);
370 public class While : Statement {
371 public Expression expr;
372 public Statement Statement;
373 bool infinite, empty;
375 public While (BooleanExpression bool_expr, Statement statement, Location l)
377 this.expr = bool_expr;
378 Statement = statement;
379 loc = l;
382 public override bool Resolve (BlockContext ec)
384 bool ok = true;
386 expr = expr.Resolve (ec);
387 if (expr == null)
388 ok = false;
391 // Inform whether we are infinite or not
393 if (expr is Constant){
394 bool value = !((Constant) expr).IsDefaultValue;
396 if (value == false){
397 if (!Statement.ResolveUnreachable (ec, true))
398 return false;
399 empty = true;
400 return true;
401 } else
402 infinite = true;
405 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
406 if (!infinite)
407 ec.CurrentBranching.CreateSibling ();
409 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
410 if (!Statement.Resolve (ec))
411 ok = false;
412 ec.EndFlowBranching ();
414 // There's no direct control flow from the end of the embedded statement to the end of the loop
415 ec.CurrentBranching.CurrentUsageVector.Goto ();
417 ec.EndFlowBranching ();
419 return ok;
422 protected override void DoEmit (EmitContext ec)
424 if (empty) {
425 expr.EmitSideEffect (ec);
426 return;
429 Label old_begin = ec.LoopBegin;
430 Label old_end = ec.LoopEnd;
432 ec.LoopBegin = ec.DefineLabel ();
433 ec.LoopEnd = ec.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr is Constant) {
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ec.MarkLabel (ec.LoopBegin);
442 if (ec.EmitAccurateDebugInfo)
443 ec.Emit (OpCodes.Nop);
445 expr.EmitSideEffect (ec);
446 Statement.Emit (ec);
447 ec.Emit (OpCodes.Br, ec.LoopBegin);
450 // Inform that we are infinite (ie, `we return'), only
451 // if we do not `break' inside the code.
453 ec.MarkLabel (ec.LoopEnd);
454 } else {
455 Label while_loop = ec.DefineLabel ();
457 ec.Emit (OpCodes.Br, ec.LoopBegin);
458 ec.MarkLabel (while_loop);
460 Statement.Emit (ec);
462 ec.MarkLabel (ec.LoopBegin);
464 ec.Mark (expr.Location);
465 expr.EmitBranchable (ec, while_loop, true);
467 ec.MarkLabel (ec.LoopEnd);
470 ec.LoopBegin = old_begin;
471 ec.LoopEnd = old_end;
474 protected override void CloneTo (CloneContext clonectx, Statement t)
476 While target = (While) t;
478 target.expr = expr.Clone (clonectx);
479 target.Statement = Statement.Clone (clonectx);
482 public override object Accept (StructuralVisitor visitor)
484 return visitor.Visit (this);
488 public class For : Statement
490 bool infinite, empty;
492 public For (Location l)
494 loc = l;
497 public Statement Initializer {
498 get; set;
501 public Expression Condition {
502 get; set;
505 public Statement Iterator {
506 get; set;
509 public Statement Statement {
510 get; set;
513 public override bool Resolve (BlockContext ec)
515 bool ok = true;
517 if (Initializer != null) {
518 if (!Initializer.Resolve (ec))
519 ok = false;
522 if (Condition != null) {
523 Condition = Condition.Resolve (ec);
524 if (Condition == null)
525 ok = false;
526 else if (Condition is Constant) {
527 bool value = !((Constant) Condition).IsDefaultValue;
529 if (value == false){
530 if (!Statement.ResolveUnreachable (ec, true))
531 return false;
532 if ((Iterator != null) &&
533 !Iterator.ResolveUnreachable (ec, false))
534 return false;
535 empty = true;
536 return true;
537 } else
538 infinite = true;
540 } else
541 infinite = true;
543 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
544 if (!infinite)
545 ec.CurrentBranching.CreateSibling ();
547 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
549 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
550 if (!Statement.Resolve (ec))
551 ok = false;
552 ec.EndFlowBranching ();
554 if (Iterator != null){
555 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
556 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
557 ok = false;
558 } else {
559 if (!Iterator.Resolve (ec))
560 ok = false;
564 // There's no direct control flow from the end of the embedded statement to the end of the loop
565 ec.CurrentBranching.CurrentUsageVector.Goto ();
567 ec.EndFlowBranching ();
569 return ok;
572 protected override void DoEmit (EmitContext ec)
574 if (Initializer != null)
575 Initializer.Emit (ec);
577 if (empty) {
578 Condition.EmitSideEffect (ec);
579 return;
582 Label old_begin = ec.LoopBegin;
583 Label old_end = ec.LoopEnd;
584 Label loop = ec.DefineLabel ();
585 Label test = ec.DefineLabel ();
587 ec.LoopBegin = ec.DefineLabel ();
588 ec.LoopEnd = ec.DefineLabel ();
590 ec.Emit (OpCodes.Br, test);
591 ec.MarkLabel (loop);
592 Statement.Emit (ec);
594 ec.MarkLabel (ec.LoopBegin);
595 Iterator.Emit (ec);
597 ec.MarkLabel (test);
599 // If test is null, there is no test, and we are just
600 // an infinite loop
602 if (Condition != null) {
603 ec.Mark (Condition.Location);
606 // The Resolve code already catches the case for
607 // Test == Constant (false) so we know that
608 // this is true
610 if (Condition is Constant) {
611 Condition.EmitSideEffect (ec);
612 ec.Emit (OpCodes.Br, loop);
613 } else {
614 Condition.EmitBranchable (ec, loop, true);
617 } else
618 ec.Emit (OpCodes.Br, loop);
619 ec.MarkLabel (ec.LoopEnd);
621 ec.LoopBegin = old_begin;
622 ec.LoopEnd = old_end;
625 protected override void CloneTo (CloneContext clonectx, Statement t)
627 For target = (For) t;
629 if (Initializer != null)
630 target.Initializer = Initializer.Clone (clonectx);
631 if (Condition != null)
632 target.Condition = Condition.Clone (clonectx);
633 if (Iterator != null)
634 target.Iterator = Iterator.Clone (clonectx);
635 target.Statement = Statement.Clone (clonectx);
638 public override object Accept (StructuralVisitor visitor)
640 return visitor.Visit (this);
644 public class StatementExpression : Statement
646 ExpressionStatement expr;
648 public StatementExpression (ExpressionStatement expr)
650 this.expr = expr;
651 loc = expr.Location;
654 public StatementExpression (ExpressionStatement expr, Location loc)
656 this.expr = expr;
657 this.loc = loc;
660 public ExpressionStatement Expr {
661 get {
662 return this.expr;
666 protected override void CloneTo (CloneContext clonectx, Statement t)
668 StatementExpression target = (StatementExpression) t;
669 target.expr = (ExpressionStatement) expr.Clone (clonectx);
672 protected override void DoEmit (EmitContext ec)
674 expr.EmitStatement (ec);
677 public override bool Resolve (BlockContext ec)
679 expr = expr.ResolveStatement (ec);
680 return expr != null;
683 public override object Accept (StructuralVisitor visitor)
685 return visitor.Visit (this);
689 public class StatementErrorExpression : Statement
691 readonly Expression expr;
693 public StatementErrorExpression (Expression expr)
695 this.expr = expr;
698 public Expression Expr {
699 get {
700 return expr;
704 protected override void DoEmit (EmitContext ec)
706 throw new NotSupportedException ();
709 protected override void CloneTo (CloneContext clonectx, Statement target)
711 throw new NotImplementedException ();
714 public override object Accept (StructuralVisitor visitor)
716 return visitor.Visit (this);
721 // Simple version of statement list not requiring a block
723 public class StatementList : Statement
725 List<Statement> statements;
727 public StatementList (Statement first, Statement second)
729 statements = new List<Statement> () { first, second };
732 #region Properties
733 public IList<Statement> Statements {
734 get {
735 return statements;
738 #endregion
740 public void Add (Statement statement)
742 statements.Add (statement);
745 public override bool Resolve (BlockContext ec)
747 foreach (var s in statements)
748 s.Resolve (ec);
750 return true;
753 protected override void DoEmit (EmitContext ec)
755 foreach (var s in statements)
756 s.Emit (ec);
759 protected override void CloneTo (CloneContext clonectx, Statement target)
761 StatementList t = (StatementList) target;
763 t.statements = new List<Statement> (statements.Count);
764 foreach (Statement s in statements)
765 t.statements.Add (s.Clone (clonectx));
768 public override object Accept (StructuralVisitor visitor)
770 return visitor.Visit (this);
774 // A 'return' or a 'yield break'
775 public abstract class ExitStatement : Statement
777 protected bool unwind_protect;
778 protected abstract bool DoResolve (BlockContext ec);
780 public virtual void Error_FinallyClause (Report Report)
782 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
785 public sealed override bool Resolve (BlockContext ec)
787 if (!DoResolve (ec))
788 return false;
790 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
791 ec.CurrentBranching.CurrentUsageVector.Goto ();
792 return true;
796 /// <summary>
797 /// Implements the return statement
798 /// </summary>
799 public class Return : ExitStatement
801 Expression expr;
803 public Return (Expression expr, Location l)
805 this.expr = expr;
806 loc = l;
809 #region Properties
811 public Expression Expr {
812 get {
813 return expr;
815 protected set {
816 expr = value;
820 #endregion
822 protected override bool DoResolve (BlockContext ec)
824 if (expr == null) {
825 if (ec.ReturnType.Kind == MemberKind.Void)
826 return true;
829 // Return must not be followed by an expression when
830 // the method return type is Task
832 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
833 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
834 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
836 // Extra trick not to emit ret/leave inside awaiter body
838 expr = EmptyExpression.Null;
839 return true;
843 if (ec.CurrentIterator != null) {
844 Error_ReturnFromIterator (ec);
845 } else {
846 ec.Report.Error (126, loc,
847 "An object of a type convertible to `{0}' is required for the return statement",
848 ec.ReturnType.GetSignatureForError ());
851 return false;
854 expr = expr.Resolve (ec);
855 TypeSpec block_return_type = ec.ReturnType;
857 AnonymousExpression am = ec.CurrentAnonymousMethod;
858 if (am == null) {
859 if (block_return_type.Kind == MemberKind.Void) {
860 ec.Report.Error (127, loc,
861 "`{0}': A return keyword must not be followed by any expression when method returns void",
862 ec.GetSignatureForError ());
864 return false;
866 } else {
867 if (am.IsIterator) {
868 Error_ReturnFromIterator (ec);
869 return false;
872 var async_block = am as AsyncInitializer;
873 if (async_block != null) {
874 if (expr != null) {
875 var storey = (AsyncTaskStorey) am.Storey;
876 var async_type = storey.ReturnType;
878 if (async_type == null && async_block.ReturnTypeInference != null) {
879 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
880 return true;
883 // TODO: Better error message
884 if (async_type.Kind == MemberKind.Void) {
885 ec.Report.Error (127, loc,
886 "`{0}': A return keyword must not be followed by any expression when method returns void",
887 ec.GetSignatureForError ());
889 return false;
892 if (!async_type.IsGenericTask) {
893 if (this is ContextualReturn)
894 return true;
896 ec.Report.Error (1997, loc,
897 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
898 ec.GetSignatureForError ());
899 return false;
903 // The return type is actually Task<T> type argument
905 if (expr.Type == async_type) {
906 ec.Report.Error (4016, loc,
907 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
908 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
909 } else {
910 block_return_type = async_type.TypeArguments[0];
913 } else {
914 var l = am as AnonymousMethodBody;
915 if (l != null && l.ReturnTypeInference != null && expr != null) {
916 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
917 return true;
922 if (expr == null)
923 return false;
925 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
926 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
928 if (expr == null) {
929 if (am != null && block_return_type == ec.ReturnType) {
930 ec.Report.Error (1662, loc,
931 "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",
932 am.ContainerType, am.GetSignatureForError ());
934 return false;
938 return true;
941 protected override void DoEmit (EmitContext ec)
943 if (expr != null) {
944 expr.Emit (ec);
946 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
947 if (async_body != null) {
948 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
950 // It's null for await without async
951 if (async_return != null) {
952 async_return.EmitAssign (ec);
954 ec.EmitEpilogue ();
956 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
959 return;
962 ec.EmitEpilogue ();
964 if (unwind_protect || ec.EmitAccurateDebugInfo)
965 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
968 if (unwind_protect) {
969 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
970 } else if (ec.EmitAccurateDebugInfo) {
971 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
972 } else {
973 ec.Emit (OpCodes.Ret);
977 void Error_ReturnFromIterator (ResolveContext rc)
979 rc.Report.Error (1622, loc,
980 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
983 protected override void CloneTo (CloneContext clonectx, Statement t)
985 Return target = (Return) t;
986 // It's null for simple return;
987 if (expr != null)
988 target.expr = expr.Clone (clonectx);
991 public override object Accept (StructuralVisitor visitor)
993 return visitor.Visit (this);
997 public class Goto : Statement {
998 string target;
999 LabeledStatement label;
1000 bool unwind_protect;
1002 public override bool Resolve (BlockContext ec)
1004 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1005 ec.CurrentBranching.CurrentUsageVector.Goto ();
1006 return true;
1009 public Goto (string label, Location l)
1011 loc = l;
1012 target = label;
1015 public string Target {
1016 get { return target; }
1019 public void SetResolvedTarget (LabeledStatement label)
1021 this.label = label;
1022 label.AddReference ();
1025 protected override void CloneTo (CloneContext clonectx, Statement target)
1027 // Nothing to clone
1030 protected override void DoEmit (EmitContext ec)
1032 if (label == null)
1033 throw new InternalErrorException ("goto emitted before target resolved");
1034 Label l = label.LabelTarget (ec);
1035 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1038 public override object Accept (StructuralVisitor visitor)
1040 return visitor.Visit (this);
1044 public class LabeledStatement : Statement {
1045 string name;
1046 bool defined;
1047 bool referenced;
1048 Label label;
1049 Block block;
1051 FlowBranching.UsageVector vectors;
1053 public LabeledStatement (string name, Block block, Location l)
1055 this.name = name;
1056 this.block = block;
1057 this.loc = l;
1060 public Label LabelTarget (EmitContext ec)
1062 if (defined)
1063 return label;
1065 label = ec.DefineLabel ();
1066 defined = true;
1067 return label;
1070 public Block Block {
1071 get {
1072 return block;
1076 public string Name {
1077 get { return name; }
1080 public bool IsDefined {
1081 get { return defined; }
1084 public bool HasBeenReferenced {
1085 get { return referenced; }
1088 public FlowBranching.UsageVector JumpOrigins {
1089 get { return vectors; }
1092 public void AddUsageVector (FlowBranching.UsageVector vector)
1094 vector = vector.Clone ();
1095 vector.Next = vectors;
1096 vectors = vector;
1099 protected override void CloneTo (CloneContext clonectx, Statement target)
1101 // nothing to clone
1104 public override bool Resolve (BlockContext ec)
1106 // this flow-branching will be terminated when the surrounding block ends
1107 ec.StartFlowBranching (this);
1108 return true;
1111 protected override void DoEmit (EmitContext ec)
1113 if (!HasBeenReferenced)
1114 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1116 LabelTarget (ec);
1117 ec.MarkLabel (label);
1120 public void AddReference ()
1122 referenced = true;
1125 public override object Accept (StructuralVisitor visitor)
1127 return visitor.Visit (this);
1132 /// <summary>
1133 /// `goto default' statement
1134 /// </summary>
1135 public class GotoDefault : Statement {
1137 public GotoDefault (Location l)
1139 loc = l;
1142 protected override void CloneTo (CloneContext clonectx, Statement target)
1144 // nothing to clone
1147 public override bool Resolve (BlockContext ec)
1149 ec.CurrentBranching.CurrentUsageVector.Goto ();
1151 if (ec.Switch == null) {
1152 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1153 return false;
1156 if (!ec.Switch.GotDefault) {
1157 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1158 return false;
1161 return true;
1164 protected override void DoEmit (EmitContext ec)
1166 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1169 public override object Accept (StructuralVisitor visitor)
1171 return visitor.Visit (this);
1175 /// <summary>
1176 /// `goto case' statement
1177 /// </summary>
1178 public class GotoCase : Statement {
1179 Expression expr;
1180 SwitchLabel sl;
1182 public GotoCase (Expression e, Location l)
1184 expr = e;
1185 loc = l;
1188 public Expression Expr {
1189 get {
1190 return this.expr;
1194 public override bool Resolve (BlockContext ec)
1196 if (ec.Switch == null){
1197 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1198 return false;
1201 ec.CurrentBranching.CurrentUsageVector.Goto ();
1203 expr = expr.Resolve (ec);
1204 if (expr == null)
1205 return false;
1207 Constant c = expr as Constant;
1208 if (c == null) {
1209 ec.Report.Error (150, expr.Location, "A constant value is expected");
1210 return false;
1213 Constant res;
1214 if (ec.Switch.IsNullable && c is NullLiteral) {
1215 res = c;
1216 } else {
1217 TypeSpec type = ec.Switch.SwitchType;
1218 res = c.TryReduce (ec, type, c.Location);
1219 if (res == null) {
1220 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1221 return false;
1224 if (!Convert.ImplicitStandardConversionExists (c, type))
1225 ec.Report.Warning (469, 2, loc,
1226 "The `goto case' value is not implicitly convertible to type `{0}'",
1227 TypeManager.CSharpName (type));
1231 sl = ec.Switch.ResolveGotoCase (ec, res);
1232 return true;
1235 protected override void DoEmit (EmitContext ec)
1237 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1240 protected override void CloneTo (CloneContext clonectx, Statement t)
1242 GotoCase target = (GotoCase) t;
1244 target.expr = expr.Clone (clonectx);
1247 public override object Accept (StructuralVisitor visitor)
1249 return visitor.Visit (this);
1253 public class Throw : Statement {
1254 Expression expr;
1256 public Throw (Expression expr, Location l)
1258 this.expr = expr;
1259 loc = l;
1262 public Expression Expr {
1263 get {
1264 return this.expr;
1268 public override bool Resolve (BlockContext ec)
1270 if (expr == null) {
1271 ec.CurrentBranching.CurrentUsageVector.Goto ();
1272 return ec.CurrentBranching.CheckRethrow (loc);
1275 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1276 ec.CurrentBranching.CurrentUsageVector.Goto ();
1278 if (expr == null)
1279 return false;
1281 var et = ec.BuiltinTypes.Exception;
1282 if (Convert.ImplicitConversionExists (ec, expr, et))
1283 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1284 else
1285 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1287 return true;
1290 protected override void DoEmit (EmitContext ec)
1292 if (expr == null)
1293 ec.Emit (OpCodes.Rethrow);
1294 else {
1295 expr.Emit (ec);
1297 ec.Emit (OpCodes.Throw);
1301 protected override void CloneTo (CloneContext clonectx, Statement t)
1303 Throw target = (Throw) t;
1305 if (expr != null)
1306 target.expr = expr.Clone (clonectx);
1309 public override object Accept (StructuralVisitor visitor)
1311 return visitor.Visit (this);
1315 public class Break : Statement {
1317 public Break (Location l)
1319 loc = l;
1322 bool unwind_protect;
1324 public override bool Resolve (BlockContext ec)
1326 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1327 ec.CurrentBranching.CurrentUsageVector.Goto ();
1328 return true;
1331 protected override void DoEmit (EmitContext ec)
1333 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1336 protected override void CloneTo (CloneContext clonectx, Statement t)
1338 // nothing needed
1341 public override object Accept (StructuralVisitor visitor)
1343 return visitor.Visit (this);
1347 public class Continue : Statement {
1349 public Continue (Location l)
1351 loc = l;
1354 bool unwind_protect;
1356 public override bool Resolve (BlockContext ec)
1358 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1359 ec.CurrentBranching.CurrentUsageVector.Goto ();
1360 return true;
1363 protected override void DoEmit (EmitContext ec)
1365 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1368 protected override void CloneTo (CloneContext clonectx, Statement t)
1370 // nothing needed.
1373 public override object Accept (StructuralVisitor visitor)
1375 return visitor.Visit (this);
1379 public interface ILocalVariable
1381 void Emit (EmitContext ec);
1382 void EmitAssign (EmitContext ec);
1383 void EmitAddressOf (EmitContext ec);
1386 public interface INamedBlockVariable
1388 Block Block { get; }
1389 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1390 bool IsDeclared { get; }
1391 bool IsParameter { get; }
1392 Location Location { get; }
1395 public class BlockVariableDeclaration : Statement
1397 public class Declarator
1399 LocalVariable li;
1400 Expression initializer;
1402 public Declarator (LocalVariable li, Expression initializer)
1404 if (li.Type != null)
1405 throw new ArgumentException ("Expected null variable type");
1407 this.li = li;
1408 this.initializer = initializer;
1411 public Declarator (Declarator clone, Expression initializer)
1413 this.li = clone.li;
1414 this.initializer = initializer;
1417 #region Properties
1419 public LocalVariable Variable {
1420 get {
1421 return li;
1425 public Expression Initializer {
1426 get {
1427 return initializer;
1429 set {
1430 initializer = value;
1434 #endregion
1437 Expression initializer;
1438 protected FullNamedExpression type_expr;
1439 protected LocalVariable li;
1440 protected List<Declarator> declarators;
1442 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1444 this.type_expr = type;
1445 this.li = li;
1446 this.loc = type_expr.Location;
1449 protected BlockVariableDeclaration (LocalVariable li)
1451 this.li = li;
1454 #region Properties
1456 public List<Declarator> Declarators {
1457 get {
1458 return declarators;
1462 public Expression Initializer {
1463 get {
1464 return initializer;
1466 set {
1467 initializer = value;
1471 public FullNamedExpression TypeExpression {
1472 get {
1473 return type_expr;
1477 public LocalVariable Variable {
1478 get {
1479 return li;
1483 #endregion
1485 public void AddDeclarator (Declarator decl)
1487 if (declarators == null)
1488 declarators = new List<Declarator> ();
1490 declarators.Add (decl);
1493 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1495 if (bc.Report.Errors != 0)
1496 return;
1498 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1500 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1501 new MemberName (li.Name, li.Location), null);
1503 container.AddField (f);
1504 f.Define ();
1506 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1507 li.SetIsUsed ();
1510 public override bool Resolve (BlockContext bc)
1512 return Resolve (bc, true);
1515 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1517 if (li.Type == null) {
1518 TypeSpec type = null;
1519 var vexpr = type_expr as VarExpr;
1522 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1523 // same name exists or as a keyword when no type was found
1525 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1526 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1527 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1529 if (li.IsFixed) {
1530 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1531 return false;
1534 if (li.IsConstant) {
1535 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1536 return false;
1539 if (Initializer == null) {
1540 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1541 return false;
1544 if (declarators != null) {
1545 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1546 declarators = null;
1549 Initializer = Initializer.Resolve (bc);
1550 if (Initializer != null) {
1551 ((VarExpr) type_expr).InferType (bc, Initializer);
1552 type = type_expr.Type;
1553 } else {
1554 // Set error type to indicate the var was placed correctly but could
1555 // not be infered
1557 // var a = missing ();
1559 type = InternalType.ErrorType;
1563 if (type == null) {
1564 type = type_expr.ResolveAsType (bc);
1565 if (type == null)
1566 return false;
1568 if (li.IsConstant && !type.IsConstantCompatible) {
1569 Const.Error_InvalidConstantType (type, loc, bc.Report);
1573 if (type.IsStatic)
1574 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1576 li.Type = type;
1579 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1580 if (eval_global) {
1581 CreateEvaluatorVariable (bc, li);
1582 } else {
1583 li.PrepareForFlowAnalysis (bc);
1586 if (initializer != null) {
1587 initializer = ResolveInitializer (bc, li, initializer);
1588 // li.Variable.DefinitelyAssigned
1591 if (declarators != null) {
1592 foreach (var d in declarators) {
1593 d.Variable.Type = li.Type;
1594 if (eval_global) {
1595 CreateEvaluatorVariable (bc, d.Variable);
1596 } else {
1597 d.Variable.PrepareForFlowAnalysis (bc);
1600 if (d.Initializer != null && resolveDeclaratorInitializers) {
1601 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1602 // d.Variable.DefinitelyAssigned
1607 return true;
1610 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1612 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1613 return a.ResolveStatement (bc);
1616 protected override void DoEmit (EmitContext ec)
1618 li.CreateBuilder (ec);
1620 if (Initializer != null)
1621 ((ExpressionStatement) Initializer).EmitStatement (ec);
1623 if (declarators != null) {
1624 foreach (var d in declarators) {
1625 d.Variable.CreateBuilder (ec);
1626 if (d.Initializer != null)
1627 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1632 protected override void CloneTo (CloneContext clonectx, Statement target)
1634 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1636 if (type_expr != null)
1637 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1639 if (initializer != null)
1640 t.initializer = initializer.Clone (clonectx);
1642 if (declarators != null) {
1643 t.declarators = null;
1644 foreach (var d in declarators)
1645 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1649 public override object Accept (StructuralVisitor visitor)
1651 return visitor.Visit (this);
1655 public class BlockConstantDeclaration : BlockVariableDeclaration
1657 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1658 : base (type, li)
1662 public override void Emit (EmitContext ec)
1664 // Nothing to emit, not even sequence point
1667 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1669 initializer = initializer.Resolve (bc);
1670 if (initializer == null)
1671 return null;
1673 var c = initializer as Constant;
1674 if (c == null) {
1675 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1676 return null;
1679 c = c.ConvertImplicitly (li.Type);
1680 if (c == null) {
1681 if (TypeSpec.IsReferenceType (li.Type))
1682 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1683 else
1684 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1686 return null;
1689 li.ConstantValue = c;
1690 return initializer;
1693 public override object Accept (StructuralVisitor visitor)
1695 return visitor.Visit (this);
1700 // The information about a user-perceived local variable
1702 public class LocalVariable : INamedBlockVariable, ILocalVariable
1704 [Flags]
1705 public enum Flags
1707 Used = 1,
1708 IsThis = 1 << 1,
1709 AddressTaken = 1 << 2,
1710 CompilerGenerated = 1 << 3,
1711 Constant = 1 << 4,
1712 ForeachVariable = 1 << 5,
1713 FixedVariable = 1 << 6,
1714 UsingVariable = 1 << 7,
1715 // DefinitelyAssigned = 1 << 8,
1716 IsLocked = 1 << 9,
1718 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1721 TypeSpec type;
1722 readonly string name;
1723 readonly Location loc;
1724 readonly Block block;
1725 Flags flags;
1726 Constant const_value;
1728 public VariableInfo VariableInfo;
1729 HoistedVariable hoisted_variant;
1731 LocalBuilder builder;
1733 public LocalVariable (Block block, string name, Location loc)
1735 this.block = block;
1736 this.name = name;
1737 this.loc = loc;
1740 public LocalVariable (Block block, string name, Flags flags, Location loc)
1741 : this (block, name, loc)
1743 this.flags = flags;
1747 // Used by variable declarators
1749 public LocalVariable (LocalVariable li, string name, Location loc)
1750 : this (li.block, name, li.flags, loc)
1754 #region Properties
1756 public bool AddressTaken {
1757 get {
1758 return (flags & Flags.AddressTaken) != 0;
1762 public Block Block {
1763 get {
1764 return block;
1768 public Constant ConstantValue {
1769 get {
1770 return const_value;
1772 set {
1773 const_value = value;
1778 // Hoisted local variable variant
1780 public HoistedVariable HoistedVariant {
1781 get {
1782 return hoisted_variant;
1784 set {
1785 hoisted_variant = value;
1789 public bool IsDeclared {
1790 get {
1791 return type != null;
1795 public bool IsCompilerGenerated {
1796 get {
1797 return (flags & Flags.CompilerGenerated) != 0;
1801 public bool IsConstant {
1802 get {
1803 return (flags & Flags.Constant) != 0;
1807 public bool IsLocked {
1808 get {
1809 return (flags & Flags.IsLocked) != 0;
1811 set {
1812 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1816 public bool IsThis {
1817 get {
1818 return (flags & Flags.IsThis) != 0;
1822 public bool IsFixed {
1823 get {
1824 return (flags & Flags.FixedVariable) != 0;
1828 bool INamedBlockVariable.IsParameter {
1829 get {
1830 return false;
1834 public bool IsReadonly {
1835 get {
1836 return (flags & Flags.ReadonlyMask) != 0;
1840 public Location Location {
1841 get {
1842 return loc;
1846 public string Name {
1847 get {
1848 return name;
1852 public TypeSpec Type {
1853 get {
1854 return type;
1856 set {
1857 type = value;
1861 #endregion
1863 public void CreateBuilder (EmitContext ec)
1865 if ((flags & Flags.Used) == 0) {
1866 if (VariableInfo == null) {
1867 // Missing flow analysis or wrong variable flags
1868 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1871 if (VariableInfo.IsEverAssigned)
1872 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1873 else
1874 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1877 if (HoistedVariant != null)
1878 return;
1880 if (builder != null) {
1881 if ((flags & Flags.CompilerGenerated) != 0)
1882 return;
1884 // To avoid Used warning duplicates
1885 throw new InternalErrorException ("Already created variable `{0}'", name);
1889 // All fixed variabled are pinned, a slot has to be alocated
1891 builder = ec.DeclareLocal (Type, IsFixed);
1892 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1893 ec.DefineLocalVariable (name, builder);
1896 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1898 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1899 li.Type = type;
1900 return li;
1903 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1905 if (IsConstant && const_value != null)
1906 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1908 return new LocalVariableReference (this, loc);
1911 public void Emit (EmitContext ec)
1913 // TODO: Need something better for temporary variables
1914 if ((flags & Flags.CompilerGenerated) != 0)
1915 CreateBuilder (ec);
1917 ec.Emit (OpCodes.Ldloc, builder);
1920 public void EmitAssign (EmitContext ec)
1922 // TODO: Need something better for temporary variables
1923 if ((flags & Flags.CompilerGenerated) != 0)
1924 CreateBuilder (ec);
1926 ec.Emit (OpCodes.Stloc, builder);
1929 public void EmitAddressOf (EmitContext ec)
1931 ec.Emit (OpCodes.Ldloca, builder);
1934 public static string GetCompilerGeneratedName (Block block)
1936 // HACK: Debugger depends on the name semantics
1937 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1940 public string GetReadOnlyContext ()
1942 switch (flags & Flags.ReadonlyMask) {
1943 case Flags.FixedVariable:
1944 return "fixed variable";
1945 case Flags.ForeachVariable:
1946 return "foreach iteration variable";
1947 case Flags.UsingVariable:
1948 return "using variable";
1951 throw new InternalErrorException ("Variable is not readonly");
1954 public bool IsThisAssigned (BlockContext ec, Block block)
1956 if (VariableInfo == null)
1957 throw new Exception ();
1959 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1960 return true;
1962 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1965 public bool IsAssigned (BlockContext ec)
1967 if (VariableInfo == null)
1968 throw new Exception ();
1970 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1973 public void PrepareForFlowAnalysis (BlockContext bc)
1976 // No need for definitely assigned check for these guys
1978 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1979 return;
1981 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1982 bc.FlowOffset += VariableInfo.Length;
1986 // Mark the variables as referenced in the user code
1988 public void SetIsUsed ()
1990 flags |= Flags.Used;
1993 public void SetHasAddressTaken ()
1995 flags |= (Flags.AddressTaken | Flags.Used);
1998 public override string ToString ()
2000 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2004 /// <summary>
2005 /// Block represents a C# block.
2006 /// </summary>
2008 /// <remarks>
2009 /// This class is used in a number of places: either to represent
2010 /// explicit blocks that the programmer places or implicit blocks.
2012 /// Implicit blocks are used as labels or to introduce variable
2013 /// declarations.
2015 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2016 /// they contain extra information that is not necessary on normal blocks.
2017 /// </remarks>
2018 public class Block : Statement {
2019 [Flags]
2020 public enum Flags
2022 Unchecked = 1,
2023 HasRet = 8,
2024 Unsafe = 16,
2025 HasCapturedVariable = 64,
2026 HasCapturedThis = 1 << 7,
2027 IsExpressionTree = 1 << 8,
2028 CompilerGenerated = 1 << 9,
2029 HasAsyncModifier = 1 << 10,
2030 Resolved = 1 << 11,
2031 YieldBlock = 1 << 12,
2032 AwaitBlock = 1 << 13
2035 public Block Parent;
2036 public Location StartLocation;
2037 public Location EndLocation;
2039 public ExplicitBlock Explicit;
2040 public ParametersBlock ParametersBlock;
2042 protected Flags flags;
2045 // The statements in this block
2047 protected List<Statement> statements;
2049 protected List<Statement> scope_initializers;
2051 int? resolving_init_idx;
2053 Block original;
2055 #if DEBUG
2056 static int id;
2057 public int ID = id++;
2059 static int clone_id_counter;
2060 int clone_id;
2061 #endif
2063 // int assignable_slots;
2064 bool unreachable_shown;
2065 bool unreachable;
2067 public Block (Block parent, Location start, Location end)
2068 : this (parent, 0, start, end)
2072 public Block (Block parent, Flags flags, Location start, Location end)
2074 if (parent != null) {
2075 // the appropriate constructors will fixup these fields
2076 ParametersBlock = parent.ParametersBlock;
2077 Explicit = parent.Explicit;
2080 this.Parent = parent;
2081 this.flags = flags;
2082 this.StartLocation = start;
2083 this.EndLocation = end;
2084 this.loc = start;
2085 statements = new List<Statement> (4);
2087 this.original = this;
2090 #region Properties
2092 public bool HasUnreachableClosingBrace {
2093 get {
2094 return (flags & Flags.HasRet) != 0;
2096 set {
2097 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2101 public Block Original {
2102 get {
2103 return original;
2105 protected set {
2106 original = value;
2110 public bool IsCompilerGenerated {
2111 get { return (flags & Flags.CompilerGenerated) != 0; }
2112 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2115 public bool Unchecked {
2116 get { return (flags & Flags.Unchecked) != 0; }
2117 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2120 public bool Unsafe {
2121 get { return (flags & Flags.Unsafe) != 0; }
2122 set { flags |= Flags.Unsafe; }
2125 public List<Statement> Statements {
2126 get { return statements; }
2129 #endregion
2131 public Block CreateSwitchBlock (Location start)
2133 // FIXME: Only explicit block should be created
2134 var new_block = new Block (this, start, start);
2135 new_block.IsCompilerGenerated = true;
2136 return new_block;
2139 public void SetEndLocation (Location loc)
2141 EndLocation = loc;
2144 public void AddLabel (LabeledStatement target)
2146 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2149 public void AddLocalName (LocalVariable li)
2151 AddLocalName (li.Name, li);
2154 public void AddLocalName (string name, INamedBlockVariable li)
2156 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2159 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2161 if (reason == null) {
2162 Error_AlreadyDeclared (name, variable);
2163 return;
2166 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2167 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2168 "to `{0}', which is already used in a `{1}' scope to denote something else",
2169 name, reason);
2172 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2174 var pi = variable as ParametersBlock.ParameterInfo;
2175 if (pi != null) {
2176 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2177 } else {
2178 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2179 "A local variable named `{0}' is already defined in this scope", name);
2183 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2185 ParametersBlock.TopBlock.Report.Error (412, loc,
2186 "The type parameter name `{0}' is the same as local variable or parameter name",
2187 name);
2191 // It should be used by expressions which require to
2192 // register a statement during resolve process.
2194 public void AddScopeStatement (Statement s)
2196 if (scope_initializers == null)
2197 scope_initializers = new List<Statement> ();
2200 // Simple recursive helper, when resolve scope initializer another
2201 // new scope initializer can be added, this ensures it's initialized
2202 // before existing one. For now this can happen with expression trees
2203 // in base ctor initializer only
2205 if (resolving_init_idx.HasValue) {
2206 scope_initializers.Insert (resolving_init_idx.Value, s);
2207 ++resolving_init_idx;
2208 } else {
2209 scope_initializers.Add (s);
2213 public void AddStatement (Statement s)
2215 statements.Add (s);
2218 public int AssignableSlots {
2219 get {
2220 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2221 return 4096;
2222 // return assignable_slots;
2226 public LabeledStatement LookupLabel (string name)
2228 return ParametersBlock.TopBlock.GetLabel (name, this);
2231 public override bool Resolve (BlockContext ec)
2233 if ((flags & Flags.Resolved) != 0)
2234 return true;
2236 Block prev_block = ec.CurrentBlock;
2237 bool ok = true;
2239 ec.CurrentBlock = this;
2240 ec.StartFlowBranching (this);
2243 // Compiler generated scope statements
2245 if (scope_initializers != null) {
2246 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2247 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2250 resolving_init_idx = null;
2254 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2255 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2256 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2257 // responsible for handling the situation.
2259 int statement_count = statements.Count;
2260 for (int ix = 0; ix < statement_count; ix++){
2261 Statement s = statements [ix];
2264 // Warn if we detect unreachable code.
2266 if (unreachable) {
2267 if (s is EmptyStatement)
2268 continue;
2270 if (!unreachable_shown && !(s is LabeledStatement)) {
2271 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2272 unreachable_shown = true;
2275 Block c_block = s as Block;
2276 if (c_block != null)
2277 c_block.unreachable = c_block.unreachable_shown = true;
2281 // Note that we're not using ResolveUnreachable() for unreachable
2282 // statements here. ResolveUnreachable() creates a temporary
2283 // flow branching and kills it afterwards. This leads to problems
2284 // if you have two unreachable statements where the first one
2285 // assigns a variable and the second one tries to access it.
2288 if (!s.Resolve (ec)) {
2289 ok = false;
2290 if (ec.IsInProbingMode)
2291 break;
2293 statements [ix] = new EmptyStatement (s.loc);
2294 continue;
2297 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2298 statements [ix] = new EmptyStatement (s.loc);
2300 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2301 if (unreachable && s is LabeledStatement)
2302 throw new InternalErrorException ("should not happen");
2305 while (ec.CurrentBranching is FlowBranchingLabeled)
2306 ec.EndFlowBranching ();
2308 bool flow_unreachable = ec.EndFlowBranching ();
2310 ec.CurrentBlock = prev_block;
2312 if (flow_unreachable)
2313 flags |= Flags.HasRet;
2315 // If we're a non-static `struct' constructor which doesn't have an
2316 // initializer, then we must initialize all of the struct's fields.
2317 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2318 ok = false;
2320 flags |= Flags.Resolved;
2321 return ok;
2324 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2326 unreachable_shown = true;
2327 unreachable = true;
2329 if (warn)
2330 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2332 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2333 fb.CurrentUsageVector.IsUnreachable = true;
2334 bool ok = Resolve (ec);
2335 ec.KillFlowBranching ();
2337 return ok;
2340 protected override void DoEmit (EmitContext ec)
2342 for (int ix = 0; ix < statements.Count; ix++){
2343 statements [ix].Emit (ec);
2347 public override void Emit (EmitContext ec)
2349 if (scope_initializers != null)
2350 EmitScopeInitializers (ec);
2352 DoEmit (ec);
2355 protected void EmitScopeInitializers (EmitContext ec)
2357 foreach (Statement s in scope_initializers)
2358 s.Emit (ec);
2361 #if DEBUG
2362 public override string ToString ()
2364 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2366 #endif
2368 protected override void CloneTo (CloneContext clonectx, Statement t)
2370 Block target = (Block) t;
2371 #if DEBUG
2372 target.clone_id = clone_id_counter++;
2373 #endif
2375 clonectx.AddBlockMap (this, target);
2376 if (original != this)
2377 clonectx.AddBlockMap (original, target);
2379 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2380 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2382 if (Parent != null)
2383 target.Parent = clonectx.RemapBlockCopy (Parent);
2385 target.statements = new List<Statement> (statements.Count);
2386 foreach (Statement s in statements)
2387 target.statements.Add (s.Clone (clonectx));
2390 public override object Accept (StructuralVisitor visitor)
2392 return visitor.Visit (this);
2396 public class ExplicitBlock : Block
2398 protected AnonymousMethodStorey am_storey;
2400 public ExplicitBlock (Block parent, Location start, Location end)
2401 : this (parent, (Flags) 0, start, end)
2405 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2406 : base (parent, flags, start, end)
2408 this.Explicit = this;
2411 #region Properties
2413 public AnonymousMethodStorey AnonymousMethodStorey {
2414 get {
2415 return am_storey;
2419 public bool HasAwait {
2420 get {
2421 return (flags & Flags.AwaitBlock) != 0;
2425 public bool HasCapturedThis {
2426 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2427 get {
2428 return (flags & Flags.HasCapturedThis) != 0;
2432 public bool HasCapturedVariable {
2433 set {
2434 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2436 get {
2437 return (flags & Flags.HasCapturedVariable) != 0;
2441 public bool HasYield {
2442 get {
2443 return (flags & Flags.YieldBlock) != 0;
2447 #endregion
2450 // Creates anonymous method storey in current block
2452 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2455 // Return same story for iterator and async blocks unless we are
2456 // in nested anonymous method
2458 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2459 return ec.CurrentAnonymousMethod.Storey;
2462 // When referencing a variable in parent iterator/async storey
2463 // from nested anonymous method
2465 if (ParametersBlock.am_storey is StateMachine) {
2466 return ParametersBlock.am_storey;
2469 if (am_storey == null) {
2470 MemberBase mc = ec.MemberContext as MemberBase;
2473 // Creates anonymous method storey for this block
2475 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2478 return am_storey;
2481 public override void Emit (EmitContext ec)
2483 // TODO: The is check should go once state machine is fully separated
2484 if (am_storey != null && !(am_storey is StateMachine)) {
2485 DefineStoreyContainer (ec, am_storey);
2486 am_storey.EmitStoreyInstantiation (ec, this);
2489 if (scope_initializers != null)
2490 EmitScopeInitializers (ec);
2492 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2493 ec.Emit (OpCodes.Nop);
2496 if (Parent != null)
2497 ec.BeginScope ();
2499 DoEmit (ec);
2501 if (Parent != null)
2502 ec.EndScope ();
2504 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2505 ec.Emit (OpCodes.Nop);
2509 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2512 // Creates anonymous method storey
2514 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2516 // Creates parent storey reference when hoisted this is accessible
2518 if (storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2519 ExplicitBlock parent = storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2522 // Hoisted this exists in top-level parent storey only
2524 while (parent.AnonymousMethodStorey == null || parent.AnonymousMethodStorey.Parent is AnonymousMethodStorey)
2525 parent = parent.Parent.Explicit;
2527 storey.AddParentStoreyReference (ec, parent.AnonymousMethodStorey);
2530 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2532 // TODO MemberCache: Review
2533 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2536 storey.CreateContainer ();
2537 storey.DefineContainer ();
2539 var ref_blocks = storey.ReferencesFromChildrenBlock;
2540 if (ref_blocks != null) {
2541 foreach (ExplicitBlock ref_block in ref_blocks) {
2542 for (ExplicitBlock b = ref_block.Explicit; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2543 if (b.AnonymousMethodStorey != null) {
2544 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2546 // Stop propagation inside same top block
2547 if (b.ParametersBlock.Original == ParametersBlock.Original)
2548 break;
2550 b = b.ParametersBlock;
2553 b.HasCapturedVariable = true;
2558 storey.Define ();
2559 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2562 public void RegisterAsyncAwait ()
2564 var block = this;
2565 while ((block.flags & Flags.AwaitBlock) == 0) {
2566 block.flags |= Flags.AwaitBlock;
2568 if (block is ParametersBlock)
2569 return;
2571 block = block.Parent.Explicit;
2575 public void RegisterIteratorYield ()
2577 var block = this;
2578 while ((block.flags & Flags.YieldBlock) == 0) {
2579 block.flags |= Flags.YieldBlock;
2581 if (block.Parent == null)
2582 return;
2584 block = block.Parent.Explicit;
2588 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2590 tryBlock.statements = statements;
2591 statements = new List<Statement> (1);
2592 statements.Add (tf);
2597 // ParametersBlock was introduced to support anonymous methods
2598 // and lambda expressions
2600 public class ParametersBlock : ExplicitBlock
2602 public class ParameterInfo : INamedBlockVariable
2604 readonly ParametersBlock block;
2605 readonly int index;
2606 public VariableInfo VariableInfo;
2607 bool is_locked;
2609 public ParameterInfo (ParametersBlock block, int index)
2611 this.block = block;
2612 this.index = index;
2615 #region Properties
2617 public Block Block {
2618 get {
2619 return block;
2623 public bool IsDeclared {
2624 get {
2625 return true;
2629 public bool IsParameter {
2630 get {
2631 return true;
2635 public bool IsLocked {
2636 get {
2637 return is_locked;
2639 set {
2640 is_locked = value;
2644 public Location Location {
2645 get {
2646 return Parameter.Location;
2650 public Parameter Parameter {
2651 get {
2652 return block.Parameters [index];
2656 public TypeSpec ParameterType {
2657 get {
2658 return Parameter.Type;
2662 #endregion
2664 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2666 return new ParameterReference (this, loc);
2671 // Block is converted into an expression
2673 sealed class BlockScopeExpression : Expression
2675 Expression child;
2676 readonly ParametersBlock block;
2678 public BlockScopeExpression (Expression child, ParametersBlock block)
2680 this.child = child;
2681 this.block = block;
2684 public override bool ContainsEmitWithAwait ()
2686 return child.ContainsEmitWithAwait ();
2689 public override Expression CreateExpressionTree (ResolveContext ec)
2691 throw new NotSupportedException ();
2694 protected override Expression DoResolve (ResolveContext ec)
2696 if (child == null)
2697 return null;
2699 child = child.Resolve (ec);
2700 if (child == null)
2701 return null;
2703 eclass = child.eclass;
2704 type = child.Type;
2705 return this;
2708 public override void Emit (EmitContext ec)
2710 block.EmitScopeInitializers (ec);
2711 child.Emit (ec);
2715 protected ParametersCompiled parameters;
2716 protected ParameterInfo[] parameter_info;
2717 bool resolved;
2718 protected bool unreachable;
2719 protected ToplevelBlock top_block;
2720 protected StateMachine state_machine;
2722 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2723 : base (parent, 0, start, start)
2725 if (parameters == null)
2726 throw new ArgumentNullException ("parameters");
2728 this.parameters = parameters;
2729 ParametersBlock = this;
2731 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2733 this.top_block = parent.ParametersBlock.top_block;
2734 ProcessParameters ();
2737 protected ParametersBlock (ParametersCompiled parameters, Location start)
2738 : base (null, 0, start, start)
2740 if (parameters == null)
2741 throw new ArgumentNullException ("parameters");
2743 this.parameters = parameters;
2744 ParametersBlock = this;
2748 // It's supposed to be used by method body implementation of anonymous methods
2750 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2751 : base (null, 0, source.StartLocation, source.EndLocation)
2753 this.parameters = parameters;
2754 this.statements = source.statements;
2755 this.scope_initializers = source.scope_initializers;
2757 this.resolved = true;
2758 this.unreachable = source.unreachable;
2759 this.am_storey = source.am_storey;
2760 this.state_machine = source.state_machine;
2762 ParametersBlock = this;
2765 // Overwrite original for comparison purposes when linking cross references
2766 // between anonymous methods
2768 Original = source;
2771 #region Properties
2773 public bool IsAsync {
2774 get {
2775 return (flags & Flags.HasAsyncModifier) != 0;
2777 set {
2778 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2783 // Block has been converted to expression tree
2785 public bool IsExpressionTree {
2786 get {
2787 return (flags & Flags.IsExpressionTree) != 0;
2792 // The parameters for the block.
2794 public ParametersCompiled Parameters {
2795 get {
2796 return parameters;
2800 public ToplevelBlock TopBlock {
2801 get {
2802 return top_block;
2806 public bool Resolved {
2807 get {
2808 return (flags & Flags.Resolved) != 0;
2812 public int TemporaryLocalsCount { get; set; }
2814 #endregion
2816 // <summary>
2817 // Check whether all `out' parameters have been assigned.
2818 // </summary>
2819 public void CheckOutParameters (FlowBranching.UsageVector vector)
2821 if (vector.IsUnreachable)
2822 return;
2824 int n = parameter_info == null ? 0 : parameter_info.Length;
2826 for (int i = 0; i < n; i++) {
2827 VariableInfo var = parameter_info[i].VariableInfo;
2829 if (var == null)
2830 continue;
2832 if (vector.IsAssigned (var, false))
2833 continue;
2835 var p = parameter_info[i].Parameter;
2836 TopBlock.Report.Error (177, p.Location,
2837 "The out parameter `{0}' must be assigned to before control leaves the current method",
2838 p.Name);
2842 public override Expression CreateExpressionTree (ResolveContext ec)
2844 if (statements.Count == 1) {
2845 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2846 if (scope_initializers != null)
2847 expr = new BlockScopeExpression (expr, this);
2849 return expr;
2852 return base.CreateExpressionTree (ec);
2855 public override void Emit (EmitContext ec)
2857 if (state_machine != null) {
2858 DefineStoreyContainer (ec, state_machine);
2859 state_machine.EmitStoreyInstantiation (ec, this);
2862 base.Emit (ec);
2865 public void EmitEmbedded (EmitContext ec)
2867 if (state_machine != null) {
2868 DefineStoreyContainer (ec, state_machine);
2869 state_machine.EmitStoreyInstantiation (ec, this);
2872 base.Emit (ec);
2875 public ParameterInfo GetParameterInfo (Parameter p)
2877 for (int i = 0; i < parameters.Count; ++i) {
2878 if (parameters[i] == p)
2879 return parameter_info[i];
2882 throw new ArgumentException ("Invalid parameter");
2885 public ParameterReference GetParameterReference (int index, Location loc)
2887 return new ParameterReference (parameter_info[index], loc);
2890 public Statement PerformClone ()
2892 CloneContext clonectx = new CloneContext ();
2893 return Clone (clonectx);
2896 protected void ProcessParameters ()
2898 if (parameters.Count == 0)
2899 return;
2901 parameter_info = new ParameterInfo[parameters.Count];
2902 for (int i = 0; i < parameter_info.Length; ++i) {
2903 var p = parameters.FixedParameters[i];
2904 if (p == null)
2905 continue;
2907 // TODO: Should use Parameter only and more block there
2908 parameter_info[i] = new ParameterInfo (this, i);
2909 if (p.Name != null)
2910 AddLocalName (p.Name, parameter_info[i]);
2914 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2916 if (resolved)
2917 return true;
2919 resolved = true;
2921 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2922 flags |= Flags.IsExpressionTree;
2924 try {
2925 ResolveMeta (rc);
2927 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2928 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2930 if (!Resolve (rc))
2931 return false;
2933 unreachable = top_level.End ();
2935 } catch (Exception e) {
2936 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
2937 throw;
2939 if (rc.CurrentBlock != null) {
2940 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2941 } else {
2942 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2945 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2946 throw;
2949 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2950 if (rc.CurrentAnonymousMethod == null) {
2951 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2952 if (md is StateMachineMethod) {
2953 unreachable = true;
2954 } else {
2955 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2956 return false;
2958 } else {
2960 // If an asynchronous body of F is either an expression classified as nothing, or a
2961 // statement block where no return statements have expressions, the inferred return type is Task
2963 if (IsAsync) {
2964 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2965 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2966 am.ReturnTypeInference = null;
2967 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2968 return true;
2972 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2973 rc.CurrentAnonymousMethod.GetSignatureForError ());
2974 return false;
2978 return true;
2981 void ResolveMeta (BlockContext ec)
2983 int orig_count = parameters.Count;
2985 for (int i = 0; i < orig_count; ++i) {
2986 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2988 if ((mod & Parameter.Modifier.OUT) == 0)
2989 continue;
2991 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2992 parameter_info[i].VariableInfo = vi;
2993 ec.FlowOffset += vi.Length;
2997 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
2999 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3000 var stateMachine = new IteratorStorey (iterator);
3002 am_storey = stateMachine;
3003 iterator.SetStateMachine (stateMachine);
3005 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3006 tlb.IsCompilerGenerated = true;
3007 tlb.state_machine = stateMachine;
3008 tlb.AddStatement (new Return (iterator, iterator.Location));
3009 return tlb;
3012 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType)
3014 for (int i = 0; i < parameters.Count; i++) {
3015 Parameter p = parameters[i];
3016 Parameter.Modifier mod = p.ModFlags;
3017 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3018 host.Compiler.Report.Error (1988, p.Location,
3019 "Async methods cannot have ref or out parameters");
3020 return this;
3023 if (p is ArglistParameter) {
3024 host.Compiler.Report.Error (4006, p.Location,
3025 "__arglist is not allowed in parameter list of async methods");
3026 return this;
3029 if (parameters.Types[i].IsPointer) {
3030 host.Compiler.Report.Error (4005, p.Location,
3031 "Async methods cannot have unsafe parameters");
3032 return this;
3036 if (!HasAwait) {
3037 host.Compiler.Report.Warning (1998, 1, loc,
3038 "Async block lacks `await' operator and will run synchronously");
3041 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3042 var initializer = new AsyncInitializer (this, host, block_type);
3043 initializer.Type = block_type;
3045 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3047 am_storey = stateMachine;
3048 initializer.SetStateMachine (stateMachine);
3050 var b = this is ToplevelBlock ?
3051 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3052 new ParametersBlock (Parent, parameters, Location.Null) {
3053 IsAsync = true,
3054 Original = this
3057 b.IsCompilerGenerated = true;
3058 b.state_machine = stateMachine;
3059 b.AddStatement (new StatementExpression (initializer));
3060 return b;
3067 public class ToplevelBlock : ParametersBlock
3069 LocalVariable this_variable;
3070 CompilerContext compiler;
3071 Dictionary<string, object> names;
3072 Dictionary<string, object> labels;
3074 public HoistedVariable HoistedThisVariable;
3076 public Report Report {
3077 get { return compiler.Report; }
3080 public ToplevelBlock (CompilerContext ctx, Location loc)
3081 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3085 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3086 : base (parameters, start)
3088 this.compiler = ctx;
3089 top_block = this;
3090 flags |= Flags.HasRet;
3092 ProcessParameters ();
3096 // Recreates a top level block from parameters block. Used for
3097 // compiler generated methods where the original block comes from
3098 // explicit child block. This works for already resolved blocks
3099 // only to ensure we resolve them in the correct flow order
3101 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3102 : base (source, parameters)
3104 this.compiler = source.TopBlock.compiler;
3105 top_block = this;
3106 flags |= Flags.HasRet;
3109 public bool IsIterator {
3110 get {
3111 return HasYield;
3115 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3117 if (names == null)
3118 names = new Dictionary<string, object> ();
3120 object value;
3121 if (!names.TryGetValue (name, out value)) {
3122 names.Add (name, li);
3123 return;
3126 INamedBlockVariable existing = value as INamedBlockVariable;
3127 List<INamedBlockVariable> existing_list;
3128 if (existing != null) {
3129 existing_list = new List<INamedBlockVariable> ();
3130 existing_list.Add (existing);
3131 names[name] = existing_list;
3132 } else {
3133 existing_list = (List<INamedBlockVariable>) value;
3137 // A collision checking between local names
3139 for (int i = 0; i < existing_list.Count; ++i) {
3140 existing = existing_list[i];
3141 Block b = existing.Block.Explicit;
3143 // Collision at same level
3144 if (li.Block.Explicit == b) {
3145 li.Block.Error_AlreadyDeclared (name, li);
3146 break;
3149 // Collision with parent
3150 Block parent = li.Block.Explicit;
3151 while ((parent = parent.Parent) != null) {
3152 if (parent == b) {
3153 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3154 i = existing_list.Count;
3155 break;
3159 if (!ignoreChildrenBlocks) {
3160 // Collision with children
3161 while ((b = b.Parent) != null) {
3162 if (li.Block.Explicit == b) {
3163 li.Block.Error_AlreadyDeclared (name, li, "child");
3164 i = existing_list.Count;
3165 break;
3171 existing_list.Add (li);
3174 public void AddLabel (string name, LabeledStatement label)
3176 if (labels == null)
3177 labels = new Dictionary<string, object> ();
3179 object value;
3180 if (!labels.TryGetValue (name, out value)) {
3181 labels.Add (name, label);
3182 return;
3185 LabeledStatement existing = value as LabeledStatement;
3186 List<LabeledStatement> existing_list;
3187 if (existing != null) {
3188 existing_list = new List<LabeledStatement> ();
3189 existing_list.Add (existing);
3190 labels[name] = existing_list;
3191 } else {
3192 existing_list = (List<LabeledStatement>) value;
3196 // A collision checking between labels
3198 for (int i = 0; i < existing_list.Count; ++i) {
3199 existing = existing_list[i];
3200 Block b = existing.Block;
3202 // Collision at same level
3203 if (label.Block == b) {
3204 Report.SymbolRelatedToPreviousError (existing.loc, name);
3205 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3206 break;
3209 // Collision with parent
3210 b = label.Block;
3211 while ((b = b.Parent) != null) {
3212 if (existing.Block == b) {
3213 Report.Error (158, label.loc,
3214 "The label `{0}' shadows another label by the same name in a contained scope", name);
3215 i = existing_list.Count;
3216 break;
3220 // Collision with with children
3221 b = existing.Block;
3222 while ((b = b.Parent) != null) {
3223 if (label.Block == b) {
3224 Report.Error (158, label.loc,
3225 "The label `{0}' shadows another label by the same name in a contained scope", name);
3226 i = existing_list.Count;
3227 break;
3232 existing_list.Add (label);
3236 // Creates an arguments set from all parameters, useful for method proxy calls
3238 public Arguments GetAllParametersArguments ()
3240 int count = parameters.Count;
3241 Arguments args = new Arguments (count);
3242 for (int i = 0; i < count; ++i) {
3243 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3244 args.Add (new Argument (arg_expr));
3247 return args;
3251 // Lookup inside a block, the returned value can represent 3 states
3253 // true+variable: A local name was found and it's valid
3254 // false+variable: A local name was found in a child block only
3255 // false+null: No local name was found
3257 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3259 if (names == null)
3260 return false;
3262 object value;
3263 if (!names.TryGetValue (name, out value))
3264 return false;
3266 variable = value as INamedBlockVariable;
3267 Block b = block;
3268 if (variable != null) {
3269 do {
3270 if (variable.Block == b.Original)
3271 return true;
3273 b = b.Parent;
3274 } while (b != null);
3276 b = variable.Block;
3277 do {
3278 if (block == b)
3279 return false;
3281 b = b.Parent;
3282 } while (b != null);
3283 } else {
3284 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3285 for (int i = 0; i < list.Count; ++i) {
3286 variable = list[i];
3287 do {
3288 if (variable.Block == b.Original)
3289 return true;
3291 b = b.Parent;
3292 } while (b != null);
3294 b = variable.Block;
3295 do {
3296 if (block == b)
3297 return false;
3299 b = b.Parent;
3300 } while (b != null);
3302 b = block;
3306 variable = null;
3307 return false;
3310 public LabeledStatement GetLabel (string name, Block block)
3312 if (labels == null)
3313 return null;
3315 object value;
3316 if (!labels.TryGetValue (name, out value)) {
3317 return null;
3320 var label = value as LabeledStatement;
3321 Block b = block;
3322 if (label != null) {
3323 if (label.Block == b.Original)
3324 return label;
3326 // TODO: Temporary workaround for the switch block implicit label block
3327 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3328 return label;
3329 } else {
3330 List<LabeledStatement> list = (List<LabeledStatement>) value;
3331 for (int i = 0; i < list.Count; ++i) {
3332 label = list[i];
3333 if (label.Block == b.Original)
3334 return label;
3336 // TODO: Temporary workaround for the switch block implicit label block
3337 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3338 return label;
3342 return null;
3345 // <summary>
3346 // Returns the "this" instance variable of this block.
3347 // See AddThisVariable() for more information.
3348 // </summary>
3349 public LocalVariable ThisVariable {
3350 get { return this_variable; }
3353 // <summary>
3354 // This is used by non-static `struct' constructors which do not have an
3355 // initializer - in this case, the constructor must initialize all of the
3356 // struct's fields. To do this, we add a "this" variable and use the flow
3357 // analysis code to ensure that it's been fully initialized before control
3358 // leaves the constructor.
3359 // </summary>
3360 public void AddThisVariable (BlockContext bc)
3362 if (this_variable != null)
3363 throw new InternalErrorException (StartLocation.ToString ());
3365 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3366 this_variable.Type = bc.CurrentType;
3367 this_variable.PrepareForFlowAnalysis (bc);
3370 public bool IsThisAssigned (BlockContext ec)
3372 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3375 public override void Emit (EmitContext ec)
3377 if (Report.Errors > 0)
3378 return;
3380 #if PRODUCTION
3381 try {
3382 #endif
3383 if (IsCompilerGenerated) {
3384 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3385 base.Emit (ec);
3387 } else {
3388 base.Emit (ec);
3392 // If `HasReturnLabel' is set, then we already emitted a
3393 // jump to the end of the method, so we must emit a `ret'
3394 // there.
3396 // Unfortunately, System.Reflection.Emit automatically emits
3397 // a leave to the end of a finally block. This is a problem
3398 // if no code is following the try/finally block since we may
3399 // jump to a point after the end of the method.
3400 // As a workaround, we're always creating a return label in
3401 // this case.
3403 if (ec.HasReturnLabel || !unreachable) {
3404 if (ec.HasReturnLabel)
3405 ec.MarkLabel (ec.ReturnLabel);
3407 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3408 ec.Mark (EndLocation);
3410 if (ec.ReturnType.Kind != MemberKind.Void)
3411 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3413 ec.Emit (OpCodes.Ret);
3416 #if PRODUCTION
3417 } catch (Exception e){
3418 Console.WriteLine ("Exception caught by the compiler while emitting:");
3419 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3421 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3422 throw;
3424 #endif
3428 public class SwitchLabel {
3429 Expression label;
3430 Constant converted;
3431 readonly Location loc;
3433 Label? il_label;
3436 // if expr == null, then it is the default case.
3438 public SwitchLabel (Expression expr, Location l)
3440 label = expr;
3441 loc = l;
3444 public bool IsDefault {
3445 get {
3446 return label == null;
3450 public Expression Label {
3451 get {
3452 return label;
3456 public Location Location {
3457 get {
3458 return loc;
3462 public Constant Converted {
3463 get {
3464 return converted;
3466 set {
3467 converted = value;
3471 public Label GetILLabel (EmitContext ec)
3473 if (il_label == null){
3474 il_label = ec.DefineLabel ();
3477 return il_label.Value;
3481 // Resolves the expression, reduces it to a literal if possible
3482 // and then converts it to the requested type.
3484 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3486 Expression e = label.Resolve (ec);
3488 if (e == null)
3489 return false;
3491 Constant c = e as Constant;
3492 if (c == null){
3493 ec.Report.Error (150, loc, "A constant value is expected");
3494 return false;
3497 if (allow_nullable && c is NullLiteral) {
3498 converted = c;
3499 return true;
3502 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3503 return converted != null;
3506 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3508 string label;
3509 if (converted == null)
3510 label = "default";
3511 else
3512 label = converted.GetValueAsLiteral ();
3514 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3515 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3518 public SwitchLabel Clone (CloneContext clonectx)
3520 if (label == null)
3521 return this;
3523 return new SwitchLabel (label.Clone (clonectx), loc);
3527 public class SwitchSection {
3528 public readonly List<SwitchLabel> Labels;
3529 public readonly Block Block;
3531 public SwitchSection (List<SwitchLabel> labels, Block block)
3533 Labels = labels;
3534 Block = block;
3537 public SwitchSection Clone (CloneContext clonectx)
3539 var cloned_labels = new List<SwitchLabel> ();
3541 foreach (SwitchLabel sl in Labels)
3542 cloned_labels.Add (sl.Clone (clonectx));
3544 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3548 public class Switch : Statement
3550 // structure used to hold blocks of keys while calculating table switch
3551 sealed class LabelsRange : IComparable<LabelsRange>
3553 public readonly long min;
3554 public long max;
3555 public readonly List<long> label_values;
3557 public LabelsRange (long value)
3559 min = max = value;
3560 label_values = new List<long> ();
3561 label_values.Add (value);
3564 public LabelsRange (long min, long max, ICollection<long> values)
3566 this.min = min;
3567 this.max = max;
3568 this.label_values = new List<long> (values);
3571 public long Range {
3572 get {
3573 return max - min + 1;
3577 public bool AddValue (long value)
3579 var gap = value - min + 1;
3580 // Ensure the range has > 50% occupancy
3581 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3582 return false;
3584 max = value;
3585 label_values.Add (value);
3586 return true;
3589 public int CompareTo (LabelsRange other)
3591 int nLength = label_values.Count;
3592 int nLengthOther = other.label_values.Count;
3593 if (nLengthOther == nLength)
3594 return (int) (other.min - min);
3596 return nLength - nLengthOther;
3600 sealed class LabelMarker : Statement
3602 readonly Switch s;
3603 readonly List<SwitchLabel> labels;
3605 public LabelMarker (Switch s, List<SwitchLabel> labels)
3607 this.s = s;
3608 this.labels = labels;
3611 protected override void CloneTo (CloneContext clonectx, Statement target)
3615 protected override void DoEmit (EmitContext ec)
3617 foreach (var l in labels) {
3618 if (l.IsDefault)
3619 ec.MarkLabel (s.DefaultLabel);
3620 else
3621 ec.MarkLabel (l.GetILLabel (ec));
3626 public List<SwitchSection> Sections;
3627 public Expression Expr;
3630 // Mapping of all labels to their SwitchLabels
3632 Dictionary<long, SwitchLabel> labels;
3633 Dictionary<string, SwitchLabel> string_labels;
3635 /// <summary>
3636 /// The governing switch type
3637 /// </summary>
3638 public TypeSpec SwitchType;
3641 // Computed
3643 Label default_target;
3644 Label null_target;
3645 Expression new_expr;
3646 bool is_constant;
3648 SwitchSection constant_section;
3649 SwitchSection default_section;
3650 SwitchLabel null_section;
3652 Statement simple_stmt;
3653 VariableReference value;
3654 ExpressionStatement string_dictionary;
3655 FieldExpr switch_cache_field;
3656 ExplicitBlock block;
3659 // Nullable Types support
3661 Nullable.Unwrap unwrap;
3663 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3665 Expr = e;
3666 this.block = block;
3667 Sections = sects;
3668 loc = l;
3671 public ExplicitBlock Block {
3672 get {
3673 return block;
3677 public Label DefaultLabel {
3678 get {
3679 return default_target;
3683 public bool GotDefault {
3684 get {
3685 return default_section != null;
3689 public bool IsNullable {
3690 get {
3691 return unwrap != null;
3696 // Determines the governing type for a switch. The returned
3697 // expression might be the expression from the switch, or an
3698 // expression that includes any potential conversions to
3700 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3702 switch (expr.Type.BuiltinType) {
3703 case BuiltinTypeSpec.Type.Byte:
3704 case BuiltinTypeSpec.Type.SByte:
3705 case BuiltinTypeSpec.Type.UShort:
3706 case BuiltinTypeSpec.Type.Short:
3707 case BuiltinTypeSpec.Type.UInt:
3708 case BuiltinTypeSpec.Type.Int:
3709 case BuiltinTypeSpec.Type.ULong:
3710 case BuiltinTypeSpec.Type.Long:
3711 case BuiltinTypeSpec.Type.Char:
3712 case BuiltinTypeSpec.Type.String:
3713 case BuiltinTypeSpec.Type.Bool:
3714 return expr;
3717 if (expr.Type.IsEnum)
3718 return expr;
3721 // Try to find a *user* defined implicit conversion.
3723 // If there is no implicit conversion, or if there are multiple
3724 // conversions, we have to report an error
3726 Expression converted = null;
3727 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3728 Expression e;
3730 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3731 if (e == null)
3732 continue;
3735 // Ignore over-worked ImplicitUserConversions that do
3736 // an implicit conversion in addition to the user conversion.
3738 if (!(e is UserCast))
3739 continue;
3741 if (converted != null){
3742 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3743 return null;
3746 converted = e;
3748 return converted;
3751 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3753 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3754 return new[] {
3755 types.SByte,
3756 types.Byte,
3757 types.Short,
3758 types.UShort,
3759 types.Int,
3760 types.UInt,
3761 types.Long,
3762 types.ULong,
3763 types.Char,
3764 types.String
3769 // Performs the basic sanity checks on the switch statement
3770 // (looks for duplicate keys and non-constant expressions).
3772 // It also returns a hashtable with the keys that we will later
3773 // use to compute the switch tables
3775 bool CheckSwitch (ResolveContext ec)
3777 bool error = false;
3778 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3779 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3780 else
3781 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3783 foreach (SwitchSection ss in Sections){
3784 foreach (SwitchLabel sl in ss.Labels){
3785 if (sl.IsDefault){
3786 if (default_section != null){
3787 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3788 error = true;
3790 default_section = ss;
3791 continue;
3794 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3795 error = true;
3796 continue;
3799 try {
3800 if (string_labels != null) {
3801 string s = sl.Converted.GetValue () as string;
3802 if (s == null)
3803 null_section = sl;
3804 else
3805 string_labels.Add (s, sl);
3806 } else {
3807 if (sl.Converted is NullLiteral) {
3808 null_section = sl;
3809 } else {
3810 labels.Add (sl.Converted.GetValueAsLong (), sl);
3813 } catch (ArgumentException) {
3814 if (string_labels != null)
3815 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3816 else
3817 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3819 error = true;
3823 return !error;
3827 // This method emits code for a lookup-based switch statement (non-string)
3828 // Basically it groups the cases into blocks that are at least half full,
3829 // and then spits out individual lookup opcodes for each block.
3830 // It emits the longest blocks first, and short blocks are just
3831 // handled with direct compares.
3833 void EmitTableSwitch (EmitContext ec, Expression val)
3835 Label lbl_default = default_target;
3837 if (labels != null && labels.Count > 0) {
3838 List<LabelsRange> ranges;
3839 if (string_labels != null) {
3840 // We have done all hard work for string already
3841 // setup single range only
3842 ranges = new List<LabelsRange> (1);
3843 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3844 } else {
3845 var element_keys = new long[labels.Count];
3846 labels.Keys.CopyTo (element_keys, 0);
3847 Array.Sort (element_keys);
3850 // Build possible ranges of switch labes to reduce number
3851 // of comparisons
3853 ranges = new List<LabelsRange> (element_keys.Length);
3854 var range = new LabelsRange (element_keys[0]);
3855 ranges.Add (range);
3856 for (int i = 1; i < element_keys.Length; ++i) {
3857 var l = element_keys[i];
3858 if (range.AddValue (l))
3859 continue;
3861 range = new LabelsRange (l);
3862 ranges.Add (range);
3865 // sort the blocks so we can tackle the largest ones first
3866 ranges.Sort ();
3869 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3871 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3872 LabelsRange kb = ranges[range_index];
3873 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3875 // Optimize small ranges using simple equality check
3876 if (kb.Range <= 2) {
3877 foreach (var key in kb.label_values) {
3878 SwitchLabel sl = labels[key];
3879 if (sl.Converted.IsDefaultValue) {
3880 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3881 } else {
3882 val.Emit (ec);
3883 sl.Converted.Emit (ec);
3884 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3887 } else {
3888 // TODO: if all the keys in the block are the same and there are
3889 // no gaps/defaults then just use a range-check.
3890 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3891 // TODO: optimize constant/I4 cases
3893 // check block range (could be > 2^31)
3894 val.Emit (ec);
3895 ec.EmitLong (kb.min);
3896 ec.Emit (OpCodes.Blt, lbl_default);
3898 val.Emit (ec);
3899 ec.EmitLong (kb.max);
3900 ec.Emit (OpCodes.Bgt, lbl_default);
3902 // normalize range
3903 val.Emit (ec);
3904 if (kb.min != 0) {
3905 ec.EmitLong (kb.min);
3906 ec.Emit (OpCodes.Sub);
3909 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3910 } else {
3911 // normalize range
3912 val.Emit (ec);
3913 int first = (int) kb.min;
3914 if (first > 0) {
3915 ec.EmitInt (first);
3916 ec.Emit (OpCodes.Sub);
3917 } else if (first < 0) {
3918 ec.EmitInt (-first);
3919 ec.Emit (OpCodes.Add);
3923 // first, build the list of labels for the switch
3924 int iKey = 0;
3925 long cJumps = kb.Range;
3926 Label[] switch_labels = new Label[cJumps];
3927 for (int iJump = 0; iJump < cJumps; iJump++) {
3928 var key = kb.label_values[iKey];
3929 if (key == kb.min + iJump) {
3930 switch_labels[iJump] = labels[key].GetILLabel (ec);
3931 iKey++;
3932 } else {
3933 switch_labels[iJump] = lbl_default;
3937 // emit the switch opcode
3938 ec.Emit (OpCodes.Switch, switch_labels);
3941 // mark the default for this block
3942 if (range_index != 0)
3943 ec.MarkLabel (lbl_default);
3946 // the last default just goes to the end
3947 if (ranges.Count > 0)
3948 ec.Emit (OpCodes.Br, lbl_default);
3951 // now emit the code for the sections
3952 bool found_default = false;
3954 foreach (SwitchSection ss in Sections) {
3955 foreach (SwitchLabel sl in ss.Labels) {
3956 if (sl.IsDefault) {
3957 ec.MarkLabel (lbl_default);
3958 found_default = true;
3959 if (null_section == null)
3960 ec.MarkLabel (null_target);
3961 } else if (sl.Converted.IsNull) {
3962 ec.MarkLabel (null_target);
3965 ec.MarkLabel (sl.GetILLabel (ec));
3968 ss.Block.Emit (ec);
3971 if (!found_default) {
3972 ec.MarkLabel (lbl_default);
3973 if (null_section == null) {
3974 ec.MarkLabel (null_target);
3979 SwitchLabel FindLabel (Constant value)
3981 SwitchLabel sl = null;
3983 if (string_labels != null) {
3984 string s = value.GetValue () as string;
3985 if (s == null) {
3986 if (null_section != null)
3987 sl = null_section;
3988 else if (default_section != null)
3989 sl = default_section.Labels[0];
3990 } else {
3991 string_labels.TryGetValue (s, out sl);
3993 } else {
3994 if (value is NullLiteral) {
3995 sl = null_section;
3996 } else {
3997 labels.TryGetValue (value.GetValueAsLong (), out sl);
4001 return sl;
4004 SwitchSection FindSection (SwitchLabel label)
4006 foreach (SwitchSection ss in Sections){
4007 foreach (SwitchLabel sl in ss.Labels){
4008 if (label == sl)
4009 return ss;
4013 return null;
4016 public override bool Resolve (BlockContext ec)
4018 Expr = Expr.Resolve (ec);
4019 if (Expr == null)
4020 return false;
4022 new_expr = SwitchGoverningType (ec, Expr);
4024 if (new_expr == null && Expr.Type.IsNullableType) {
4025 unwrap = Nullable.Unwrap.Create (Expr, false);
4026 if (unwrap == null)
4027 return false;
4029 new_expr = SwitchGoverningType (ec, unwrap);
4032 if (new_expr == null){
4033 ec.Report.Error (151, loc,
4034 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4035 TypeManager.CSharpName (Expr.Type));
4036 return false;
4039 // Validate switch.
4040 SwitchType = new_expr.Type;
4042 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4043 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4044 return false;
4047 if (!CheckSwitch (ec))
4048 return false;
4050 Switch old_switch = ec.Switch;
4051 ec.Switch = this;
4052 ec.Switch.SwitchType = SwitchType;
4054 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4056 var constant = new_expr as Constant;
4057 if (constant != null) {
4058 is_constant = true;
4059 SwitchLabel label = FindLabel (constant);
4060 if (label != null)
4061 constant_section = FindSection (label);
4063 if (constant_section == null)
4064 constant_section = default_section;
4065 } else {
4067 // Store switch expression for comparission purposes
4069 value = new_expr as VariableReference;
4070 if (value == null)
4071 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4074 bool first = true;
4075 bool ok = true;
4076 foreach (SwitchSection ss in Sections){
4077 if (!first)
4078 ec.CurrentBranching.CreateSibling (
4079 null, FlowBranching.SiblingType.SwitchSection);
4080 else
4081 first = false;
4083 if (is_constant && (ss != constant_section)) {
4084 // If we're a constant switch, we're only emitting
4085 // one single section - mark all the others as
4086 // unreachable.
4087 ec.CurrentBranching.CurrentUsageVector.Goto ();
4088 if (!ss.Block.ResolveUnreachable (ec, true)) {
4089 ok = false;
4091 } else {
4092 if (!ss.Block.Resolve (ec))
4093 ok = false;
4097 if (default_section == null)
4098 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4100 ec.EndFlowBranching ();
4101 ec.Switch = old_switch;
4103 if (!ok)
4104 return false;
4106 if (!is_constant) {
4107 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4108 if (string_labels.Count < 7)
4109 ResolveSimpleSwitch (ec);
4110 else
4111 ResolveStringSwitchMap (ec);
4112 } else if (labels.Count < 3 && !IsNullable) {
4113 ResolveSimpleSwitch (ec);
4117 return true;
4120 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4122 var sl = FindLabel (value);
4124 if (sl == null) {
4125 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4128 return sl;
4132 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4134 void ResolveSimpleSwitch (BlockContext bc)
4136 simple_stmt = default_section != null ? default_section.Block : null;
4138 for (int i = Sections.Count - 1; i >= 0; --i) {
4139 var s = Sections[i];
4141 if (s == default_section) {
4142 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4143 continue;
4146 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4148 Expression cond = null;
4149 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4150 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4152 if (ci > 0) {
4153 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4154 } else {
4155 cond = e;
4160 // Compiler generated, hide from symbol file
4162 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4165 // It's null for empty switch
4166 if (simple_stmt != null)
4167 simple_stmt.Resolve (bc);
4171 // Converts string switch into string hashtable
4173 void ResolveStringSwitchMap (ResolveContext ec)
4175 FullNamedExpression string_dictionary_type;
4176 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4177 string_dictionary_type = new TypeExpression (
4178 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4179 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4180 loc);
4181 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4182 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4183 } else {
4184 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4185 return;
4188 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4189 Field field = new Field (ctype, string_dictionary_type,
4190 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4191 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4192 if (!field.Define ())
4193 return;
4194 ctype.AddField (field);
4196 var init = new List<Expression> ();
4197 int counter = 0;
4198 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4199 string value = null;
4200 foreach (SwitchSection section in Sections) {
4201 bool contains_label = false;
4202 foreach (SwitchLabel sl in section.Labels) {
4203 if (sl.IsDefault || sl.Converted.IsNull)
4204 continue;
4206 if (!contains_label) {
4207 labels.Add (counter, sl);
4208 contains_label = true;
4211 value = (string) sl.Converted.GetValue ();
4212 var init_args = new List<Expression> (2);
4213 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4215 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4216 init_args.Add (sl.Converted);
4218 init.Add (new CollectionElementInitializer (init_args, loc));
4222 // Don't add empty sections
4224 if (contains_label)
4225 ++counter;
4228 Arguments args = new Arguments (1);
4229 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4230 Expression initializer = new NewInitialize (string_dictionary_type, args,
4231 new CollectionOrObjectInitializers (init, loc), loc);
4233 switch_cache_field = new FieldExpr (field, loc);
4234 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4237 void DoEmitStringSwitch (EmitContext ec)
4239 Label l_initialized = ec.DefineLabel ();
4242 // Skip initialization when value is null
4244 value.EmitBranchable (ec, null_target, false);
4247 // Check if string dictionary is initialized and initialize
4249 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4250 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4251 string_dictionary.EmitStatement (ec);
4253 ec.MarkLabel (l_initialized);
4255 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4257 ResolveContext rc = new ResolveContext (ec.MemberContext);
4259 if (switch_cache_field.Type.IsGeneric) {
4260 Arguments get_value_args = new Arguments (2);
4261 get_value_args.Add (new Argument (value));
4262 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4263 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4264 if (get_item == null)
4265 return;
4268 // A value was not found, go to default case
4270 get_item.EmitBranchable (ec, default_target, false);
4271 } else {
4272 Arguments get_value_args = new Arguments (1);
4273 get_value_args.Add (new Argument (value));
4275 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4276 if (get_item == null)
4277 return;
4279 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4280 get_item_object.EmitAssign (ec, get_item, true, false);
4281 ec.Emit (OpCodes.Brfalse, default_target);
4283 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4284 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4286 get_item_int.EmitStatement (ec);
4287 get_item_object.Release (ec);
4290 EmitTableSwitch (ec, string_switch_variable);
4291 string_switch_variable.Release (ec);
4294 protected override void DoEmit (EmitContext ec)
4296 // Workaround broken flow-analysis
4297 block.HasUnreachableClosingBrace = true;
4300 // Needed to emit anonymous storey initialization
4301 // Otherwise it does not contain any statements for now
4303 block.Emit (ec);
4305 default_target = ec.DefineLabel ();
4306 null_target = ec.DefineLabel ();
4308 if (IsNullable) {
4309 unwrap.EmitCheck (ec);
4310 ec.Emit (OpCodes.Brfalse, null_target);
4311 value.EmitAssign (ec, new_expr, false, false);
4312 } else if (new_expr != value && !is_constant) {
4313 value.EmitAssign (ec, new_expr, false, false);
4317 // Setup the codegen context
4319 Label old_end = ec.LoopEnd;
4320 Switch old_switch = ec.Switch;
4322 ec.LoopEnd = ec.DefineLabel ();
4323 ec.Switch = this;
4325 // Emit Code.
4326 if (is_constant) {
4327 if (constant_section != null)
4328 constant_section.Block.Emit (ec);
4329 } else if (string_dictionary != null) {
4330 DoEmitStringSwitch (ec);
4331 } else if (simple_stmt != null) {
4332 simple_stmt.Emit (ec);
4333 } else {
4334 EmitTableSwitch (ec, value);
4337 // Restore context state.
4338 ec.MarkLabel (ec.LoopEnd);
4341 // Restore the previous context
4343 ec.LoopEnd = old_end;
4344 ec.Switch = old_switch;
4347 protected override void CloneTo (CloneContext clonectx, Statement t)
4349 Switch target = (Switch) t;
4351 target.Expr = Expr.Clone (clonectx);
4352 target.Sections = new List<SwitchSection> ();
4353 foreach (SwitchSection ss in Sections){
4354 target.Sections.Add (ss.Clone (clonectx));
4358 public override object Accept (StructuralVisitor visitor)
4360 return visitor.Visit (this);
4364 // A place where execution can restart in an iterator
4365 public abstract class ResumableStatement : Statement
4367 bool prepared;
4368 protected Label resume_point;
4370 public Label PrepareForEmit (EmitContext ec)
4372 if (!prepared) {
4373 prepared = true;
4374 resume_point = ec.DefineLabel ();
4376 return resume_point;
4379 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4381 return end;
4384 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4389 public abstract class TryFinallyBlock : ExceptionStatement
4391 protected Statement stmt;
4392 Label dispose_try_block;
4393 bool prepared_for_dispose, emitted_dispose;
4395 protected TryFinallyBlock (Statement stmt, Location loc)
4396 : base (loc)
4398 this.stmt = stmt;
4401 #region Properties
4403 public Statement Statement {
4404 get {
4405 return stmt;
4409 #endregion
4411 protected abstract void EmitTryBody (EmitContext ec);
4412 protected abstract void EmitFinallyBody (EmitContext ec);
4414 public override Label PrepareForDispose (EmitContext ec, Label end)
4416 if (!prepared_for_dispose) {
4417 prepared_for_dispose = true;
4418 dispose_try_block = ec.DefineLabel ();
4420 return dispose_try_block;
4423 protected sealed override void DoEmit (EmitContext ec)
4425 EmitTryBodyPrepare (ec);
4426 EmitTryBody (ec);
4428 ec.BeginFinallyBlock ();
4430 Label start_finally = ec.DefineLabel ();
4431 if (resume_points != null) {
4432 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4434 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4435 ec.Emit (OpCodes.Brfalse_S, start_finally);
4436 ec.Emit (OpCodes.Endfinally);
4439 ec.MarkLabel (start_finally);
4440 EmitFinallyBody (ec);
4442 ec.EndExceptionBlock ();
4445 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4447 if (emitted_dispose)
4448 return;
4450 emitted_dispose = true;
4452 Label end_of_try = ec.DefineLabel ();
4454 // Ensure that the only way we can get into this code is through a dispatcher
4455 if (have_dispatcher)
4456 ec.Emit (OpCodes.Br, end);
4458 ec.BeginExceptionBlock ();
4460 ec.MarkLabel (dispose_try_block);
4462 Label[] labels = null;
4463 for (int i = 0; i < resume_points.Count; ++i) {
4464 ResumableStatement s = resume_points[i];
4465 Label ret = s.PrepareForDispose (ec, end_of_try);
4466 if (ret.Equals (end_of_try) && labels == null)
4467 continue;
4468 if (labels == null) {
4469 labels = new Label[resume_points.Count];
4470 for (int j = 0; j < i; ++j)
4471 labels[j] = end_of_try;
4473 labels[i] = ret;
4476 if (labels != null) {
4477 int j;
4478 for (j = 1; j < labels.Length; ++j)
4479 if (!labels[0].Equals (labels[j]))
4480 break;
4481 bool emit_dispatcher = j < labels.Length;
4483 if (emit_dispatcher) {
4484 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4485 ec.Emit (OpCodes.Ldloc, pc);
4486 ec.EmitInt (first_resume_pc);
4487 ec.Emit (OpCodes.Sub);
4488 ec.Emit (OpCodes.Switch, labels);
4489 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4492 foreach (ResumableStatement s in resume_points)
4493 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4496 ec.MarkLabel (end_of_try);
4498 ec.BeginFinallyBlock ();
4500 EmitFinallyBody (ec);
4502 ec.EndExceptionBlock ();
4507 // Base class for blocks using exception handling
4509 public abstract class ExceptionStatement : ResumableStatement
4511 #if !STATIC
4512 bool code_follows;
4513 #endif
4514 protected List<ResumableStatement> resume_points;
4515 protected int first_resume_pc;
4517 protected ExceptionStatement (Location loc)
4519 this.loc = loc;
4522 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4524 StateMachineInitializer state_machine = null;
4525 if (resume_points != null) {
4526 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4528 ec.EmitInt ((int) IteratorStorey.State.Running);
4529 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4532 ec.BeginExceptionBlock ();
4534 if (resume_points != null) {
4535 ec.MarkLabel (resume_point);
4537 // For normal control flow, we want to fall-through the Switch
4538 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4539 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4540 ec.EmitInt (first_resume_pc);
4541 ec.Emit (OpCodes.Sub);
4543 Label[] labels = new Label[resume_points.Count];
4544 for (int i = 0; i < resume_points.Count; ++i)
4545 labels[i] = resume_points[i].PrepareForEmit (ec);
4546 ec.Emit (OpCodes.Switch, labels);
4550 public void SomeCodeFollows ()
4552 #if !STATIC
4553 code_follows = true;
4554 #endif
4557 public override bool Resolve (BlockContext ec)
4559 #if !STATIC
4560 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4561 // So, ensure there's some IL code after this statement.
4562 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4563 ec.NeedReturnLabel ();
4564 #endif
4565 return true;
4568 public void AddResumePoint (ResumableStatement stmt, int pc)
4570 if (resume_points == null) {
4571 resume_points = new List<ResumableStatement> ();
4572 first_resume_pc = pc;
4575 if (pc != first_resume_pc + resume_points.Count)
4576 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4578 resume_points.Add (stmt);
4583 public class Lock : TryFinallyBlock
4585 Expression expr;
4586 TemporaryVariableReference expr_copy;
4587 TemporaryVariableReference lock_taken;
4589 public Lock (Expression expr, Statement stmt, Location loc)
4590 : base (stmt, loc)
4592 this.expr = expr;
4595 public Expression Expr {
4596 get {
4597 return this.expr;
4601 public override bool Resolve (BlockContext ec)
4603 expr = expr.Resolve (ec);
4604 if (expr == null)
4605 return false;
4607 if (!TypeSpec.IsReferenceType (expr.Type)) {
4608 ec.Report.Error (185, loc,
4609 "`{0}' is not a reference type as required by the lock statement",
4610 expr.Type.GetSignatureForError ());
4613 if (expr.Type.IsGenericParameter) {
4614 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4617 VariableReference lv = expr as VariableReference;
4618 bool locked;
4619 if (lv != null) {
4620 locked = lv.IsLockedByStatement;
4621 lv.IsLockedByStatement = true;
4622 } else {
4623 lv = null;
4624 locked = false;
4627 using (ec.Set (ResolveContext.Options.LockScope)) {
4628 ec.StartFlowBranching (this);
4629 Statement.Resolve (ec);
4630 ec.EndFlowBranching ();
4633 if (lv != null) {
4634 lv.IsLockedByStatement = locked;
4637 base.Resolve (ec);
4640 // Have to keep original lock value around to unlock same location
4641 // in the case the original has changed or is null
4643 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4644 expr_copy.Resolve (ec);
4647 // Ensure Monitor methods are available
4649 if (ResolvePredefinedMethods (ec) > 1) {
4650 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4651 lock_taken.Resolve (ec);
4654 return true;
4657 protected override void EmitTryBodyPrepare (EmitContext ec)
4659 expr_copy.EmitAssign (ec, expr);
4661 if (lock_taken != null) {
4663 // Initialize ref variable
4665 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4666 } else {
4668 // Monitor.Enter (expr_copy)
4670 expr_copy.Emit (ec);
4671 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4674 base.EmitTryBodyPrepare (ec);
4677 protected override void EmitTryBody (EmitContext ec)
4680 // Monitor.Enter (expr_copy, ref lock_taken)
4682 if (lock_taken != null) {
4683 expr_copy.Emit (ec);
4684 lock_taken.LocalInfo.CreateBuilder (ec);
4685 lock_taken.AddressOf (ec, AddressOp.Load);
4686 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4689 Statement.Emit (ec);
4692 protected override void EmitFinallyBody (EmitContext ec)
4695 // if (lock_taken) Monitor.Exit (expr_copy)
4697 Label skip = ec.DefineLabel ();
4699 if (lock_taken != null) {
4700 lock_taken.Emit (ec);
4701 ec.Emit (OpCodes.Brfalse_S, skip);
4704 expr_copy.Emit (ec);
4705 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4706 if (m != null)
4707 ec.Emit (OpCodes.Call, m);
4709 ec.MarkLabel (skip);
4712 int ResolvePredefinedMethods (ResolveContext rc)
4714 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4715 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4716 if (m != null)
4717 return 4;
4719 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4720 if (m != null)
4721 return 1;
4723 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4724 return 0;
4727 protected override void CloneTo (CloneContext clonectx, Statement t)
4729 Lock target = (Lock) t;
4731 target.expr = expr.Clone (clonectx);
4732 target.stmt = Statement.Clone (clonectx);
4735 public override object Accept (StructuralVisitor visitor)
4737 return visitor.Visit (this);
4742 public class Unchecked : Statement {
4743 public Block Block;
4745 public Unchecked (Block b, Location loc)
4747 Block = b;
4748 b.Unchecked = true;
4749 this.loc = loc;
4752 public override bool Resolve (BlockContext ec)
4754 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4755 return Block.Resolve (ec);
4758 protected override void DoEmit (EmitContext ec)
4760 using (ec.With (EmitContext.Options.CheckedScope, false))
4761 Block.Emit (ec);
4764 protected override void CloneTo (CloneContext clonectx, Statement t)
4766 Unchecked target = (Unchecked) t;
4768 target.Block = clonectx.LookupBlock (Block);
4771 public override object Accept (StructuralVisitor visitor)
4773 return visitor.Visit (this);
4777 public class Checked : Statement {
4778 public Block Block;
4780 public Checked (Block b, Location loc)
4782 Block = b;
4783 b.Unchecked = false;
4784 this.loc = loc;
4787 public override bool Resolve (BlockContext ec)
4789 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4790 return Block.Resolve (ec);
4793 protected override void DoEmit (EmitContext ec)
4795 using (ec.With (EmitContext.Options.CheckedScope, true))
4796 Block.Emit (ec);
4799 protected override void CloneTo (CloneContext clonectx, Statement t)
4801 Checked target = (Checked) t;
4803 target.Block = clonectx.LookupBlock (Block);
4806 public override object Accept (StructuralVisitor visitor)
4808 return visitor.Visit (this);
4812 public class Unsafe : Statement {
4813 public Block Block;
4815 public Unsafe (Block b, Location loc)
4817 Block = b;
4818 Block.Unsafe = true;
4819 this.loc = loc;
4822 public override bool Resolve (BlockContext ec)
4824 if (ec.CurrentIterator != null)
4825 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4827 using (ec.Set (ResolveContext.Options.UnsafeScope))
4828 return Block.Resolve (ec);
4831 protected override void DoEmit (EmitContext ec)
4833 Block.Emit (ec);
4836 protected override void CloneTo (CloneContext clonectx, Statement t)
4838 Unsafe target = (Unsafe) t;
4840 target.Block = clonectx.LookupBlock (Block);
4843 public override object Accept (StructuralVisitor visitor)
4845 return visitor.Visit (this);
4850 // Fixed statement
4852 public class Fixed : Statement
4854 abstract class Emitter : ShimExpression
4856 protected LocalVariable vi;
4858 protected Emitter (Expression expr, LocalVariable li)
4859 : base (expr)
4861 vi = li;
4864 public abstract void EmitExit (EmitContext ec);
4867 class ExpressionEmitter : Emitter {
4868 public ExpressionEmitter (Expression converted, LocalVariable li) :
4869 base (converted, li)
4873 protected override Expression DoResolve (ResolveContext rc)
4875 throw new NotImplementedException ();
4878 public override void Emit (EmitContext ec) {
4880 // Store pointer in pinned location
4882 expr.Emit (ec);
4883 vi.EmitAssign (ec);
4886 public override void EmitExit (EmitContext ec)
4888 ec.EmitInt (0);
4889 ec.Emit (OpCodes.Conv_U);
4890 vi.EmitAssign (ec);
4894 class StringEmitter : Emitter
4896 LocalVariable pinned_string;
4898 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4899 : base (expr, li)
4903 protected override Expression DoResolve (ResolveContext rc)
4905 pinned_string = new LocalVariable (vi.Block, "$pinned",
4906 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4907 vi.Location);
4908 pinned_string.Type = rc.BuiltinTypes.String;
4910 eclass = ExprClass.Variable;
4911 type = rc.BuiltinTypes.Int;
4912 return this;
4915 public override void Emit (EmitContext ec)
4917 pinned_string.CreateBuilder (ec);
4919 expr.Emit (ec);
4920 pinned_string.EmitAssign (ec);
4922 // TODO: Should use Binary::Add
4923 pinned_string.Emit (ec);
4924 ec.Emit (OpCodes.Conv_I);
4926 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4927 if (m == null)
4928 return;
4930 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4931 //pe.InstanceExpression = pinned_string;
4932 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4934 ec.Emit (OpCodes.Add);
4935 vi.EmitAssign (ec);
4938 public override void EmitExit (EmitContext ec)
4940 ec.EmitNull ();
4941 pinned_string.EmitAssign (ec);
4945 public class VariableDeclaration : BlockVariableDeclaration
4947 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4948 : base (type, li)
4952 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4954 if (!Variable.Type.IsPointer && li == Variable) {
4955 bc.Report.Error (209, TypeExpression.Location,
4956 "The type of locals declared in a fixed statement must be a pointer type");
4957 return null;
4961 // The rules for the possible declarators are pretty wise,
4962 // but the production on the grammar is more concise.
4964 // So we have to enforce these rules here.
4966 // We do not resolve before doing the case 1 test,
4967 // because the grammar is explicit in that the token &
4968 // is present, so we need to test for this particular case.
4971 if (initializer is Cast) {
4972 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4973 return null;
4976 initializer = initializer.Resolve (bc);
4978 if (initializer == null)
4979 return null;
4982 // Case 1: Array
4984 if (initializer.Type.IsArray) {
4985 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4988 // Provided that array_type is unmanaged,
4990 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4991 return null;
4994 // and T* is implicitly convertible to the
4995 // pointer type given in the fixed statement.
4997 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4999 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5000 if (converted == null)
5001 return null;
5004 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5006 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5007 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
5008 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
5009 new NullLiteral (loc),
5010 converted, loc);
5012 converted = converted.Resolve (bc);
5014 return new ExpressionEmitter (converted, li);
5018 // Case 2: string
5020 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5021 return new StringEmitter (initializer, li, loc).Resolve (bc);
5024 // Case 3: fixed buffer
5025 if (initializer is FixedBufferPtr) {
5026 return new ExpressionEmitter (initializer, li);
5030 // Case 4: & object.
5032 bool already_fixed = true;
5033 Unary u = initializer as Unary;
5034 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5035 IVariableReference vr = u.Expr as IVariableReference;
5036 if (vr == null || !vr.IsFixed) {
5037 already_fixed = false;
5041 if (already_fixed) {
5042 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5045 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5046 return new ExpressionEmitter (initializer, li);
5051 VariableDeclaration decl;
5052 Statement statement;
5053 bool has_ret;
5055 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5057 this.decl = decl;
5058 statement = stmt;
5059 loc = l;
5062 #region Properties
5064 public Statement Statement {
5065 get {
5066 return statement;
5070 public BlockVariableDeclaration Variables {
5071 get {
5072 return decl;
5076 #endregion
5078 public override bool Resolve (BlockContext ec)
5080 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5081 if (!decl.Resolve (ec))
5082 return false;
5085 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5086 bool ok = statement.Resolve (ec);
5087 bool flow_unreachable = ec.EndFlowBranching ();
5088 has_ret = flow_unreachable;
5090 return ok;
5093 protected override void DoEmit (EmitContext ec)
5095 decl.Variable.CreateBuilder (ec);
5096 decl.Initializer.Emit (ec);
5097 if (decl.Declarators != null) {
5098 foreach (var d in decl.Declarators) {
5099 d.Variable.CreateBuilder (ec);
5100 d.Initializer.Emit (ec);
5104 statement.Emit (ec);
5106 if (has_ret)
5107 return;
5110 // Clear the pinned variable
5112 ((Emitter) decl.Initializer).EmitExit (ec);
5113 if (decl.Declarators != null) {
5114 foreach (var d in decl.Declarators) {
5115 ((Emitter)d.Initializer).EmitExit (ec);
5120 protected override void CloneTo (CloneContext clonectx, Statement t)
5122 Fixed target = (Fixed) t;
5124 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5125 target.statement = statement.Clone (clonectx);
5128 public override object Accept (StructuralVisitor visitor)
5130 return visitor.Visit (this);
5134 public class Catch : Statement
5136 Block block;
5137 LocalVariable li;
5138 FullNamedExpression type_expr;
5139 CompilerAssign assign;
5140 TypeSpec type;
5142 public Catch (Block block, Location loc)
5144 this.block = block;
5145 this.loc = loc;
5148 #region Properties
5150 public Block Block {
5151 get {
5152 return block;
5156 public TypeSpec CatchType {
5157 get {
5158 return type;
5162 public bool IsGeneral {
5163 get {
5164 return type_expr == null;
5168 public FullNamedExpression TypeExpression {
5169 get {
5170 return type_expr;
5172 set {
5173 type_expr = value;
5177 public LocalVariable Variable {
5178 get {
5179 return li;
5181 set {
5182 li = value;
5186 #endregion
5188 protected override void DoEmit (EmitContext ec)
5190 if (IsGeneral)
5191 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5192 else
5193 ec.BeginCatchBlock (CatchType);
5195 if (li != null) {
5196 li.CreateBuilder (ec);
5199 // Special case hoisted catch variable, we have to use a temporary variable
5200 // to pass via anonymous storey initialization with the value still on top
5201 // of the stack
5203 if (li.HoistedVariant != null) {
5204 LocalTemporary lt = new LocalTemporary (li.Type);
5205 lt.Store (ec);
5207 // switch to assigning from the temporary variable and not from top of the stack
5208 assign.UpdateSource (lt);
5210 } else {
5211 ec.Emit (OpCodes.Pop);
5214 Block.Emit (ec);
5217 public override bool Resolve (BlockContext ec)
5219 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5220 if (type_expr != null) {
5221 type = type_expr.ResolveAsType (ec);
5222 if (type == null)
5223 return false;
5225 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5226 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5227 } else if (li != null) {
5228 li.Type = type;
5229 li.PrepareForFlowAnalysis (ec);
5231 // source variable is at the top of the stack
5232 Expression source = new EmptyExpression (li.Type);
5233 if (li.Type.IsGenericParameter)
5234 source = new UnboxCast (source, li.Type);
5237 // Uses Location.Null to hide from symbol file
5239 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5240 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5244 return Block.Resolve (ec);
5248 protected override void CloneTo (CloneContext clonectx, Statement t)
5250 Catch target = (Catch) t;
5252 if (type_expr != null)
5253 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5255 target.block = clonectx.LookupBlock (block);
5259 public class TryFinally : TryFinallyBlock
5261 Block fini;
5263 public TryFinally (Statement stmt, Block fini, Location loc)
5264 : base (stmt, loc)
5266 this.fini = fini;
5269 public Block Finallyblock {
5270 get {
5271 return fini;
5275 public override bool Resolve (BlockContext ec)
5277 bool ok = true;
5279 ec.StartFlowBranching (this);
5281 if (!stmt.Resolve (ec))
5282 ok = false;
5284 if (ok)
5285 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5286 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5287 if (!fini.Resolve (ec))
5288 ok = false;
5291 ec.EndFlowBranching ();
5293 ok &= base.Resolve (ec);
5295 return ok;
5298 protected override void EmitTryBody (EmitContext ec)
5300 stmt.Emit (ec);
5303 protected override void EmitFinallyBody (EmitContext ec)
5305 fini.Emit (ec);
5308 protected override void CloneTo (CloneContext clonectx, Statement t)
5310 TryFinally target = (TryFinally) t;
5312 target.stmt = (Statement) stmt.Clone (clonectx);
5313 if (fini != null)
5314 target.fini = clonectx.LookupBlock (fini);
5317 public override object Accept (StructuralVisitor visitor)
5319 return visitor.Visit (this);
5323 public class TryCatch : ExceptionStatement
5325 public Block Block;
5326 List<Catch> clauses;
5327 readonly bool inside_try_finally;
5329 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5330 : base (l)
5332 this.Block = block;
5333 this.clauses = catch_clauses;
5334 this.inside_try_finally = inside_try_finally;
5337 public List<Catch> Clauses {
5338 get {
5339 return clauses;
5343 public bool IsTryCatchFinally {
5344 get {
5345 return inside_try_finally;
5349 public override bool Resolve (BlockContext ec)
5351 bool ok = true;
5353 ec.StartFlowBranching (this);
5355 if (!Block.Resolve (ec))
5356 ok = false;
5358 for (int i = 0; i < clauses.Count; ++i) {
5359 var c = clauses[i];
5360 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5362 if (!c.Resolve (ec)) {
5363 ok = false;
5364 continue;
5367 TypeSpec resolved_type = c.CatchType;
5368 for (int ii = 0; ii < clauses.Count; ++ii) {
5369 if (ii == i)
5370 continue;
5372 if (clauses[ii].IsGeneral) {
5373 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5374 continue;
5376 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5377 continue;
5379 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5380 continue;
5382 ec.Report.Warning (1058, 1, c.loc,
5383 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5385 continue;
5388 if (ii >= i)
5389 continue;
5391 var ct = clauses[ii].CatchType;
5392 if (ct == null)
5393 continue;
5395 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5396 ec.Report.Error (160, c.loc,
5397 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5398 ct.GetSignatureForError ());
5399 ok = false;
5404 ec.EndFlowBranching ();
5406 return base.Resolve (ec) && ok;
5409 protected sealed override void DoEmit (EmitContext ec)
5411 if (!inside_try_finally)
5412 EmitTryBodyPrepare (ec);
5414 Block.Emit (ec);
5416 foreach (Catch c in clauses)
5417 c.Emit (ec);
5419 if (!inside_try_finally)
5420 ec.EndExceptionBlock ();
5423 protected override void CloneTo (CloneContext clonectx, Statement t)
5425 TryCatch target = (TryCatch) t;
5427 target.Block = clonectx.LookupBlock (Block);
5428 if (clauses != null){
5429 target.clauses = new List<Catch> ();
5430 foreach (Catch c in clauses)
5431 target.clauses.Add ((Catch) c.Clone (clonectx));
5435 public override object Accept (StructuralVisitor visitor)
5437 return visitor.Visit (this);
5441 public class Using : TryFinallyBlock
5443 public class VariableDeclaration : BlockVariableDeclaration
5445 Statement dispose_call;
5447 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5448 : base (type, li)
5452 public VariableDeclaration (LocalVariable li, Location loc)
5453 : base (li)
5455 this.loc = loc;
5458 public VariableDeclaration (Expression expr)
5459 : base (null)
5461 loc = expr.Location;
5462 Initializer = expr;
5465 #region Properties
5467 public bool IsNested { get; private set; }
5469 #endregion
5471 public void EmitDispose (EmitContext ec)
5473 dispose_call.Emit (ec);
5476 public override bool Resolve (BlockContext bc)
5478 if (IsNested)
5479 return true;
5481 return base.Resolve (bc, false);
5484 public Expression ResolveExpression (BlockContext bc)
5486 var e = Initializer.Resolve (bc);
5487 if (e == null)
5488 return null;
5490 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5491 Initializer = ResolveInitializer (bc, Variable, e);
5492 return e;
5495 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5497 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5498 initializer = initializer.Resolve (bc);
5499 if (initializer == null)
5500 return null;
5502 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5503 Arguments args = new Arguments (1);
5504 args.Add (new Argument (initializer));
5505 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5506 if (initializer == null)
5507 return null;
5509 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5510 dispose_call = CreateDisposeCall (bc, var);
5511 dispose_call.Resolve (bc);
5513 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5516 if (li == Variable) {
5517 CheckIDiposableConversion (bc, li, initializer);
5518 dispose_call = CreateDisposeCall (bc, li);
5519 dispose_call.Resolve (bc);
5522 return base.ResolveInitializer (bc, li, initializer);
5525 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5527 var type = li.Type;
5529 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5530 if (type.IsNullableType) {
5531 // it's handled in CreateDisposeCall
5532 return;
5535 bc.Report.SymbolRelatedToPreviousError (type);
5536 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5537 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5538 type.GetSignatureForError ());
5540 return;
5544 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5546 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5547 var type = lv.Type;
5548 var loc = lv.Location;
5550 var idt = bc.BuiltinTypes.IDisposable;
5551 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5553 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5554 dispose_mg.InstanceExpression = type.IsNullableType ?
5555 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5556 lvr;
5559 // Hide it from symbol file via null location
5561 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5563 // Add conditional call when disposing possible null variable
5564 if (!type.IsStruct || type.IsNullableType)
5565 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, dispose.loc);
5567 return dispose;
5570 public void ResolveDeclaratorInitializer (BlockContext bc)
5572 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5575 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5577 for (int i = declarators.Count - 1; i >= 0; --i) {
5578 var d = declarators [i];
5579 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5580 vd.Initializer = d.Initializer;
5581 vd.IsNested = true;
5582 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5583 vd.dispose_call.Resolve (bc);
5585 stmt = new Using (vd, stmt, d.Variable.Location);
5588 declarators = null;
5589 return stmt;
5592 public override object Accept (StructuralVisitor visitor)
5594 return visitor.Visit (this);
5598 VariableDeclaration decl;
5600 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5601 : base (stmt, loc)
5603 this.decl = decl;
5606 public Using (Expression expr, Statement stmt, Location loc)
5607 : base (stmt, loc)
5609 this.decl = new VariableDeclaration (expr);
5612 #region Properties
5614 public Expression Expr {
5615 get {
5616 return decl.Variable == null ? decl.Initializer : null;
5620 public BlockVariableDeclaration Variables {
5621 get {
5622 return decl;
5626 #endregion
5628 public override void Emit (EmitContext ec)
5631 // Don't emit sequence point it will be set on variable declaration
5633 DoEmit (ec);
5636 protected override void EmitTryBodyPrepare (EmitContext ec)
5638 decl.Emit (ec);
5639 base.EmitTryBodyPrepare (ec);
5642 protected override void EmitTryBody (EmitContext ec)
5644 stmt.Emit (ec);
5647 protected override void EmitFinallyBody (EmitContext ec)
5649 decl.EmitDispose (ec);
5652 public override bool Resolve (BlockContext ec)
5654 VariableReference vr;
5655 bool vr_locked = false;
5657 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5658 if (decl.Variable == null) {
5659 vr = decl.ResolveExpression (ec) as VariableReference;
5660 if (vr != null) {
5661 vr_locked = vr.IsLockedByStatement;
5662 vr.IsLockedByStatement = true;
5664 } else {
5665 if (decl.IsNested) {
5666 decl.ResolveDeclaratorInitializer (ec);
5667 } else {
5668 if (!decl.Resolve (ec))
5669 return false;
5671 if (decl.Declarators != null) {
5672 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5676 vr = null;
5680 ec.StartFlowBranching (this);
5682 stmt.Resolve (ec);
5684 ec.EndFlowBranching ();
5686 if (vr != null)
5687 vr.IsLockedByStatement = vr_locked;
5689 base.Resolve (ec);
5691 return true;
5694 protected override void CloneTo (CloneContext clonectx, Statement t)
5696 Using target = (Using) t;
5698 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5699 target.stmt = stmt.Clone (clonectx);
5702 public override object Accept (StructuralVisitor visitor)
5704 return visitor.Visit (this);
5708 /// <summary>
5709 /// Implementation of the foreach C# statement
5710 /// </summary>
5711 public class Foreach : Statement
5713 abstract class IteratorStatement : Statement
5715 protected readonly Foreach for_each;
5717 protected IteratorStatement (Foreach @foreach)
5719 this.for_each = @foreach;
5720 this.loc = @foreach.expr.Location;
5723 protected override void CloneTo (CloneContext clonectx, Statement target)
5725 throw new NotImplementedException ();
5728 public override void Emit (EmitContext ec)
5730 if (ec.EmitAccurateDebugInfo) {
5731 ec.Emit (OpCodes.Nop);
5734 base.Emit (ec);
5738 sealed class ArrayForeach : IteratorStatement
5740 TemporaryVariableReference[] lengths;
5741 Expression [] length_exprs;
5742 StatementExpression[] counter;
5743 TemporaryVariableReference[] variables;
5745 TemporaryVariableReference copy;
5747 public ArrayForeach (Foreach @foreach, int rank)
5748 : base (@foreach)
5750 counter = new StatementExpression[rank];
5751 variables = new TemporaryVariableReference[rank];
5752 length_exprs = new Expression [rank];
5755 // Only use temporary length variables when dealing with
5756 // multi-dimensional arrays
5758 if (rank > 1)
5759 lengths = new TemporaryVariableReference [rank];
5762 public override bool Resolve (BlockContext ec)
5764 Block variables_block = for_each.variable.Block;
5765 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5766 copy.Resolve (ec);
5768 int rank = length_exprs.Length;
5769 Arguments list = new Arguments (rank);
5770 for (int i = 0; i < rank; i++) {
5771 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5772 variables[i] = v;
5773 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5774 counter[i].Resolve (ec);
5776 if (rank == 1) {
5777 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5778 } else {
5779 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5780 lengths[i].Resolve (ec);
5782 Arguments args = new Arguments (1);
5783 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5784 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5787 list.Add (new Argument (v));
5790 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5791 if (access == null)
5792 return false;
5794 TypeSpec var_type;
5795 if (for_each.type is VarExpr) {
5796 // Infer implicitly typed local variable from foreach array type
5797 var_type = access.Type;
5798 } else {
5799 var_type = for_each.type.ResolveAsType (ec);
5801 if (var_type == null)
5802 return false;
5804 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5805 if (access == null)
5806 return false;
5809 for_each.variable.Type = var_type;
5811 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5812 if (variable_ref == null)
5813 return false;
5815 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.variable.Location));
5817 bool ok = true;
5819 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5820 ec.CurrentBranching.CreateSibling ();
5822 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5823 if (!for_each.body.Resolve (ec))
5824 ok = false;
5825 ec.EndFlowBranching ();
5827 // There's no direct control flow from the end of the embedded statement to the end of the loop
5828 ec.CurrentBranching.CurrentUsageVector.Goto ();
5830 ec.EndFlowBranching ();
5832 return ok;
5835 protected override void DoEmit (EmitContext ec)
5837 copy.EmitAssign (ec, for_each.expr);
5839 int rank = length_exprs.Length;
5840 Label[] test = new Label [rank];
5841 Label[] loop = new Label [rank];
5843 for (int i = 0; i < rank; i++) {
5844 test [i] = ec.DefineLabel ();
5845 loop [i] = ec.DefineLabel ();
5847 if (lengths != null)
5848 lengths [i].EmitAssign (ec, length_exprs [i]);
5851 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5852 for (int i = 0; i < rank; i++) {
5853 variables [i].EmitAssign (ec, zero);
5855 ec.Emit (OpCodes.Br, test [i]);
5856 ec.MarkLabel (loop [i]);
5859 for_each.body.Emit (ec);
5861 ec.MarkLabel (ec.LoopBegin);
5862 ec.Mark (for_each.expr.Location);
5864 for (int i = rank - 1; i >= 0; i--){
5865 counter [i].Emit (ec);
5867 ec.MarkLabel (test [i]);
5868 variables [i].Emit (ec);
5870 if (lengths != null)
5871 lengths [i].Emit (ec);
5872 else
5873 length_exprs [i].Emit (ec);
5875 ec.Emit (OpCodes.Blt, loop [i]);
5878 ec.MarkLabel (ec.LoopEnd);
5882 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
5884 class RuntimeDispose : Using.VariableDeclaration
5886 public RuntimeDispose (LocalVariable lv, Location loc)
5887 : base (lv, loc)
5891 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5893 // Defered to runtime check
5896 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5898 var idt = bc.BuiltinTypes.IDisposable;
5901 // Fabricates code like
5903 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5906 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5908 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5909 dispose_variable.CreateReferenceExpression (bc, loc),
5910 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5911 loc), new NullLiteral (loc), loc);
5913 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5915 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5916 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5918 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5919 return new If (idisaposable_test, dispose, loc);
5923 LocalVariable variable;
5924 Expression expr;
5925 Statement statement;
5926 ExpressionStatement init;
5927 TemporaryVariableReference enumerator_variable;
5928 bool ambiguous_getenumerator_name;
5930 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
5931 : base (@foreach)
5933 this.variable = var;
5934 this.expr = expr;
5937 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5939 rc.Report.SymbolRelatedToPreviousError (enumerator);
5940 rc.Report.Error (202, loc,
5941 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5942 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5945 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5948 // Option 1: Try to match by name GetEnumerator first
5950 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5951 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5953 var mg = mexpr as MethodGroupExpr;
5954 if (mg != null) {
5955 mg.InstanceExpression = expr;
5956 Arguments args = new Arguments (0);
5957 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5959 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5960 if (ambiguous_getenumerator_name)
5961 mg = null;
5963 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5964 return mg;
5969 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5971 var t = expr.Type;
5972 PredefinedMember<MethodSpec> iface_candidate = null;
5973 var ptypes = rc.Module.PredefinedTypes;
5974 var gen_ienumerable = ptypes.IEnumerableGeneric;
5975 if (!gen_ienumerable.Define ())
5976 gen_ienumerable = null;
5978 do {
5979 var ifaces = t.Interfaces;
5980 if (ifaces != null) {
5981 foreach (var iface in ifaces) {
5982 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5983 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5984 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5985 rc.Report.Error (1640, loc,
5986 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5987 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5989 return null;
5992 // TODO: Cache this somehow
5993 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5994 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5996 continue;
5999 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6000 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6005 if (t.IsGenericParameter)
6006 t = t.BaseType;
6007 else
6008 t = null;
6010 } while (t != null);
6012 if (iface_candidate == null) {
6013 if (expr.Type != InternalType.ErrorType) {
6014 rc.Report.Error (1579, loc,
6015 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6016 expr.Type.GetSignatureForError (), "GetEnumerator");
6019 return null;
6022 var method = iface_candidate.Resolve (loc);
6023 if (method == null)
6024 return null;
6026 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6027 mg.InstanceExpression = expr;
6028 return mg;
6031 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6033 var ms = MemberCache.FindMember (enumerator.ReturnType,
6034 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6035 BindingRestriction.InstanceOnly) as MethodSpec;
6037 if (ms == null || !ms.IsPublic) {
6038 Error_WrongEnumerator (rc, enumerator);
6039 return null;
6042 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6045 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6047 var ps = MemberCache.FindMember (enumerator.ReturnType,
6048 MemberFilter.Property ("Current", null),
6049 BindingRestriction.InstanceOnly) as PropertySpec;
6051 if (ps == null || !ps.IsPublic) {
6052 Error_WrongEnumerator (rc, enumerator);
6053 return null;
6056 return ps;
6059 public override bool Resolve (BlockContext ec)
6061 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6063 if (is_dynamic) {
6064 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6065 } else if (expr.Type.IsNullableType) {
6066 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6069 var get_enumerator_mg = ResolveGetEnumerator (ec);
6070 if (get_enumerator_mg == null) {
6071 return false;
6074 var get_enumerator = get_enumerator_mg.BestCandidate;
6075 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6076 enumerator_variable.Resolve (ec);
6078 // Prepare bool MoveNext ()
6079 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6080 if (move_next_mg == null) {
6081 return false;
6084 move_next_mg.InstanceExpression = enumerator_variable;
6086 // Prepare ~T~ Current { get; }
6087 var current_prop = ResolveCurrent (ec, get_enumerator);
6088 if (current_prop == null) {
6089 return false;
6092 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6093 if (current_pe == null)
6094 return false;
6096 VarExpr ve = for_each.type as VarExpr;
6098 if (ve != null) {
6099 if (is_dynamic) {
6100 // Source type is dynamic, set element type to dynamic too
6101 variable.Type = ec.BuiltinTypes.Dynamic;
6102 } else {
6103 // Infer implicitly typed local variable from foreach enumerable type
6104 variable.Type = current_pe.Type;
6106 } else {
6107 if (is_dynamic) {
6108 // Explicit cast of dynamic collection elements has to be done at runtime
6109 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6112 variable.Type = for_each.type.ResolveAsType (ec);
6114 if (variable.Type == null)
6115 return false;
6117 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6118 if (current_pe == null)
6119 return false;
6122 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6123 if (variable_ref == null)
6124 return false;
6126 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), variable.Location));
6128 var init = new Invocation (get_enumerator_mg, null);
6130 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6131 for_each.body, Location.Null);
6133 var enum_type = enumerator_variable.Type;
6136 // Add Dispose method call when enumerator can be IDisposable
6138 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6139 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6141 // Runtime Dispose check
6143 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6144 vd.Initializer = init;
6145 statement = new Using (vd, statement, Location.Null);
6146 } else {
6148 // No Dispose call needed
6150 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6151 this.init.Resolve (ec);
6153 } else {
6155 // Static Dispose check
6157 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6158 vd.Initializer = init;
6159 statement = new Using (vd, statement, Location.Null);
6162 return statement.Resolve (ec);
6165 protected override void DoEmit (EmitContext ec)
6167 enumerator_variable.LocalInfo.CreateBuilder (ec);
6169 if (init != null)
6170 init.EmitStatement (ec);
6172 statement.Emit (ec);
6175 #region IErrorHandler Members
6177 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6179 ec.Report.SymbolRelatedToPreviousError (best);
6180 ec.Report.Warning (278, 2, expr.Location,
6181 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6182 expr.Type.GetSignatureForError (), "enumerable",
6183 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6185 ambiguous_getenumerator_name = true;
6186 return true;
6189 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6191 return false;
6194 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6196 return false;
6199 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6201 return false;
6204 #endregion
6207 Expression type;
6208 LocalVariable variable;
6209 Expression expr;
6210 Statement statement;
6211 Block body;
6213 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6215 this.type = type;
6216 this.variable = var;
6217 this.expr = expr;
6218 this.statement = stmt;
6219 this.body = body;
6220 loc = l;
6223 public Expression Expr {
6224 get { return expr; }
6227 public Statement Statement {
6228 get { return statement; }
6231 public Expression TypeExpression {
6232 get { return type; }
6235 public LocalVariable Variable {
6236 get { return variable; }
6239 public override bool Resolve (BlockContext ec)
6241 expr = expr.Resolve (ec);
6242 if (expr == null)
6243 return false;
6245 if (expr.IsNull) {
6246 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6247 return false;
6250 body.AddStatement (statement);
6252 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6253 statement = new ArrayForeach (this, 1);
6254 } else if (expr.Type is ArrayContainer) {
6255 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6256 } else {
6257 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6258 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6259 expr.ExprClassName);
6260 return false;
6263 statement = new CollectionForeach (this, variable, expr);
6266 return statement.Resolve (ec);
6269 protected override void DoEmit (EmitContext ec)
6271 variable.CreateBuilder (ec);
6273 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6274 ec.LoopBegin = ec.DefineLabel ();
6275 ec.LoopEnd = ec.DefineLabel ();
6277 statement.Emit (ec);
6279 ec.LoopBegin = old_begin;
6280 ec.LoopEnd = old_end;
6283 protected override void CloneTo (CloneContext clonectx, Statement t)
6285 Foreach target = (Foreach) t;
6287 target.type = type.Clone (clonectx);
6288 target.expr = expr.Clone (clonectx);
6289 target.body = (Block) body.Clone (clonectx);
6292 public override object Accept (StructuralVisitor visitor)
6294 return visitor.Visit (this);