2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
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.
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 ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
72 if (ec.StatementEpilogue != null) {
78 // This routine must be overrided in derived classes and make copies
79 // of all the data that might be modified if resolved
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);
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");
96 public virtual object Accept (StructuralVisitor visitor)
98 return visitor.Visit (this);
102 public sealed class EmptyStatement : Statement
104 public EmptyStatement (Location loc)
109 public override bool Resolve (BlockContext ec)
114 public override bool ResolveUnreachable (BlockContext ec, bool warn)
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)
133 public override object Accept (StructuralVisitor visitor)
135 return visitor.Visit (this);
139 public class If : Statement {
141 public Statement TrueStatement;
142 public Statement FalseStatement;
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,
156 this.expr = bool_expr;
157 TrueStatement = true_statement;
158 FalseStatement = false_statement;
162 public Expression Expr {
168 public override bool Resolve (BlockContext ec)
172 expr = expr.Resolve (ec);
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
183 if (!TrueStatement.Resolve (ec))
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
189 FalseStatement = null;
191 if (!TrueStatement.ResolveUnreachable (ec, true))
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
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 ();
220 protected override void DoEmit (EmitContext ec)
222 Label false_target = ec.DefineLabel ();
226 // If we're a boolean constant, Resolve() already
227 // eliminated dead code for us.
229 Constant c = expr as Constant;
231 c.EmitSideEffect (ec);
233 if (!c.IsDefaultValue)
234 TrueStatement.Emit (ec);
235 else if (FalseStatement != null)
236 FalseStatement.Emit (ec);
241 expr.EmitBranchable (ec, false_target, false);
243 TrueStatement.Emit (ec);
245 if (FalseStatement != null){
246 bool branch_emitted = false;
248 end = ec.DefineLabel ();
250 ec.Emit (OpCodes.Br, end);
251 branch_emitted = true;
254 ec.MarkLabel (false_target);
255 FalseStatement.Emit (ec);
260 ec.MarkLabel (false_target);
264 protected override void CloneTo (CloneContext clonectx, Statement 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 doLocation, Location whileLocation)
287 EmbeddedStatement = statement;
289 WhileLocation = whileLocation;
292 public Location WhileLocation {
296 public override bool Resolve (BlockContext ec)
300 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
302 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
304 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
305 if (!EmbeddedStatement.Resolve (ec))
307 ec.EndFlowBranching ();
309 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
310 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
312 expr = expr.Resolve (ec);
315 else if (expr is Constant){
316 bool infinite = !((Constant) expr).IsDefaultValue;
318 ec.CurrentBranching.CurrentUsageVector.Goto ();
321 ec.EndFlowBranching ();
326 protected override void DoEmit (EmitContext ec)
328 Label loop = ec.DefineLabel ();
329 Label old_begin = ec.LoopBegin;
330 Label old_end = ec.LoopEnd;
332 ec.LoopBegin = ec.DefineLabel ();
333 ec.LoopEnd = ec.DefineLabel ();
336 EmbeddedStatement.Emit (ec);
337 ec.MarkLabel (ec.LoopBegin);
339 // Mark start of while condition
340 ec.Mark (WhileLocation);
343 // Dead code elimination
345 if (expr is Constant) {
346 bool res = !((Constant) expr).IsDefaultValue;
348 expr.EmitSideEffect (ec);
350 ec.Emit (OpCodes.Br, loop);
352 expr.EmitBranchable (ec, loop, true);
355 ec.MarkLabel (ec.LoopEnd);
357 ec.LoopBegin = old_begin;
358 ec.LoopEnd = old_end;
361 protected override void CloneTo (CloneContext clonectx, Statement t)
365 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
366 target.expr = expr.Clone (clonectx);
369 public override object Accept (StructuralVisitor visitor)
371 return visitor.Visit (this);
375 public class While : Statement {
376 public Expression expr;
377 public Statement Statement;
378 bool infinite, empty;
380 public While (BooleanExpression bool_expr, Statement statement, Location l)
382 this.expr = bool_expr;
383 Statement = statement;
387 public override bool Resolve (BlockContext ec)
391 expr = expr.Resolve (ec);
396 // Inform whether we are infinite or not
398 if (expr is Constant){
399 bool value = !((Constant) expr).IsDefaultValue;
402 if (!Statement.ResolveUnreachable (ec, true))
410 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
412 ec.CurrentBranching.CreateSibling ();
414 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
415 if (!Statement.Resolve (ec))
417 ec.EndFlowBranching ();
419 // There's no direct control flow from the end of the embedded statement to the end of the loop
420 ec.CurrentBranching.CurrentUsageVector.Goto ();
422 ec.EndFlowBranching ();
427 protected override void DoEmit (EmitContext ec)
430 expr.EmitSideEffect (ec);
434 Label old_begin = ec.LoopBegin;
435 Label old_end = ec.LoopEnd;
437 ec.LoopBegin = ec.DefineLabel ();
438 ec.LoopEnd = ec.DefineLabel ();
441 // Inform whether we are infinite or not
443 if (expr is Constant) {
444 // expr is 'true', since the 'empty' case above handles the 'false' case
445 ec.MarkLabel (ec.LoopBegin);
447 if (ec.EmitAccurateDebugInfo)
448 ec.Emit (OpCodes.Nop);
450 expr.EmitSideEffect (ec);
452 ec.Emit (OpCodes.Br, ec.LoopBegin);
455 // Inform that we are infinite (ie, `we return'), only
456 // if we do not `break' inside the code.
458 ec.MarkLabel (ec.LoopEnd);
460 Label while_loop = ec.DefineLabel ();
462 ec.Emit (OpCodes.Br, ec.LoopBegin);
463 ec.MarkLabel (while_loop);
467 ec.MarkLabel (ec.LoopBegin);
470 expr.EmitBranchable (ec, while_loop, true);
472 ec.MarkLabel (ec.LoopEnd);
475 ec.LoopBegin = old_begin;
476 ec.LoopEnd = old_end;
479 protected override void CloneTo (CloneContext clonectx, Statement t)
481 While target = (While) t;
483 target.expr = expr.Clone (clonectx);
484 target.Statement = Statement.Clone (clonectx);
487 public override object Accept (StructuralVisitor visitor)
489 return visitor.Visit (this);
493 public class For : Statement
495 bool infinite, empty;
497 public For (Location l)
502 public Statement Initializer {
506 public Expression Condition {
510 public Statement Iterator {
514 public Statement Statement {
518 public override bool Resolve (BlockContext ec)
522 if (Initializer != null) {
523 if (!Initializer.Resolve (ec))
527 if (Condition != null) {
528 Condition = Condition.Resolve (ec);
529 if (Condition == null)
531 else if (Condition is Constant) {
532 bool value = !((Constant) Condition).IsDefaultValue;
535 if (!Statement.ResolveUnreachable (ec, true))
537 if ((Iterator != null) &&
538 !Iterator.ResolveUnreachable (ec, false))
548 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
550 ec.CurrentBranching.CreateSibling ();
552 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
554 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
555 if (!Statement.Resolve (ec))
557 ec.EndFlowBranching ();
559 if (Iterator != null){
560 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
561 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
564 if (!Iterator.Resolve (ec))
569 // There's no direct control flow from the end of the embedded statement to the end of the loop
570 ec.CurrentBranching.CurrentUsageVector.Goto ();
572 ec.EndFlowBranching ();
577 protected override void DoEmit (EmitContext ec)
579 if (Initializer != null)
580 Initializer.Emit (ec);
583 Condition.EmitSideEffect (ec);
587 Label old_begin = ec.LoopBegin;
588 Label old_end = ec.LoopEnd;
589 Label loop = ec.DefineLabel ();
590 Label test = ec.DefineLabel ();
592 ec.LoopBegin = ec.DefineLabel ();
593 ec.LoopEnd = ec.DefineLabel ();
595 ec.Emit (OpCodes.Br, test);
599 ec.MarkLabel (ec.LoopBegin);
604 // If test is null, there is no test, and we are just
607 if (Condition != null) {
608 ec.Mark (Condition.Location);
611 // The Resolve code already catches the case for
612 // Test == Constant (false) so we know that
615 if (Condition is Constant) {
616 Condition.EmitSideEffect (ec);
617 ec.Emit (OpCodes.Br, loop);
619 Condition.EmitBranchable (ec, loop, true);
623 ec.Emit (OpCodes.Br, loop);
624 ec.MarkLabel (ec.LoopEnd);
626 ec.LoopBegin = old_begin;
627 ec.LoopEnd = old_end;
630 protected override void CloneTo (CloneContext clonectx, Statement t)
632 For target = (For) t;
634 if (Initializer != null)
635 target.Initializer = Initializer.Clone (clonectx);
636 if (Condition != null)
637 target.Condition = Condition.Clone (clonectx);
638 if (Iterator != null)
639 target.Iterator = Iterator.Clone (clonectx);
640 target.Statement = Statement.Clone (clonectx);
643 public override object Accept (StructuralVisitor visitor)
645 return visitor.Visit (this);
649 public class StatementExpression : Statement
651 ExpressionStatement expr;
653 public StatementExpression (ExpressionStatement expr)
659 public StatementExpression (ExpressionStatement expr, Location loc)
665 public ExpressionStatement Expr {
671 protected override void CloneTo (CloneContext clonectx, Statement t)
673 StatementExpression target = (StatementExpression) t;
674 target.expr = (ExpressionStatement) expr.Clone (clonectx);
677 protected override void DoEmit (EmitContext ec)
679 expr.EmitStatement (ec);
682 public override bool Resolve (BlockContext ec)
684 expr = expr.ResolveStatement (ec);
688 public override object Accept (StructuralVisitor visitor)
690 return visitor.Visit (this);
694 public class StatementErrorExpression : Statement
696 readonly Expression expr;
698 public StatementErrorExpression (Expression expr)
703 public Expression Expr {
709 protected override void DoEmit (EmitContext ec)
711 throw new NotSupportedException ();
714 protected override void CloneTo (CloneContext clonectx, Statement target)
716 throw new NotImplementedException ();
719 public override object Accept (StructuralVisitor visitor)
721 return visitor.Visit (this);
726 // Simple version of statement list not requiring a block
728 public class StatementList : Statement
730 List<Statement> statements;
732 public StatementList (Statement first, Statement second)
734 statements = new List<Statement> () { first, second };
738 public IList<Statement> Statements {
745 public void Add (Statement statement)
747 statements.Add (statement);
750 public override bool Resolve (BlockContext ec)
752 foreach (var s in statements)
758 protected override void DoEmit (EmitContext ec)
760 foreach (var s in statements)
764 protected override void CloneTo (CloneContext clonectx, Statement target)
766 StatementList t = (StatementList) target;
768 t.statements = new List<Statement> (statements.Count);
769 foreach (Statement s in statements)
770 t.statements.Add (s.Clone (clonectx));
773 public override object Accept (StructuralVisitor visitor)
775 return visitor.Visit (this);
779 // A 'return' or a 'yield break'
780 public abstract class ExitStatement : Statement
782 protected bool unwind_protect;
783 protected abstract bool DoResolve (BlockContext ec);
785 public virtual void Error_FinallyClause (Report Report)
787 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
790 public sealed override bool Resolve (BlockContext ec)
792 var res = DoResolve (ec);
793 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
794 ec.CurrentBranching.CurrentUsageVector.Goto ();
800 /// Implements the return statement
802 public class Return : ExitStatement
806 public Return (Expression expr, Location l)
814 public Expression Expr {
825 protected override bool DoResolve (BlockContext ec)
828 if (ec.ReturnType.Kind == MemberKind.Void)
832 // Return must not be followed by an expression when
833 // the method return type is Task
835 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
836 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
837 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
839 // Extra trick not to emit ret/leave inside awaiter body
841 expr = EmptyExpression.Null;
846 if (ec.CurrentIterator != null) {
847 Error_ReturnFromIterator (ec);
849 ec.Report.Error (126, loc,
850 "An object of a type convertible to `{0}' is required for the return statement",
851 ec.ReturnType.GetSignatureForError ());
857 expr = expr.Resolve (ec);
858 TypeSpec block_return_type = ec.ReturnType;
860 AnonymousExpression am = ec.CurrentAnonymousMethod;
862 if (block_return_type.Kind == MemberKind.Void) {
863 ec.Report.Error (127, loc,
864 "`{0}': A return keyword must not be followed by any expression when method returns void",
865 ec.GetSignatureForError ());
871 Error_ReturnFromIterator (ec);
875 var async_block = am as AsyncInitializer;
876 if (async_block != null) {
878 var storey = (AsyncTaskStorey) am.Storey;
879 var async_type = storey.ReturnType;
881 if (async_type == null && async_block.ReturnTypeInference != null) {
882 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
886 if (async_type.Kind == MemberKind.Void) {
887 ec.Report.Error (127, loc,
888 "`{0}': A return keyword must not be followed by any expression when method returns void",
889 ec.GetSignatureForError ());
894 if (!async_type.IsGenericTask) {
895 if (this is ContextualReturn)
898 ec.Report.Error (1997, loc,
899 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
900 ec.GetSignatureForError ());
905 // The return type is actually Task<T> type argument
907 if (expr.Type == async_type) {
908 ec.Report.Error (4016, loc,
909 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
910 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
912 block_return_type = async_type.TypeArguments[0];
916 // Same error code as .NET but better error message
917 if (block_return_type.Kind == MemberKind.Void) {
918 ec.Report.Error (127, loc,
919 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
920 am.GetSignatureForError ());
925 var l = am as AnonymousMethodBody;
926 if (l != null && l.ReturnTypeInference != null && expr != null) {
927 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
936 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
937 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
940 if (am != null && block_return_type == ec.ReturnType) {
941 ec.Report.Error (1662, loc,
942 "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",
943 am.ContainerType, am.GetSignatureForError ());
952 protected override void DoEmit (EmitContext ec)
957 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
958 if (async_body != null) {
959 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
961 // It's null for await without async
962 if (async_return != null) {
963 async_return.EmitAssign (ec);
968 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
974 if (unwind_protect || ec.EmitAccurateDebugInfo)
975 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
978 if (unwind_protect) {
979 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
980 } else if (ec.EmitAccurateDebugInfo) {
981 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
983 ec.Emit (OpCodes.Ret);
987 void Error_ReturnFromIterator (ResolveContext rc)
989 rc.Report.Error (1622, loc,
990 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
993 protected override void CloneTo (CloneContext clonectx, Statement t)
995 Return target = (Return) t;
996 // It's null for simple return;
998 target.expr = expr.Clone (clonectx);
1001 public override object Accept (StructuralVisitor visitor)
1003 return visitor.Visit (this);
1007 public class Goto : Statement {
1009 LabeledStatement label;
1010 bool unwind_protect;
1012 public override bool Resolve (BlockContext ec)
1014 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1015 ec.CurrentBranching.CurrentUsageVector.Goto ();
1019 public Goto (string label, Location l)
1025 public string Target {
1026 get { return target; }
1029 public void SetResolvedTarget (LabeledStatement label)
1032 label.AddReference ();
1035 protected override void CloneTo (CloneContext clonectx, Statement target)
1040 protected override void DoEmit (EmitContext ec)
1043 throw new InternalErrorException ("goto emitted before target resolved");
1044 Label l = label.LabelTarget (ec);
1045 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1048 public override object Accept (StructuralVisitor visitor)
1050 return visitor.Visit (this);
1054 public class LabeledStatement : Statement {
1061 FlowBranching.UsageVector vectors;
1063 public LabeledStatement (string name, Block block, Location l)
1070 public Label LabelTarget (EmitContext ec)
1075 label = ec.DefineLabel ();
1080 public Block Block {
1086 public string Name {
1087 get { return name; }
1090 public bool IsDefined {
1091 get { return defined; }
1094 public bool HasBeenReferenced {
1095 get { return referenced; }
1098 public FlowBranching.UsageVector JumpOrigins {
1099 get { return vectors; }
1102 public void AddUsageVector (FlowBranching.UsageVector vector)
1104 vector = vector.Clone ();
1105 vector.Next = vectors;
1109 protected override void CloneTo (CloneContext clonectx, Statement target)
1114 public override bool Resolve (BlockContext ec)
1116 // this flow-branching will be terminated when the surrounding block ends
1117 ec.StartFlowBranching (this);
1121 protected override void DoEmit (EmitContext ec)
1123 if (!HasBeenReferenced)
1124 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1127 ec.MarkLabel (label);
1130 public void AddReference ()
1135 public override object Accept (StructuralVisitor visitor)
1137 return visitor.Visit (this);
1143 /// `goto default' statement
1145 public class GotoDefault : Statement {
1147 public GotoDefault (Location l)
1152 protected override void CloneTo (CloneContext clonectx, Statement target)
1157 public override bool Resolve (BlockContext ec)
1159 ec.CurrentBranching.CurrentUsageVector.Goto ();
1161 if (ec.Switch == null) {
1162 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1166 if (!ec.Switch.GotDefault) {
1167 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1174 protected override void DoEmit (EmitContext ec)
1176 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1179 public override object Accept (StructuralVisitor visitor)
1181 return visitor.Visit (this);
1186 /// `goto case' statement
1188 public class GotoCase : Statement {
1192 public GotoCase (Expression e, Location l)
1198 public Expression Expr {
1204 public override bool Resolve (BlockContext ec)
1206 if (ec.Switch == null){
1207 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1211 ec.CurrentBranching.CurrentUsageVector.Goto ();
1213 expr = expr.Resolve (ec);
1217 Constant c = expr as Constant;
1219 ec.Report.Error (150, expr.Location, "A constant value is expected");
1224 if (ec.Switch.IsNullable && c is NullLiteral) {
1227 TypeSpec type = ec.Switch.SwitchType;
1228 res = c.TryReduce (ec, type);
1230 c.Error_ValueCannotBeConverted (ec, type, true);
1234 if (!Convert.ImplicitStandardConversionExists (c, type))
1235 ec.Report.Warning (469, 2, loc,
1236 "The `goto case' value is not implicitly convertible to type `{0}'",
1237 TypeManager.CSharpName (type));
1241 sl = ec.Switch.ResolveGotoCase (ec, res);
1245 protected override void DoEmit (EmitContext ec)
1247 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1250 protected override void CloneTo (CloneContext clonectx, Statement t)
1252 GotoCase target = (GotoCase) t;
1254 target.expr = expr.Clone (clonectx);
1257 public override object Accept (StructuralVisitor visitor)
1259 return visitor.Visit (this);
1263 public class Throw : Statement {
1266 public Throw (Expression expr, Location l)
1272 public Expression Expr {
1278 public override bool Resolve (BlockContext ec)
1281 ec.CurrentBranching.CurrentUsageVector.Goto ();
1282 return ec.CurrentBranching.CheckRethrow (loc);
1285 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1286 ec.CurrentBranching.CurrentUsageVector.Goto ();
1291 var et = ec.BuiltinTypes.Exception;
1292 if (Convert.ImplicitConversionExists (ec, expr, et))
1293 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1295 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1300 protected override void DoEmit (EmitContext ec)
1303 ec.Emit (OpCodes.Rethrow);
1307 ec.Emit (OpCodes.Throw);
1311 protected override void CloneTo (CloneContext clonectx, Statement t)
1313 Throw target = (Throw) t;
1316 target.expr = expr.Clone (clonectx);
1319 public override object Accept (StructuralVisitor visitor)
1321 return visitor.Visit (this);
1325 public class Break : Statement {
1327 public Break (Location l)
1332 bool unwind_protect;
1334 public override bool Resolve (BlockContext ec)
1336 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1337 ec.CurrentBranching.CurrentUsageVector.Goto ();
1341 protected override void DoEmit (EmitContext ec)
1343 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1346 protected override void CloneTo (CloneContext clonectx, Statement t)
1351 public override object Accept (StructuralVisitor visitor)
1353 return visitor.Visit (this);
1357 public class Continue : Statement {
1359 public Continue (Location l)
1364 bool unwind_protect;
1366 public override bool Resolve (BlockContext ec)
1368 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1369 ec.CurrentBranching.CurrentUsageVector.Goto ();
1373 protected override void DoEmit (EmitContext ec)
1375 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1378 protected override void CloneTo (CloneContext clonectx, Statement t)
1383 public override object Accept (StructuralVisitor visitor)
1385 return visitor.Visit (this);
1389 public interface ILocalVariable
1391 void Emit (EmitContext ec);
1392 void EmitAssign (EmitContext ec);
1393 void EmitAddressOf (EmitContext ec);
1396 public interface INamedBlockVariable
1398 Block Block { get; }
1399 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1400 bool IsDeclared { get; }
1401 bool IsParameter { get; }
1402 Location Location { get; }
1405 public class BlockVariableDeclaration : Statement
1407 public class Declarator
1410 Expression initializer;
1412 public Declarator (LocalVariable li, Expression initializer)
1414 if (li.Type != null)
1415 throw new ArgumentException ("Expected null variable type");
1418 this.initializer = initializer;
1421 public Declarator (Declarator clone, Expression initializer)
1424 this.initializer = initializer;
1429 public LocalVariable Variable {
1435 public Expression Initializer {
1440 initializer = value;
1447 Expression initializer;
1448 protected FullNamedExpression type_expr;
1449 protected LocalVariable li;
1450 protected List<Declarator> declarators;
1453 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1455 this.type_expr = type;
1457 this.loc = type_expr.Location;
1460 protected BlockVariableDeclaration (LocalVariable li)
1467 public List<Declarator> Declarators {
1473 public Expression Initializer {
1478 initializer = value;
1482 public FullNamedExpression TypeExpression {
1488 public LocalVariable Variable {
1496 public void AddDeclarator (Declarator decl)
1498 if (declarators == null)
1499 declarators = new List<Declarator> ();
1501 declarators.Add (decl);
1504 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1506 if (bc.Report.Errors != 0)
1509 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1511 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1512 new MemberName (li.Name, li.Location), null);
1514 container.AddField (f);
1517 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1521 public override bool Resolve (BlockContext bc)
1523 return Resolve (bc, true);
1526 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1528 if (type == null && !li.IsCompilerGenerated) {
1529 var vexpr = type_expr as VarExpr;
1532 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1533 // same name exists or as a keyword when no type was found
1535 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1536 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1537 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1540 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1544 if (li.IsConstant) {
1545 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1549 if (Initializer == null) {
1550 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1554 if (declarators != null) {
1555 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1559 Initializer = Initializer.Resolve (bc);
1560 if (Initializer != null) {
1561 ((VarExpr) type_expr).InferType (bc, Initializer);
1562 type = type_expr.Type;
1564 // Set error type to indicate the var was placed correctly but could
1567 // var a = missing ();
1569 type = InternalType.ErrorType;
1574 type = type_expr.ResolveAsType (bc);
1578 if (li.IsConstant && !type.IsConstantCompatible) {
1579 Const.Error_InvalidConstantType (type, loc, bc.Report);
1584 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1589 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1591 CreateEvaluatorVariable (bc, li);
1593 li.PrepareForFlowAnalysis (bc);
1596 if (initializer != null) {
1597 initializer = ResolveInitializer (bc, li, initializer);
1598 // li.Variable.DefinitelyAssigned
1601 if (declarators != null) {
1602 foreach (var d in declarators) {
1603 d.Variable.Type = li.Type;
1605 CreateEvaluatorVariable (bc, d.Variable);
1607 d.Variable.PrepareForFlowAnalysis (bc);
1610 if (d.Initializer != null && resolveDeclaratorInitializers) {
1611 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1612 // d.Variable.DefinitelyAssigned
1620 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1622 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1623 return a.ResolveStatement (bc);
1626 protected override void DoEmit (EmitContext ec)
1628 li.CreateBuilder (ec);
1630 if (Initializer != null)
1631 ((ExpressionStatement) Initializer).EmitStatement (ec);
1633 if (declarators != null) {
1634 foreach (var d in declarators) {
1635 d.Variable.CreateBuilder (ec);
1636 if (d.Initializer != null) {
1637 ec.Mark (d.Variable.Location);
1638 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1644 protected override void CloneTo (CloneContext clonectx, Statement target)
1646 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1648 if (type_expr != null)
1649 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1651 if (initializer != null)
1652 t.initializer = initializer.Clone (clonectx);
1654 if (declarators != null) {
1655 t.declarators = null;
1656 foreach (var d in declarators)
1657 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1661 public override object Accept (StructuralVisitor visitor)
1663 return visitor.Visit (this);
1667 public class BlockConstantDeclaration : BlockVariableDeclaration
1669 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1674 public override void Emit (EmitContext ec)
1676 // Nothing to emit, not even sequence point
1679 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1681 initializer = initializer.Resolve (bc);
1682 if (initializer == null)
1685 var c = initializer as Constant;
1687 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1691 c = c.ConvertImplicitly (li.Type);
1693 if (TypeSpec.IsReferenceType (li.Type))
1694 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1696 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1701 li.ConstantValue = c;
1705 public override object Accept (StructuralVisitor visitor)
1707 return visitor.Visit (this);
1712 // The information about a user-perceived local variable
1714 public class LocalVariable : INamedBlockVariable, ILocalVariable
1721 AddressTaken = 1 << 2,
1722 CompilerGenerated = 1 << 3,
1724 ForeachVariable = 1 << 5,
1725 FixedVariable = 1 << 6,
1726 UsingVariable = 1 << 7,
1727 // DefinitelyAssigned = 1 << 8,
1730 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1734 readonly string name;
1735 readonly Location loc;
1736 readonly Block block;
1738 Constant const_value;
1740 public VariableInfo VariableInfo;
1741 HoistedVariable hoisted_variant;
1743 LocalBuilder builder;
1745 public LocalVariable (Block block, string name, Location loc)
1752 public LocalVariable (Block block, string name, Flags flags, Location loc)
1753 : this (block, name, loc)
1759 // Used by variable declarators
1761 public LocalVariable (LocalVariable li, string name, Location loc)
1762 : this (li.block, name, li.flags, loc)
1768 public bool AddressTaken {
1770 return (flags & Flags.AddressTaken) != 0;
1774 public Block Block {
1780 public Constant ConstantValue {
1785 const_value = value;
1790 // Hoisted local variable variant
1792 public HoistedVariable HoistedVariant {
1794 return hoisted_variant;
1797 hoisted_variant = value;
1801 public bool IsDeclared {
1803 return type != null;
1807 public bool IsCompilerGenerated {
1809 return (flags & Flags.CompilerGenerated) != 0;
1813 public bool IsConstant {
1815 return (flags & Flags.Constant) != 0;
1819 public bool IsLocked {
1821 return (flags & Flags.IsLocked) != 0;
1824 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1828 public bool IsThis {
1830 return (flags & Flags.IsThis) != 0;
1834 public bool IsFixed {
1836 return (flags & Flags.FixedVariable) != 0;
1840 bool INamedBlockVariable.IsParameter {
1846 public bool IsReadonly {
1848 return (flags & Flags.ReadonlyMask) != 0;
1852 public Location Location {
1858 public string Name {
1864 public TypeSpec Type {
1875 public void CreateBuilder (EmitContext ec)
1877 if ((flags & Flags.Used) == 0) {
1878 if (VariableInfo == null) {
1879 // Missing flow analysis or wrong variable flags
1880 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1883 if (VariableInfo.IsEverAssigned)
1884 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1886 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1889 if (HoistedVariant != null)
1892 if (builder != null) {
1893 if ((flags & Flags.CompilerGenerated) != 0)
1896 // To avoid Used warning duplicates
1897 throw new InternalErrorException ("Already created variable `{0}'", name);
1901 // All fixed variabled are pinned, a slot has to be alocated
1903 builder = ec.DeclareLocal (Type, IsFixed);
1904 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1905 ec.DefineLocalVariable (name, builder);
1908 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1910 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1915 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1917 if (IsConstant && const_value != null)
1918 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1920 return new LocalVariableReference (this, loc);
1923 public void Emit (EmitContext ec)
1925 // TODO: Need something better for temporary variables
1926 if ((flags & Flags.CompilerGenerated) != 0)
1929 ec.Emit (OpCodes.Ldloc, builder);
1932 public void EmitAssign (EmitContext ec)
1934 // TODO: Need something better for temporary variables
1935 if ((flags & Flags.CompilerGenerated) != 0)
1938 ec.Emit (OpCodes.Stloc, builder);
1941 public void EmitAddressOf (EmitContext ec)
1943 ec.Emit (OpCodes.Ldloca, builder);
1946 public static string GetCompilerGeneratedName (Block block)
1948 // HACK: Debugger depends on the name semantics
1949 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1952 public string GetReadOnlyContext ()
1954 switch (flags & Flags.ReadonlyMask) {
1955 case Flags.FixedVariable:
1956 return "fixed variable";
1957 case Flags.ForeachVariable:
1958 return "foreach iteration variable";
1959 case Flags.UsingVariable:
1960 return "using variable";
1963 throw new InternalErrorException ("Variable is not readonly");
1966 public bool IsThisAssigned (BlockContext ec, Block block)
1968 if (VariableInfo == null)
1969 throw new Exception ();
1971 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1974 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1977 public bool IsAssigned (BlockContext ec)
1979 if (VariableInfo == null)
1980 throw new Exception ();
1982 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1985 public void PrepareForFlowAnalysis (BlockContext bc)
1988 // No need for definitely assigned check for these guys
1990 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1993 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1994 bc.FlowOffset += VariableInfo.Length;
1998 // Mark the variables as referenced in the user code
2000 public void SetIsUsed ()
2002 flags |= Flags.Used;
2005 public void SetHasAddressTaken ()
2007 flags |= (Flags.AddressTaken | Flags.Used);
2010 public override string ToString ()
2012 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2017 /// Block represents a C# block.
2021 /// This class is used in a number of places: either to represent
2022 /// explicit blocks that the programmer places or implicit blocks.
2024 /// Implicit blocks are used as labels or to introduce variable
2027 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2028 /// they contain extra information that is not necessary on normal blocks.
2030 public class Block : Statement {
2037 HasCapturedVariable = 64,
2038 HasCapturedThis = 1 << 7,
2039 IsExpressionTree = 1 << 8,
2040 CompilerGenerated = 1 << 9,
2041 HasAsyncModifier = 1 << 10,
2043 YieldBlock = 1 << 12,
2044 AwaitBlock = 1 << 13
2047 public Block Parent;
2048 public Location StartLocation;
2049 public Location EndLocation;
2051 public ExplicitBlock Explicit;
2052 public ParametersBlock ParametersBlock;
2054 protected Flags flags;
2057 // The statements in this block
2059 protected List<Statement> statements;
2061 protected List<Statement> scope_initializers;
2063 int? resolving_init_idx;
2069 public int ID = id++;
2071 static int clone_id_counter;
2075 // int assignable_slots;
2076 bool unreachable_shown;
2079 public Block (Block parent, Location start, Location end)
2080 : this (parent, 0, start, end)
2084 public Block (Block parent, Flags flags, Location start, Location end)
2086 if (parent != null) {
2087 // the appropriate constructors will fixup these fields
2088 ParametersBlock = parent.ParametersBlock;
2089 Explicit = parent.Explicit;
2092 this.Parent = parent;
2094 this.StartLocation = start;
2095 this.EndLocation = end;
2097 statements = new List<Statement> (4);
2099 this.original = this;
2104 public bool HasUnreachableClosingBrace {
2106 return (flags & Flags.HasRet) != 0;
2109 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2113 public Block Original {
2122 public bool IsCompilerGenerated {
2123 get { return (flags & Flags.CompilerGenerated) != 0; }
2124 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2127 public bool Unchecked {
2128 get { return (flags & Flags.Unchecked) != 0; }
2129 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2132 public bool Unsafe {
2133 get { return (flags & Flags.Unsafe) != 0; }
2134 set { flags |= Flags.Unsafe; }
2137 public List<Statement> Statements {
2138 get { return statements; }
2143 public Block CreateSwitchBlock (Location start)
2145 // FIXME: Only explicit block should be created
2146 var new_block = new Block (this, start, start);
2147 new_block.IsCompilerGenerated = true;
2151 public void SetEndLocation (Location loc)
2156 public void AddLabel (LabeledStatement target)
2158 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2161 public void AddLocalName (LocalVariable li)
2163 AddLocalName (li.Name, li);
2166 public void AddLocalName (string name, INamedBlockVariable li)
2168 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2171 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2173 if (reason == null) {
2174 Error_AlreadyDeclared (name, variable);
2178 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2179 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2180 "to `{0}', which is already used in a `{1}' scope to denote something else",
2184 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2186 var pi = variable as ParametersBlock.ParameterInfo;
2188 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2190 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2191 "A local variable named `{0}' is already defined in this scope", name);
2195 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2197 ParametersBlock.TopBlock.Report.Error (412, loc,
2198 "The type parameter name `{0}' is the same as local variable or parameter name",
2203 // It should be used by expressions which require to
2204 // register a statement during resolve process.
2206 public void AddScopeStatement (Statement s)
2208 if (scope_initializers == null)
2209 scope_initializers = new List<Statement> ();
2212 // Simple recursive helper, when resolve scope initializer another
2213 // new scope initializer can be added, this ensures it's initialized
2214 // before existing one. For now this can happen with expression trees
2215 // in base ctor initializer only
2217 if (resolving_init_idx.HasValue) {
2218 scope_initializers.Insert (resolving_init_idx.Value, s);
2219 ++resolving_init_idx;
2221 scope_initializers.Add (s);
2225 public void AddStatement (Statement s)
2230 public int AssignableSlots {
2232 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2234 // return assignable_slots;
2238 public LabeledStatement LookupLabel (string name)
2240 return ParametersBlock.TopBlock.GetLabel (name, this);
2243 public override bool Resolve (BlockContext ec)
2245 if ((flags & Flags.Resolved) != 0)
2248 Block prev_block = ec.CurrentBlock;
2251 ec.CurrentBlock = this;
2252 ec.StartFlowBranching (this);
2255 // Compiler generated scope statements
2257 if (scope_initializers != null) {
2258 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2259 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2262 resolving_init_idx = null;
2266 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2267 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2268 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2269 // responsible for handling the situation.
2271 int statement_count = statements.Count;
2272 for (int ix = 0; ix < statement_count; ix++){
2273 Statement s = statements [ix];
2276 // Warn if we detect unreachable code.
2279 if (s is EmptyStatement)
2282 if (!unreachable_shown && !(s is LabeledStatement)) {
2283 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2284 unreachable_shown = true;
2287 Block c_block = s as Block;
2288 if (c_block != null)
2289 c_block.unreachable = c_block.unreachable_shown = true;
2293 // Note that we're not using ResolveUnreachable() for unreachable
2294 // statements here. ResolveUnreachable() creates a temporary
2295 // flow branching and kills it afterwards. This leads to problems
2296 // if you have two unreachable statements where the first one
2297 // assigns a variable and the second one tries to access it.
2300 if (!s.Resolve (ec)) {
2302 if (ec.IsInProbingMode)
2305 statements [ix] = new EmptyStatement (s.loc);
2309 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2310 statements [ix] = new EmptyStatement (s.loc);
2312 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2313 if (unreachable && s is LabeledStatement)
2314 throw new InternalErrorException ("should not happen");
2317 while (ec.CurrentBranching is FlowBranchingLabeled)
2318 ec.EndFlowBranching ();
2320 bool flow_unreachable = ec.EndFlowBranching ();
2322 ec.CurrentBlock = prev_block;
2324 if (flow_unreachable)
2325 flags |= Flags.HasRet;
2327 // If we're a non-static `struct' constructor which doesn't have an
2328 // initializer, then we must initialize all of the struct's fields.
2329 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2332 flags |= Flags.Resolved;
2336 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2338 unreachable_shown = true;
2342 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2344 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2345 fb.CurrentUsageVector.IsUnreachable = true;
2346 bool ok = Resolve (ec);
2347 ec.KillFlowBranching ();
2352 protected override void DoEmit (EmitContext ec)
2354 for (int ix = 0; ix < statements.Count; ix++){
2355 statements [ix].Emit (ec);
2359 public override void Emit (EmitContext ec)
2361 if (scope_initializers != null)
2362 EmitScopeInitializers (ec);
2367 protected void EmitScopeInitializers (EmitContext ec)
2369 foreach (Statement s in scope_initializers)
2374 public override string ToString ()
2376 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2380 protected override void CloneTo (CloneContext clonectx, Statement t)
2382 Block target = (Block) t;
2384 target.clone_id = clone_id_counter++;
2387 clonectx.AddBlockMap (this, target);
2388 if (original != this)
2389 clonectx.AddBlockMap (original, target);
2391 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2392 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2395 target.Parent = clonectx.RemapBlockCopy (Parent);
2397 target.statements = new List<Statement> (statements.Count);
2398 foreach (Statement s in statements)
2399 target.statements.Add (s.Clone (clonectx));
2402 public override object Accept (StructuralVisitor visitor)
2404 return visitor.Visit (this);
2408 public class ExplicitBlock : Block
2410 protected AnonymousMethodStorey am_storey;
2412 public ExplicitBlock (Block parent, Location start, Location end)
2413 : this (parent, (Flags) 0, start, end)
2417 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2418 : base (parent, flags, start, end)
2420 this.Explicit = this;
2425 public AnonymousMethodStorey AnonymousMethodStorey {
2431 public bool HasAwait {
2433 return (flags & Flags.AwaitBlock) != 0;
2437 public bool HasCapturedThis {
2439 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2442 return (flags & Flags.HasCapturedThis) != 0;
2447 // Used to indicate that the block has reference to parent
2448 // block and cannot be made static when defining anonymous method
2450 public bool HasCapturedVariable {
2452 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2455 return (flags & Flags.HasCapturedVariable) != 0;
2459 public bool HasYield {
2461 return (flags & Flags.YieldBlock) != 0;
2468 // Creates anonymous method storey in current block
2470 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2473 // Return same story for iterator and async blocks unless we are
2474 // in nested anonymous method
2476 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2477 return ec.CurrentAnonymousMethod.Storey;
2479 if (am_storey == null) {
2480 MemberBase mc = ec.MemberContext as MemberBase;
2483 // Creates anonymous method storey for this block
2485 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2491 public override void Emit (EmitContext ec)
2493 if (am_storey != null) {
2494 DefineStoreyContainer (ec, am_storey);
2495 am_storey.EmitStoreyInstantiation (ec, this);
2498 if (scope_initializers != null)
2499 EmitScopeInitializers (ec);
2501 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2502 ec.Emit (OpCodes.Nop);
2513 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2514 ec.Emit (OpCodes.Nop);
2518 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2520 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2521 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2522 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2526 // Creates anonymous method storey
2528 storey.CreateContainer ();
2529 storey.DefineContainer ();
2531 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2534 // Only first storey in path will hold this reference. All children blocks will
2535 // reference it indirectly using $ref field
2537 for (Block b = Original.Explicit.Parent; b != null; b = b.Parent) {
2538 var s = b.Explicit.AnonymousMethodStorey;
2540 storey.HoistedThis = s.HoistedThis;
2546 // We are the first storey on path and this has to be hoisted
2548 if (storey.HoistedThis == null) {
2549 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2551 // ThisReferencesFromChildrenBlock holds all reference even if they
2552 // are not on this path. It saves some memory otherwise it'd have to
2553 // be in every explicit block. We run this check to see if the reference
2554 // is valid for this storey
2556 Block block_on_path = ref_block;
2557 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2559 if (block_on_path == null)
2562 if (storey.HoistedThis == null)
2563 storey.AddCapturedThisField (ec);
2565 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2566 if (b.AnonymousMethodStorey != null) {
2567 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2568 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2571 // Stop propagation inside same top block
2573 if (b.ParametersBlock == ParametersBlock.Original)
2576 b = b.ParametersBlock;
2579 var pb = b as ParametersBlock;
2580 if (pb != null && pb.StateMachine != null) {
2581 if (pb.StateMachine == storey)
2584 pb.StateMachine.AddParentStoreyReference (ec, storey);
2587 b.HasCapturedVariable = true;
2593 var ref_blocks = storey.ReferencesFromChildrenBlock;
2594 if (ref_blocks != null) {
2595 foreach (ExplicitBlock ref_block in ref_blocks) {
2596 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2597 if (b.AnonymousMethodStorey != null) {
2598 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2601 // Stop propagation inside same top block
2603 if (b.ParametersBlock == ParametersBlock.Original)
2606 b = b.ParametersBlock;
2609 var pb = b as ParametersBlock;
2610 if (pb != null && pb.StateMachine != null) {
2611 if (pb.StateMachine == storey)
2614 pb.StateMachine.AddParentStoreyReference (ec, storey);
2617 b.HasCapturedVariable = true;
2623 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2626 public void RegisterAsyncAwait ()
2629 while ((block.flags & Flags.AwaitBlock) == 0) {
2630 block.flags |= Flags.AwaitBlock;
2632 if (block is ParametersBlock)
2635 block = block.Parent.Explicit;
2639 public void RegisterIteratorYield ()
2642 while ((block.flags & Flags.YieldBlock) == 0) {
2643 block.flags |= Flags.YieldBlock;
2645 if (block.Parent == null)
2648 block = block.Parent.Explicit;
2652 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2654 tryBlock.statements = statements;
2655 statements = new List<Statement> (1);
2656 statements.Add (tf);
2661 // ParametersBlock was introduced to support anonymous methods
2662 // and lambda expressions
2664 public class ParametersBlock : ExplicitBlock
2666 public class ParameterInfo : INamedBlockVariable
2668 readonly ParametersBlock block;
2670 public VariableInfo VariableInfo;
2673 public ParameterInfo (ParametersBlock block, int index)
2681 public ParametersBlock Block {
2687 Block INamedBlockVariable.Block {
2693 public bool IsDeclared {
2699 public bool IsParameter {
2705 public bool IsLocked {
2714 public Location Location {
2716 return Parameter.Location;
2720 public Parameter Parameter {
2722 return block.Parameters [index];
2726 public TypeSpec ParameterType {
2728 return Parameter.Type;
2734 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2736 return new ParameterReference (this, loc);
2741 // Block is converted into an expression
2743 sealed class BlockScopeExpression : Expression
2746 readonly ParametersBlock block;
2748 public BlockScopeExpression (Expression child, ParametersBlock block)
2754 public override bool ContainsEmitWithAwait ()
2756 return child.ContainsEmitWithAwait ();
2759 public override Expression CreateExpressionTree (ResolveContext ec)
2761 throw new NotSupportedException ();
2764 protected override Expression DoResolve (ResolveContext ec)
2769 child = child.Resolve (ec);
2773 eclass = child.eclass;
2778 public override void Emit (EmitContext ec)
2780 block.EmitScopeInitializers (ec);
2785 protected ParametersCompiled parameters;
2786 protected ParameterInfo[] parameter_info;
2788 protected bool unreachable;
2789 protected ToplevelBlock top_block;
2790 protected StateMachine state_machine;
2792 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2793 : base (parent, 0, start, start)
2795 if (parameters == null)
2796 throw new ArgumentNullException ("parameters");
2798 this.parameters = parameters;
2799 ParametersBlock = this;
2801 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2803 this.top_block = parent.ParametersBlock.top_block;
2804 ProcessParameters ();
2807 protected ParametersBlock (ParametersCompiled parameters, Location start)
2808 : base (null, 0, start, start)
2810 if (parameters == null)
2811 throw new ArgumentNullException ("parameters");
2813 this.parameters = parameters;
2814 ParametersBlock = this;
2818 // It's supposed to be used by method body implementation of anonymous methods
2820 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2821 : base (null, 0, source.StartLocation, source.EndLocation)
2823 this.parameters = parameters;
2824 this.statements = source.statements;
2825 this.scope_initializers = source.scope_initializers;
2827 this.resolved = true;
2828 this.unreachable = source.unreachable;
2829 this.am_storey = source.am_storey;
2830 this.state_machine = source.state_machine;
2832 ParametersBlock = this;
2835 // Overwrite original for comparison purposes when linking cross references
2836 // between anonymous methods
2838 Original = source.Original;
2843 public bool IsAsync {
2845 return (flags & Flags.HasAsyncModifier) != 0;
2848 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2853 // Block has been converted to expression tree
2855 public bool IsExpressionTree {
2857 return (flags & Flags.IsExpressionTree) != 0;
2862 // The parameters for the block.
2864 public ParametersCompiled Parameters {
2870 public StateMachine StateMachine {
2872 return state_machine;
2876 public ToplevelBlock TopBlock {
2882 public bool Resolved {
2884 return (flags & Flags.Resolved) != 0;
2888 public int TemporaryLocalsCount { get; set; }
2893 // Check whether all `out' parameters have been assigned.
2895 public void CheckOutParameters (FlowBranching.UsageVector vector)
2897 if (vector.IsUnreachable)
2900 int n = parameter_info == null ? 0 : parameter_info.Length;
2902 for (int i = 0; i < n; i++) {
2903 VariableInfo var = parameter_info[i].VariableInfo;
2908 if (vector.IsAssigned (var, false))
2911 var p = parameter_info[i].Parameter;
2912 TopBlock.Report.Error (177, p.Location,
2913 "The out parameter `{0}' must be assigned to before control leaves the current method",
2918 public override Expression CreateExpressionTree (ResolveContext ec)
2920 if (statements.Count == 1) {
2921 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2922 if (scope_initializers != null)
2923 expr = new BlockScopeExpression (expr, this);
2928 return base.CreateExpressionTree (ec);
2931 public override void Emit (EmitContext ec)
2933 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2934 DefineStoreyContainer (ec, state_machine);
2935 state_machine.EmitStoreyInstantiation (ec, this);
2941 public void EmitEmbedded (EmitContext ec)
2943 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2944 DefineStoreyContainer (ec, state_machine);
2945 state_machine.EmitStoreyInstantiation (ec, this);
2951 public ParameterInfo GetParameterInfo (Parameter p)
2953 for (int i = 0; i < parameters.Count; ++i) {
2954 if (parameters[i] == p)
2955 return parameter_info[i];
2958 throw new ArgumentException ("Invalid parameter");
2961 public ParameterReference GetParameterReference (int index, Location loc)
2963 return new ParameterReference (parameter_info[index], loc);
2966 public Statement PerformClone ()
2968 CloneContext clonectx = new CloneContext ();
2969 return Clone (clonectx);
2972 protected void ProcessParameters ()
2974 if (parameters.Count == 0)
2977 parameter_info = new ParameterInfo[parameters.Count];
2978 for (int i = 0; i < parameter_info.Length; ++i) {
2979 var p = parameters.FixedParameters[i];
2983 // TODO: Should use Parameter only and more block there
2984 parameter_info[i] = new ParameterInfo (this, i);
2986 AddLocalName (p.Name, parameter_info[i]);
2990 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2997 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2998 flags |= Flags.IsExpressionTree;
3003 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3004 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3009 unreachable = top_level.End ();
3011 } catch (Exception e) {
3012 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3015 if (rc.CurrentBlock != null) {
3016 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3018 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3021 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3025 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3026 if (rc.CurrentAnonymousMethod == null) {
3027 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3028 if (md is StateMachineMethod) {
3031 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3036 // If an asynchronous body of F is either an expression classified as nothing, or a
3037 // statement block where no return statements have expressions, the inferred return type is Task
3040 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3041 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3042 am.ReturnTypeInference = null;
3043 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3048 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3049 rc.CurrentAnonymousMethod.GetSignatureForError ());
3057 void ResolveMeta (BlockContext ec)
3059 int orig_count = parameters.Count;
3061 for (int i = 0; i < orig_count; ++i) {
3062 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3064 if ((mod & Parameter.Modifier.OUT) == 0)
3067 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3068 parameter_info[i].VariableInfo = vi;
3069 ec.FlowOffset += vi.Length;
3073 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3075 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3076 var stateMachine = new IteratorStorey (iterator);
3078 state_machine = stateMachine;
3079 iterator.SetStateMachine (stateMachine);
3081 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3082 tlb.Original = this;
3083 tlb.IsCompilerGenerated = true;
3084 tlb.state_machine = stateMachine;
3085 tlb.AddStatement (new Return (iterator, iterator.Location));
3089 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3091 for (int i = 0; i < parameters.Count; i++) {
3092 Parameter p = parameters[i];
3093 Parameter.Modifier mod = p.ModFlags;
3094 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3095 host.Compiler.Report.Error (1988, p.Location,
3096 "Async methods cannot have ref or out parameters");
3100 if (p is ArglistParameter) {
3101 host.Compiler.Report.Error (4006, p.Location,
3102 "__arglist is not allowed in parameter list of async methods");
3106 if (parameters.Types[i].IsPointer) {
3107 host.Compiler.Report.Error (4005, p.Location,
3108 "Async methods cannot have unsafe parameters");
3114 host.Compiler.Report.Warning (1998, 1, loc,
3115 "Async block lacks `await' operator and will run synchronously");
3118 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3119 var initializer = new AsyncInitializer (this, host, block_type);
3120 initializer.Type = block_type;
3122 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3124 state_machine = stateMachine;
3125 initializer.SetStateMachine (stateMachine);
3127 var b = this is ToplevelBlock ?
3128 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3129 new ParametersBlock (Parent, parameters, Location.Null) {
3134 b.IsCompilerGenerated = true;
3135 b.state_machine = stateMachine;
3136 b.AddStatement (new StatementExpression (initializer));
3144 public class ToplevelBlock : ParametersBlock
3146 LocalVariable this_variable;
3147 CompilerContext compiler;
3148 Dictionary<string, object> names;
3149 Dictionary<string, object> labels;
3151 List<ExplicitBlock> this_references;
3153 public ToplevelBlock (CompilerContext ctx, Location loc)
3154 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3158 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3159 : base (parameters, start)
3161 this.compiler = ctx;
3163 flags |= Flags.HasRet;
3165 ProcessParameters ();
3169 // Recreates a top level block from parameters block. Used for
3170 // compiler generated methods where the original block comes from
3171 // explicit child block. This works for already resolved blocks
3172 // only to ensure we resolve them in the correct flow order
3174 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3175 : base (source, parameters)
3177 this.compiler = source.TopBlock.compiler;
3179 flags |= Flags.HasRet;
3182 public bool IsIterator {
3188 public Report Report {
3190 return compiler.Report;
3195 // Used by anonymous blocks to track references of `this' variable
3197 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3199 return this_references;
3204 // Returns the "this" instance variable of this block.
3205 // See AddThisVariable() for more information.
3207 public LocalVariable ThisVariable {
3209 return this_variable;
3213 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3216 names = new Dictionary<string, object> ();
3219 if (!names.TryGetValue (name, out value)) {
3220 names.Add (name, li);
3224 INamedBlockVariable existing = value as INamedBlockVariable;
3225 List<INamedBlockVariable> existing_list;
3226 if (existing != null) {
3227 existing_list = new List<INamedBlockVariable> ();
3228 existing_list.Add (existing);
3229 names[name] = existing_list;
3231 existing_list = (List<INamedBlockVariable>) value;
3235 // A collision checking between local names
3237 for (int i = 0; i < existing_list.Count; ++i) {
3238 existing = existing_list[i];
3239 Block b = existing.Block.Explicit;
3241 // Collision at same level
3242 if (li.Block.Explicit == b) {
3243 li.Block.Error_AlreadyDeclared (name, li);
3247 // Collision with parent
3248 Block parent = li.Block.Explicit;
3249 while ((parent = parent.Parent) != null) {
3251 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3252 i = existing_list.Count;
3257 if (!ignoreChildrenBlocks) {
3258 // Collision with children
3259 while ((b = b.Parent) != null) {
3260 if (li.Block.Explicit == b) {
3261 li.Block.Error_AlreadyDeclared (name, li, "child");
3262 i = existing_list.Count;
3269 existing_list.Add (li);
3272 public void AddLabel (string name, LabeledStatement label)
3275 labels = new Dictionary<string, object> ();
3278 if (!labels.TryGetValue (name, out value)) {
3279 labels.Add (name, label);
3283 LabeledStatement existing = value as LabeledStatement;
3284 List<LabeledStatement> existing_list;
3285 if (existing != null) {
3286 existing_list = new List<LabeledStatement> ();
3287 existing_list.Add (existing);
3288 labels[name] = existing_list;
3290 existing_list = (List<LabeledStatement>) value;
3294 // A collision checking between labels
3296 for (int i = 0; i < existing_list.Count; ++i) {
3297 existing = existing_list[i];
3298 Block b = existing.Block;
3300 // Collision at same level
3301 if (label.Block == b) {
3302 Report.SymbolRelatedToPreviousError (existing.loc, name);
3303 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3307 // Collision with parent
3309 while ((b = b.Parent) != null) {
3310 if (existing.Block == b) {
3311 Report.Error (158, label.loc,
3312 "The label `{0}' shadows another label by the same name in a contained scope", name);
3313 i = existing_list.Count;
3318 // Collision with with children
3320 while ((b = b.Parent) != null) {
3321 if (label.Block == b) {
3322 Report.Error (158, label.loc,
3323 "The label `{0}' shadows another label by the same name in a contained scope", name);
3324 i = existing_list.Count;
3330 existing_list.Add (label);
3333 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3335 if (this_references == null)
3336 this_references = new List<ExplicitBlock> ();
3338 if (!this_references.Contains (block))
3339 this_references.Add (block);
3342 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3344 this_references.Remove (block);
3348 // Creates an arguments set from all parameters, useful for method proxy calls
3350 public Arguments GetAllParametersArguments ()
3352 int count = parameters.Count;
3353 Arguments args = new Arguments (count);
3354 for (int i = 0; i < count; ++i) {
3355 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3356 args.Add (new Argument (arg_expr));
3363 // Lookup inside a block, the returned value can represent 3 states
3365 // true+variable: A local name was found and it's valid
3366 // false+variable: A local name was found in a child block only
3367 // false+null: No local name was found
3369 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3375 if (!names.TryGetValue (name, out value))
3378 variable = value as INamedBlockVariable;
3380 if (variable != null) {
3382 if (variable.Block == b.Original)
3386 } while (b != null);
3394 } while (b != null);
3396 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3397 for (int i = 0; i < list.Count; ++i) {
3400 if (variable.Block == b.Original)
3404 } while (b != null);
3412 } while (b != null);
3422 public LabeledStatement GetLabel (string name, Block block)
3428 if (!labels.TryGetValue (name, out value)) {
3432 var label = value as LabeledStatement;
3434 if (label != null) {
3435 if (label.Block == b.Original)
3438 // TODO: Temporary workaround for the switch block implicit label block
3439 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3442 List<LabeledStatement> list = (List<LabeledStatement>) value;
3443 for (int i = 0; i < list.Count; ++i) {
3445 if (label.Block == b.Original)
3448 // TODO: Temporary workaround for the switch block implicit label block
3449 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3458 // This is used by non-static `struct' constructors which do not have an
3459 // initializer - in this case, the constructor must initialize all of the
3460 // struct's fields. To do this, we add a "this" variable and use the flow
3461 // analysis code to ensure that it's been fully initialized before control
3462 // leaves the constructor.
3464 public void AddThisVariable (BlockContext bc)
3466 if (this_variable != null)
3467 throw new InternalErrorException (StartLocation.ToString ());
3469 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3470 this_variable.Type = bc.CurrentType;
3471 this_variable.PrepareForFlowAnalysis (bc);
3474 public bool IsThisAssigned (BlockContext ec)
3476 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3479 public override void Emit (EmitContext ec)
3481 if (Report.Errors > 0)
3487 if (IsCompilerGenerated) {
3488 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3496 // If `HasReturnLabel' is set, then we already emitted a
3497 // jump to the end of the method, so we must emit a `ret'
3500 // Unfortunately, System.Reflection.Emit automatically emits
3501 // a leave to the end of a finally block. This is a problem
3502 // if no code is following the try/finally block since we may
3503 // jump to a point after the end of the method.
3504 // As a workaround, we're always creating a return label in
3507 if (ec.HasReturnLabel || !unreachable) {
3508 if (ec.HasReturnLabel)
3509 ec.MarkLabel (ec.ReturnLabel);
3511 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3512 ec.Mark (EndLocation);
3514 if (ec.ReturnType.Kind != MemberKind.Void)
3515 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3517 ec.Emit (OpCodes.Ret);
3521 } catch (Exception e){
3522 Console.WriteLine ("Exception caught by the compiler while emitting:");
3523 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3525 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3532 public class SwitchLabel {
3535 readonly Location loc;
3540 // if expr == null, then it is the default case.
3542 public SwitchLabel (Expression expr, Location l)
3548 public bool IsDefault {
3550 return label == null;
3554 public Expression Label {
3560 public Location Location {
3566 public Constant Converted {
3575 public Label GetILLabel (EmitContext ec)
3577 if (il_label == null){
3578 il_label = ec.DefineLabel ();
3581 return il_label.Value;
3585 // Resolves the expression, reduces it to a literal if possible
3586 // and then converts it to the requested type.
3588 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3590 Expression e = label.Resolve (ec);
3595 Constant c = e as Constant;
3597 ec.Report.Error (150, loc, "A constant value is expected");
3601 if (allow_nullable && c is NullLiteral) {
3606 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3607 return converted != null;
3610 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3613 if (converted == null)
3616 label = converted.GetValueAsLiteral ();
3618 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3619 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3622 public SwitchLabel Clone (CloneContext clonectx)
3627 return new SwitchLabel (label.Clone (clonectx), loc);
3631 public class SwitchSection {
3632 public readonly List<SwitchLabel> Labels;
3633 public readonly Block Block;
3635 public SwitchSection (List<SwitchLabel> labels, Block block)
3641 public SwitchSection Clone (CloneContext clonectx)
3643 var cloned_labels = new List<SwitchLabel> ();
3645 foreach (SwitchLabel sl in Labels)
3646 cloned_labels.Add (sl.Clone (clonectx));
3648 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3652 public class Switch : Statement
3654 // structure used to hold blocks of keys while calculating table switch
3655 sealed class LabelsRange : IComparable<LabelsRange>
3657 public readonly long min;
3659 public readonly List<long> label_values;
3661 public LabelsRange (long value)
3664 label_values = new List<long> ();
3665 label_values.Add (value);
3668 public LabelsRange (long min, long max, ICollection<long> values)
3672 this.label_values = new List<long> (values);
3677 return max - min + 1;
3681 public bool AddValue (long value)
3683 var gap = value - min + 1;
3684 // Ensure the range has > 50% occupancy
3685 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3689 label_values.Add (value);
3693 public int CompareTo (LabelsRange other)
3695 int nLength = label_values.Count;
3696 int nLengthOther = other.label_values.Count;
3697 if (nLengthOther == nLength)
3698 return (int) (other.min - min);
3700 return nLength - nLengthOther;
3704 sealed class LabelMarker : Statement
3707 readonly List<SwitchLabel> labels;
3709 public LabelMarker (Switch s, List<SwitchLabel> labels)
3712 this.labels = labels;
3715 protected override void CloneTo (CloneContext clonectx, Statement target)
3719 protected override void DoEmit (EmitContext ec)
3721 foreach (var l in labels) {
3723 ec.MarkLabel (s.DefaultLabel);
3725 ec.MarkLabel (l.GetILLabel (ec));
3730 public List<SwitchSection> Sections;
3731 public Expression Expr;
3734 // Mapping of all labels to their SwitchLabels
3736 Dictionary<long, SwitchLabel> labels;
3737 Dictionary<string, SwitchLabel> string_labels;
3740 /// The governing switch type
3742 public TypeSpec SwitchType;
3747 Label default_target;
3749 Expression new_expr;
3752 SwitchSection constant_section;
3753 SwitchSection default_section;
3754 SwitchLabel null_section;
3756 Statement simple_stmt;
3757 VariableReference value;
3758 ExpressionStatement string_dictionary;
3759 FieldExpr switch_cache_field;
3760 ExplicitBlock block;
3763 // Nullable Types support
3765 Nullable.Unwrap unwrap;
3767 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3775 public ExplicitBlock Block {
3781 public Label DefaultLabel {
3783 return default_target;
3787 public bool GotDefault {
3789 return default_section != null;
3793 public bool IsNullable {
3795 return unwrap != null;
3800 // Determines the governing type for a switch. The returned
3801 // expression might be the expression from the switch, or an
3802 // expression that includes any potential conversions to
3804 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3806 switch (expr.Type.BuiltinType) {
3807 case BuiltinTypeSpec.Type.Byte:
3808 case BuiltinTypeSpec.Type.SByte:
3809 case BuiltinTypeSpec.Type.UShort:
3810 case BuiltinTypeSpec.Type.Short:
3811 case BuiltinTypeSpec.Type.UInt:
3812 case BuiltinTypeSpec.Type.Int:
3813 case BuiltinTypeSpec.Type.ULong:
3814 case BuiltinTypeSpec.Type.Long:
3815 case BuiltinTypeSpec.Type.Char:
3816 case BuiltinTypeSpec.Type.String:
3817 case BuiltinTypeSpec.Type.Bool:
3821 if (expr.Type.IsEnum)
3825 // Try to find a *user* defined implicit conversion.
3827 // If there is no implicit conversion, or if there are multiple
3828 // conversions, we have to report an error
3830 Expression converted = null;
3831 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3834 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3839 // Ignore over-worked ImplicitUserConversions that do
3840 // an implicit conversion in addition to the user conversion.
3842 if (!(e is UserCast))
3845 if (converted != null){
3846 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3855 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3857 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3873 // Performs the basic sanity checks on the switch statement
3874 // (looks for duplicate keys and non-constant expressions).
3876 // It also returns a hashtable with the keys that we will later
3877 // use to compute the switch tables
3879 bool CheckSwitch (ResolveContext ec)
3882 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3883 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3885 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3887 foreach (SwitchSection ss in Sections){
3888 foreach (SwitchLabel sl in ss.Labels){
3890 if (default_section != null){
3891 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3894 default_section = ss;
3898 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3904 if (string_labels != null) {
3905 string s = sl.Converted.GetValue () as string;
3909 string_labels.Add (s, sl);
3911 if (sl.Converted is NullLiteral) {
3914 labels.Add (sl.Converted.GetValueAsLong (), sl);
3917 } catch (ArgumentException) {
3918 if (string_labels != null)
3919 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3921 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3931 // This method emits code for a lookup-based switch statement (non-string)
3932 // Basically it groups the cases into blocks that are at least half full,
3933 // and then spits out individual lookup opcodes for each block.
3934 // It emits the longest blocks first, and short blocks are just
3935 // handled with direct compares.
3937 void EmitTableSwitch (EmitContext ec, Expression val)
3939 Label lbl_default = default_target;
3941 if (labels != null && labels.Count > 0) {
3942 List<LabelsRange> ranges;
3943 if (string_labels != null) {
3944 // We have done all hard work for string already
3945 // setup single range only
3946 ranges = new List<LabelsRange> (1);
3947 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3949 var element_keys = new long[labels.Count];
3950 labels.Keys.CopyTo (element_keys, 0);
3951 Array.Sort (element_keys);
3954 // Build possible ranges of switch labes to reduce number
3957 ranges = new List<LabelsRange> (element_keys.Length);
3958 var range = new LabelsRange (element_keys[0]);
3960 for (int i = 1; i < element_keys.Length; ++i) {
3961 var l = element_keys[i];
3962 if (range.AddValue (l))
3965 range = new LabelsRange (l);
3969 // sort the blocks so we can tackle the largest ones first
3973 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3975 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3976 LabelsRange kb = ranges[range_index];
3977 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3979 // Optimize small ranges using simple equality check
3980 if (kb.Range <= 2) {
3981 foreach (var key in kb.label_values) {
3982 SwitchLabel sl = labels[key];
3983 if (sl.Converted.IsDefaultValue) {
3984 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3987 sl.Converted.Emit (ec);
3988 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3992 // TODO: if all the keys in the block are the same and there are
3993 // no gaps/defaults then just use a range-check.
3994 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3995 // TODO: optimize constant/I4 cases
3997 // check block range (could be > 2^31)
3999 ec.EmitLong (kb.min);
4000 ec.Emit (OpCodes.Blt, lbl_default);
4003 ec.EmitLong (kb.max);
4004 ec.Emit (OpCodes.Bgt, lbl_default);
4009 ec.EmitLong (kb.min);
4010 ec.Emit (OpCodes.Sub);
4013 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4017 int first = (int) kb.min;
4020 ec.Emit (OpCodes.Sub);
4021 } else if (first < 0) {
4022 ec.EmitInt (-first);
4023 ec.Emit (OpCodes.Add);
4027 // first, build the list of labels for the switch
4029 long cJumps = kb.Range;
4030 Label[] switch_labels = new Label[cJumps];
4031 for (int iJump = 0; iJump < cJumps; iJump++) {
4032 var key = kb.label_values[iKey];
4033 if (key == kb.min + iJump) {
4034 switch_labels[iJump] = labels[key].GetILLabel (ec);
4037 switch_labels[iJump] = lbl_default;
4041 // emit the switch opcode
4042 ec.Emit (OpCodes.Switch, switch_labels);
4045 // mark the default for this block
4046 if (range_index != 0)
4047 ec.MarkLabel (lbl_default);
4050 // the last default just goes to the end
4051 if (ranges.Count > 0)
4052 ec.Emit (OpCodes.Br, lbl_default);
4055 // now emit the code for the sections
4056 bool found_default = false;
4058 foreach (SwitchSection ss in Sections) {
4059 foreach (SwitchLabel sl in ss.Labels) {
4061 ec.MarkLabel (lbl_default);
4062 found_default = true;
4063 if (null_section == null)
4064 ec.MarkLabel (null_target);
4065 } else if (sl.Converted.IsNull) {
4066 ec.MarkLabel (null_target);
4069 ec.MarkLabel (sl.GetILLabel (ec));
4075 if (!found_default) {
4076 ec.MarkLabel (lbl_default);
4077 if (null_section == null) {
4078 ec.MarkLabel (null_target);
4083 SwitchLabel FindLabel (Constant value)
4085 SwitchLabel sl = null;
4087 if (string_labels != null) {
4088 string s = value.GetValue () as string;
4090 if (null_section != null)
4092 else if (default_section != null)
4093 sl = default_section.Labels[0];
4095 string_labels.TryGetValue (s, out sl);
4098 if (value is NullLiteral) {
4101 labels.TryGetValue (value.GetValueAsLong (), out sl);
4108 SwitchSection FindSection (SwitchLabel label)
4110 foreach (SwitchSection ss in Sections){
4111 foreach (SwitchLabel sl in ss.Labels){
4120 public override bool Resolve (BlockContext ec)
4122 Expr = Expr.Resolve (ec);
4126 new_expr = SwitchGoverningType (ec, Expr);
4128 if (new_expr == null && Expr.Type.IsNullableType) {
4129 unwrap = Nullable.Unwrap.Create (Expr, false);
4133 new_expr = SwitchGoverningType (ec, unwrap);
4136 if (new_expr == null){
4137 ec.Report.Error (151, loc,
4138 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4139 TypeManager.CSharpName (Expr.Type));
4144 SwitchType = new_expr.Type;
4146 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4147 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4151 if (!CheckSwitch (ec))
4154 Switch old_switch = ec.Switch;
4156 ec.Switch.SwitchType = SwitchType;
4158 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4160 var constant = new_expr as Constant;
4161 if (constant != null) {
4163 SwitchLabel label = FindLabel (constant);
4165 constant_section = FindSection (label);
4167 if (constant_section == null)
4168 constant_section = default_section;
4171 // Store switch expression for comparission purposes
4173 value = new_expr as VariableReference;
4175 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4180 foreach (SwitchSection ss in Sections){
4182 ec.CurrentBranching.CreateSibling (
4183 null, FlowBranching.SiblingType.SwitchSection);
4187 if (is_constant && (ss != constant_section)) {
4188 // If we're a constant switch, we're only emitting
4189 // one single section - mark all the others as
4191 ec.CurrentBranching.CurrentUsageVector.Goto ();
4192 if (!ss.Block.ResolveUnreachable (ec, true)) {
4196 if (!ss.Block.Resolve (ec))
4201 if (default_section == null)
4202 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4204 ec.EndFlowBranching ();
4205 ec.Switch = old_switch;
4211 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4212 if (string_labels.Count < 7)
4213 ResolveSimpleSwitch (ec);
4215 ResolveStringSwitchMap (ec);
4216 } else if (labels.Count < 3 && !IsNullable) {
4217 ResolveSimpleSwitch (ec);
4224 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4226 var sl = FindLabel (value);
4229 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4236 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4238 void ResolveSimpleSwitch (BlockContext bc)
4240 simple_stmt = default_section != null ? default_section.Block : null;
4242 for (int i = Sections.Count - 1; i >= 0; --i) {
4243 var s = Sections[i];
4245 if (s == default_section) {
4246 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4250 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4252 Expression cond = null;
4253 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4254 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted);
4257 cond = new Binary (Binary.Operator.LogicalOr, cond, e);
4264 // Compiler generated, hide from symbol file
4266 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4269 // It's null for empty switch
4270 if (simple_stmt != null)
4271 simple_stmt.Resolve (bc);
4275 // Converts string switch into string hashtable
4277 void ResolveStringSwitchMap (ResolveContext ec)
4279 FullNamedExpression string_dictionary_type;
4280 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4281 string_dictionary_type = new TypeExpression (
4282 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4283 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4285 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4286 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4288 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4292 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4293 Field field = new Field (ctype, string_dictionary_type,
4294 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4295 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4296 if (!field.Define ())
4298 ctype.AddField (field);
4300 var init = new List<Expression> ();
4302 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4303 string value = null;
4304 foreach (SwitchSection section in Sections) {
4305 bool contains_label = false;
4306 foreach (SwitchLabel sl in section.Labels) {
4307 if (sl.IsDefault || sl.Converted.IsNull)
4310 if (!contains_label) {
4311 labels.Add (counter, sl);
4312 contains_label = true;
4315 value = (string) sl.Converted.GetValue ();
4316 var init_args = new List<Expression> (2);
4317 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4319 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4320 init_args.Add (sl.Converted);
4322 init.Add (new CollectionElementInitializer (init_args, loc));
4326 // Don't add empty sections
4332 Arguments args = new Arguments (1);
4333 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4334 Expression initializer = new NewInitialize (string_dictionary_type, args,
4335 new CollectionOrObjectInitializers (init, loc), loc);
4337 switch_cache_field = new FieldExpr (field, loc);
4338 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4341 void DoEmitStringSwitch (EmitContext ec)
4343 Label l_initialized = ec.DefineLabel ();
4346 // Skip initialization when value is null
4348 value.EmitBranchable (ec, null_target, false);
4351 // Check if string dictionary is initialized and initialize
4353 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4354 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4355 string_dictionary.EmitStatement (ec);
4357 ec.MarkLabel (l_initialized);
4359 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4361 ResolveContext rc = new ResolveContext (ec.MemberContext);
4363 if (switch_cache_field.Type.IsGeneric) {
4364 Arguments get_value_args = new Arguments (2);
4365 get_value_args.Add (new Argument (value));
4366 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4367 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4368 if (get_item == null)
4372 // A value was not found, go to default case
4374 get_item.EmitBranchable (ec, default_target, false);
4376 Arguments get_value_args = new Arguments (1);
4377 get_value_args.Add (new Argument (value));
4379 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4380 if (get_item == null)
4383 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4384 get_item_object.EmitAssign (ec, get_item, true, false);
4385 ec.Emit (OpCodes.Brfalse, default_target);
4387 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4388 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4390 get_item_int.EmitStatement (ec);
4391 get_item_object.Release (ec);
4394 EmitTableSwitch (ec, string_switch_variable);
4395 string_switch_variable.Release (ec);
4398 protected override void DoEmit (EmitContext ec)
4400 // Workaround broken flow-analysis
4401 block.HasUnreachableClosingBrace = true;
4404 // Needed to emit anonymous storey initialization
4405 // Otherwise it does not contain any statements for now
4409 default_target = ec.DefineLabel ();
4410 null_target = ec.DefineLabel ();
4413 unwrap.EmitCheck (ec);
4414 ec.Emit (OpCodes.Brfalse, null_target);
4415 value.EmitAssign (ec, new_expr, false, false);
4416 } else if (new_expr != value && !is_constant) {
4417 value.EmitAssign (ec, new_expr, false, false);
4421 // Setup the codegen context
4423 Label old_end = ec.LoopEnd;
4424 Switch old_switch = ec.Switch;
4426 ec.LoopEnd = ec.DefineLabel ();
4431 if (constant_section != null)
4432 constant_section.Block.Emit (ec);
4433 } else if (string_dictionary != null) {
4434 DoEmitStringSwitch (ec);
4435 } else if (simple_stmt != null) {
4436 simple_stmt.Emit (ec);
4438 EmitTableSwitch (ec, value);
4441 // Restore context state.
4442 ec.MarkLabel (ec.LoopEnd);
4445 // Restore the previous context
4447 ec.LoopEnd = old_end;
4448 ec.Switch = old_switch;
4451 protected override void CloneTo (CloneContext clonectx, Statement t)
4453 Switch target = (Switch) t;
4455 target.Expr = Expr.Clone (clonectx);
4456 target.Sections = new List<SwitchSection> ();
4457 foreach (SwitchSection ss in Sections){
4458 target.Sections.Add (ss.Clone (clonectx));
4462 public override object Accept (StructuralVisitor visitor)
4464 return visitor.Visit (this);
4468 // A place where execution can restart in an iterator
4469 public abstract class ResumableStatement : Statement
4472 protected Label resume_point;
4474 public Label PrepareForEmit (EmitContext ec)
4478 resume_point = ec.DefineLabel ();
4480 return resume_point;
4483 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4488 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4493 public abstract class TryFinallyBlock : ExceptionStatement
4495 protected Statement stmt;
4496 Label dispose_try_block;
4497 bool prepared_for_dispose, emitted_dispose;
4498 Method finally_host;
4500 protected TryFinallyBlock (Statement stmt, Location loc)
4508 public Statement Statement {
4516 protected abstract void EmitTryBody (EmitContext ec);
4517 public abstract void EmitFinallyBody (EmitContext ec);
4519 public override Label PrepareForDispose (EmitContext ec, Label end)
4521 if (!prepared_for_dispose) {
4522 prepared_for_dispose = true;
4523 dispose_try_block = ec.DefineLabel ();
4525 return dispose_try_block;
4528 protected sealed override void DoEmit (EmitContext ec)
4530 EmitTryBodyPrepare (ec);
4533 ec.BeginFinallyBlock ();
4535 Label start_finally = ec.DefineLabel ();
4536 if (resume_points != null) {
4537 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4539 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4540 ec.Emit (OpCodes.Brfalse_S, start_finally);
4541 ec.Emit (OpCodes.Endfinally);
4544 ec.MarkLabel (start_finally);
4546 if (finally_host != null) {
4547 finally_host.Define ();
4548 finally_host.Emit ();
4550 // Now it's safe to add, to close it properly and emit sequence points
4551 finally_host.Parent.AddMember (finally_host);
4553 var ce = new CallEmitter ();
4554 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4555 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4557 EmitFinallyBody (ec);
4560 ec.EndExceptionBlock ();
4563 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4565 if (emitted_dispose)
4568 emitted_dispose = true;
4570 Label end_of_try = ec.DefineLabel ();
4572 // Ensure that the only way we can get into this code is through a dispatcher
4573 if (have_dispatcher)
4574 ec.Emit (OpCodes.Br, end);
4576 ec.BeginExceptionBlock ();
4578 ec.MarkLabel (dispose_try_block);
4580 Label[] labels = null;
4581 for (int i = 0; i < resume_points.Count; ++i) {
4582 ResumableStatement s = resume_points[i];
4583 Label ret = s.PrepareForDispose (ec, end_of_try);
4584 if (ret.Equals (end_of_try) && labels == null)
4586 if (labels == null) {
4587 labels = new Label[resume_points.Count];
4588 for (int j = 0; j < i; ++j)
4589 labels[j] = end_of_try;
4594 if (labels != null) {
4596 for (j = 1; j < labels.Length; ++j)
4597 if (!labels[0].Equals (labels[j]))
4599 bool emit_dispatcher = j < labels.Length;
4601 if (emit_dispatcher) {
4602 ec.Emit (OpCodes.Ldloc, pc);
4603 ec.EmitInt (first_resume_pc);
4604 ec.Emit (OpCodes.Sub);
4605 ec.Emit (OpCodes.Switch, labels);
4608 foreach (ResumableStatement s in resume_points)
4609 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4612 ec.MarkLabel (end_of_try);
4614 ec.BeginFinallyBlock ();
4616 if (finally_host != null) {
4617 var ce = new CallEmitter ();
4618 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4619 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4621 EmitFinallyBody (ec);
4624 ec.EndExceptionBlock ();
4627 public override bool Resolve (BlockContext bc)
4630 // Finally block inside iterator is called from MoveNext and
4631 // Dispose methods that means we need to lift the block into
4632 // newly created host method to emit the body only once. The
4633 // original block then simply calls the newly generated method.
4635 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4636 var b = stmt as Block;
4637 if (b != null && b.Explicit.HasYield) {
4638 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4642 return base.Resolve (bc);
4647 // Base class for blocks using exception handling
4649 public abstract class ExceptionStatement : ResumableStatement
4654 protected List<ResumableStatement> resume_points;
4655 protected int first_resume_pc;
4657 protected ExceptionStatement (Location loc)
4662 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4664 StateMachineInitializer state_machine = null;
4665 if (resume_points != null) {
4666 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4668 ec.EmitInt ((int) IteratorStorey.State.Running);
4669 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4672 ec.BeginExceptionBlock ();
4674 if (resume_points != null) {
4675 ec.MarkLabel (resume_point);
4677 // For normal control flow, we want to fall-through the Switch
4678 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4679 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4680 ec.EmitInt (first_resume_pc);
4681 ec.Emit (OpCodes.Sub);
4683 Label[] labels = new Label[resume_points.Count];
4684 for (int i = 0; i < resume_points.Count; ++i)
4685 labels[i] = resume_points[i].PrepareForEmit (ec);
4686 ec.Emit (OpCodes.Switch, labels);
4690 public void SomeCodeFollows ()
4693 code_follows = true;
4697 public override bool Resolve (BlockContext ec)
4700 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4701 // So, ensure there's some IL code after this statement.
4702 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4703 ec.NeedReturnLabel ();
4708 public void AddResumePoint (ResumableStatement stmt, int pc)
4710 if (resume_points == null) {
4711 resume_points = new List<ResumableStatement> ();
4712 first_resume_pc = pc;
4715 if (pc != first_resume_pc + resume_points.Count)
4716 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4718 resume_points.Add (stmt);
4723 public class Lock : TryFinallyBlock
4726 TemporaryVariableReference expr_copy;
4727 TemporaryVariableReference lock_taken;
4729 public Lock (Expression expr, Statement stmt, Location loc)
4735 public Expression Expr {
4741 public override bool Resolve (BlockContext ec)
4743 expr = expr.Resolve (ec);
4747 if (!TypeSpec.IsReferenceType (expr.Type)) {
4748 ec.Report.Error (185, loc,
4749 "`{0}' is not a reference type as required by the lock statement",
4750 expr.Type.GetSignatureForError ());
4753 if (expr.Type.IsGenericParameter) {
4754 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4757 VariableReference lv = expr as VariableReference;
4760 locked = lv.IsLockedByStatement;
4761 lv.IsLockedByStatement = true;
4768 // Have to keep original lock value around to unlock same location
4769 // in the case of original value has changed or is null
4771 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4772 expr_copy.Resolve (ec);
4775 // Ensure Monitor methods are available
4777 if (ResolvePredefinedMethods (ec) > 1) {
4778 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4779 lock_taken.Resolve (ec);
4782 using (ec.Set (ResolveContext.Options.LockScope)) {
4783 ec.StartFlowBranching (this);
4784 Statement.Resolve (ec);
4785 ec.EndFlowBranching ();
4789 lv.IsLockedByStatement = locked;
4797 protected override void EmitTryBodyPrepare (EmitContext ec)
4799 expr_copy.EmitAssign (ec, expr);
4801 if (lock_taken != null) {
4803 // Initialize ref variable
4805 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4808 // Monitor.Enter (expr_copy)
4810 expr_copy.Emit (ec);
4811 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4814 base.EmitTryBodyPrepare (ec);
4817 protected override void EmitTryBody (EmitContext ec)
4820 // Monitor.Enter (expr_copy, ref lock_taken)
4822 if (lock_taken != null) {
4823 expr_copy.Emit (ec);
4824 lock_taken.LocalInfo.CreateBuilder (ec);
4825 lock_taken.AddressOf (ec, AddressOp.Load);
4826 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4829 Statement.Emit (ec);
4832 public override void EmitFinallyBody (EmitContext ec)
4835 // if (lock_taken) Monitor.Exit (expr_copy)
4837 Label skip = ec.DefineLabel ();
4839 if (lock_taken != null) {
4840 lock_taken.Emit (ec);
4841 ec.Emit (OpCodes.Brfalse_S, skip);
4844 expr_copy.Emit (ec);
4845 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4847 ec.Emit (OpCodes.Call, m);
4849 ec.MarkLabel (skip);
4852 int ResolvePredefinedMethods (ResolveContext rc)
4854 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4855 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4859 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4863 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4867 protected override void CloneTo (CloneContext clonectx, Statement t)
4869 Lock target = (Lock) t;
4871 target.expr = expr.Clone (clonectx);
4872 target.stmt = Statement.Clone (clonectx);
4875 public override object Accept (StructuralVisitor visitor)
4877 return visitor.Visit (this);
4882 public class Unchecked : Statement {
4885 public Unchecked (Block b, Location loc)
4892 public override bool Resolve (BlockContext ec)
4894 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4895 return Block.Resolve (ec);
4898 protected override void DoEmit (EmitContext ec)
4900 using (ec.With (EmitContext.Options.CheckedScope, false))
4904 protected override void CloneTo (CloneContext clonectx, Statement t)
4906 Unchecked target = (Unchecked) t;
4908 target.Block = clonectx.LookupBlock (Block);
4911 public override object Accept (StructuralVisitor visitor)
4913 return visitor.Visit (this);
4917 public class Checked : Statement {
4920 public Checked (Block b, Location loc)
4923 b.Unchecked = false;
4927 public override bool Resolve (BlockContext ec)
4929 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4930 return Block.Resolve (ec);
4933 protected override void DoEmit (EmitContext ec)
4935 using (ec.With (EmitContext.Options.CheckedScope, true))
4939 protected override void CloneTo (CloneContext clonectx, Statement t)
4941 Checked target = (Checked) t;
4943 target.Block = clonectx.LookupBlock (Block);
4946 public override object Accept (StructuralVisitor visitor)
4948 return visitor.Visit (this);
4952 public class Unsafe : Statement {
4955 public Unsafe (Block b, Location loc)
4958 Block.Unsafe = true;
4962 public override bool Resolve (BlockContext ec)
4964 if (ec.CurrentIterator != null)
4965 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4967 using (ec.Set (ResolveContext.Options.UnsafeScope))
4968 return Block.Resolve (ec);
4971 protected override void DoEmit (EmitContext ec)
4976 protected override void CloneTo (CloneContext clonectx, Statement t)
4978 Unsafe target = (Unsafe) t;
4980 target.Block = clonectx.LookupBlock (Block);
4983 public override object Accept (StructuralVisitor visitor)
4985 return visitor.Visit (this);
4992 public class Fixed : Statement
4994 abstract class Emitter : ShimExpression
4996 protected LocalVariable vi;
4998 protected Emitter (Expression expr, LocalVariable li)
5004 public abstract void EmitExit (EmitContext ec);
5007 class ExpressionEmitter : Emitter {
5008 public ExpressionEmitter (Expression converted, LocalVariable li) :
5009 base (converted, li)
5013 protected override Expression DoResolve (ResolveContext rc)
5015 throw new NotImplementedException ();
5018 public override void Emit (EmitContext ec) {
5020 // Store pointer in pinned location
5026 public override void EmitExit (EmitContext ec)
5029 ec.Emit (OpCodes.Conv_U);
5034 class StringEmitter : Emitter
5036 LocalVariable pinned_string;
5038 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5043 protected override Expression DoResolve (ResolveContext rc)
5045 pinned_string = new LocalVariable (vi.Block, "$pinned",
5046 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5048 pinned_string.Type = rc.BuiltinTypes.String;
5050 eclass = ExprClass.Variable;
5051 type = rc.BuiltinTypes.Int;
5055 public override void Emit (EmitContext ec)
5057 pinned_string.CreateBuilder (ec);
5060 pinned_string.EmitAssign (ec);
5062 // TODO: Should use Binary::Add
5063 pinned_string.Emit (ec);
5064 ec.Emit (OpCodes.Conv_I);
5066 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5070 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5071 //pe.InstanceExpression = pinned_string;
5072 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5074 ec.Emit (OpCodes.Add);
5078 public override void EmitExit (EmitContext ec)
5081 pinned_string.EmitAssign (ec);
5085 public class VariableDeclaration : BlockVariableDeclaration
5087 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5092 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5094 if (!Variable.Type.IsPointer && li == Variable) {
5095 bc.Report.Error (209, TypeExpression.Location,
5096 "The type of locals declared in a fixed statement must be a pointer type");
5101 // The rules for the possible declarators are pretty wise,
5102 // but the production on the grammar is more concise.
5104 // So we have to enforce these rules here.
5106 // We do not resolve before doing the case 1 test,
5107 // because the grammar is explicit in that the token &
5108 // is present, so we need to test for this particular case.
5111 if (initializer is Cast) {
5112 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5116 initializer = initializer.Resolve (bc);
5118 if (initializer == null)
5124 if (initializer.Type.IsArray) {
5125 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5128 // Provided that array_type is unmanaged,
5130 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5134 // and T* is implicitly convertible to the
5135 // pointer type given in the fixed statement.
5137 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5139 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5140 if (converted == null)
5144 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5146 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5147 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5148 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5149 new NullLiteral (loc),
5152 converted = converted.Resolve (bc);
5154 return new ExpressionEmitter (converted, li);
5160 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5161 return new StringEmitter (initializer, li, loc).Resolve (bc);
5164 // Case 3: fixed buffer
5165 if (initializer is FixedBufferPtr) {
5166 return new ExpressionEmitter (initializer, li);
5170 // Case 4: & object.
5172 bool already_fixed = true;
5173 Unary u = initializer as Unary;
5174 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5175 IVariableReference vr = u.Expr as IVariableReference;
5176 if (vr == null || !vr.IsFixed) {
5177 already_fixed = false;
5181 if (already_fixed) {
5182 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5185 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5186 return new ExpressionEmitter (initializer, li);
5191 VariableDeclaration decl;
5192 Statement statement;
5195 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5204 public Statement Statement {
5210 public BlockVariableDeclaration Variables {
5218 public override bool Resolve (BlockContext ec)
5220 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5221 if (!decl.Resolve (ec))
5225 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5226 bool ok = statement.Resolve (ec);
5227 bool flow_unreachable = ec.EndFlowBranching ();
5228 has_ret = flow_unreachable;
5233 protected override void DoEmit (EmitContext ec)
5235 decl.Variable.CreateBuilder (ec);
5236 decl.Initializer.Emit (ec);
5237 if (decl.Declarators != null) {
5238 foreach (var d in decl.Declarators) {
5239 d.Variable.CreateBuilder (ec);
5240 d.Initializer.Emit (ec);
5244 statement.Emit (ec);
5250 // Clear the pinned variable
5252 ((Emitter) decl.Initializer).EmitExit (ec);
5253 if (decl.Declarators != null) {
5254 foreach (var d in decl.Declarators) {
5255 ((Emitter)d.Initializer).EmitExit (ec);
5260 protected override void CloneTo (CloneContext clonectx, Statement t)
5262 Fixed target = (Fixed) t;
5264 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5265 target.statement = statement.Clone (clonectx);
5268 public override object Accept (StructuralVisitor visitor)
5270 return visitor.Visit (this);
5274 public class Catch : Statement
5278 FullNamedExpression type_expr;
5279 CompilerAssign assign;
5282 public Catch (Block block, Location loc)
5290 public Block Block {
5296 public TypeSpec CatchType {
5302 public bool IsGeneral {
5304 return type_expr == null;
5308 public FullNamedExpression TypeExpression {
5317 public LocalVariable Variable {
5328 protected override void DoEmit (EmitContext ec)
5331 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5333 ec.BeginCatchBlock (CatchType);
5336 li.CreateBuilder (ec);
5339 // Special case hoisted catch variable, we have to use a temporary variable
5340 // to pass via anonymous storey initialization with the value still on top
5343 if (li.HoistedVariant != null) {
5344 LocalTemporary lt = new LocalTemporary (li.Type);
5347 // switch to assigning from the temporary variable and not from top of the stack
5348 assign.UpdateSource (lt);
5351 ec.Emit (OpCodes.Pop);
5357 public override bool Resolve (BlockContext ec)
5359 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5360 if (type_expr != null) {
5361 type = type_expr.ResolveAsType (ec);
5365 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5366 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5367 } else if (li != null) {
5369 li.PrepareForFlowAnalysis (ec);
5371 // source variable is at the top of the stack
5372 Expression source = new EmptyExpression (li.Type);
5373 if (li.Type.IsGenericParameter)
5374 source = new UnboxCast (source, li.Type);
5377 // Uses Location.Null to hide from symbol file
5379 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5380 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5384 return Block.Resolve (ec);
5388 protected override void CloneTo (CloneContext clonectx, Statement t)
5390 Catch target = (Catch) t;
5392 if (type_expr != null)
5393 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5395 target.block = clonectx.LookupBlock (block);
5399 public class TryFinally : TryFinallyBlock
5403 public TryFinally (Statement stmt, Block fini, Location loc)
5409 public Block Finallyblock {
5415 public override bool Resolve (BlockContext ec)
5419 ec.StartFlowBranching (this);
5421 if (!stmt.Resolve (ec))
5425 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5427 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5428 if (!fini.Resolve (ec))
5432 ec.EndFlowBranching ();
5434 ok &= base.Resolve (ec);
5439 protected override void EmitTryBody (EmitContext ec)
5444 public override void EmitFinallyBody (EmitContext ec)
5449 protected override void CloneTo (CloneContext clonectx, Statement t)
5451 TryFinally target = (TryFinally) t;
5453 target.stmt = (Statement) stmt.Clone (clonectx);
5455 target.fini = clonectx.LookupBlock (fini);
5458 public override object Accept (StructuralVisitor visitor)
5460 return visitor.Visit (this);
5464 public class TryCatch : ExceptionStatement
5467 List<Catch> clauses;
5468 readonly bool inside_try_finally;
5470 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5474 this.clauses = catch_clauses;
5475 this.inside_try_finally = inside_try_finally;
5478 public List<Catch> Clauses {
5484 public bool IsTryCatchFinally {
5486 return inside_try_finally;
5490 public override bool Resolve (BlockContext ec)
5494 ec.StartFlowBranching (this);
5496 if (!Block.Resolve (ec))
5499 for (int i = 0; i < clauses.Count; ++i) {
5501 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5503 if (!c.Resolve (ec)) {
5508 TypeSpec resolved_type = c.CatchType;
5509 for (int ii = 0; ii < clauses.Count; ++ii) {
5513 if (clauses[ii].IsGeneral) {
5514 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5517 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5520 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5523 ec.Report.Warning (1058, 1, c.loc,
5524 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5532 var ct = clauses[ii].CatchType;
5536 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5537 ec.Report.Error (160, c.loc,
5538 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5539 ct.GetSignatureForError ());
5545 ec.EndFlowBranching ();
5547 return base.Resolve (ec) && ok;
5550 protected sealed override void DoEmit (EmitContext ec)
5552 if (!inside_try_finally)
5553 EmitTryBodyPrepare (ec);
5557 foreach (Catch c in clauses)
5560 if (!inside_try_finally)
5561 ec.EndExceptionBlock ();
5564 protected override void CloneTo (CloneContext clonectx, Statement t)
5566 TryCatch target = (TryCatch) t;
5568 target.Block = clonectx.LookupBlock (Block);
5569 if (clauses != null){
5570 target.clauses = new List<Catch> ();
5571 foreach (Catch c in clauses)
5572 target.clauses.Add ((Catch) c.Clone (clonectx));
5576 public override object Accept (StructuralVisitor visitor)
5578 return visitor.Visit (this);
5582 public class Using : TryFinallyBlock
5584 public class VariableDeclaration : BlockVariableDeclaration
5586 Statement dispose_call;
5588 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5593 public VariableDeclaration (LocalVariable li, Location loc)
5599 public VariableDeclaration (Expression expr)
5602 loc = expr.Location;
5608 public bool IsNested { get; private set; }
5612 public void EmitDispose (EmitContext ec)
5614 dispose_call.Emit (ec);
5617 public override bool Resolve (BlockContext bc)
5622 return base.Resolve (bc, false);
5625 public Expression ResolveExpression (BlockContext bc)
5627 var e = Initializer.Resolve (bc);
5631 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5632 Initializer = ResolveInitializer (bc, Variable, e);
5636 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5638 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5639 initializer = initializer.Resolve (bc);
5640 if (initializer == null)
5643 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5644 Arguments args = new Arguments (1);
5645 args.Add (new Argument (initializer));
5646 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5647 if (initializer == null)
5650 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5651 dispose_call = CreateDisposeCall (bc, var);
5652 dispose_call.Resolve (bc);
5654 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5657 if (li == Variable) {
5658 CheckIDiposableConversion (bc, li, initializer);
5659 dispose_call = CreateDisposeCall (bc, li);
5660 dispose_call.Resolve (bc);
5663 return base.ResolveInitializer (bc, li, initializer);
5666 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5670 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5671 if (type.IsNullableType) {
5672 // it's handled in CreateDisposeCall
5676 bc.Report.SymbolRelatedToPreviousError (type);
5677 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5678 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5679 type.GetSignatureForError ());
5685 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5687 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5689 var loc = lv.Location;
5691 var idt = bc.BuiltinTypes.IDisposable;
5692 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5694 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5695 dispose_mg.InstanceExpression = type.IsNullableType ?
5696 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5700 // Hide it from symbol file via null location
5702 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5704 // Add conditional call when disposing possible null variable
5705 if (!type.IsStruct || type.IsNullableType)
5706 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5711 public void ResolveDeclaratorInitializer (BlockContext bc)
5713 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5716 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5718 for (int i = declarators.Count - 1; i >= 0; --i) {
5719 var d = declarators [i];
5720 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5721 vd.Initializer = d.Initializer;
5723 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5724 vd.dispose_call.Resolve (bc);
5726 stmt = new Using (vd, stmt, d.Variable.Location);
5733 public override object Accept (StructuralVisitor visitor)
5735 return visitor.Visit (this);
5739 VariableDeclaration decl;
5741 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5747 public Using (Expression expr, Statement stmt, Location loc)
5750 this.decl = new VariableDeclaration (expr);
5755 public Expression Expr {
5757 return decl.Variable == null ? decl.Initializer : null;
5761 public BlockVariableDeclaration Variables {
5769 public override void Emit (EmitContext ec)
5772 // Don't emit sequence point it will be set on variable declaration
5777 protected override void EmitTryBodyPrepare (EmitContext ec)
5780 base.EmitTryBodyPrepare (ec);
5783 protected override void EmitTryBody (EmitContext ec)
5788 public override void EmitFinallyBody (EmitContext ec)
5790 decl.EmitDispose (ec);
5793 public override bool Resolve (BlockContext ec)
5795 VariableReference vr;
5796 bool vr_locked = false;
5798 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5799 if (decl.Variable == null) {
5800 vr = decl.ResolveExpression (ec) as VariableReference;
5802 vr_locked = vr.IsLockedByStatement;
5803 vr.IsLockedByStatement = true;
5806 if (decl.IsNested) {
5807 decl.ResolveDeclaratorInitializer (ec);
5809 if (!decl.Resolve (ec))
5812 if (decl.Declarators != null) {
5813 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5821 ec.StartFlowBranching (this);
5825 ec.EndFlowBranching ();
5828 vr.IsLockedByStatement = vr_locked;
5835 protected override void CloneTo (CloneContext clonectx, Statement t)
5837 Using target = (Using) t;
5839 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5840 target.stmt = stmt.Clone (clonectx);
5843 public override object Accept (StructuralVisitor visitor)
5845 return visitor.Visit (this);
5850 /// Implementation of the foreach C# statement
5852 public class Foreach : Statement
5854 abstract class IteratorStatement : Statement
5856 protected readonly Foreach for_each;
5858 protected IteratorStatement (Foreach @foreach)
5860 this.for_each = @foreach;
5861 this.loc = @foreach.expr.Location;
5864 protected override void CloneTo (CloneContext clonectx, Statement target)
5866 throw new NotImplementedException ();
5869 public override void Emit (EmitContext ec)
5871 if (ec.EmitAccurateDebugInfo) {
5872 ec.Emit (OpCodes.Nop);
5879 sealed class ArrayForeach : IteratorStatement
5881 TemporaryVariableReference[] lengths;
5882 Expression [] length_exprs;
5883 StatementExpression[] counter;
5884 TemporaryVariableReference[] variables;
5886 TemporaryVariableReference copy;
5888 public ArrayForeach (Foreach @foreach, int rank)
5891 counter = new StatementExpression[rank];
5892 variables = new TemporaryVariableReference[rank];
5893 length_exprs = new Expression [rank];
5896 // Only use temporary length variables when dealing with
5897 // multi-dimensional arrays
5900 lengths = new TemporaryVariableReference [rank];
5903 public override bool Resolve (BlockContext ec)
5905 Block variables_block = for_each.variable.Block;
5906 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5909 int rank = length_exprs.Length;
5910 Arguments list = new Arguments (rank);
5911 for (int i = 0; i < rank; i++) {
5912 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5914 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5915 counter[i].Resolve (ec);
5918 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5920 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5921 lengths[i].Resolve (ec);
5923 Arguments args = new Arguments (1);
5924 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5925 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5928 list.Add (new Argument (v));
5931 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5936 if (for_each.type is VarExpr) {
5937 // Infer implicitly typed local variable from foreach array type
5938 var_type = access.Type;
5940 var_type = for_each.type.ResolveAsType (ec);
5942 if (var_type == null)
5945 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5950 for_each.variable.Type = var_type;
5952 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5953 if (variable_ref == null)
5956 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5960 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5961 ec.CurrentBranching.CreateSibling ();
5963 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5964 if (!for_each.body.Resolve (ec))
5966 ec.EndFlowBranching ();
5968 // There's no direct control flow from the end of the embedded statement to the end of the loop
5969 ec.CurrentBranching.CurrentUsageVector.Goto ();
5971 ec.EndFlowBranching ();
5976 protected override void DoEmit (EmitContext ec)
5978 copy.EmitAssign (ec, for_each.expr);
5980 int rank = length_exprs.Length;
5981 Label[] test = new Label [rank];
5982 Label[] loop = new Label [rank];
5984 for (int i = 0; i < rank; i++) {
5985 test [i] = ec.DefineLabel ();
5986 loop [i] = ec.DefineLabel ();
5988 if (lengths != null)
5989 lengths [i].EmitAssign (ec, length_exprs [i]);
5992 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5993 for (int i = 0; i < rank; i++) {
5994 variables [i].EmitAssign (ec, zero);
5996 ec.Emit (OpCodes.Br, test [i]);
5997 ec.MarkLabel (loop [i]);
6000 for_each.body.Emit (ec);
6002 ec.MarkLabel (ec.LoopBegin);
6003 ec.Mark (for_each.expr.Location);
6005 for (int i = rank - 1; i >= 0; i--){
6006 counter [i].Emit (ec);
6008 ec.MarkLabel (test [i]);
6009 variables [i].Emit (ec);
6011 if (lengths != null)
6012 lengths [i].Emit (ec);
6014 length_exprs [i].Emit (ec);
6016 ec.Emit (OpCodes.Blt, loop [i]);
6019 ec.MarkLabel (ec.LoopEnd);
6023 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6025 class RuntimeDispose : Using.VariableDeclaration
6027 public RuntimeDispose (LocalVariable lv, Location loc)
6032 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6034 // Defered to runtime check
6037 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6039 var idt = bc.BuiltinTypes.IDisposable;
6042 // Fabricates code like
6044 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6047 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6049 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6050 dispose_variable.CreateReferenceExpression (bc, loc),
6051 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6052 loc), new NullLiteral (loc));
6054 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6056 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6057 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6059 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6060 return new If (idisaposable_test, dispose, loc);
6064 LocalVariable variable;
6066 Statement statement;
6067 ExpressionStatement init;
6068 TemporaryVariableReference enumerator_variable;
6069 bool ambiguous_getenumerator_name;
6071 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6074 this.variable = var;
6078 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6080 rc.Report.SymbolRelatedToPreviousError (enumerator);
6081 rc.Report.Error (202, loc,
6082 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6083 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6086 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6089 // Option 1: Try to match by name GetEnumerator first
6091 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6092 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6094 var mg = mexpr as MethodGroupExpr;
6096 mg.InstanceExpression = expr;
6097 Arguments args = new Arguments (0);
6098 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
6100 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6101 if (ambiguous_getenumerator_name)
6104 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6110 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6113 PredefinedMember<MethodSpec> iface_candidate = null;
6114 var ptypes = rc.Module.PredefinedTypes;
6115 var gen_ienumerable = ptypes.IEnumerableGeneric;
6116 if (!gen_ienumerable.Define ())
6117 gen_ienumerable = null;
6120 var ifaces = t.Interfaces;
6121 if (ifaces != null) {
6122 foreach (var iface in ifaces) {
6123 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6124 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6125 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6126 rc.Report.Error (1640, loc,
6127 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6128 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6133 // TODO: Cache this somehow
6134 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6135 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6140 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6141 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6146 if (t.IsGenericParameter)
6151 } while (t != null);
6153 if (iface_candidate == null) {
6154 if (expr.Type != InternalType.ErrorType) {
6155 rc.Report.Error (1579, loc,
6156 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6157 expr.Type.GetSignatureForError (), "GetEnumerator");
6163 var method = iface_candidate.Resolve (loc);
6167 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6168 mg.InstanceExpression = expr;
6172 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6174 var ms = MemberCache.FindMember (enumerator.ReturnType,
6175 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6176 BindingRestriction.InstanceOnly) as MethodSpec;
6178 if (ms == null || !ms.IsPublic) {
6179 Error_WrongEnumerator (rc, enumerator);
6183 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6186 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6188 var ps = MemberCache.FindMember (enumerator.ReturnType,
6189 MemberFilter.Property ("Current", null),
6190 BindingRestriction.InstanceOnly) as PropertySpec;
6192 if (ps == null || !ps.IsPublic) {
6193 Error_WrongEnumerator (rc, enumerator);
6200 public override bool Resolve (BlockContext ec)
6202 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6205 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6206 } else if (expr.Type.IsNullableType) {
6207 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6210 var get_enumerator_mg = ResolveGetEnumerator (ec);
6211 if (get_enumerator_mg == null) {
6215 var get_enumerator = get_enumerator_mg.BestCandidate;
6216 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6217 enumerator_variable.Resolve (ec);
6219 // Prepare bool MoveNext ()
6220 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6221 if (move_next_mg == null) {
6225 move_next_mg.InstanceExpression = enumerator_variable;
6227 // Prepare ~T~ Current { get; }
6228 var current_prop = ResolveCurrent (ec, get_enumerator);
6229 if (current_prop == null) {
6233 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6234 if (current_pe == null)
6237 VarExpr ve = for_each.type as VarExpr;
6241 // Source type is dynamic, set element type to dynamic too
6242 variable.Type = ec.BuiltinTypes.Dynamic;
6244 // Infer implicitly typed local variable from foreach enumerable type
6245 variable.Type = current_pe.Type;
6249 // Explicit cast of dynamic collection elements has to be done at runtime
6250 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6253 variable.Type = for_each.type.ResolveAsType (ec);
6255 if (variable.Type == null)
6258 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6259 if (current_pe == null)
6263 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6264 if (variable_ref == null)
6267 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6269 var init = new Invocation (get_enumerator_mg, null);
6271 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6272 for_each.body, Location.Null);
6274 var enum_type = enumerator_variable.Type;
6277 // Add Dispose method call when enumerator can be IDisposable
6279 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6280 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6282 // Runtime Dispose check
6284 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6285 vd.Initializer = init;
6286 statement = new Using (vd, statement, Location.Null);
6289 // No Dispose call needed
6291 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6292 this.init.Resolve (ec);
6296 // Static Dispose check
6298 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6299 vd.Initializer = init;
6300 statement = new Using (vd, statement, Location.Null);
6303 return statement.Resolve (ec);
6306 protected override void DoEmit (EmitContext ec)
6308 enumerator_variable.LocalInfo.CreateBuilder (ec);
6311 init.EmitStatement (ec);
6313 statement.Emit (ec);
6316 #region IErrorHandler Members
6318 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6320 ec.Report.SymbolRelatedToPreviousError (best);
6321 ec.Report.Warning (278, 2, expr.Location,
6322 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6323 expr.Type.GetSignatureForError (), "enumerable",
6324 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6326 ambiguous_getenumerator_name = true;
6330 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6335 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6340 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6349 LocalVariable variable;
6351 Statement statement;
6354 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6357 this.variable = var;
6359 this.statement = stmt;
6364 public Expression Expr {
6365 get { return expr; }
6368 public Statement Statement {
6369 get { return statement; }
6372 public Expression TypeExpression {
6373 get { return type; }
6376 public LocalVariable Variable {
6377 get { return variable; }
6380 public override bool Resolve (BlockContext ec)
6382 expr = expr.Resolve (ec);
6387 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6391 body.AddStatement (statement);
6393 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6394 statement = new ArrayForeach (this, 1);
6395 } else if (expr.Type is ArrayContainer) {
6396 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6398 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6399 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6400 expr.ExprClassName);
6404 statement = new CollectionForeach (this, variable, expr);
6407 return statement.Resolve (ec);
6410 protected override void DoEmit (EmitContext ec)
6412 variable.CreateBuilder (ec);
6414 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6415 ec.LoopBegin = ec.DefineLabel ();
6416 ec.LoopEnd = ec.DefineLabel ();
6418 statement.Emit (ec);
6420 ec.LoopBegin = old_begin;
6421 ec.LoopEnd = old_end;
6424 protected override void CloneTo (CloneContext clonectx, Statement t)
6426 Foreach target = (Foreach) t;
6428 target.type = type.Clone (clonectx);
6429 target.expr = expr.Clone (clonectx);
6430 target.body = (Block) body.Clone (clonectx);
6431 target.statement = statement.Clone (clonectx);
6434 public override object Accept (StructuralVisitor visitor)
6436 return visitor.Visit (this);