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.
52 bool unreachable = false;
53 if (warn && !ec.UnreachableReported) {
54 ec.UnreachableReported = true;
56 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
59 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
60 bool ok = Resolve (ec);
61 ec.KillFlowBranching ();
64 ec.UnreachableReported = false;
71 /// Return value indicates whether all code paths emitted return.
73 protected abstract void DoEmit (EmitContext ec);
75 public virtual void Emit (EmitContext ec)
80 if (ec.StatementEpilogue != null) {
86 // This routine must be overrided in derived classes and make copies
87 // of all the data that might be modified if resolved
89 protected abstract void CloneTo (CloneContext clonectx, Statement target);
91 public Statement Clone (CloneContext clonectx)
93 Statement s = (Statement) this.MemberwiseClone ();
94 CloneTo (clonectx, s);
98 public virtual Expression CreateExpressionTree (ResolveContext ec)
100 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
104 public virtual object Accept (StructuralVisitor visitor)
106 return visitor.Visit (this);
110 public sealed class EmptyStatement : Statement
112 public EmptyStatement (Location loc)
117 public override bool Resolve (BlockContext ec)
122 public override bool ResolveUnreachable (BlockContext ec, bool warn)
127 public override void Emit (EmitContext ec)
131 protected override void DoEmit (EmitContext ec)
133 throw new NotSupportedException ();
136 protected override void CloneTo (CloneContext clonectx, Statement target)
141 public override object Accept (StructuralVisitor visitor)
143 return visitor.Visit (this);
147 public class If : Statement {
149 public Statement TrueStatement;
150 public Statement FalseStatement;
154 public If (Expression bool_expr, Statement true_statement, Location l)
155 : this (bool_expr, true_statement, null, l)
159 public If (Expression bool_expr,
160 Statement true_statement,
161 Statement false_statement,
164 this.expr = bool_expr;
165 TrueStatement = true_statement;
166 FalseStatement = false_statement;
170 public Expression Expr {
176 public override bool Resolve (BlockContext ec)
180 expr = expr.Resolve (ec);
185 // Dead code elimination
187 if (expr is Constant) {
188 bool take = !((Constant) expr).IsDefaultValue;
191 if (!TrueStatement.Resolve (ec))
194 if ((FalseStatement != null) &&
195 !FalseStatement.ResolveUnreachable (ec, true))
197 FalseStatement = null;
199 if (!TrueStatement.ResolveUnreachable (ec, true))
201 TrueStatement = null;
203 if ((FalseStatement != null) &&
204 !FalseStatement.Resolve (ec))
212 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
214 ok &= TrueStatement.Resolve (ec);
216 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
218 ec.CurrentBranching.CreateSibling ();
220 if (FalseStatement != null)
221 ok &= FalseStatement.Resolve (ec);
223 ec.EndFlowBranching ();
228 protected override void DoEmit (EmitContext ec)
230 Label false_target = ec.DefineLabel ();
234 // If we're a boolean constant, Resolve() already
235 // eliminated dead code for us.
237 Constant c = expr as Constant;
239 c.EmitSideEffect (ec);
241 if (!c.IsDefaultValue)
242 TrueStatement.Emit (ec);
243 else if (FalseStatement != null)
244 FalseStatement.Emit (ec);
249 expr.EmitBranchable (ec, false_target, false);
251 TrueStatement.Emit (ec);
253 if (FalseStatement != null){
254 bool branch_emitted = false;
256 end = ec.DefineLabel ();
258 ec.Emit (OpCodes.Br, end);
259 branch_emitted = true;
262 ec.MarkLabel (false_target);
263 FalseStatement.Emit (ec);
268 ec.MarkLabel (false_target);
272 protected override void CloneTo (CloneContext clonectx, Statement t)
276 target.expr = expr.Clone (clonectx);
277 target.TrueStatement = TrueStatement.Clone (clonectx);
278 if (FalseStatement != null)
279 target.FalseStatement = FalseStatement.Clone (clonectx);
282 public override object Accept (StructuralVisitor visitor)
284 return visitor.Visit (this);
288 public class Do : Statement {
289 public Expression expr;
290 public Statement EmbeddedStatement;
292 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
295 EmbeddedStatement = statement;
297 WhileLocation = whileLocation;
300 public Location WhileLocation {
304 public override bool Resolve (BlockContext ec)
308 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
310 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
312 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
313 if (!EmbeddedStatement.Resolve (ec))
315 ec.EndFlowBranching ();
317 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
318 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
320 expr = expr.Resolve (ec);
323 else if (expr is Constant){
324 bool infinite = !((Constant) expr).IsDefaultValue;
326 ec.CurrentBranching.CurrentUsageVector.Goto ();
329 ec.EndFlowBranching ();
334 protected override void DoEmit (EmitContext ec)
336 Label loop = ec.DefineLabel ();
337 Label old_begin = ec.LoopBegin;
338 Label old_end = ec.LoopEnd;
340 ec.LoopBegin = ec.DefineLabel ();
341 ec.LoopEnd = ec.DefineLabel ();
344 EmbeddedStatement.Emit (ec);
345 ec.MarkLabel (ec.LoopBegin);
347 // Mark start of while condition
348 ec.Mark (WhileLocation);
351 // Dead code elimination
353 if (expr is Constant) {
354 bool res = !((Constant) expr).IsDefaultValue;
356 expr.EmitSideEffect (ec);
358 ec.Emit (OpCodes.Br, loop);
360 expr.EmitBranchable (ec, loop, true);
363 ec.MarkLabel (ec.LoopEnd);
365 ec.LoopBegin = old_begin;
366 ec.LoopEnd = old_end;
369 protected override void CloneTo (CloneContext clonectx, Statement t)
373 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
374 target.expr = expr.Clone (clonectx);
377 public override object Accept (StructuralVisitor visitor)
379 return visitor.Visit (this);
383 public class While : Statement {
384 public Expression expr;
385 public Statement Statement;
386 bool infinite, empty;
388 public While (BooleanExpression bool_expr, Statement statement, Location l)
390 this.expr = bool_expr;
391 Statement = statement;
395 public override bool Resolve (BlockContext ec)
399 expr = expr.Resolve (ec);
404 // Inform whether we are infinite or not
406 if (expr is Constant){
407 bool value = !((Constant) expr).IsDefaultValue;
410 if (!Statement.ResolveUnreachable (ec, true))
418 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
420 ec.CurrentBranching.CreateSibling ();
422 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
423 if (!Statement.Resolve (ec))
425 ec.EndFlowBranching ();
427 // There's no direct control flow from the end of the embedded statement to the end of the loop
428 ec.CurrentBranching.CurrentUsageVector.Goto ();
430 ec.EndFlowBranching ();
435 protected override void DoEmit (EmitContext ec)
438 expr.EmitSideEffect (ec);
442 Label old_begin = ec.LoopBegin;
443 Label old_end = ec.LoopEnd;
445 ec.LoopBegin = ec.DefineLabel ();
446 ec.LoopEnd = ec.DefineLabel ();
449 // Inform whether we are infinite or not
451 if (expr is Constant) {
452 // expr is 'true', since the 'empty' case above handles the 'false' case
453 ec.MarkLabel (ec.LoopBegin);
455 if (ec.EmitAccurateDebugInfo)
456 ec.Emit (OpCodes.Nop);
458 expr.EmitSideEffect (ec);
460 ec.Emit (OpCodes.Br, ec.LoopBegin);
463 // Inform that we are infinite (ie, `we return'), only
464 // if we do not `break' inside the code.
466 ec.MarkLabel (ec.LoopEnd);
468 Label while_loop = ec.DefineLabel ();
470 ec.Emit (OpCodes.Br, ec.LoopBegin);
471 ec.MarkLabel (while_loop);
475 ec.MarkLabel (ec.LoopBegin);
478 expr.EmitBranchable (ec, while_loop, true);
480 ec.MarkLabel (ec.LoopEnd);
483 ec.LoopBegin = old_begin;
484 ec.LoopEnd = old_end;
487 protected override void CloneTo (CloneContext clonectx, Statement t)
489 While target = (While) t;
491 target.expr = expr.Clone (clonectx);
492 target.Statement = Statement.Clone (clonectx);
495 public override object Accept (StructuralVisitor visitor)
497 return visitor.Visit (this);
501 public class For : Statement
503 bool infinite, empty;
505 public For (Location l)
510 public Statement Initializer {
514 public Expression Condition {
518 public Statement Iterator {
522 public Statement Statement {
526 public override bool Resolve (BlockContext ec)
530 if (Initializer != null) {
531 if (!Initializer.Resolve (ec))
535 if (Condition != null) {
536 Condition = Condition.Resolve (ec);
537 if (Condition == null)
539 else if (Condition is Constant) {
540 bool value = !((Constant) Condition).IsDefaultValue;
543 if (!Statement.ResolveUnreachable (ec, true))
545 if ((Iterator != null) &&
546 !Iterator.ResolveUnreachable (ec, false))
556 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
558 ec.CurrentBranching.CreateSibling ();
560 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
562 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
563 if (!Statement.Resolve (ec))
565 ec.EndFlowBranching ();
567 if (Iterator != null){
568 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
569 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
572 if (!Iterator.Resolve (ec))
577 // There's no direct control flow from the end of the embedded statement to the end of the loop
578 ec.CurrentBranching.CurrentUsageVector.Goto ();
580 ec.EndFlowBranching ();
585 protected override void DoEmit (EmitContext ec)
587 if (Initializer != null)
588 Initializer.Emit (ec);
591 Condition.EmitSideEffect (ec);
595 Label old_begin = ec.LoopBegin;
596 Label old_end = ec.LoopEnd;
597 Label loop = ec.DefineLabel ();
598 Label test = ec.DefineLabel ();
600 ec.LoopBegin = ec.DefineLabel ();
601 ec.LoopEnd = ec.DefineLabel ();
603 ec.Emit (OpCodes.Br, test);
607 ec.MarkLabel (ec.LoopBegin);
612 // If test is null, there is no test, and we are just
615 if (Condition != null) {
616 ec.Mark (Condition.Location);
619 // The Resolve code already catches the case for
620 // Test == Constant (false) so we know that
623 if (Condition is Constant) {
624 Condition.EmitSideEffect (ec);
625 ec.Emit (OpCodes.Br, loop);
627 Condition.EmitBranchable (ec, loop, true);
631 ec.Emit (OpCodes.Br, loop);
632 ec.MarkLabel (ec.LoopEnd);
634 ec.LoopBegin = old_begin;
635 ec.LoopEnd = old_end;
638 protected override void CloneTo (CloneContext clonectx, Statement t)
640 For target = (For) t;
642 if (Initializer != null)
643 target.Initializer = Initializer.Clone (clonectx);
644 if (Condition != null)
645 target.Condition = Condition.Clone (clonectx);
646 if (Iterator != null)
647 target.Iterator = Iterator.Clone (clonectx);
648 target.Statement = Statement.Clone (clonectx);
651 public override object Accept (StructuralVisitor visitor)
653 return visitor.Visit (this);
657 public class StatementExpression : Statement
659 ExpressionStatement expr;
661 public StatementExpression (ExpressionStatement expr)
664 loc = expr.StartLocation;
667 public StatementExpression (ExpressionStatement expr, Location loc)
673 public ExpressionStatement Expr {
679 protected override void CloneTo (CloneContext clonectx, Statement t)
681 StatementExpression target = (StatementExpression) t;
682 target.expr = (ExpressionStatement) expr.Clone (clonectx);
685 protected override void DoEmit (EmitContext ec)
687 expr.EmitStatement (ec);
690 public override bool Resolve (BlockContext ec)
692 expr = expr.ResolveStatement (ec);
696 public override object Accept (StructuralVisitor visitor)
698 return visitor.Visit (this);
702 public class StatementErrorExpression : Statement
704 readonly Expression expr;
706 public StatementErrorExpression (Expression expr)
711 public Expression Expr {
717 protected override void DoEmit (EmitContext ec)
719 throw new NotSupportedException ();
722 protected override void CloneTo (CloneContext clonectx, Statement target)
724 throw new NotImplementedException ();
727 public override object Accept (StructuralVisitor visitor)
729 return visitor.Visit (this);
734 // Simple version of statement list not requiring a block
736 public class StatementList : Statement
738 List<Statement> statements;
740 public StatementList (Statement first, Statement second)
742 statements = new List<Statement> () { first, second };
746 public IList<Statement> Statements {
753 public void Add (Statement statement)
755 statements.Add (statement);
758 public override bool Resolve (BlockContext ec)
760 foreach (var s in statements)
766 protected override void DoEmit (EmitContext ec)
768 foreach (var s in statements)
772 protected override void CloneTo (CloneContext clonectx, Statement target)
774 StatementList t = (StatementList) target;
776 t.statements = new List<Statement> (statements.Count);
777 foreach (Statement s in statements)
778 t.statements.Add (s.Clone (clonectx));
781 public override object Accept (StructuralVisitor visitor)
783 return visitor.Visit (this);
787 // A 'return' or a 'yield break'
788 public abstract class ExitStatement : Statement
790 protected bool unwind_protect;
791 protected abstract bool DoResolve (BlockContext ec);
793 public virtual void Error_FinallyClause (Report Report)
795 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
798 public sealed override bool Resolve (BlockContext ec)
800 var res = DoResolve (ec);
801 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
802 ec.CurrentBranching.CurrentUsageVector.Goto ();
808 /// Implements the return statement
810 public class Return : ExitStatement
814 public Return (Expression expr, Location l)
822 public Expression Expr {
833 protected override bool DoResolve (BlockContext ec)
836 if (ec.ReturnType.Kind == MemberKind.Void)
840 // Return must not be followed by an expression when
841 // the method return type is Task
843 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
844 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
845 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
847 // Extra trick not to emit ret/leave inside awaiter body
849 expr = EmptyExpression.Null;
854 if (ec.CurrentIterator != null) {
855 Error_ReturnFromIterator (ec);
856 } else if (ec.ReturnType != InternalType.ErrorType) {
857 ec.Report.Error (126, loc,
858 "An object of a type convertible to `{0}' is required for the return statement",
859 ec.ReturnType.GetSignatureForError ());
865 expr = expr.Resolve (ec);
866 TypeSpec block_return_type = ec.ReturnType;
868 AnonymousExpression am = ec.CurrentAnonymousMethod;
870 if (block_return_type.Kind == MemberKind.Void) {
871 ec.Report.Error (127, loc,
872 "`{0}': A return keyword must not be followed by any expression when method returns void",
873 ec.GetSignatureForError ());
879 Error_ReturnFromIterator (ec);
883 var async_block = am as AsyncInitializer;
884 if (async_block != null) {
886 var storey = (AsyncTaskStorey) am.Storey;
887 var async_type = storey.ReturnType;
889 if (async_type == null && async_block.ReturnTypeInference != null) {
890 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
894 if (async_type.Kind == MemberKind.Void) {
895 ec.Report.Error (127, loc,
896 "`{0}': A return keyword must not be followed by any expression when method returns void",
897 ec.GetSignatureForError ());
902 if (!async_type.IsGenericTask) {
903 if (this is ContextualReturn)
906 ec.Report.Error (1997, loc,
907 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
908 ec.GetSignatureForError ());
913 // The return type is actually Task<T> type argument
915 if (expr.Type == async_type) {
916 ec.Report.Error (4016, loc,
917 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
918 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
920 block_return_type = async_type.TypeArguments[0];
924 // Same error code as .NET but better error message
925 if (block_return_type.Kind == MemberKind.Void) {
926 ec.Report.Error (127, loc,
927 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
928 am.GetSignatureForError ());
933 var l = am as AnonymousMethodBody;
934 if (l != null && expr != null) {
935 if (l.ReturnTypeInference != null) {
936 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
941 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
942 // unexpected debugging experience
944 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
945 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
954 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
955 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
958 if (am != null && block_return_type == ec.ReturnType) {
959 ec.Report.Error (1662, loc,
960 "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",
961 am.ContainerType, am.GetSignatureForError ());
970 protected override void DoEmit (EmitContext ec)
975 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
976 if (async_body != null) {
977 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
979 // It's null for await without async
980 if (async_return != null) {
981 async_return.EmitAssign (ec);
986 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
992 if (unwind_protect || ec.EmitAccurateDebugInfo)
993 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
996 if (unwind_protect) {
997 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
998 } else if (ec.EmitAccurateDebugInfo) {
999 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1001 ec.Emit (OpCodes.Ret);
1005 void Error_ReturnFromIterator (ResolveContext rc)
1007 rc.Report.Error (1622, loc,
1008 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1011 protected override void CloneTo (CloneContext clonectx, Statement t)
1013 Return target = (Return) t;
1014 // It's null for simple return;
1016 target.expr = expr.Clone (clonectx);
1019 public override object Accept (StructuralVisitor visitor)
1021 return visitor.Visit (this);
1025 public class Goto : Statement {
1027 LabeledStatement label;
1028 bool unwind_protect;
1030 public override bool Resolve (BlockContext ec)
1032 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1033 ec.CurrentBranching.CurrentUsageVector.Goto ();
1037 public Goto (string label, Location l)
1043 public string Target {
1044 get { return target; }
1047 public void SetResolvedTarget (LabeledStatement label)
1050 label.AddReference ();
1053 protected override void CloneTo (CloneContext clonectx, Statement target)
1058 protected override void DoEmit (EmitContext ec)
1061 throw new InternalErrorException ("goto emitted before target resolved");
1062 Label l = label.LabelTarget (ec);
1063 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1066 public override object Accept (StructuralVisitor visitor)
1068 return visitor.Visit (this);
1072 public class LabeledStatement : Statement {
1079 FlowBranching.UsageVector vectors;
1081 public LabeledStatement (string name, Block block, Location l)
1088 public Label LabelTarget (EmitContext ec)
1093 label = ec.DefineLabel ();
1098 public Block Block {
1104 public string Name {
1105 get { return name; }
1108 public bool IsDefined {
1109 get { return defined; }
1112 public bool HasBeenReferenced {
1113 get { return referenced; }
1116 public FlowBranching.UsageVector JumpOrigins {
1117 get { return vectors; }
1120 public void AddUsageVector (FlowBranching.UsageVector vector)
1122 vector = vector.Clone ();
1123 vector.Next = vectors;
1127 protected override void CloneTo (CloneContext clonectx, Statement target)
1132 public override bool Resolve (BlockContext ec)
1134 // this flow-branching will be terminated when the surrounding block ends
1135 ec.StartFlowBranching (this);
1139 protected override void DoEmit (EmitContext ec)
1141 if (!HasBeenReferenced)
1142 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1145 ec.MarkLabel (label);
1148 public void AddReference ()
1153 public override object Accept (StructuralVisitor visitor)
1155 return visitor.Visit (this);
1161 /// `goto default' statement
1163 public class GotoDefault : Statement {
1165 public GotoDefault (Location l)
1170 protected override void CloneTo (CloneContext clonectx, Statement target)
1175 public override bool Resolve (BlockContext ec)
1177 ec.CurrentBranching.CurrentUsageVector.Goto ();
1179 if (ec.Switch == null) {
1180 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1184 if (ec.Switch.DefaultLabel == null) {
1185 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1192 protected override void DoEmit (EmitContext ec)
1194 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1197 public override object Accept (StructuralVisitor visitor)
1199 return visitor.Visit (this);
1204 /// `goto case' statement
1206 public class GotoCase : Statement {
1210 public GotoCase (Expression e, Location l)
1216 public Expression Expr {
1222 public override bool Resolve (BlockContext ec)
1224 if (ec.Switch == null){
1225 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1229 ec.CurrentBranching.CurrentUsageVector.Goto ();
1231 expr = expr.Resolve (ec);
1235 Constant c = expr as Constant;
1237 ec.Report.Error (150, expr.Location, "A constant value is expected");
1242 if (ec.Switch.IsNullable && c is NullLiteral) {
1245 TypeSpec type = ec.Switch.SwitchType;
1246 res = c.Reduce (ec, type);
1248 c.Error_ValueCannotBeConverted (ec, type, true);
1252 if (!Convert.ImplicitStandardConversionExists (c, type))
1253 ec.Report.Warning (469, 2, loc,
1254 "The `goto case' value is not implicitly convertible to type `{0}'",
1255 TypeManager.CSharpName (type));
1259 sl = ec.Switch.ResolveGotoCase (ec, res);
1263 protected override void DoEmit (EmitContext ec)
1265 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1268 protected override void CloneTo (CloneContext clonectx, Statement t)
1270 GotoCase target = (GotoCase) t;
1272 target.expr = expr.Clone (clonectx);
1275 public override object Accept (StructuralVisitor visitor)
1277 return visitor.Visit (this);
1281 public class Throw : Statement {
1284 public Throw (Expression expr, Location l)
1290 public Expression Expr {
1296 public override bool Resolve (BlockContext ec)
1299 ec.CurrentBranching.CurrentUsageVector.Goto ();
1300 return ec.CurrentBranching.CheckRethrow (loc);
1303 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1304 ec.CurrentBranching.CurrentUsageVector.Goto ();
1309 var et = ec.BuiltinTypes.Exception;
1310 if (Convert.ImplicitConversionExists (ec, expr, et))
1311 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1313 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1318 protected override void DoEmit (EmitContext ec)
1321 ec.Emit (OpCodes.Rethrow);
1325 ec.Emit (OpCodes.Throw);
1329 protected override void CloneTo (CloneContext clonectx, Statement t)
1331 Throw target = (Throw) t;
1334 target.expr = expr.Clone (clonectx);
1337 public override object Accept (StructuralVisitor visitor)
1339 return visitor.Visit (this);
1343 public class Break : Statement {
1345 public Break (Location l)
1350 bool unwind_protect;
1352 public override bool Resolve (BlockContext ec)
1354 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1355 ec.CurrentBranching.CurrentUsageVector.Goto ();
1359 protected override void DoEmit (EmitContext ec)
1361 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1364 protected override void CloneTo (CloneContext clonectx, Statement t)
1369 public override object Accept (StructuralVisitor visitor)
1371 return visitor.Visit (this);
1375 public class Continue : Statement {
1377 public Continue (Location l)
1382 bool unwind_protect;
1384 public override bool Resolve (BlockContext ec)
1386 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1387 ec.CurrentBranching.CurrentUsageVector.Goto ();
1391 protected override void DoEmit (EmitContext ec)
1393 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1396 protected override void CloneTo (CloneContext clonectx, Statement t)
1401 public override object Accept (StructuralVisitor visitor)
1403 return visitor.Visit (this);
1407 public interface ILocalVariable
1409 void Emit (EmitContext ec);
1410 void EmitAssign (EmitContext ec);
1411 void EmitAddressOf (EmitContext ec);
1414 public interface INamedBlockVariable
1416 Block Block { get; }
1417 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1418 bool IsDeclared { get; }
1419 bool IsParameter { get; }
1420 Location Location { get; }
1423 public class BlockVariableDeclaration : Statement
1425 public class Declarator
1428 Expression initializer;
1430 public Declarator (LocalVariable li, Expression initializer)
1432 if (li.Type != null)
1433 throw new ArgumentException ("Expected null variable type");
1436 this.initializer = initializer;
1439 public Declarator (Declarator clone, Expression initializer)
1442 this.initializer = initializer;
1447 public LocalVariable Variable {
1453 public Expression Initializer {
1458 initializer = value;
1465 Expression initializer;
1466 protected FullNamedExpression type_expr;
1467 protected LocalVariable li;
1468 protected List<Declarator> declarators;
1471 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1473 this.type_expr = type;
1475 this.loc = type_expr.Location;
1478 protected BlockVariableDeclaration (LocalVariable li)
1485 public List<Declarator> Declarators {
1491 public Expression Initializer {
1496 initializer = value;
1500 public FullNamedExpression TypeExpression {
1506 public LocalVariable Variable {
1514 public void AddDeclarator (Declarator decl)
1516 if (declarators == null)
1517 declarators = new List<Declarator> ();
1519 declarators.Add (decl);
1522 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1524 if (bc.Report.Errors != 0)
1527 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1529 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1530 new MemberName (li.Name, li.Location), null);
1532 container.AddField (f);
1535 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1539 public override bool Resolve (BlockContext bc)
1541 return Resolve (bc, true);
1544 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1546 if (type == null && !li.IsCompilerGenerated) {
1547 var vexpr = type_expr as VarExpr;
1550 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1551 // same name exists or as a keyword when no type was found
1553 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1554 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1555 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1558 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1562 if (li.IsConstant) {
1563 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1567 if (Initializer == null) {
1568 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1572 if (declarators != null) {
1573 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1577 Initializer = Initializer.Resolve (bc);
1578 if (Initializer != null) {
1579 ((VarExpr) type_expr).InferType (bc, Initializer);
1580 type = type_expr.Type;
1582 // Set error type to indicate the var was placed correctly but could
1585 // var a = missing ();
1587 type = InternalType.ErrorType;
1592 type = type_expr.ResolveAsType (bc);
1596 if (li.IsConstant && !type.IsConstantCompatible) {
1597 Const.Error_InvalidConstantType (type, loc, bc.Report);
1602 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1607 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1609 CreateEvaluatorVariable (bc, li);
1611 li.PrepareForFlowAnalysis (bc);
1614 if (initializer != null) {
1615 initializer = ResolveInitializer (bc, li, initializer);
1616 // li.Variable.DefinitelyAssigned
1619 if (declarators != null) {
1620 foreach (var d in declarators) {
1621 d.Variable.Type = li.Type;
1623 CreateEvaluatorVariable (bc, d.Variable);
1625 d.Variable.PrepareForFlowAnalysis (bc);
1628 if (d.Initializer != null && resolveDeclaratorInitializers) {
1629 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1630 // d.Variable.DefinitelyAssigned
1638 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1640 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1641 return a.ResolveStatement (bc);
1644 protected override void DoEmit (EmitContext ec)
1646 li.CreateBuilder (ec);
1648 if (Initializer != null)
1649 ((ExpressionStatement) Initializer).EmitStatement (ec);
1651 if (declarators != null) {
1652 foreach (var d in declarators) {
1653 d.Variable.CreateBuilder (ec);
1654 if (d.Initializer != null) {
1655 ec.Mark (d.Variable.Location);
1656 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1662 protected override void CloneTo (CloneContext clonectx, Statement target)
1664 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1666 if (type_expr != null)
1667 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1669 if (initializer != null)
1670 t.initializer = initializer.Clone (clonectx);
1672 if (declarators != null) {
1673 t.declarators = null;
1674 foreach (var d in declarators)
1675 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1679 public override object Accept (StructuralVisitor visitor)
1681 return visitor.Visit (this);
1685 public class BlockConstantDeclaration : BlockVariableDeclaration
1687 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1692 public override void Emit (EmitContext ec)
1694 // Nothing to emit, not even sequence point
1697 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1699 initializer = initializer.Resolve (bc);
1700 if (initializer == null)
1703 var c = initializer as Constant;
1705 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1709 c = c.ConvertImplicitly (li.Type);
1711 if (TypeSpec.IsReferenceType (li.Type))
1712 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1714 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1719 li.ConstantValue = c;
1723 public override object Accept (StructuralVisitor visitor)
1725 return visitor.Visit (this);
1730 // The information about a user-perceived local variable
1732 public class LocalVariable : INamedBlockVariable, ILocalVariable
1739 AddressTaken = 1 << 2,
1740 CompilerGenerated = 1 << 3,
1742 ForeachVariable = 1 << 5,
1743 FixedVariable = 1 << 6,
1744 UsingVariable = 1 << 7,
1745 // DefinitelyAssigned = 1 << 8,
1748 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1752 readonly string name;
1753 readonly Location loc;
1754 readonly Block block;
1756 Constant const_value;
1758 public VariableInfo VariableInfo;
1759 HoistedVariable hoisted_variant;
1761 LocalBuilder builder;
1763 public LocalVariable (Block block, string name, Location loc)
1770 public LocalVariable (Block block, string name, Flags flags, Location loc)
1771 : this (block, name, loc)
1777 // Used by variable declarators
1779 public LocalVariable (LocalVariable li, string name, Location loc)
1780 : this (li.block, name, li.flags, loc)
1786 public bool AddressTaken {
1788 return (flags & Flags.AddressTaken) != 0;
1792 public Block Block {
1798 public Constant ConstantValue {
1803 const_value = value;
1808 // Hoisted local variable variant
1810 public HoistedVariable HoistedVariant {
1812 return hoisted_variant;
1815 hoisted_variant = value;
1819 public bool IsDeclared {
1821 return type != null;
1825 public bool IsCompilerGenerated {
1827 return (flags & Flags.CompilerGenerated) != 0;
1831 public bool IsConstant {
1833 return (flags & Flags.Constant) != 0;
1837 public bool IsLocked {
1839 return (flags & Flags.IsLocked) != 0;
1842 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1846 public bool IsThis {
1848 return (flags & Flags.IsThis) != 0;
1852 public bool IsFixed {
1854 return (flags & Flags.FixedVariable) != 0;
1858 bool INamedBlockVariable.IsParameter {
1864 public bool IsReadonly {
1866 return (flags & Flags.ReadonlyMask) != 0;
1870 public Location Location {
1876 public string Name {
1882 public TypeSpec Type {
1893 public void CreateBuilder (EmitContext ec)
1895 if ((flags & Flags.Used) == 0) {
1896 if (VariableInfo == null) {
1897 // Missing flow analysis or wrong variable flags
1898 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1901 if (VariableInfo.IsEverAssigned)
1902 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1904 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1907 if (HoistedVariant != null)
1910 if (builder != null) {
1911 if ((flags & Flags.CompilerGenerated) != 0)
1914 // To avoid Used warning duplicates
1915 throw new InternalErrorException ("Already created variable `{0}'", name);
1919 // All fixed variabled are pinned, a slot has to be alocated
1921 builder = ec.DeclareLocal (Type, IsFixed);
1922 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1923 ec.DefineLocalVariable (name, builder);
1926 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1928 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1933 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1935 if (IsConstant && const_value != null)
1936 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1938 return new LocalVariableReference (this, loc);
1941 public void Emit (EmitContext ec)
1943 // TODO: Need something better for temporary variables
1944 if ((flags & Flags.CompilerGenerated) != 0)
1947 ec.Emit (OpCodes.Ldloc, builder);
1950 public void EmitAssign (EmitContext ec)
1952 // TODO: Need something better for temporary variables
1953 if ((flags & Flags.CompilerGenerated) != 0)
1956 ec.Emit (OpCodes.Stloc, builder);
1959 public void EmitAddressOf (EmitContext ec)
1961 ec.Emit (OpCodes.Ldloca, builder);
1964 public static string GetCompilerGeneratedName (Block block)
1966 // HACK: Debugger depends on the name semantics
1967 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1970 public string GetReadOnlyContext ()
1972 switch (flags & Flags.ReadonlyMask) {
1973 case Flags.FixedVariable:
1974 return "fixed variable";
1975 case Flags.ForeachVariable:
1976 return "foreach iteration variable";
1977 case Flags.UsingVariable:
1978 return "using variable";
1981 throw new InternalErrorException ("Variable is not readonly");
1984 public bool IsThisAssigned (BlockContext ec, Block block)
1986 if (VariableInfo == null)
1987 throw new Exception ();
1989 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1992 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1995 public bool IsAssigned (BlockContext ec)
1997 if (VariableInfo == null)
1998 throw new Exception ();
2000 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
2003 public void PrepareForFlowAnalysis (BlockContext bc)
2006 // No need for definitely assigned check for these guys
2008 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2011 VariableInfo = new VariableInfo (this, bc.FlowOffset);
2012 bc.FlowOffset += VariableInfo.Length;
2016 // Mark the variables as referenced in the user code
2018 public void SetIsUsed ()
2020 flags |= Flags.Used;
2023 public void SetHasAddressTaken ()
2025 flags |= (Flags.AddressTaken | Flags.Used);
2028 public override string ToString ()
2030 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2035 /// Block represents a C# block.
2039 /// This class is used in a number of places: either to represent
2040 /// explicit blocks that the programmer places or implicit blocks.
2042 /// Implicit blocks are used as labels or to introduce variable
2045 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2046 /// they contain extra information that is not necessary on normal blocks.
2048 public class Block : Statement {
2055 HasCapturedVariable = 64,
2056 HasCapturedThis = 1 << 7,
2057 IsExpressionTree = 1 << 8,
2058 CompilerGenerated = 1 << 9,
2059 HasAsyncModifier = 1 << 10,
2061 YieldBlock = 1 << 12,
2062 AwaitBlock = 1 << 13,
2066 public Block Parent;
2067 public Location StartLocation;
2068 public Location EndLocation;
2070 public ExplicitBlock Explicit;
2071 public ParametersBlock ParametersBlock;
2073 protected Flags flags;
2076 // The statements in this block
2078 protected List<Statement> statements;
2080 protected List<Statement> scope_initializers;
2082 int? resolving_init_idx;
2088 public int ID = id++;
2090 static int clone_id_counter;
2094 // int assignable_slots;
2096 public Block (Block parent, Location start, Location end)
2097 : this (parent, 0, start, end)
2101 public Block (Block parent, Flags flags, Location start, Location end)
2103 if (parent != null) {
2104 // the appropriate constructors will fixup these fields
2105 ParametersBlock = parent.ParametersBlock;
2106 Explicit = parent.Explicit;
2109 this.Parent = parent;
2111 this.StartLocation = start;
2112 this.EndLocation = end;
2114 statements = new List<Statement> (4);
2116 this.original = this;
2121 public bool HasUnreachableClosingBrace {
2123 return (flags & Flags.HasRet) != 0;
2126 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2130 public Block Original {
2139 public bool IsCompilerGenerated {
2140 get { return (flags & Flags.CompilerGenerated) != 0; }
2141 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2144 public bool Unchecked {
2145 get { return (flags & Flags.Unchecked) != 0; }
2146 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2149 public bool Unsafe {
2150 get { return (flags & Flags.Unsafe) != 0; }
2151 set { flags |= Flags.Unsafe; }
2154 public List<Statement> Statements {
2155 get { return statements; }
2160 public void SetEndLocation (Location loc)
2165 public void AddLabel (LabeledStatement target)
2167 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2170 public void AddLocalName (LocalVariable li)
2172 AddLocalName (li.Name, li);
2175 public void AddLocalName (string name, INamedBlockVariable li)
2177 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2180 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2182 if (reason == null) {
2183 Error_AlreadyDeclared (name, variable);
2187 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2188 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2189 "to `{0}', which is already used in a `{1}' scope to denote something else",
2193 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2195 var pi = variable as ParametersBlock.ParameterInfo;
2197 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2199 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2200 "A local variable named `{0}' is already defined in this scope", name);
2204 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2206 ParametersBlock.TopBlock.Report.Error (412, loc,
2207 "The type parameter name `{0}' is the same as local variable or parameter name",
2212 // It should be used by expressions which require to
2213 // register a statement during resolve process.
2215 public void AddScopeStatement (Statement s)
2217 if (scope_initializers == null)
2218 scope_initializers = new List<Statement> ();
2221 // Simple recursive helper, when resolve scope initializer another
2222 // new scope initializer can be added, this ensures it's initialized
2223 // before existing one. For now this can happen with expression trees
2224 // in base ctor initializer only
2226 if (resolving_init_idx.HasValue) {
2227 scope_initializers.Insert (resolving_init_idx.Value, s);
2228 ++resolving_init_idx;
2230 scope_initializers.Add (s);
2234 public void AddStatement (Statement s)
2239 public int AssignableSlots {
2241 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2243 // return assignable_slots;
2247 public LabeledStatement LookupLabel (string name)
2249 return ParametersBlock.TopBlock.GetLabel (name, this);
2252 public override bool Resolve (BlockContext ec)
2254 if ((flags & Flags.Resolved) != 0)
2257 Block prev_block = ec.CurrentBlock;
2259 bool unreachable = ec.IsUnreachable;
2260 bool prev_unreachable = unreachable;
2262 ec.CurrentBlock = this;
2263 ec.StartFlowBranching (this);
2266 // Compiler generated scope statements
2268 if (scope_initializers != null) {
2269 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2270 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2273 resolving_init_idx = null;
2277 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2278 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2279 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2280 // responsible for handling the situation.
2282 int statement_count = statements.Count;
2283 for (int ix = 0; ix < statement_count; ix++){
2284 Statement s = statements [ix];
2287 // Warn if we detect unreachable code.
2290 if (s is EmptyStatement)
2293 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2294 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2295 ec.UnreachableReported = true;
2300 // Note that we're not using ResolveUnreachable() for unreachable
2301 // statements here. ResolveUnreachable() creates a temporary
2302 // flow branching and kills it afterwards. This leads to problems
2303 // if you have two unreachable statements where the first one
2304 // assigns a variable and the second one tries to access it.
2307 if (!s.Resolve (ec)) {
2309 if (!ec.IsInProbingMode)
2310 statements [ix] = new EmptyStatement (s.loc);
2315 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2316 statements [ix] = new EmptyStatement (s.loc);
2318 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2320 ec.IsUnreachable = true;
2321 } else if (ec.IsUnreachable)
2322 ec.IsUnreachable = false;
2325 if (unreachable != prev_unreachable) {
2326 ec.IsUnreachable = prev_unreachable;
2327 ec.UnreachableReported = false;
2330 while (ec.CurrentBranching is FlowBranchingLabeled)
2331 ec.EndFlowBranching ();
2333 bool flow_unreachable = ec.EndFlowBranching ();
2335 ec.CurrentBlock = prev_block;
2337 if (flow_unreachable)
2338 flags |= Flags.HasRet;
2340 // If we're a non-static `struct' constructor which doesn't have an
2341 // initializer, then we must initialize all of the struct's fields.
2342 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2345 flags |= Flags.Resolved;
2349 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2351 bool unreachable = false;
2352 if (warn && !ec.UnreachableReported) {
2353 ec.UnreachableReported = true;
2355 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2358 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2359 fb.CurrentUsageVector.IsUnreachable = true;
2360 bool ok = Resolve (ec);
2361 ec.KillFlowBranching ();
2364 ec.UnreachableReported = false;
2369 protected override void DoEmit (EmitContext ec)
2371 for (int ix = 0; ix < statements.Count; ix++){
2372 statements [ix].Emit (ec);
2376 public override void Emit (EmitContext ec)
2378 if (scope_initializers != null)
2379 EmitScopeInitializers (ec);
2384 protected void EmitScopeInitializers (EmitContext ec)
2386 foreach (Statement s in scope_initializers)
2391 public override string ToString ()
2393 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2397 protected override void CloneTo (CloneContext clonectx, Statement t)
2399 Block target = (Block) t;
2401 target.clone_id = clone_id_counter++;
2404 clonectx.AddBlockMap (this, target);
2405 if (original != this)
2406 clonectx.AddBlockMap (original, target);
2408 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2409 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2412 target.Parent = clonectx.RemapBlockCopy (Parent);
2414 target.statements = new List<Statement> (statements.Count);
2415 foreach (Statement s in statements)
2416 target.statements.Add (s.Clone (clonectx));
2419 public override object Accept (StructuralVisitor visitor)
2421 return visitor.Visit (this);
2425 public class ExplicitBlock : Block
2427 protected AnonymousMethodStorey am_storey;
2429 public ExplicitBlock (Block parent, Location start, Location end)
2430 : this (parent, (Flags) 0, start, end)
2434 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2435 : base (parent, flags, start, end)
2437 this.Explicit = this;
2442 public AnonymousMethodStorey AnonymousMethodStorey {
2448 public bool HasAwait {
2450 return (flags & Flags.AwaitBlock) != 0;
2454 public bool HasCapturedThis {
2456 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2459 return (flags & Flags.HasCapturedThis) != 0;
2464 // Used to indicate that the block has reference to parent
2465 // block and cannot be made static when defining anonymous method
2467 public bool HasCapturedVariable {
2469 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2472 return (flags & Flags.HasCapturedVariable) != 0;
2476 public bool HasYield {
2478 return (flags & Flags.YieldBlock) != 0;
2485 // Creates anonymous method storey in current block
2487 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2490 // Return same story for iterator and async blocks unless we are
2491 // in nested anonymous method
2493 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2494 return ec.CurrentAnonymousMethod.Storey;
2496 if (am_storey == null) {
2497 MemberBase mc = ec.MemberContext as MemberBase;
2500 // Creates anonymous method storey for this block
2502 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2508 public override void Emit (EmitContext ec)
2510 if (am_storey != null) {
2511 DefineStoreyContainer (ec, am_storey);
2512 am_storey.EmitStoreyInstantiation (ec, this);
2515 if (scope_initializers != null)
2516 EmitScopeInitializers (ec);
2518 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2519 ec.Emit (OpCodes.Nop);
2530 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2531 ec.Emit (OpCodes.Nop);
2535 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2537 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2538 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2539 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2543 // Creates anonymous method storey
2545 storey.CreateContainer ();
2546 storey.DefineContainer ();
2548 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2551 // Only first storey in path will hold this reference. All children blocks will
2552 // reference it indirectly using $ref field
2554 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2555 if (b.Parent != null) {
2556 var s = b.Parent.Explicit.AnonymousMethodStorey;
2558 storey.HoistedThis = s.HoistedThis;
2563 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2564 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2566 if (storey.HoistedThis != null)
2572 // We are the first storey on path and 'this' has to be hoisted
2574 if (storey.HoistedThis == null) {
2575 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2577 // ThisReferencesFromChildrenBlock holds all reference even if they
2578 // are not on this path. It saves some memory otherwise it'd have to
2579 // be in every explicit block. We run this check to see if the reference
2580 // is valid for this storey
2582 Block block_on_path = ref_block;
2583 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2585 if (block_on_path == null)
2588 if (storey.HoistedThis == null) {
2589 storey.AddCapturedThisField (ec);
2592 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2593 if (b.AnonymousMethodStorey != null) {
2594 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2595 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2598 // Stop propagation inside same top block
2600 if (b.ParametersBlock == ParametersBlock.Original)
2603 b = b.ParametersBlock;
2606 var pb = b as ParametersBlock;
2607 if (pb != null && pb.StateMachine != null) {
2608 if (pb.StateMachine == storey)
2611 pb.StateMachine.AddParentStoreyReference (ec, storey);
2614 b.HasCapturedVariable = true;
2620 var ref_blocks = storey.ReferencesFromChildrenBlock;
2621 if (ref_blocks != null) {
2622 foreach (ExplicitBlock ref_block in ref_blocks) {
2623 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2624 if (b.AnonymousMethodStorey != null) {
2625 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2628 // Stop propagation inside same top block
2630 if (b.ParametersBlock == ParametersBlock.Original)
2633 b = b.ParametersBlock;
2636 var pb = b as ParametersBlock;
2637 if (pb != null && pb.StateMachine != null) {
2638 if (pb.StateMachine == storey)
2641 pb.StateMachine.AddParentStoreyReference (ec, storey);
2644 b.HasCapturedVariable = true;
2650 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2653 public void RegisterAsyncAwait ()
2656 while ((block.flags & Flags.AwaitBlock) == 0) {
2657 block.flags |= Flags.AwaitBlock;
2659 if (block is ParametersBlock)
2662 block = block.Parent.Explicit;
2666 public void RegisterIteratorYield ()
2668 ParametersBlock.TopBlock.IsIterator = true;
2671 while ((block.flags & Flags.YieldBlock) == 0) {
2672 block.flags |= Flags.YieldBlock;
2674 if (block.Parent == null)
2677 block = block.Parent.Explicit;
2681 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2683 tryBlock.statements = statements;
2684 statements = new List<Statement> (1);
2685 statements.Add (tf);
2690 // ParametersBlock was introduced to support anonymous methods
2691 // and lambda expressions
2693 public class ParametersBlock : ExplicitBlock
2695 public class ParameterInfo : INamedBlockVariable
2697 readonly ParametersBlock block;
2699 public VariableInfo VariableInfo;
2702 public ParameterInfo (ParametersBlock block, int index)
2710 public ParametersBlock Block {
2716 Block INamedBlockVariable.Block {
2722 public bool IsDeclared {
2728 public bool IsParameter {
2734 public bool IsLocked {
2743 public Location Location {
2745 return Parameter.Location;
2749 public Parameter Parameter {
2751 return block.Parameters [index];
2755 public TypeSpec ParameterType {
2757 return Parameter.Type;
2763 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2765 return new ParameterReference (this, loc);
2770 // Block is converted into an expression
2772 sealed class BlockScopeExpression : Expression
2775 readonly ParametersBlock block;
2777 public BlockScopeExpression (Expression child, ParametersBlock block)
2783 public override bool ContainsEmitWithAwait ()
2785 return child.ContainsEmitWithAwait ();
2788 public override Expression CreateExpressionTree (ResolveContext ec)
2790 throw new NotSupportedException ();
2793 protected override Expression DoResolve (ResolveContext ec)
2798 child = child.Resolve (ec);
2802 eclass = child.eclass;
2807 public override void Emit (EmitContext ec)
2809 block.EmitScopeInitializers (ec);
2814 protected ParametersCompiled parameters;
2815 protected ParameterInfo[] parameter_info;
2817 protected bool unreachable;
2818 protected ToplevelBlock top_block;
2819 protected StateMachine state_machine;
2821 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2822 : base (parent, 0, start, start)
2824 if (parameters == null)
2825 throw new ArgumentNullException ("parameters");
2827 this.parameters = parameters;
2828 ParametersBlock = this;
2830 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2832 this.top_block = parent.ParametersBlock.top_block;
2833 ProcessParameters ();
2836 protected ParametersBlock (ParametersCompiled parameters, Location start)
2837 : base (null, 0, start, start)
2839 if (parameters == null)
2840 throw new ArgumentNullException ("parameters");
2842 this.parameters = parameters;
2843 ParametersBlock = this;
2847 // It's supposed to be used by method body implementation of anonymous methods
2849 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2850 : base (null, 0, source.StartLocation, source.EndLocation)
2852 this.parameters = parameters;
2853 this.statements = source.statements;
2854 this.scope_initializers = source.scope_initializers;
2856 this.resolved = true;
2857 this.unreachable = source.unreachable;
2858 this.am_storey = source.am_storey;
2859 this.state_machine = source.state_machine;
2861 ParametersBlock = this;
2864 // Overwrite original for comparison purposes when linking cross references
2865 // between anonymous methods
2867 Original = source.Original;
2872 public bool IsAsync {
2874 return (flags & Flags.HasAsyncModifier) != 0;
2877 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2882 // Block has been converted to expression tree
2884 public bool IsExpressionTree {
2886 return (flags & Flags.IsExpressionTree) != 0;
2891 // The parameters for the block.
2893 public ParametersCompiled Parameters {
2899 public StateMachine StateMachine {
2901 return state_machine;
2905 public ToplevelBlock TopBlock {
2911 public bool Resolved {
2913 return (flags & Flags.Resolved) != 0;
2917 public int TemporaryLocalsCount { get; set; }
2922 // Check whether all `out' parameters have been assigned.
2924 public void CheckOutParameters (FlowBranching.UsageVector vector)
2926 if (vector.IsUnreachable)
2929 int n = parameter_info == null ? 0 : parameter_info.Length;
2931 for (int i = 0; i < n; i++) {
2932 VariableInfo var = parameter_info[i].VariableInfo;
2937 if (vector.IsAssigned (var, false))
2940 var p = parameter_info[i].Parameter;
2941 TopBlock.Report.Error (177, p.Location,
2942 "The out parameter `{0}' must be assigned to before control leaves the current method",
2947 public override Expression CreateExpressionTree (ResolveContext ec)
2949 if (statements.Count == 1) {
2950 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2951 if (scope_initializers != null)
2952 expr = new BlockScopeExpression (expr, this);
2957 return base.CreateExpressionTree (ec);
2960 public override void Emit (EmitContext ec)
2962 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2963 DefineStoreyContainer (ec, state_machine);
2964 state_machine.EmitStoreyInstantiation (ec, this);
2970 public void EmitEmbedded (EmitContext ec)
2972 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2973 DefineStoreyContainer (ec, state_machine);
2974 state_machine.EmitStoreyInstantiation (ec, this);
2980 public ParameterInfo GetParameterInfo (Parameter p)
2982 for (int i = 0; i < parameters.Count; ++i) {
2983 if (parameters[i] == p)
2984 return parameter_info[i];
2987 throw new ArgumentException ("Invalid parameter");
2990 public ParameterReference GetParameterReference (int index, Location loc)
2992 return new ParameterReference (parameter_info[index], loc);
2995 public Statement PerformClone ()
2997 CloneContext clonectx = new CloneContext ();
2998 return Clone (clonectx);
3001 protected void ProcessParameters ()
3003 if (parameters.Count == 0)
3006 parameter_info = new ParameterInfo[parameters.Count];
3007 for (int i = 0; i < parameter_info.Length; ++i) {
3008 var p = parameters.FixedParameters[i];
3012 // TODO: Should use Parameter only and more block there
3013 parameter_info[i] = new ParameterInfo (this, i);
3015 AddLocalName (p.Name, parameter_info[i]);
3019 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3026 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3027 flags |= Flags.IsExpressionTree;
3032 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3033 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3038 unreachable = top_level.End ();
3040 } catch (Exception e) {
3041 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3044 if (rc.CurrentBlock != null) {
3045 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3047 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3050 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3054 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3055 if (rc.CurrentAnonymousMethod == null) {
3056 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3057 if (md is StateMachineMethod) {
3060 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3065 // If an asynchronous body of F is either an expression classified as nothing, or a
3066 // statement block where no return statements have expressions, the inferred return type is Task
3069 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3070 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3071 am.ReturnTypeInference = null;
3072 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3077 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3078 rc.CurrentAnonymousMethod.GetSignatureForError ());
3086 void ResolveMeta (BlockContext ec)
3088 int orig_count = parameters.Count;
3090 for (int i = 0; i < orig_count; ++i) {
3091 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3093 if ((mod & Parameter.Modifier.OUT) == 0)
3096 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3097 parameter_info[i].VariableInfo = vi;
3098 ec.FlowOffset += vi.Length;
3102 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3104 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3105 var stateMachine = new IteratorStorey (iterator);
3107 state_machine = stateMachine;
3108 iterator.SetStateMachine (stateMachine);
3110 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3111 tlb.Original = this;
3112 tlb.IsCompilerGenerated = true;
3113 tlb.state_machine = stateMachine;
3114 tlb.AddStatement (new Return (iterator, iterator.Location));
3118 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3120 for (int i = 0; i < parameters.Count; i++) {
3121 Parameter p = parameters[i];
3122 Parameter.Modifier mod = p.ModFlags;
3123 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3124 host.Compiler.Report.Error (1988, p.Location,
3125 "Async methods cannot have ref or out parameters");
3129 if (p is ArglistParameter) {
3130 host.Compiler.Report.Error (4006, p.Location,
3131 "__arglist is not allowed in parameter list of async methods");
3135 if (parameters.Types[i].IsPointer) {
3136 host.Compiler.Report.Error (4005, p.Location,
3137 "Async methods cannot have unsafe parameters");
3143 host.Compiler.Report.Warning (1998, 1, loc,
3144 "Async block lacks `await' operator and will run synchronously");
3147 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3148 var initializer = new AsyncInitializer (this, host, block_type);
3149 initializer.Type = block_type;
3151 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3153 state_machine = stateMachine;
3154 initializer.SetStateMachine (stateMachine);
3156 var b = this is ToplevelBlock ?
3157 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3158 new ParametersBlock (Parent, parameters, Location.Null) {
3163 b.IsCompilerGenerated = true;
3164 b.state_machine = stateMachine;
3165 b.AddStatement (new StatementExpression (initializer));
3173 public class ToplevelBlock : ParametersBlock
3175 LocalVariable this_variable;
3176 CompilerContext compiler;
3177 Dictionary<string, object> names;
3178 Dictionary<string, object> labels;
3180 List<ExplicitBlock> this_references;
3182 public ToplevelBlock (CompilerContext ctx, Location loc)
3183 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3187 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3188 : base (parameters, start)
3190 this.compiler = ctx;
3192 flags |= Flags.HasRet;
3194 ProcessParameters ();
3198 // Recreates a top level block from parameters block. Used for
3199 // compiler generated methods where the original block comes from
3200 // explicit child block. This works for already resolved blocks
3201 // only to ensure we resolve them in the correct flow order
3203 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3204 : base (source, parameters)
3206 this.compiler = source.TopBlock.compiler;
3208 flags |= Flags.HasRet;
3211 public bool IsIterator {
3213 return (flags & Flags.Iterator) != 0;
3216 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3220 public Report Report {
3222 return compiler.Report;
3227 // Used by anonymous blocks to track references of `this' variable
3229 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3231 return this_references;
3236 // Returns the "this" instance variable of this block.
3237 // See AddThisVariable() for more information.
3239 public LocalVariable ThisVariable {
3241 return this_variable;
3245 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3248 names = new Dictionary<string, object> ();
3251 if (!names.TryGetValue (name, out value)) {
3252 names.Add (name, li);
3256 INamedBlockVariable existing = value as INamedBlockVariable;
3257 List<INamedBlockVariable> existing_list;
3258 if (existing != null) {
3259 existing_list = new List<INamedBlockVariable> ();
3260 existing_list.Add (existing);
3261 names[name] = existing_list;
3263 existing_list = (List<INamedBlockVariable>) value;
3267 // A collision checking between local names
3269 var variable_block = li.Block.Explicit;
3270 for (int i = 0; i < existing_list.Count; ++i) {
3271 existing = existing_list[i];
3272 Block b = existing.Block.Explicit;
3274 // Collision at same level
3275 if (variable_block == b) {
3276 li.Block.Error_AlreadyDeclared (name, li);
3280 // Collision with parent
3281 Block parent = variable_block;
3282 while ((parent = parent.Parent) != null) {
3284 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3285 i = existing_list.Count;
3290 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3291 // Collision with children
3292 while ((b = b.Parent) != null) {
3293 if (variable_block == b) {
3294 li.Block.Error_AlreadyDeclared (name, li, "child");
3295 i = existing_list.Count;
3302 existing_list.Add (li);
3305 public void AddLabel (string name, LabeledStatement label)
3308 labels = new Dictionary<string, object> ();
3311 if (!labels.TryGetValue (name, out value)) {
3312 labels.Add (name, label);
3316 LabeledStatement existing = value as LabeledStatement;
3317 List<LabeledStatement> existing_list;
3318 if (existing != null) {
3319 existing_list = new List<LabeledStatement> ();
3320 existing_list.Add (existing);
3321 labels[name] = existing_list;
3323 existing_list = (List<LabeledStatement>) value;
3327 // A collision checking between labels
3329 for (int i = 0; i < existing_list.Count; ++i) {
3330 existing = existing_list[i];
3331 Block b = existing.Block;
3333 // Collision at same level
3334 if (label.Block == b) {
3335 Report.SymbolRelatedToPreviousError (existing.loc, name);
3336 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3340 // Collision with parent
3342 while ((b = b.Parent) != null) {
3343 if (existing.Block == b) {
3344 Report.Error (158, label.loc,
3345 "The label `{0}' shadows another label by the same name in a contained scope", name);
3346 i = existing_list.Count;
3351 // Collision with with children
3353 while ((b = b.Parent) != null) {
3354 if (label.Block == b) {
3355 Report.Error (158, label.loc,
3356 "The label `{0}' shadows another label by the same name in a contained scope", name);
3357 i = existing_list.Count;
3363 existing_list.Add (label);
3366 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3368 if (this_references == null)
3369 this_references = new List<ExplicitBlock> ();
3371 if (!this_references.Contains (block))
3372 this_references.Add (block);
3375 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3377 this_references.Remove (block);
3381 // Creates an arguments set from all parameters, useful for method proxy calls
3383 public Arguments GetAllParametersArguments ()
3385 int count = parameters.Count;
3386 Arguments args = new Arguments (count);
3387 for (int i = 0; i < count; ++i) {
3388 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3389 args.Add (new Argument (arg_expr));
3396 // Lookup inside a block, the returned value can represent 3 states
3398 // true+variable: A local name was found and it's valid
3399 // false+variable: A local name was found in a child block only
3400 // false+null: No local name was found
3402 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3408 if (!names.TryGetValue (name, out value))
3411 variable = value as INamedBlockVariable;
3413 if (variable != null) {
3415 if (variable.Block == b.Original)
3419 } while (b != null);
3427 } while (b != null);
3429 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3430 for (int i = 0; i < list.Count; ++i) {
3433 if (variable.Block == b.Original)
3437 } while (b != null);
3445 } while (b != null);
3455 public LabeledStatement GetLabel (string name, Block block)
3461 if (!labels.TryGetValue (name, out value)) {
3465 var label = value as LabeledStatement;
3467 if (label != null) {
3468 if (label.Block == b.Original)
3471 List<LabeledStatement> list = (List<LabeledStatement>) value;
3472 for (int i = 0; i < list.Count; ++i) {
3474 if (label.Block == b.Original)
3483 // This is used by non-static `struct' constructors which do not have an
3484 // initializer - in this case, the constructor must initialize all of the
3485 // struct's fields. To do this, we add a "this" variable and use the flow
3486 // analysis code to ensure that it's been fully initialized before control
3487 // leaves the constructor.
3489 public void AddThisVariable (BlockContext bc)
3491 if (this_variable != null)
3492 throw new InternalErrorException (StartLocation.ToString ());
3494 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3495 this_variable.Type = bc.CurrentType;
3496 this_variable.PrepareForFlowAnalysis (bc);
3499 public bool IsThisAssigned (BlockContext ec)
3501 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3504 public override void Emit (EmitContext ec)
3506 if (Report.Errors > 0)
3510 if (IsCompilerGenerated) {
3511 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3519 // If `HasReturnLabel' is set, then we already emitted a
3520 // jump to the end of the method, so we must emit a `ret'
3523 // Unfortunately, System.Reflection.Emit automatically emits
3524 // a leave to the end of a finally block. This is a problem
3525 // if no code is following the try/finally block since we may
3526 // jump to a point after the end of the method.
3527 // As a workaround, we're always creating a return label in
3530 if (ec.HasReturnLabel || !unreachable) {
3531 if (ec.HasReturnLabel)
3532 ec.MarkLabel (ec.ReturnLabel);
3534 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3535 ec.Mark (EndLocation);
3537 if (ec.ReturnType.Kind != MemberKind.Void)
3538 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3540 ec.Emit (OpCodes.Ret);
3543 } catch (Exception e) {
3544 throw new InternalErrorException (e, StartLocation);
3549 public class SwitchLabel : Statement
3557 // if expr == null, then it is the default case.
3559 public SwitchLabel (Expression expr, Location l)
3565 public bool IsDefault {
3567 return label == null;
3571 public Expression Label {
3577 public Location Location {
3583 public Constant Converted {
3592 public bool SectionStart { get; set; }
3594 public Label GetILLabel (EmitContext ec)
3596 if (il_label == null){
3597 il_label = ec.DefineLabel ();
3600 return il_label.Value;
3603 protected override void DoEmit (EmitContext ec)
3605 ec.MarkLabel (GetILLabel (ec));
3608 public override bool Resolve (BlockContext bc)
3610 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3612 return base.Resolve (bc);
3616 // Resolves the expression, reduces it to a literal if possible
3617 // and then converts it to the requested type.
3619 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3621 Expression e = label.Resolve (ec);
3626 Constant c = e as Constant;
3628 ec.Report.Error (150, loc, "A constant value is expected");
3632 if (allow_nullable && c is NullLiteral) {
3637 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3638 return converted != null;
3641 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3644 if (converted == null)
3647 label = converted.GetValueAsLiteral ();
3649 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3650 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3653 protected override void CloneTo (CloneContext clonectx, Statement target)
3655 var t = (SwitchLabel) target;
3657 t.label = label.Clone (clonectx);
3660 public override object Accept (StructuralVisitor visitor)
3662 return visitor.Visit (this);
3666 public class Switch : Statement
3668 // structure used to hold blocks of keys while calculating table switch
3669 sealed class LabelsRange : IComparable<LabelsRange>
3671 public readonly long min;
3673 public readonly List<long> label_values;
3675 public LabelsRange (long value)
3678 label_values = new List<long> ();
3679 label_values.Add (value);
3682 public LabelsRange (long min, long max, ICollection<long> values)
3686 this.label_values = new List<long> (values);
3691 return max - min + 1;
3695 public bool AddValue (long value)
3697 var gap = value - min + 1;
3698 // Ensure the range has > 50% occupancy
3699 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3703 label_values.Add (value);
3707 public int CompareTo (LabelsRange other)
3709 int nLength = label_values.Count;
3710 int nLengthOther = other.label_values.Count;
3711 if (nLengthOther == nLength)
3712 return (int) (other.min - min);
3714 return nLength - nLengthOther;
3718 sealed class DispatchStatement : Statement
3720 readonly Switch body;
3722 public DispatchStatement (Switch body)
3727 protected override void CloneTo (CloneContext clonectx, Statement target)
3729 throw new NotImplementedException ();
3732 protected override void DoEmit (EmitContext ec)
3734 body.EmitDispatch (ec);
3738 public Expression Expr;
3741 // Mapping of all labels to their SwitchLabels
3743 Dictionary<long, SwitchLabel> labels;
3744 Dictionary<string, SwitchLabel> string_labels;
3745 List<SwitchLabel> case_labels;
3748 /// The governing switch type
3750 public TypeSpec SwitchType;
3752 Expression new_expr;
3754 SwitchLabel case_null;
3755 SwitchLabel case_default;
3757 Label defaultLabel, nullLabel;
3758 VariableReference value;
3759 ExpressionStatement string_dictionary;
3760 FieldExpr switch_cache_field;
3761 ExplicitBlock block;
3764 // Nullable Types support
3766 Nullable.Unwrap unwrap;
3768 public Switch (Expression e, ExplicitBlock block, Location l)
3775 public ExplicitBlock Block {
3781 public SwitchLabel DefaultLabel {
3783 return case_default;
3787 public bool IsNullable {
3789 return unwrap != null;
3794 // Determines the governing type for a switch. The returned
3795 // expression might be the expression from the switch, or an
3796 // expression that includes any potential conversions to
3798 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3800 switch (expr.Type.BuiltinType) {
3801 case BuiltinTypeSpec.Type.Byte:
3802 case BuiltinTypeSpec.Type.SByte:
3803 case BuiltinTypeSpec.Type.UShort:
3804 case BuiltinTypeSpec.Type.Short:
3805 case BuiltinTypeSpec.Type.UInt:
3806 case BuiltinTypeSpec.Type.Int:
3807 case BuiltinTypeSpec.Type.ULong:
3808 case BuiltinTypeSpec.Type.Long:
3809 case BuiltinTypeSpec.Type.Char:
3810 case BuiltinTypeSpec.Type.String:
3811 case BuiltinTypeSpec.Type.Bool:
3815 if (expr.Type.IsEnum)
3819 // Try to find a *user* defined implicit conversion.
3821 // If there is no implicit conversion, or if there are multiple
3822 // conversions, we have to report an error
3824 Expression converted = null;
3825 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3828 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3833 // Ignore over-worked ImplicitUserConversions that do
3834 // an implicit conversion in addition to the user conversion.
3836 if (!(e is UserCast))
3839 if (converted != null){
3840 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3849 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3851 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3867 // Performs the basic sanity checks on the switch statement
3868 // (looks for duplicate keys and non-constant expressions).
3870 // It also returns a hashtable with the keys that we will later
3871 // use to compute the switch tables
3873 bool ResolveLabels (ResolveContext ec, Constant value)
3876 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3877 string_labels = new Dictionary<string, SwitchLabel> ();
3879 labels = new Dictionary<long, SwitchLabel> ();
3882 case_labels = new List<SwitchLabel> ();
3883 int default_label_index = -1;
3884 bool constant_label_found = false;
3886 for (int i = 0; i < block.Statements.Count; ++i) {
3887 var s = block.Statements[i];
3889 var sl = s as SwitchLabel;
3894 case_labels.Add (sl);
3897 if (case_default != null) {
3898 sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3902 default_label_index = i;
3907 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3913 if (string_labels != null) {
3914 string string_value = sl.Converted.GetValue () as string;
3915 if (string_value == null)
3918 string_labels.Add (string_value, sl);
3920 if (sl.Converted is NullLiteral) {
3923 labels.Add (sl.Converted.GetValueAsLong (), sl);
3926 } catch (ArgumentException) {
3927 if (string_labels != null)
3928 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3930 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3935 if (value != null) {
3936 var constant_label = constant_label_found ? null : FindLabel (value);
3937 if (constant_label == null || constant_label != sl)
3938 block.Statements[i] = new EmptyStatement (s.loc);
3940 constant_label_found = true;
3944 if (value != null && constant_label_found && default_label_index >= 0)
3945 block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3951 // This method emits code for a lookup-based switch statement (non-string)
3952 // Basically it groups the cases into blocks that are at least half full,
3953 // and then spits out individual lookup opcodes for each block.
3954 // It emits the longest blocks first, and short blocks are just
3955 // handled with direct compares.
3957 void EmitTableSwitch (EmitContext ec, Expression val)
3959 if (labels != null && labels.Count > 0) {
3960 List<LabelsRange> ranges;
3961 if (string_labels != null) {
3962 // We have done all hard work for string already
3963 // setup single range only
3964 ranges = new List<LabelsRange> (1);
3965 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3967 var element_keys = new long[labels.Count];
3968 labels.Keys.CopyTo (element_keys, 0);
3969 Array.Sort (element_keys);
3972 // Build possible ranges of switch labes to reduce number
3975 ranges = new List<LabelsRange> (element_keys.Length);
3976 var range = new LabelsRange (element_keys[0]);
3978 for (int i = 1; i < element_keys.Length; ++i) {
3979 var l = element_keys[i];
3980 if (range.AddValue (l))
3983 range = new LabelsRange (l);
3987 // sort the blocks so we can tackle the largest ones first
3991 Label lbl_default = defaultLabel;
3992 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3994 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3995 LabelsRange kb = ranges[range_index];
3996 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
3998 // Optimize small ranges using simple equality check
3999 if (kb.Range <= 2) {
4000 foreach (var key in kb.label_values) {
4001 SwitchLabel sl = labels[key];
4002 if (sl == case_default || sl == case_null)
4005 if (sl.Converted.IsZeroInteger) {
4006 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4009 sl.Converted.Emit (ec);
4010 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4014 // TODO: if all the keys in the block are the same and there are
4015 // no gaps/defaults then just use a range-check.
4016 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4017 // TODO: optimize constant/I4 cases
4019 // check block range (could be > 2^31)
4021 ec.EmitLong (kb.min);
4022 ec.Emit (OpCodes.Blt, lbl_default);
4025 ec.EmitLong (kb.max);
4026 ec.Emit (OpCodes.Bgt, lbl_default);
4031 ec.EmitLong (kb.min);
4032 ec.Emit (OpCodes.Sub);
4035 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4039 int first = (int) kb.min;
4042 ec.Emit (OpCodes.Sub);
4043 } else if (first < 0) {
4044 ec.EmitInt (-first);
4045 ec.Emit (OpCodes.Add);
4049 // first, build the list of labels for the switch
4051 long cJumps = kb.Range;
4052 Label[] switch_labels = new Label[cJumps];
4053 for (int iJump = 0; iJump < cJumps; iJump++) {
4054 var key = kb.label_values[iKey];
4055 if (key == kb.min + iJump) {
4056 switch_labels[iJump] = labels[key].GetILLabel (ec);
4059 switch_labels[iJump] = lbl_default;
4063 // emit the switch opcode
4064 ec.Emit (OpCodes.Switch, switch_labels);
4067 // mark the default for this block
4068 if (range_index != 0)
4069 ec.MarkLabel (lbl_default);
4072 // the last default just goes to the end
4073 if (ranges.Count > 0)
4074 ec.Emit (OpCodes.Br, lbl_default);
4078 SwitchLabel FindLabel (Constant value)
4080 SwitchLabel sl = null;
4082 if (string_labels != null) {
4083 string s = value.GetValue () as string;
4085 if (case_null != null)
4087 else if (case_default != null)
4090 string_labels.TryGetValue (s, out sl);
4093 if (value is NullLiteral) {
4096 labels.TryGetValue (value.GetValueAsLong (), out sl);
4103 public override bool Resolve (BlockContext ec)
4105 Expr = Expr.Resolve (ec);
4109 new_expr = SwitchGoverningType (ec, Expr);
4111 if (new_expr == null && Expr.Type.IsNullableType) {
4112 unwrap = Nullable.Unwrap.Create (Expr, false);
4116 new_expr = SwitchGoverningType (ec, unwrap);
4119 if (new_expr == null){
4120 ec.Report.Error (151, loc,
4121 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4122 TypeManager.CSharpName (Expr.Type));
4127 SwitchType = new_expr.Type;
4129 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4130 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4134 if (block.Statements.Count == 0)
4137 var constant = new_expr as Constant;
4139 if (!ResolveLabels (ec, constant))
4143 // Don't need extra variable for constant switch or switch with
4144 // only default case
4146 if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4148 // Store switch expression for comparison purposes
4150 value = new_expr as VariableReference;
4151 if (value == null) {
4152 // Create temporary variable inside switch scope
4153 var current_block = ec.CurrentBlock;
4154 ec.CurrentBlock = Block;
4155 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4157 ec.CurrentBlock = current_block;
4161 Switch old_switch = ec.Switch;
4163 ec.Switch.SwitchType = SwitchType;
4165 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4167 ec.CurrentBranching.CurrentUsageVector.Goto ();
4169 var ok = block.Resolve (ec);
4171 if (case_default == null)
4172 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4174 ec.EndFlowBranching ();
4175 ec.Switch = old_switch;
4180 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4181 ResolveStringSwitchMap (ec);
4185 // Needed to emit anonymous storey initialization before
4186 // any generated switch dispatch
4188 block.AddScopeStatement (new DispatchStatement (this));
4193 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4195 var sl = FindLabel (value);
4198 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4205 // Converts string switch into string hashtable
4207 void ResolveStringSwitchMap (ResolveContext ec)
4209 FullNamedExpression string_dictionary_type;
4210 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4211 string_dictionary_type = new TypeExpression (
4212 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4213 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4215 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4216 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4218 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4222 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4223 Field field = new Field (ctype, string_dictionary_type,
4224 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4225 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4226 if (!field.Define ())
4228 ctype.AddField (field);
4230 var init = new List<Expression> ();
4232 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4233 string value = null;
4235 foreach (SwitchLabel sl in case_labels) {
4237 if (sl.SectionStart)
4238 labels.Add (++counter, sl);
4240 if (sl == case_default || sl == case_null)
4243 value = (string) sl.Converted.GetValue ();
4244 var init_args = new List<Expression> (2);
4245 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4247 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4248 init_args.Add (sl.Converted);
4250 init.Add (new CollectionElementInitializer (init_args, loc));
4253 Arguments args = new Arguments (1);
4254 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4255 Expression initializer = new NewInitialize (string_dictionary_type, args,
4256 new CollectionOrObjectInitializers (init, loc), loc);
4258 switch_cache_field = new FieldExpr (field, loc);
4259 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4262 void DoEmitStringSwitch (EmitContext ec)
4264 Label l_initialized = ec.DefineLabel ();
4267 // Skip initialization when value is null
4269 value.EmitBranchable (ec, nullLabel, false);
4272 // Check if string dictionary is initialized and initialize
4274 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4275 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4276 string_dictionary.EmitStatement (ec);
4278 ec.MarkLabel (l_initialized);
4280 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4282 ResolveContext rc = new ResolveContext (ec.MemberContext);
4284 if (switch_cache_field.Type.IsGeneric) {
4285 Arguments get_value_args = new Arguments (2);
4286 get_value_args.Add (new Argument (value));
4287 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4288 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4289 if (get_item == null)
4293 // A value was not found, go to default case
4295 get_item.EmitBranchable (ec, defaultLabel, false);
4297 Arguments get_value_args = new Arguments (1);
4298 get_value_args.Add (new Argument (value));
4300 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4301 if (get_item == null)
4304 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4305 get_item_object.EmitAssign (ec, get_item, true, false);
4306 ec.Emit (OpCodes.Brfalse, defaultLabel);
4308 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4309 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4311 get_item_int.EmitStatement (ec);
4312 get_item_object.Release (ec);
4315 EmitTableSwitch (ec, string_switch_variable);
4316 string_switch_variable.Release (ec);
4320 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4322 void EmitShortSwitch (EmitContext ec)
4324 MethodSpec equal_method = null;
4325 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4326 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4329 if (equal_method != null) {
4330 value.EmitBranchable (ec, nullLabel, false);
4333 for (int i = 0; i < case_labels.Count; ++i) {
4334 var label = case_labels [i];
4335 if (label == case_default || label == case_null)
4338 var constant = label.Converted;
4340 if (equal_method != null) {
4344 var call = new CallEmitter ();
4345 call.EmitPredefined (ec, equal_method, new Arguments (0));
4346 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4350 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4351 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4357 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4360 ec.Emit (OpCodes.Br, defaultLabel);
4363 void EmitDispatch (EmitContext ec)
4365 if (value == null) {
4367 // Constant switch, we already done the work
4373 // Mark sequence point explicitly to switch
4375 ec.Mark (block.StartLocation);
4376 block.IsCompilerGenerated = true;
4378 if (string_dictionary != null) {
4379 DoEmitStringSwitch (ec);
4380 } else if (case_labels.Count < 4 || string_labels != null) {
4381 EmitShortSwitch (ec);
4383 EmitTableSwitch (ec, value);
4387 protected override void DoEmit (EmitContext ec)
4389 // Workaround broken flow-analysis
4390 block.HasUnreachableClosingBrace = true;
4393 // Setup the codegen context
4395 Label old_end = ec.LoopEnd;
4396 Switch old_switch = ec.Switch;
4398 ec.LoopEnd = ec.DefineLabel ();
4401 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4402 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4404 if (value != null) {
4407 unwrap.EmitCheck (ec);
4408 ec.Emit (OpCodes.Brfalse, nullLabel);
4409 value.EmitAssign (ec, new_expr, false, false);
4410 } else if (new_expr != value) {
4411 value.EmitAssign (ec, new_expr, false, false);
4417 // Restore context state.
4418 ec.MarkLabel (ec.LoopEnd);
4421 // Restore the previous context
4423 ec.LoopEnd = old_end;
4424 ec.Switch = old_switch;
4427 protected override void CloneTo (CloneContext clonectx, Statement t)
4429 Switch target = (Switch) t;
4431 target.Expr = Expr.Clone (clonectx);
4432 target.block = (ExplicitBlock) block.Clone (clonectx);
4435 public override object Accept (StructuralVisitor visitor)
4437 return visitor.Visit (this);
4441 // A place where execution can restart in an iterator
4442 public abstract class ResumableStatement : Statement
4445 protected Label resume_point;
4447 public Label PrepareForEmit (EmitContext ec)
4451 resume_point = ec.DefineLabel ();
4453 return resume_point;
4456 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4461 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4466 public abstract class TryFinallyBlock : ExceptionStatement
4468 protected Statement stmt;
4469 Label dispose_try_block;
4470 bool prepared_for_dispose, emitted_dispose;
4471 Method finally_host;
4473 protected TryFinallyBlock (Statement stmt, Location loc)
4481 public Statement Statement {
4489 protected abstract void EmitTryBody (EmitContext ec);
4490 public abstract void EmitFinallyBody (EmitContext ec);
4492 public override Label PrepareForDispose (EmitContext ec, Label end)
4494 if (!prepared_for_dispose) {
4495 prepared_for_dispose = true;
4496 dispose_try_block = ec.DefineLabel ();
4498 return dispose_try_block;
4501 protected sealed override void DoEmit (EmitContext ec)
4503 EmitTryBodyPrepare (ec);
4506 ec.BeginFinallyBlock ();
4508 Label start_finally = ec.DefineLabel ();
4509 if (resume_points != null) {
4510 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4512 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4513 ec.Emit (OpCodes.Brfalse_S, start_finally);
4514 ec.Emit (OpCodes.Endfinally);
4517 ec.MarkLabel (start_finally);
4519 if (finally_host != null) {
4520 finally_host.Define ();
4521 finally_host.Emit ();
4523 // Now it's safe to add, to close it properly and emit sequence points
4524 finally_host.Parent.AddMember (finally_host);
4526 var ce = new CallEmitter ();
4527 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4528 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4530 EmitFinallyBody (ec);
4533 ec.EndExceptionBlock ();
4536 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4538 if (emitted_dispose)
4541 emitted_dispose = true;
4543 Label end_of_try = ec.DefineLabel ();
4545 // Ensure that the only way we can get into this code is through a dispatcher
4546 if (have_dispatcher)
4547 ec.Emit (OpCodes.Br, end);
4549 ec.BeginExceptionBlock ();
4551 ec.MarkLabel (dispose_try_block);
4553 Label[] labels = null;
4554 for (int i = 0; i < resume_points.Count; ++i) {
4555 ResumableStatement s = resume_points[i];
4556 Label ret = s.PrepareForDispose (ec, end_of_try);
4557 if (ret.Equals (end_of_try) && labels == null)
4559 if (labels == null) {
4560 labels = new Label[resume_points.Count];
4561 for (int j = 0; j < i; ++j)
4562 labels[j] = end_of_try;
4567 if (labels != null) {
4569 for (j = 1; j < labels.Length; ++j)
4570 if (!labels[0].Equals (labels[j]))
4572 bool emit_dispatcher = j < labels.Length;
4574 if (emit_dispatcher) {
4575 ec.Emit (OpCodes.Ldloc, pc);
4576 ec.EmitInt (first_resume_pc);
4577 ec.Emit (OpCodes.Sub);
4578 ec.Emit (OpCodes.Switch, labels);
4581 foreach (ResumableStatement s in resume_points)
4582 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4585 ec.MarkLabel (end_of_try);
4587 ec.BeginFinallyBlock ();
4589 if (finally_host != null) {
4590 var ce = new CallEmitter ();
4591 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4592 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4594 EmitFinallyBody (ec);
4597 ec.EndExceptionBlock ();
4600 public override bool Resolve (BlockContext bc)
4603 // Finally block inside iterator is called from MoveNext and
4604 // Dispose methods that means we need to lift the block into
4605 // newly created host method to emit the body only once. The
4606 // original block then simply calls the newly generated method.
4608 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4609 var b = stmt as Block;
4610 if (b != null && b.Explicit.HasYield) {
4611 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4615 return base.Resolve (bc);
4620 // Base class for blocks using exception handling
4622 public abstract class ExceptionStatement : ResumableStatement
4627 protected List<ResumableStatement> resume_points;
4628 protected int first_resume_pc;
4630 protected ExceptionStatement (Location loc)
4635 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4637 StateMachineInitializer state_machine = null;
4638 if (resume_points != null) {
4639 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4641 ec.EmitInt ((int) IteratorStorey.State.Running);
4642 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4645 ec.BeginExceptionBlock ();
4647 if (resume_points != null) {
4648 ec.MarkLabel (resume_point);
4650 // For normal control flow, we want to fall-through the Switch
4651 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4652 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4653 ec.EmitInt (first_resume_pc);
4654 ec.Emit (OpCodes.Sub);
4656 Label[] labels = new Label[resume_points.Count];
4657 for (int i = 0; i < resume_points.Count; ++i)
4658 labels[i] = resume_points[i].PrepareForEmit (ec);
4659 ec.Emit (OpCodes.Switch, labels);
4663 public void SomeCodeFollows ()
4666 code_follows = true;
4670 public override bool Resolve (BlockContext ec)
4673 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4674 // So, ensure there's some IL code after this statement.
4675 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4676 ec.NeedReturnLabel ();
4681 public void AddResumePoint (ResumableStatement stmt, int pc)
4683 if (resume_points == null) {
4684 resume_points = new List<ResumableStatement> ();
4685 first_resume_pc = pc;
4688 if (pc != first_resume_pc + resume_points.Count)
4689 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4691 resume_points.Add (stmt);
4696 public class Lock : TryFinallyBlock
4699 TemporaryVariableReference expr_copy;
4700 TemporaryVariableReference lock_taken;
4702 public Lock (Expression expr, Statement stmt, Location loc)
4708 public Expression Expr {
4714 public override bool Resolve (BlockContext ec)
4716 expr = expr.Resolve (ec);
4720 if (!TypeSpec.IsReferenceType (expr.Type)) {
4721 ec.Report.Error (185, loc,
4722 "`{0}' is not a reference type as required by the lock statement",
4723 expr.Type.GetSignatureForError ());
4726 if (expr.Type.IsGenericParameter) {
4727 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4730 VariableReference lv = expr as VariableReference;
4733 locked = lv.IsLockedByStatement;
4734 lv.IsLockedByStatement = true;
4741 // Have to keep original lock value around to unlock same location
4742 // in the case of original value has changed or is null
4744 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4745 expr_copy.Resolve (ec);
4748 // Ensure Monitor methods are available
4750 if (ResolvePredefinedMethods (ec) > 1) {
4751 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4752 lock_taken.Resolve (ec);
4755 using (ec.Set (ResolveContext.Options.LockScope)) {
4756 ec.StartFlowBranching (this);
4757 Statement.Resolve (ec);
4758 ec.EndFlowBranching ();
4762 lv.IsLockedByStatement = locked;
4770 protected override void EmitTryBodyPrepare (EmitContext ec)
4772 expr_copy.EmitAssign (ec, expr);
4774 if (lock_taken != null) {
4776 // Initialize ref variable
4778 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4781 // Monitor.Enter (expr_copy)
4783 expr_copy.Emit (ec);
4784 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4787 base.EmitTryBodyPrepare (ec);
4790 protected override void EmitTryBody (EmitContext ec)
4793 // Monitor.Enter (expr_copy, ref lock_taken)
4795 if (lock_taken != null) {
4796 expr_copy.Emit (ec);
4797 lock_taken.LocalInfo.CreateBuilder (ec);
4798 lock_taken.AddressOf (ec, AddressOp.Load);
4799 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4802 Statement.Emit (ec);
4805 public override void EmitFinallyBody (EmitContext ec)
4808 // if (lock_taken) Monitor.Exit (expr_copy)
4810 Label skip = ec.DefineLabel ();
4812 if (lock_taken != null) {
4813 lock_taken.Emit (ec);
4814 ec.Emit (OpCodes.Brfalse_S, skip);
4817 expr_copy.Emit (ec);
4818 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4820 ec.Emit (OpCodes.Call, m);
4822 ec.MarkLabel (skip);
4825 int ResolvePredefinedMethods (ResolveContext rc)
4827 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4828 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4832 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4836 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4840 protected override void CloneTo (CloneContext clonectx, Statement t)
4842 Lock target = (Lock) t;
4844 target.expr = expr.Clone (clonectx);
4845 target.stmt = Statement.Clone (clonectx);
4848 public override object Accept (StructuralVisitor visitor)
4850 return visitor.Visit (this);
4855 public class Unchecked : Statement {
4858 public Unchecked (Block b, Location loc)
4865 public override bool Resolve (BlockContext ec)
4867 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4868 return Block.Resolve (ec);
4871 protected override void DoEmit (EmitContext ec)
4873 using (ec.With (EmitContext.Options.CheckedScope, false))
4877 protected override void CloneTo (CloneContext clonectx, Statement t)
4879 Unchecked target = (Unchecked) t;
4881 target.Block = clonectx.LookupBlock (Block);
4884 public override object Accept (StructuralVisitor visitor)
4886 return visitor.Visit (this);
4890 public class Checked : Statement {
4893 public Checked (Block b, Location loc)
4896 b.Unchecked = false;
4900 public override bool Resolve (BlockContext ec)
4902 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4903 return Block.Resolve (ec);
4906 protected override void DoEmit (EmitContext ec)
4908 using (ec.With (EmitContext.Options.CheckedScope, true))
4912 protected override void CloneTo (CloneContext clonectx, Statement t)
4914 Checked target = (Checked) t;
4916 target.Block = clonectx.LookupBlock (Block);
4919 public override object Accept (StructuralVisitor visitor)
4921 return visitor.Visit (this);
4925 public class Unsafe : Statement {
4928 public Unsafe (Block b, Location loc)
4931 Block.Unsafe = true;
4935 public override bool Resolve (BlockContext ec)
4937 if (ec.CurrentIterator != null)
4938 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4940 using (ec.Set (ResolveContext.Options.UnsafeScope))
4941 return Block.Resolve (ec);
4944 protected override void DoEmit (EmitContext ec)
4949 protected override void CloneTo (CloneContext clonectx, Statement t)
4951 Unsafe target = (Unsafe) t;
4953 target.Block = clonectx.LookupBlock (Block);
4956 public override object Accept (StructuralVisitor visitor)
4958 return visitor.Visit (this);
4965 public class Fixed : Statement
4967 abstract class Emitter : ShimExpression
4969 protected LocalVariable vi;
4971 protected Emitter (Expression expr, LocalVariable li)
4977 public abstract void EmitExit (EmitContext ec);
4980 class ExpressionEmitter : Emitter {
4981 public ExpressionEmitter (Expression converted, LocalVariable li) :
4982 base (converted, li)
4986 protected override Expression DoResolve (ResolveContext rc)
4988 throw new NotImplementedException ();
4991 public override void Emit (EmitContext ec) {
4993 // Store pointer in pinned location
4999 public override void EmitExit (EmitContext ec)
5002 ec.Emit (OpCodes.Conv_U);
5007 class StringEmitter : Emitter
5009 LocalVariable pinned_string;
5011 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5016 protected override Expression DoResolve (ResolveContext rc)
5018 pinned_string = new LocalVariable (vi.Block, "$pinned",
5019 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5021 pinned_string.Type = rc.BuiltinTypes.String;
5023 eclass = ExprClass.Variable;
5024 type = rc.BuiltinTypes.Int;
5028 public override void Emit (EmitContext ec)
5030 pinned_string.CreateBuilder (ec);
5033 pinned_string.EmitAssign (ec);
5035 // TODO: Should use Binary::Add
5036 pinned_string.Emit (ec);
5037 ec.Emit (OpCodes.Conv_I);
5039 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5043 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5044 //pe.InstanceExpression = pinned_string;
5045 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5047 ec.Emit (OpCodes.Add);
5051 public override void EmitExit (EmitContext ec)
5054 pinned_string.EmitAssign (ec);
5058 public class VariableDeclaration : BlockVariableDeclaration
5060 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5065 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5067 if (!Variable.Type.IsPointer && li == Variable) {
5068 bc.Report.Error (209, TypeExpression.Location,
5069 "The type of locals declared in a fixed statement must be a pointer type");
5074 // The rules for the possible declarators are pretty wise,
5075 // but the production on the grammar is more concise.
5077 // So we have to enforce these rules here.
5079 // We do not resolve before doing the case 1 test,
5080 // because the grammar is explicit in that the token &
5081 // is present, so we need to test for this particular case.
5084 if (initializer is Cast) {
5085 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5089 initializer = initializer.Resolve (bc);
5091 if (initializer == null)
5097 if (initializer.Type.IsArray) {
5098 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5101 // Provided that array_type is unmanaged,
5103 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5107 // and T* is implicitly convertible to the
5108 // pointer type given in the fixed statement.
5110 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5112 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5113 if (converted == null)
5117 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5119 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5120 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5121 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5122 new NullLiteral (loc),
5125 converted = converted.Resolve (bc);
5127 return new ExpressionEmitter (converted, li);
5133 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5134 return new StringEmitter (initializer, li, loc).Resolve (bc);
5137 // Case 3: fixed buffer
5138 if (initializer is FixedBufferPtr) {
5139 return new ExpressionEmitter (initializer, li);
5143 // Case 4: & object.
5145 bool already_fixed = true;
5146 Unary u = initializer as Unary;
5147 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5148 IVariableReference vr = u.Expr as IVariableReference;
5149 if (vr == null || !vr.IsFixed) {
5150 already_fixed = false;
5154 if (already_fixed) {
5155 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5158 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5159 return new ExpressionEmitter (initializer, li);
5164 VariableDeclaration decl;
5165 Statement statement;
5168 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5177 public Statement Statement {
5183 public BlockVariableDeclaration Variables {
5191 public override bool Resolve (BlockContext ec)
5193 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5194 if (!decl.Resolve (ec))
5198 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5199 bool ok = statement.Resolve (ec);
5200 bool flow_unreachable = ec.EndFlowBranching ();
5201 has_ret = flow_unreachable;
5206 protected override void DoEmit (EmitContext ec)
5208 decl.Variable.CreateBuilder (ec);
5209 decl.Initializer.Emit (ec);
5210 if (decl.Declarators != null) {
5211 foreach (var d in decl.Declarators) {
5212 d.Variable.CreateBuilder (ec);
5213 d.Initializer.Emit (ec);
5217 statement.Emit (ec);
5223 // Clear the pinned variable
5225 ((Emitter) decl.Initializer).EmitExit (ec);
5226 if (decl.Declarators != null) {
5227 foreach (var d in decl.Declarators) {
5228 ((Emitter)d.Initializer).EmitExit (ec);
5233 protected override void CloneTo (CloneContext clonectx, Statement t)
5235 Fixed target = (Fixed) t;
5237 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5238 target.statement = statement.Clone (clonectx);
5241 public override object Accept (StructuralVisitor visitor)
5243 return visitor.Visit (this);
5247 public class Catch : Statement
5251 FullNamedExpression type_expr;
5252 CompilerAssign assign;
5255 public Catch (Block block, Location loc)
5263 public Block Block {
5269 public TypeSpec CatchType {
5275 public bool IsGeneral {
5277 return type_expr == null;
5281 public FullNamedExpression TypeExpression {
5290 public LocalVariable Variable {
5301 protected override void DoEmit (EmitContext ec)
5304 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5306 ec.BeginCatchBlock (CatchType);
5309 li.CreateBuilder (ec);
5312 // Special case hoisted catch variable, we have to use a temporary variable
5313 // to pass via anonymous storey initialization with the value still on top
5316 if (li.HoistedVariant != null) {
5317 LocalTemporary lt = new LocalTemporary (li.Type);
5320 // switch to assigning from the temporary variable and not from top of the stack
5321 assign.UpdateSource (lt);
5324 ec.Emit (OpCodes.Pop);
5330 public override bool Resolve (BlockContext ec)
5332 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5333 if (type_expr != null) {
5334 type = type_expr.ResolveAsType (ec);
5338 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5339 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5340 } else if (li != null) {
5342 li.PrepareForFlowAnalysis (ec);
5344 // source variable is at the top of the stack
5345 Expression source = new EmptyExpression (li.Type);
5346 if (li.Type.IsGenericParameter)
5347 source = new UnboxCast (source, li.Type);
5350 // Uses Location.Null to hide from symbol file
5352 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5353 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5357 return Block.Resolve (ec);
5361 protected override void CloneTo (CloneContext clonectx, Statement t)
5363 Catch target = (Catch) t;
5365 if (type_expr != null)
5366 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5368 target.block = clonectx.LookupBlock (block);
5372 public class TryFinally : TryFinallyBlock
5376 public TryFinally (Statement stmt, Block fini, Location loc)
5382 public Block Finallyblock {
5388 public override bool Resolve (BlockContext ec)
5392 ec.StartFlowBranching (this);
5394 if (!stmt.Resolve (ec))
5398 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5400 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5401 if (!fini.Resolve (ec))
5405 ec.EndFlowBranching ();
5407 ok &= base.Resolve (ec);
5412 protected override void EmitTryBody (EmitContext ec)
5417 public override void EmitFinallyBody (EmitContext ec)
5422 protected override void CloneTo (CloneContext clonectx, Statement t)
5424 TryFinally target = (TryFinally) t;
5426 target.stmt = (Statement) stmt.Clone (clonectx);
5428 target.fini = clonectx.LookupBlock (fini);
5431 public override object Accept (StructuralVisitor visitor)
5433 return visitor.Visit (this);
5437 public class TryCatch : ExceptionStatement
5440 List<Catch> clauses;
5441 readonly bool inside_try_finally;
5443 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5447 this.clauses = catch_clauses;
5448 this.inside_try_finally = inside_try_finally;
5451 public List<Catch> Clauses {
5457 public bool IsTryCatchFinally {
5459 return inside_try_finally;
5463 public override bool Resolve (BlockContext ec)
5467 ec.StartFlowBranching (this);
5469 if (!Block.Resolve (ec))
5472 for (int i = 0; i < clauses.Count; ++i) {
5474 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5476 if (!c.Resolve (ec)) {
5481 TypeSpec resolved_type = c.CatchType;
5482 for (int ii = 0; ii < clauses.Count; ++ii) {
5486 if (clauses[ii].IsGeneral) {
5487 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5490 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5493 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5496 ec.Report.Warning (1058, 1, c.loc,
5497 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5505 var ct = clauses[ii].CatchType;
5509 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5510 ec.Report.Error (160, c.loc,
5511 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5512 ct.GetSignatureForError ());
5518 ec.EndFlowBranching ();
5520 return base.Resolve (ec) && ok;
5523 protected sealed override void DoEmit (EmitContext ec)
5525 if (!inside_try_finally)
5526 EmitTryBodyPrepare (ec);
5530 foreach (Catch c in clauses)
5533 if (!inside_try_finally)
5534 ec.EndExceptionBlock ();
5537 protected override void CloneTo (CloneContext clonectx, Statement t)
5539 TryCatch target = (TryCatch) t;
5541 target.Block = clonectx.LookupBlock (Block);
5542 if (clauses != null){
5543 target.clauses = new List<Catch> ();
5544 foreach (Catch c in clauses)
5545 target.clauses.Add ((Catch) c.Clone (clonectx));
5549 public override object Accept (StructuralVisitor visitor)
5551 return visitor.Visit (this);
5555 public class Using : TryFinallyBlock
5557 public class VariableDeclaration : BlockVariableDeclaration
5559 Statement dispose_call;
5561 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5566 public VariableDeclaration (LocalVariable li, Location loc)
5572 public VariableDeclaration (Expression expr)
5575 loc = expr.Location;
5581 public bool IsNested { get; private set; }
5585 public void EmitDispose (EmitContext ec)
5587 dispose_call.Emit (ec);
5590 public override bool Resolve (BlockContext bc)
5595 return base.Resolve (bc, false);
5598 public Expression ResolveExpression (BlockContext bc)
5600 var e = Initializer.Resolve (bc);
5604 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5605 Initializer = ResolveInitializer (bc, Variable, e);
5609 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5611 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5612 initializer = initializer.Resolve (bc);
5613 if (initializer == null)
5616 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5617 Arguments args = new Arguments (1);
5618 args.Add (new Argument (initializer));
5619 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5620 if (initializer == null)
5623 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5624 dispose_call = CreateDisposeCall (bc, var);
5625 dispose_call.Resolve (bc);
5627 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5630 if (li == Variable) {
5631 CheckIDiposableConversion (bc, li, initializer);
5632 dispose_call = CreateDisposeCall (bc, li);
5633 dispose_call.Resolve (bc);
5636 return base.ResolveInitializer (bc, li, initializer);
5639 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5643 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5644 if (type.IsNullableType) {
5645 // it's handled in CreateDisposeCall
5649 bc.Report.SymbolRelatedToPreviousError (type);
5650 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5651 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5652 type.GetSignatureForError ());
5658 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5660 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5662 var loc = lv.Location;
5664 var idt = bc.BuiltinTypes.IDisposable;
5665 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5667 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5668 dispose_mg.InstanceExpression = type.IsNullableType ?
5669 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5673 // Hide it from symbol file via null location
5675 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5677 // Add conditional call when disposing possible null variable
5678 if (!type.IsStruct || type.IsNullableType)
5679 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5684 public void ResolveDeclaratorInitializer (BlockContext bc)
5686 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5689 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5691 for (int i = declarators.Count - 1; i >= 0; --i) {
5692 var d = declarators [i];
5693 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5694 vd.Initializer = d.Initializer;
5696 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5697 vd.dispose_call.Resolve (bc);
5699 stmt = new Using (vd, stmt, d.Variable.Location);
5706 public override object Accept (StructuralVisitor visitor)
5708 return visitor.Visit (this);
5712 VariableDeclaration decl;
5714 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5720 public Using (Expression expr, Statement stmt, Location loc)
5723 this.decl = new VariableDeclaration (expr);
5728 public Expression Expr {
5730 return decl.Variable == null ? decl.Initializer : null;
5734 public BlockVariableDeclaration Variables {
5742 public override void Emit (EmitContext ec)
5745 // Don't emit sequence point it will be set on variable declaration
5750 protected override void EmitTryBodyPrepare (EmitContext ec)
5753 base.EmitTryBodyPrepare (ec);
5756 protected override void EmitTryBody (EmitContext ec)
5761 public override void EmitFinallyBody (EmitContext ec)
5763 decl.EmitDispose (ec);
5766 public override bool Resolve (BlockContext ec)
5768 VariableReference vr;
5769 bool vr_locked = false;
5771 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5772 if (decl.Variable == null) {
5773 vr = decl.ResolveExpression (ec) as VariableReference;
5775 vr_locked = vr.IsLockedByStatement;
5776 vr.IsLockedByStatement = true;
5779 if (decl.IsNested) {
5780 decl.ResolveDeclaratorInitializer (ec);
5782 if (!decl.Resolve (ec))
5785 if (decl.Declarators != null) {
5786 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5794 ec.StartFlowBranching (this);
5798 ec.EndFlowBranching ();
5801 vr.IsLockedByStatement = vr_locked;
5808 protected override void CloneTo (CloneContext clonectx, Statement t)
5810 Using target = (Using) t;
5812 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5813 target.stmt = stmt.Clone (clonectx);
5816 public override object Accept (StructuralVisitor visitor)
5818 return visitor.Visit (this);
5823 /// Implementation of the foreach C# statement
5825 public class Foreach : Statement
5827 abstract class IteratorStatement : Statement
5829 protected readonly Foreach for_each;
5831 protected IteratorStatement (Foreach @foreach)
5833 this.for_each = @foreach;
5834 this.loc = @foreach.expr.Location;
5837 protected override void CloneTo (CloneContext clonectx, Statement target)
5839 throw new NotImplementedException ();
5842 public override void Emit (EmitContext ec)
5844 if (ec.EmitAccurateDebugInfo) {
5845 ec.Emit (OpCodes.Nop);
5852 sealed class ArrayForeach : IteratorStatement
5854 TemporaryVariableReference[] lengths;
5855 Expression [] length_exprs;
5856 StatementExpression[] counter;
5857 TemporaryVariableReference[] variables;
5859 TemporaryVariableReference copy;
5861 public ArrayForeach (Foreach @foreach, int rank)
5864 counter = new StatementExpression[rank];
5865 variables = new TemporaryVariableReference[rank];
5866 length_exprs = new Expression [rank];
5869 // Only use temporary length variables when dealing with
5870 // multi-dimensional arrays
5873 lengths = new TemporaryVariableReference [rank];
5876 public override bool Resolve (BlockContext ec)
5878 Block variables_block = for_each.variable.Block;
5879 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5882 int rank = length_exprs.Length;
5883 Arguments list = new Arguments (rank);
5884 for (int i = 0; i < rank; i++) {
5885 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5887 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5888 counter[i].Resolve (ec);
5891 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5893 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5894 lengths[i].Resolve (ec);
5896 Arguments args = new Arguments (1);
5897 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5898 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5901 list.Add (new Argument (v));
5904 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5909 if (for_each.type is VarExpr) {
5910 // Infer implicitly typed local variable from foreach array type
5911 var_type = access.Type;
5913 var_type = for_each.type.ResolveAsType (ec);
5915 if (var_type == null)
5918 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5923 for_each.variable.Type = var_type;
5925 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5926 if (variable_ref == null)
5929 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5933 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5934 ec.CurrentBranching.CreateSibling ();
5936 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5937 if (!for_each.body.Resolve (ec))
5939 ec.EndFlowBranching ();
5941 // There's no direct control flow from the end of the embedded statement to the end of the loop
5942 ec.CurrentBranching.CurrentUsageVector.Goto ();
5944 ec.EndFlowBranching ();
5949 protected override void DoEmit (EmitContext ec)
5951 copy.EmitAssign (ec, for_each.expr);
5953 int rank = length_exprs.Length;
5954 Label[] test = new Label [rank];
5955 Label[] loop = new Label [rank];
5957 for (int i = 0; i < rank; i++) {
5958 test [i] = ec.DefineLabel ();
5959 loop [i] = ec.DefineLabel ();
5961 if (lengths != null)
5962 lengths [i].EmitAssign (ec, length_exprs [i]);
5965 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5966 for (int i = 0; i < rank; i++) {
5967 variables [i].EmitAssign (ec, zero);
5969 ec.Emit (OpCodes.Br, test [i]);
5970 ec.MarkLabel (loop [i]);
5973 for_each.body.Emit (ec);
5975 ec.MarkLabel (ec.LoopBegin);
5976 ec.Mark (for_each.expr.Location);
5978 for (int i = rank - 1; i >= 0; i--){
5979 counter [i].Emit (ec);
5981 ec.MarkLabel (test [i]);
5982 variables [i].Emit (ec);
5984 if (lengths != null)
5985 lengths [i].Emit (ec);
5987 length_exprs [i].Emit (ec);
5989 ec.Emit (OpCodes.Blt, loop [i]);
5992 ec.MarkLabel (ec.LoopEnd);
5996 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
5998 class RuntimeDispose : Using.VariableDeclaration
6000 public RuntimeDispose (LocalVariable lv, Location loc)
6005 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6007 // Defered to runtime check
6010 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6012 var idt = bc.BuiltinTypes.IDisposable;
6015 // Fabricates code like
6017 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6020 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6022 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6023 dispose_variable.CreateReferenceExpression (bc, loc),
6024 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6025 loc), new NullLiteral (loc));
6027 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6029 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6030 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6032 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6033 return new If (idisaposable_test, dispose, loc);
6037 LocalVariable variable;
6039 Statement statement;
6040 ExpressionStatement init;
6041 TemporaryVariableReference enumerator_variable;
6042 bool ambiguous_getenumerator_name;
6044 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6047 this.variable = var;
6051 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6053 rc.Report.SymbolRelatedToPreviousError (enumerator);
6054 rc.Report.Error (202, loc,
6055 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6056 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6059 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6062 // Option 1: Try to match by name GetEnumerator first
6064 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6065 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6067 var mg = mexpr as MethodGroupExpr;
6069 mg.InstanceExpression = expr;
6070 Arguments args = new Arguments (0);
6071 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6073 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6074 if (ambiguous_getenumerator_name)
6077 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6083 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6086 PredefinedMember<MethodSpec> iface_candidate = null;
6087 var ptypes = rc.Module.PredefinedTypes;
6088 var gen_ienumerable = ptypes.IEnumerableGeneric;
6089 if (!gen_ienumerable.Define ())
6090 gen_ienumerable = null;
6092 var ifaces = t.Interfaces;
6093 if (ifaces != null) {
6094 foreach (var iface in ifaces) {
6095 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6096 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6097 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6098 rc.Report.Error (1640, loc,
6099 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6100 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6105 // TODO: Cache this somehow
6106 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6107 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6112 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6113 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6118 if (iface_candidate == null) {
6119 if (expr.Type != InternalType.ErrorType) {
6120 rc.Report.Error (1579, loc,
6121 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6122 expr.Type.GetSignatureForError (), "GetEnumerator");
6128 var method = iface_candidate.Resolve (loc);
6132 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6133 mg.InstanceExpression = expr;
6137 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6139 var ms = MemberCache.FindMember (enumerator.ReturnType,
6140 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6141 BindingRestriction.InstanceOnly) as MethodSpec;
6143 if (ms == null || !ms.IsPublic) {
6144 Error_WrongEnumerator (rc, enumerator);
6148 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6151 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6153 var ps = MemberCache.FindMember (enumerator.ReturnType,
6154 MemberFilter.Property ("Current", null),
6155 BindingRestriction.InstanceOnly) as PropertySpec;
6157 if (ps == null || !ps.IsPublic) {
6158 Error_WrongEnumerator (rc, enumerator);
6165 public override bool Resolve (BlockContext ec)
6167 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6170 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6171 } else if (expr.Type.IsNullableType) {
6172 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6175 var get_enumerator_mg = ResolveGetEnumerator (ec);
6176 if (get_enumerator_mg == null) {
6180 var get_enumerator = get_enumerator_mg.BestCandidate;
6181 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6182 enumerator_variable.Resolve (ec);
6184 // Prepare bool MoveNext ()
6185 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6186 if (move_next_mg == null) {
6190 move_next_mg.InstanceExpression = enumerator_variable;
6192 // Prepare ~T~ Current { get; }
6193 var current_prop = ResolveCurrent (ec, get_enumerator);
6194 if (current_prop == null) {
6198 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6199 if (current_pe == null)
6202 VarExpr ve = for_each.type as VarExpr;
6206 // Source type is dynamic, set element type to dynamic too
6207 variable.Type = ec.BuiltinTypes.Dynamic;
6209 // Infer implicitly typed local variable from foreach enumerable type
6210 variable.Type = current_pe.Type;
6214 // Explicit cast of dynamic collection elements has to be done at runtime
6215 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6218 variable.Type = for_each.type.ResolveAsType (ec);
6220 if (variable.Type == null)
6223 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6224 if (current_pe == null)
6228 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6229 if (variable_ref == null)
6232 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6234 var init = new Invocation (get_enumerator_mg, null);
6236 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6237 for_each.body, Location.Null);
6239 var enum_type = enumerator_variable.Type;
6242 // Add Dispose method call when enumerator can be IDisposable
6244 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6245 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6247 // Runtime Dispose check
6249 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6250 vd.Initializer = init;
6251 statement = new Using (vd, statement, Location.Null);
6254 // No Dispose call needed
6256 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6257 this.init.Resolve (ec);
6261 // Static Dispose check
6263 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6264 vd.Initializer = init;
6265 statement = new Using (vd, statement, Location.Null);
6268 return statement.Resolve (ec);
6271 protected override void DoEmit (EmitContext ec)
6273 enumerator_variable.LocalInfo.CreateBuilder (ec);
6276 init.EmitStatement (ec);
6278 statement.Emit (ec);
6281 #region IErrorHandler Members
6283 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6285 ec.Report.SymbolRelatedToPreviousError (best);
6286 ec.Report.Warning (278, 2, expr.Location,
6287 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6288 expr.Type.GetSignatureForError (), "enumerable",
6289 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6291 ambiguous_getenumerator_name = true;
6295 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6300 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6305 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6314 LocalVariable variable;
6316 Statement statement;
6319 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6322 this.variable = var;
6324 this.statement = stmt;
6329 public Expression Expr {
6330 get { return expr; }
6333 public Statement Statement {
6334 get { return statement; }
6337 public Expression TypeExpression {
6338 get { return type; }
6341 public LocalVariable Variable {
6342 get { return variable; }
6345 public override bool Resolve (BlockContext ec)
6347 expr = expr.Resolve (ec);
6352 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6356 body.AddStatement (statement);
6358 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6359 statement = new ArrayForeach (this, 1);
6360 } else if (expr.Type is ArrayContainer) {
6361 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6363 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6364 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6365 expr.ExprClassName);
6369 statement = new CollectionForeach (this, variable, expr);
6372 return statement.Resolve (ec);
6375 protected override void DoEmit (EmitContext ec)
6377 variable.CreateBuilder (ec);
6379 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6380 ec.LoopBegin = ec.DefineLabel ();
6381 ec.LoopEnd = ec.DefineLabel ();
6383 statement.Emit (ec);
6385 ec.LoopBegin = old_begin;
6386 ec.LoopEnd = old_end;
6389 protected override void CloneTo (CloneContext clonectx, Statement t)
6391 Foreach target = (Foreach) t;
6393 target.type = type.Clone (clonectx);
6394 target.expr = expr.Clone (clonectx);
6395 target.body = (Block) body.Clone (clonectx);
6396 target.statement = statement.Clone (clonectx);
6399 public override object Accept (StructuralVisitor visitor)
6401 return visitor.Visit (this);