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
2065 public Block Parent;
2066 public Location StartLocation;
2067 public Location EndLocation;
2069 public ExplicitBlock Explicit;
2070 public ParametersBlock ParametersBlock;
2072 protected Flags flags;
2075 // The statements in this block
2077 protected List<Statement> statements;
2079 protected List<Statement> scope_initializers;
2081 int? resolving_init_idx;
2087 public int ID = id++;
2089 static int clone_id_counter;
2093 // int assignable_slots;
2095 public Block (Block parent, Location start, Location end)
2096 : this (parent, 0, start, end)
2100 public Block (Block parent, Flags flags, Location start, Location end)
2102 if (parent != null) {
2103 // the appropriate constructors will fixup these fields
2104 ParametersBlock = parent.ParametersBlock;
2105 Explicit = parent.Explicit;
2108 this.Parent = parent;
2110 this.StartLocation = start;
2111 this.EndLocation = end;
2113 statements = new List<Statement> (4);
2115 this.original = this;
2120 public bool HasUnreachableClosingBrace {
2122 return (flags & Flags.HasRet) != 0;
2125 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2129 public Block Original {
2138 public bool IsCompilerGenerated {
2139 get { return (flags & Flags.CompilerGenerated) != 0; }
2140 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2143 public bool Unchecked {
2144 get { return (flags & Flags.Unchecked) != 0; }
2145 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2148 public bool Unsafe {
2149 get { return (flags & Flags.Unsafe) != 0; }
2150 set { flags |= Flags.Unsafe; }
2153 public List<Statement> Statements {
2154 get { return statements; }
2159 public void SetEndLocation (Location loc)
2164 public void AddLabel (LabeledStatement target)
2166 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2169 public void AddLocalName (LocalVariable li)
2171 AddLocalName (li.Name, li);
2174 public void AddLocalName (string name, INamedBlockVariable li)
2176 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2179 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2181 if (reason == null) {
2182 Error_AlreadyDeclared (name, variable);
2186 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2187 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2188 "to `{0}', which is already used in a `{1}' scope to denote something else",
2192 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2194 var pi = variable as ParametersBlock.ParameterInfo;
2196 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2198 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2199 "A local variable named `{0}' is already defined in this scope", name);
2203 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2205 ParametersBlock.TopBlock.Report.Error (412, loc,
2206 "The type parameter name `{0}' is the same as local variable or parameter name",
2211 // It should be used by expressions which require to
2212 // register a statement during resolve process.
2214 public void AddScopeStatement (Statement s)
2216 if (scope_initializers == null)
2217 scope_initializers = new List<Statement> ();
2220 // Simple recursive helper, when resolve scope initializer another
2221 // new scope initializer can be added, this ensures it's initialized
2222 // before existing one. For now this can happen with expression trees
2223 // in base ctor initializer only
2225 if (resolving_init_idx.HasValue) {
2226 scope_initializers.Insert (resolving_init_idx.Value, s);
2227 ++resolving_init_idx;
2229 scope_initializers.Add (s);
2233 public void AddStatement (Statement s)
2238 public int AssignableSlots {
2240 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2242 // return assignable_slots;
2246 public LabeledStatement LookupLabel (string name)
2248 return ParametersBlock.TopBlock.GetLabel (name, this);
2251 public override bool Resolve (BlockContext ec)
2253 if ((flags & Flags.Resolved) != 0)
2256 Block prev_block = ec.CurrentBlock;
2258 bool unreachable = ec.IsUnreachable;
2259 bool prev_unreachable = unreachable;
2261 ec.CurrentBlock = this;
2262 ec.StartFlowBranching (this);
2265 // Compiler generated scope statements
2267 if (scope_initializers != null) {
2268 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2269 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2272 resolving_init_idx = null;
2276 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2277 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2278 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2279 // responsible for handling the situation.
2281 int statement_count = statements.Count;
2282 for (int ix = 0; ix < statement_count; ix++){
2283 Statement s = statements [ix];
2286 // Warn if we detect unreachable code.
2289 if (s is EmptyStatement)
2292 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2293 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2294 ec.UnreachableReported = true;
2299 // Note that we're not using ResolveUnreachable() for unreachable
2300 // statements here. ResolveUnreachable() creates a temporary
2301 // flow branching and kills it afterwards. This leads to problems
2302 // if you have two unreachable statements where the first one
2303 // assigns a variable and the second one tries to access it.
2306 if (!s.Resolve (ec)) {
2308 if (!ec.IsInProbingMode)
2309 statements [ix] = new EmptyStatement (s.loc);
2314 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2315 statements [ix] = new EmptyStatement (s.loc);
2317 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2319 ec.IsUnreachable = true;
2320 } else if (ec.IsUnreachable)
2321 ec.IsUnreachable = false;
2324 if (unreachable != prev_unreachable) {
2325 ec.IsUnreachable = prev_unreachable;
2326 ec.UnreachableReported = false;
2329 while (ec.CurrentBranching is FlowBranchingLabeled)
2330 ec.EndFlowBranching ();
2332 bool flow_unreachable = ec.EndFlowBranching ();
2334 ec.CurrentBlock = prev_block;
2336 if (flow_unreachable)
2337 flags |= Flags.HasRet;
2339 // If we're a non-static `struct' constructor which doesn't have an
2340 // initializer, then we must initialize all of the struct's fields.
2341 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2344 flags |= Flags.Resolved;
2348 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2350 bool unreachable = false;
2351 if (warn && !ec.UnreachableReported) {
2352 ec.UnreachableReported = true;
2354 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2357 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2358 fb.CurrentUsageVector.IsUnreachable = true;
2359 bool ok = Resolve (ec);
2360 ec.KillFlowBranching ();
2363 ec.UnreachableReported = false;
2368 protected override void DoEmit (EmitContext ec)
2370 for (int ix = 0; ix < statements.Count; ix++){
2371 statements [ix].Emit (ec);
2375 public override void Emit (EmitContext ec)
2377 if (scope_initializers != null)
2378 EmitScopeInitializers (ec);
2383 protected void EmitScopeInitializers (EmitContext ec)
2385 foreach (Statement s in scope_initializers)
2390 public override string ToString ()
2392 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2396 protected override void CloneTo (CloneContext clonectx, Statement t)
2398 Block target = (Block) t;
2400 target.clone_id = clone_id_counter++;
2403 clonectx.AddBlockMap (this, target);
2404 if (original != this)
2405 clonectx.AddBlockMap (original, target);
2407 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2408 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2411 target.Parent = clonectx.RemapBlockCopy (Parent);
2413 target.statements = new List<Statement> (statements.Count);
2414 foreach (Statement s in statements)
2415 target.statements.Add (s.Clone (clonectx));
2418 public override object Accept (StructuralVisitor visitor)
2420 return visitor.Visit (this);
2424 public class ExplicitBlock : Block
2426 protected AnonymousMethodStorey am_storey;
2428 public ExplicitBlock (Block parent, Location start, Location end)
2429 : this (parent, (Flags) 0, start, end)
2433 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2434 : base (parent, flags, start, end)
2436 this.Explicit = this;
2441 public AnonymousMethodStorey AnonymousMethodStorey {
2447 public bool HasAwait {
2449 return (flags & Flags.AwaitBlock) != 0;
2453 public bool HasCapturedThis {
2455 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2458 return (flags & Flags.HasCapturedThis) != 0;
2463 // Used to indicate that the block has reference to parent
2464 // block and cannot be made static when defining anonymous method
2466 public bool HasCapturedVariable {
2468 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2471 return (flags & Flags.HasCapturedVariable) != 0;
2475 public bool HasYield {
2477 return (flags & Flags.YieldBlock) != 0;
2484 // Creates anonymous method storey in current block
2486 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2489 // Return same story for iterator and async blocks unless we are
2490 // in nested anonymous method
2492 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2493 return ec.CurrentAnonymousMethod.Storey;
2495 if (am_storey == null) {
2496 MemberBase mc = ec.MemberContext as MemberBase;
2499 // Creates anonymous method storey for this block
2501 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2507 public override void Emit (EmitContext ec)
2509 if (am_storey != null) {
2510 DefineStoreyContainer (ec, am_storey);
2511 am_storey.EmitStoreyInstantiation (ec, this);
2514 if (scope_initializers != null)
2515 EmitScopeInitializers (ec);
2517 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2518 ec.Emit (OpCodes.Nop);
2529 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2530 ec.Emit (OpCodes.Nop);
2534 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2536 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2537 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2538 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2542 // Creates anonymous method storey
2544 storey.CreateContainer ();
2545 storey.DefineContainer ();
2547 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2550 // Only first storey in path will hold this reference. All children blocks will
2551 // reference it indirectly using $ref field
2553 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2554 if (b.Parent != null) {
2555 var s = b.Parent.Explicit.AnonymousMethodStorey;
2557 storey.HoistedThis = s.HoistedThis;
2562 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2563 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2565 if (storey.HoistedThis != null)
2571 // We are the first storey on path and 'this' has to be hoisted
2573 if (storey.HoistedThis == null) {
2574 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2576 // ThisReferencesFromChildrenBlock holds all reference even if they
2577 // are not on this path. It saves some memory otherwise it'd have to
2578 // be in every explicit block. We run this check to see if the reference
2579 // is valid for this storey
2581 Block block_on_path = ref_block;
2582 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2584 if (block_on_path == null)
2587 if (storey.HoistedThis == null) {
2588 storey.AddCapturedThisField (ec);
2591 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2592 if (b.AnonymousMethodStorey != null) {
2593 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2594 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2597 // Stop propagation inside same top block
2599 if (b.ParametersBlock == ParametersBlock.Original)
2602 b = b.ParametersBlock;
2605 var pb = b as ParametersBlock;
2606 if (pb != null && pb.StateMachine != null) {
2607 if (pb.StateMachine == storey)
2610 pb.StateMachine.AddParentStoreyReference (ec, storey);
2613 b.HasCapturedVariable = true;
2619 var ref_blocks = storey.ReferencesFromChildrenBlock;
2620 if (ref_blocks != null) {
2621 foreach (ExplicitBlock ref_block in ref_blocks) {
2622 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2623 if (b.AnonymousMethodStorey != null) {
2624 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2627 // Stop propagation inside same top block
2629 if (b.ParametersBlock == ParametersBlock.Original)
2632 b = b.ParametersBlock;
2635 var pb = b as ParametersBlock;
2636 if (pb != null && pb.StateMachine != null) {
2637 if (pb.StateMachine == storey)
2640 pb.StateMachine.AddParentStoreyReference (ec, storey);
2643 b.HasCapturedVariable = true;
2649 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2652 public void RegisterAsyncAwait ()
2655 while ((block.flags & Flags.AwaitBlock) == 0) {
2656 block.flags |= Flags.AwaitBlock;
2658 if (block is ParametersBlock)
2661 block = block.Parent.Explicit;
2665 public void RegisterIteratorYield ()
2668 while ((block.flags & Flags.YieldBlock) == 0) {
2669 block.flags |= Flags.YieldBlock;
2671 if (block.Parent == null)
2674 block = block.Parent.Explicit;
2678 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2680 tryBlock.statements = statements;
2681 statements = new List<Statement> (1);
2682 statements.Add (tf);
2687 // ParametersBlock was introduced to support anonymous methods
2688 // and lambda expressions
2690 public class ParametersBlock : ExplicitBlock
2692 public class ParameterInfo : INamedBlockVariable
2694 readonly ParametersBlock block;
2696 public VariableInfo VariableInfo;
2699 public ParameterInfo (ParametersBlock block, int index)
2707 public ParametersBlock Block {
2713 Block INamedBlockVariable.Block {
2719 public bool IsDeclared {
2725 public bool IsParameter {
2731 public bool IsLocked {
2740 public Location Location {
2742 return Parameter.Location;
2746 public Parameter Parameter {
2748 return block.Parameters [index];
2752 public TypeSpec ParameterType {
2754 return Parameter.Type;
2760 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2762 return new ParameterReference (this, loc);
2767 // Block is converted into an expression
2769 sealed class BlockScopeExpression : Expression
2772 readonly ParametersBlock block;
2774 public BlockScopeExpression (Expression child, ParametersBlock block)
2780 public override bool ContainsEmitWithAwait ()
2782 return child.ContainsEmitWithAwait ();
2785 public override Expression CreateExpressionTree (ResolveContext ec)
2787 throw new NotSupportedException ();
2790 protected override Expression DoResolve (ResolveContext ec)
2795 child = child.Resolve (ec);
2799 eclass = child.eclass;
2804 public override void Emit (EmitContext ec)
2806 block.EmitScopeInitializers (ec);
2811 protected ParametersCompiled parameters;
2812 protected ParameterInfo[] parameter_info;
2814 protected bool unreachable;
2815 protected ToplevelBlock top_block;
2816 protected StateMachine state_machine;
2818 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2819 : base (parent, 0, start, start)
2821 if (parameters == null)
2822 throw new ArgumentNullException ("parameters");
2824 this.parameters = parameters;
2825 ParametersBlock = this;
2827 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2829 this.top_block = parent.ParametersBlock.top_block;
2830 ProcessParameters ();
2833 protected ParametersBlock (ParametersCompiled parameters, Location start)
2834 : base (null, 0, start, start)
2836 if (parameters == null)
2837 throw new ArgumentNullException ("parameters");
2839 this.parameters = parameters;
2840 ParametersBlock = this;
2844 // It's supposed to be used by method body implementation of anonymous methods
2846 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2847 : base (null, 0, source.StartLocation, source.EndLocation)
2849 this.parameters = parameters;
2850 this.statements = source.statements;
2851 this.scope_initializers = source.scope_initializers;
2853 this.resolved = true;
2854 this.unreachable = source.unreachable;
2855 this.am_storey = source.am_storey;
2856 this.state_machine = source.state_machine;
2858 ParametersBlock = this;
2861 // Overwrite original for comparison purposes when linking cross references
2862 // between anonymous methods
2864 Original = source.Original;
2869 public bool IsAsync {
2871 return (flags & Flags.HasAsyncModifier) != 0;
2874 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2879 // Block has been converted to expression tree
2881 public bool IsExpressionTree {
2883 return (flags & Flags.IsExpressionTree) != 0;
2888 // The parameters for the block.
2890 public ParametersCompiled Parameters {
2896 public StateMachine StateMachine {
2898 return state_machine;
2902 public ToplevelBlock TopBlock {
2908 public bool Resolved {
2910 return (flags & Flags.Resolved) != 0;
2914 public int TemporaryLocalsCount { get; set; }
2919 // Check whether all `out' parameters have been assigned.
2921 public void CheckOutParameters (FlowBranching.UsageVector vector)
2923 if (vector.IsUnreachable)
2926 int n = parameter_info == null ? 0 : parameter_info.Length;
2928 for (int i = 0; i < n; i++) {
2929 VariableInfo var = parameter_info[i].VariableInfo;
2934 if (vector.IsAssigned (var, false))
2937 var p = parameter_info[i].Parameter;
2938 TopBlock.Report.Error (177, p.Location,
2939 "The out parameter `{0}' must be assigned to before control leaves the current method",
2944 public override Expression CreateExpressionTree (ResolveContext ec)
2946 if (statements.Count == 1) {
2947 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2948 if (scope_initializers != null)
2949 expr = new BlockScopeExpression (expr, this);
2954 return base.CreateExpressionTree (ec);
2957 public override void Emit (EmitContext ec)
2959 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2960 DefineStoreyContainer (ec, state_machine);
2961 state_machine.EmitStoreyInstantiation (ec, this);
2967 public void EmitEmbedded (EmitContext ec)
2969 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2970 DefineStoreyContainer (ec, state_machine);
2971 state_machine.EmitStoreyInstantiation (ec, this);
2977 public ParameterInfo GetParameterInfo (Parameter p)
2979 for (int i = 0; i < parameters.Count; ++i) {
2980 if (parameters[i] == p)
2981 return parameter_info[i];
2984 throw new ArgumentException ("Invalid parameter");
2987 public ParameterReference GetParameterReference (int index, Location loc)
2989 return new ParameterReference (parameter_info[index], loc);
2992 public Statement PerformClone ()
2994 CloneContext clonectx = new CloneContext ();
2995 return Clone (clonectx);
2998 protected void ProcessParameters ()
3000 if (parameters.Count == 0)
3003 parameter_info = new ParameterInfo[parameters.Count];
3004 for (int i = 0; i < parameter_info.Length; ++i) {
3005 var p = parameters.FixedParameters[i];
3009 // TODO: Should use Parameter only and more block there
3010 parameter_info[i] = new ParameterInfo (this, i);
3012 AddLocalName (p.Name, parameter_info[i]);
3016 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3023 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3024 flags |= Flags.IsExpressionTree;
3029 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3030 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3035 unreachable = top_level.End ();
3037 } catch (Exception e) {
3038 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3041 if (rc.CurrentBlock != null) {
3042 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3044 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3047 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3051 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3052 if (rc.CurrentAnonymousMethod == null) {
3053 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3054 if (md is StateMachineMethod) {
3057 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3062 // If an asynchronous body of F is either an expression classified as nothing, or a
3063 // statement block where no return statements have expressions, the inferred return type is Task
3066 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3067 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3068 am.ReturnTypeInference = null;
3069 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3074 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3075 rc.CurrentAnonymousMethod.GetSignatureForError ());
3083 void ResolveMeta (BlockContext ec)
3085 int orig_count = parameters.Count;
3087 for (int i = 0; i < orig_count; ++i) {
3088 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3090 if ((mod & Parameter.Modifier.OUT) == 0)
3093 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3094 parameter_info[i].VariableInfo = vi;
3095 ec.FlowOffset += vi.Length;
3099 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3101 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3102 var stateMachine = new IteratorStorey (iterator);
3104 state_machine = stateMachine;
3105 iterator.SetStateMachine (stateMachine);
3107 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3108 tlb.Original = this;
3109 tlb.IsCompilerGenerated = true;
3110 tlb.state_machine = stateMachine;
3111 tlb.AddStatement (new Return (iterator, iterator.Location));
3115 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3117 for (int i = 0; i < parameters.Count; i++) {
3118 Parameter p = parameters[i];
3119 Parameter.Modifier mod = p.ModFlags;
3120 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3121 host.Compiler.Report.Error (1988, p.Location,
3122 "Async methods cannot have ref or out parameters");
3126 if (p is ArglistParameter) {
3127 host.Compiler.Report.Error (4006, p.Location,
3128 "__arglist is not allowed in parameter list of async methods");
3132 if (parameters.Types[i].IsPointer) {
3133 host.Compiler.Report.Error (4005, p.Location,
3134 "Async methods cannot have unsafe parameters");
3140 host.Compiler.Report.Warning (1998, 1, loc,
3141 "Async block lacks `await' operator and will run synchronously");
3144 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3145 var initializer = new AsyncInitializer (this, host, block_type);
3146 initializer.Type = block_type;
3148 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3150 state_machine = stateMachine;
3151 initializer.SetStateMachine (stateMachine);
3153 var b = this is ToplevelBlock ?
3154 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3155 new ParametersBlock (Parent, parameters, Location.Null) {
3160 b.IsCompilerGenerated = true;
3161 b.state_machine = stateMachine;
3162 b.AddStatement (new StatementExpression (initializer));
3170 public class ToplevelBlock : ParametersBlock
3172 LocalVariable this_variable;
3173 CompilerContext compiler;
3174 Dictionary<string, object> names;
3175 Dictionary<string, object> labels;
3177 List<ExplicitBlock> this_references;
3179 public ToplevelBlock (CompilerContext ctx, Location loc)
3180 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3184 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3185 : base (parameters, start)
3187 this.compiler = ctx;
3189 flags |= Flags.HasRet;
3191 ProcessParameters ();
3195 // Recreates a top level block from parameters block. Used for
3196 // compiler generated methods where the original block comes from
3197 // explicit child block. This works for already resolved blocks
3198 // only to ensure we resolve them in the correct flow order
3200 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3201 : base (source, parameters)
3203 this.compiler = source.TopBlock.compiler;
3205 flags |= Flags.HasRet;
3208 public bool IsIterator {
3214 public Report Report {
3216 return compiler.Report;
3221 // Used by anonymous blocks to track references of `this' variable
3223 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3225 return this_references;
3230 // Returns the "this" instance variable of this block.
3231 // See AddThisVariable() for more information.
3233 public LocalVariable ThisVariable {
3235 return this_variable;
3239 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3242 names = new Dictionary<string, object> ();
3245 if (!names.TryGetValue (name, out value)) {
3246 names.Add (name, li);
3250 INamedBlockVariable existing = value as INamedBlockVariable;
3251 List<INamedBlockVariable> existing_list;
3252 if (existing != null) {
3253 existing_list = new List<INamedBlockVariable> ();
3254 existing_list.Add (existing);
3255 names[name] = existing_list;
3257 existing_list = (List<INamedBlockVariable>) value;
3261 // A collision checking between local names
3263 var variable_block = li.Block.Explicit;
3264 for (int i = 0; i < existing_list.Count; ++i) {
3265 existing = existing_list[i];
3266 Block b = existing.Block.Explicit;
3268 // Collision at same level
3269 if (variable_block == b) {
3270 li.Block.Error_AlreadyDeclared (name, li);
3274 // Collision with parent
3275 Block parent = variable_block;
3276 while ((parent = parent.Parent) != null) {
3278 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3279 i = existing_list.Count;
3284 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3285 // Collision with children
3286 while ((b = b.Parent) != null) {
3287 if (variable_block == b) {
3288 li.Block.Error_AlreadyDeclared (name, li, "child");
3289 i = existing_list.Count;
3296 existing_list.Add (li);
3299 public void AddLabel (string name, LabeledStatement label)
3302 labels = new Dictionary<string, object> ();
3305 if (!labels.TryGetValue (name, out value)) {
3306 labels.Add (name, label);
3310 LabeledStatement existing = value as LabeledStatement;
3311 List<LabeledStatement> existing_list;
3312 if (existing != null) {
3313 existing_list = new List<LabeledStatement> ();
3314 existing_list.Add (existing);
3315 labels[name] = existing_list;
3317 existing_list = (List<LabeledStatement>) value;
3321 // A collision checking between labels
3323 for (int i = 0; i < existing_list.Count; ++i) {
3324 existing = existing_list[i];
3325 Block b = existing.Block;
3327 // Collision at same level
3328 if (label.Block == b) {
3329 Report.SymbolRelatedToPreviousError (existing.loc, name);
3330 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3334 // Collision with parent
3336 while ((b = b.Parent) != null) {
3337 if (existing.Block == b) {
3338 Report.Error (158, label.loc,
3339 "The label `{0}' shadows another label by the same name in a contained scope", name);
3340 i = existing_list.Count;
3345 // Collision with with children
3347 while ((b = b.Parent) != null) {
3348 if (label.Block == b) {
3349 Report.Error (158, label.loc,
3350 "The label `{0}' shadows another label by the same name in a contained scope", name);
3351 i = existing_list.Count;
3357 existing_list.Add (label);
3360 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3362 if (this_references == null)
3363 this_references = new List<ExplicitBlock> ();
3365 if (!this_references.Contains (block))
3366 this_references.Add (block);
3369 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3371 this_references.Remove (block);
3375 // Creates an arguments set from all parameters, useful for method proxy calls
3377 public Arguments GetAllParametersArguments ()
3379 int count = parameters.Count;
3380 Arguments args = new Arguments (count);
3381 for (int i = 0; i < count; ++i) {
3382 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3383 args.Add (new Argument (arg_expr));
3390 // Lookup inside a block, the returned value can represent 3 states
3392 // true+variable: A local name was found and it's valid
3393 // false+variable: A local name was found in a child block only
3394 // false+null: No local name was found
3396 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3402 if (!names.TryGetValue (name, out value))
3405 variable = value as INamedBlockVariable;
3407 if (variable != null) {
3409 if (variable.Block == b.Original)
3413 } while (b != null);
3421 } while (b != null);
3423 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3424 for (int i = 0; i < list.Count; ++i) {
3427 if (variable.Block == b.Original)
3431 } while (b != null);
3439 } while (b != null);
3449 public LabeledStatement GetLabel (string name, Block block)
3455 if (!labels.TryGetValue (name, out value)) {
3459 var label = value as LabeledStatement;
3461 if (label != null) {
3462 if (label.Block == b.Original)
3465 List<LabeledStatement> list = (List<LabeledStatement>) value;
3466 for (int i = 0; i < list.Count; ++i) {
3468 if (label.Block == b.Original)
3477 // This is used by non-static `struct' constructors which do not have an
3478 // initializer - in this case, the constructor must initialize all of the
3479 // struct's fields. To do this, we add a "this" variable and use the flow
3480 // analysis code to ensure that it's been fully initialized before control
3481 // leaves the constructor.
3483 public void AddThisVariable (BlockContext bc)
3485 if (this_variable != null)
3486 throw new InternalErrorException (StartLocation.ToString ());
3488 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3489 this_variable.Type = bc.CurrentType;
3490 this_variable.PrepareForFlowAnalysis (bc);
3493 public bool IsThisAssigned (BlockContext ec)
3495 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3498 public override void Emit (EmitContext ec)
3500 if (Report.Errors > 0)
3504 if (IsCompilerGenerated) {
3505 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3513 // If `HasReturnLabel' is set, then we already emitted a
3514 // jump to the end of the method, so we must emit a `ret'
3517 // Unfortunately, System.Reflection.Emit automatically emits
3518 // a leave to the end of a finally block. This is a problem
3519 // if no code is following the try/finally block since we may
3520 // jump to a point after the end of the method.
3521 // As a workaround, we're always creating a return label in
3524 if (ec.HasReturnLabel || !unreachable) {
3525 if (ec.HasReturnLabel)
3526 ec.MarkLabel (ec.ReturnLabel);
3528 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3529 ec.Mark (EndLocation);
3531 if (ec.ReturnType.Kind != MemberKind.Void)
3532 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3534 ec.Emit (OpCodes.Ret);
3537 } catch (Exception e) {
3538 throw new InternalErrorException (e, StartLocation);
3543 public class SwitchLabel : Statement
3551 // if expr == null, then it is the default case.
3553 public SwitchLabel (Expression expr, Location l)
3559 public bool IsDefault {
3561 return label == null;
3565 public Expression Label {
3571 public Location Location {
3577 public Constant Converted {
3586 public bool SectionStart { get; set; }
3588 public Label GetILLabel (EmitContext ec)
3590 if (il_label == null){
3591 il_label = ec.DefineLabel ();
3594 return il_label.Value;
3597 protected override void DoEmit (EmitContext ec)
3599 ec.MarkLabel (GetILLabel (ec));
3602 public override bool Resolve (BlockContext bc)
3604 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3606 return base.Resolve (bc);
3610 // Resolves the expression, reduces it to a literal if possible
3611 // and then converts it to the requested type.
3613 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3615 Expression e = label.Resolve (ec);
3620 Constant c = e as Constant;
3622 ec.Report.Error (150, loc, "A constant value is expected");
3626 if (allow_nullable && c is NullLiteral) {
3631 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3632 return converted != null;
3635 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3638 if (converted == null)
3641 label = converted.GetValueAsLiteral ();
3643 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3644 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3647 protected override void CloneTo (CloneContext clonectx, Statement target)
3649 var t = (SwitchLabel) target;
3651 t.label = label.Clone (clonectx);
3654 public override object Accept (StructuralVisitor visitor)
3656 return visitor.Visit (this);
3660 public class Switch : Statement
3662 // structure used to hold blocks of keys while calculating table switch
3663 sealed class LabelsRange : IComparable<LabelsRange>
3665 public readonly long min;
3667 public readonly List<long> label_values;
3669 public LabelsRange (long value)
3672 label_values = new List<long> ();
3673 label_values.Add (value);
3676 public LabelsRange (long min, long max, ICollection<long> values)
3680 this.label_values = new List<long> (values);
3685 return max - min + 1;
3689 public bool AddValue (long value)
3691 var gap = value - min + 1;
3692 // Ensure the range has > 50% occupancy
3693 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3697 label_values.Add (value);
3701 public int CompareTo (LabelsRange other)
3703 int nLength = label_values.Count;
3704 int nLengthOther = other.label_values.Count;
3705 if (nLengthOther == nLength)
3706 return (int) (other.min - min);
3708 return nLength - nLengthOther;
3712 sealed class DispatchStatement : Statement
3714 readonly Switch body;
3716 public DispatchStatement (Switch body)
3721 protected override void CloneTo (CloneContext clonectx, Statement target)
3723 throw new NotImplementedException ();
3726 protected override void DoEmit (EmitContext ec)
3728 body.EmitDispatch (ec);
3732 public Expression Expr;
3735 // Mapping of all labels to their SwitchLabels
3737 Dictionary<long, SwitchLabel> labels;
3738 Dictionary<string, SwitchLabel> string_labels;
3739 List<SwitchLabel> case_labels;
3742 /// The governing switch type
3744 public TypeSpec SwitchType;
3746 Expression new_expr;
3748 SwitchLabel case_null;
3749 SwitchLabel case_default;
3751 Label defaultLabel, nullLabel;
3752 VariableReference value;
3753 ExpressionStatement string_dictionary;
3754 FieldExpr switch_cache_field;
3755 ExplicitBlock block;
3758 // Nullable Types support
3760 Nullable.Unwrap unwrap;
3762 public Switch (Expression e, ExplicitBlock block, Location l)
3769 public ExplicitBlock Block {
3775 public SwitchLabel DefaultLabel {
3777 return case_default;
3781 public bool IsNullable {
3783 return unwrap != null;
3788 // Determines the governing type for a switch. The returned
3789 // expression might be the expression from the switch, or an
3790 // expression that includes any potential conversions to
3792 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3794 switch (expr.Type.BuiltinType) {
3795 case BuiltinTypeSpec.Type.Byte:
3796 case BuiltinTypeSpec.Type.SByte:
3797 case BuiltinTypeSpec.Type.UShort:
3798 case BuiltinTypeSpec.Type.Short:
3799 case BuiltinTypeSpec.Type.UInt:
3800 case BuiltinTypeSpec.Type.Int:
3801 case BuiltinTypeSpec.Type.ULong:
3802 case BuiltinTypeSpec.Type.Long:
3803 case BuiltinTypeSpec.Type.Char:
3804 case BuiltinTypeSpec.Type.String:
3805 case BuiltinTypeSpec.Type.Bool:
3809 if (expr.Type.IsEnum)
3813 // Try to find a *user* defined implicit conversion.
3815 // If there is no implicit conversion, or if there are multiple
3816 // conversions, we have to report an error
3818 Expression converted = null;
3819 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3822 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3827 // Ignore over-worked ImplicitUserConversions that do
3828 // an implicit conversion in addition to the user conversion.
3830 if (!(e is UserCast))
3833 if (converted != null){
3834 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3843 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3845 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3861 // Performs the basic sanity checks on the switch statement
3862 // (looks for duplicate keys and non-constant expressions).
3864 // It also returns a hashtable with the keys that we will later
3865 // use to compute the switch tables
3867 bool ResolveLabels (ResolveContext ec, Constant value)
3870 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3871 string_labels = new Dictionary<string, SwitchLabel> ();
3873 labels = new Dictionary<long, SwitchLabel> ();
3876 case_labels = new List<SwitchLabel> ();
3877 int default_label_index = -1;
3878 bool constant_label_found = false;
3880 for (int i = 0; i < block.Statements.Count; ++i) {
3881 var s = block.Statements[i];
3883 var sl = s as SwitchLabel;
3888 case_labels.Add (sl);
3891 if (case_default != null) {
3892 sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3896 default_label_index = i;
3901 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3907 if (string_labels != null) {
3908 string string_value = sl.Converted.GetValue () as string;
3909 if (string_value == null)
3912 string_labels.Add (string_value, sl);
3914 if (sl.Converted is NullLiteral) {
3917 labels.Add (sl.Converted.GetValueAsLong (), sl);
3920 } catch (ArgumentException) {
3921 if (string_labels != null)
3922 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3924 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3929 if (value != null) {
3930 var constant_label = constant_label_found ? null : FindLabel (value);
3931 if (constant_label == null || constant_label != sl)
3932 block.Statements[i] = new EmptyStatement (s.loc);
3934 constant_label_found = true;
3938 if (value != null && constant_label_found && default_label_index >= 0)
3939 block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3945 // This method emits code for a lookup-based switch statement (non-string)
3946 // Basically it groups the cases into blocks that are at least half full,
3947 // and then spits out individual lookup opcodes for each block.
3948 // It emits the longest blocks first, and short blocks are just
3949 // handled with direct compares.
3951 void EmitTableSwitch (EmitContext ec, Expression val)
3953 if (labels != null && labels.Count > 0) {
3954 List<LabelsRange> ranges;
3955 if (string_labels != null) {
3956 // We have done all hard work for string already
3957 // setup single range only
3958 ranges = new List<LabelsRange> (1);
3959 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3961 var element_keys = new long[labels.Count];
3962 labels.Keys.CopyTo (element_keys, 0);
3963 Array.Sort (element_keys);
3966 // Build possible ranges of switch labes to reduce number
3969 ranges = new List<LabelsRange> (element_keys.Length);
3970 var range = new LabelsRange (element_keys[0]);
3972 for (int i = 1; i < element_keys.Length; ++i) {
3973 var l = element_keys[i];
3974 if (range.AddValue (l))
3977 range = new LabelsRange (l);
3981 // sort the blocks so we can tackle the largest ones first
3985 Label lbl_default = defaultLabel;
3986 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3988 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3989 LabelsRange kb = ranges[range_index];
3990 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
3992 // Optimize small ranges using simple equality check
3993 if (kb.Range <= 2) {
3994 foreach (var key in kb.label_values) {
3995 SwitchLabel sl = labels[key];
3996 if (sl == case_default || sl == case_null)
3999 if (sl.Converted.IsZeroInteger) {
4000 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4003 sl.Converted.Emit (ec);
4004 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4008 // TODO: if all the keys in the block are the same and there are
4009 // no gaps/defaults then just use a range-check.
4010 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4011 // TODO: optimize constant/I4 cases
4013 // check block range (could be > 2^31)
4015 ec.EmitLong (kb.min);
4016 ec.Emit (OpCodes.Blt, lbl_default);
4019 ec.EmitLong (kb.max);
4020 ec.Emit (OpCodes.Bgt, lbl_default);
4025 ec.EmitLong (kb.min);
4026 ec.Emit (OpCodes.Sub);
4029 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4033 int first = (int) kb.min;
4036 ec.Emit (OpCodes.Sub);
4037 } else if (first < 0) {
4038 ec.EmitInt (-first);
4039 ec.Emit (OpCodes.Add);
4043 // first, build the list of labels for the switch
4045 long cJumps = kb.Range;
4046 Label[] switch_labels = new Label[cJumps];
4047 for (int iJump = 0; iJump < cJumps; iJump++) {
4048 var key = kb.label_values[iKey];
4049 if (key == kb.min + iJump) {
4050 switch_labels[iJump] = labels[key].GetILLabel (ec);
4053 switch_labels[iJump] = lbl_default;
4057 // emit the switch opcode
4058 ec.Emit (OpCodes.Switch, switch_labels);
4061 // mark the default for this block
4062 if (range_index != 0)
4063 ec.MarkLabel (lbl_default);
4066 // the last default just goes to the end
4067 if (ranges.Count > 0)
4068 ec.Emit (OpCodes.Br, lbl_default);
4072 SwitchLabel FindLabel (Constant value)
4074 SwitchLabel sl = null;
4076 if (string_labels != null) {
4077 string s = value.GetValue () as string;
4079 if (case_null != null)
4081 else if (case_default != null)
4084 string_labels.TryGetValue (s, out sl);
4087 if (value is NullLiteral) {
4090 labels.TryGetValue (value.GetValueAsLong (), out sl);
4097 public override bool Resolve (BlockContext ec)
4099 Expr = Expr.Resolve (ec);
4103 new_expr = SwitchGoverningType (ec, Expr);
4105 if (new_expr == null && Expr.Type.IsNullableType) {
4106 unwrap = Nullable.Unwrap.Create (Expr, false);
4110 new_expr = SwitchGoverningType (ec, unwrap);
4113 if (new_expr == null){
4114 ec.Report.Error (151, loc,
4115 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4116 TypeManager.CSharpName (Expr.Type));
4121 SwitchType = new_expr.Type;
4123 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4124 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4128 if (block.Statements.Count == 0)
4131 var constant = new_expr as Constant;
4133 if (!ResolveLabels (ec, constant))
4137 // Don't need extra variable for constant switch or switch with
4138 // only default case
4140 if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4142 // Store switch expression for comparison purposes
4144 value = new_expr as VariableReference;
4145 if (value == null) {
4146 // Create temporary variable inside switch scope
4147 var current_block = ec.CurrentBlock;
4148 ec.CurrentBlock = Block;
4149 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4151 ec.CurrentBlock = current_block;
4155 Switch old_switch = ec.Switch;
4157 ec.Switch.SwitchType = SwitchType;
4159 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4161 ec.CurrentBranching.CurrentUsageVector.Goto ();
4163 var ok = block.Resolve (ec);
4165 if (case_default == null)
4166 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4168 ec.EndFlowBranching ();
4169 ec.Switch = old_switch;
4174 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4175 ResolveStringSwitchMap (ec);
4179 // Needed to emit anonymous storey initialization before
4180 // any generated switch dispatch
4182 block.AddScopeStatement (new DispatchStatement (this));
4187 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4189 var sl = FindLabel (value);
4192 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4199 // Converts string switch into string hashtable
4201 void ResolveStringSwitchMap (ResolveContext ec)
4203 FullNamedExpression string_dictionary_type;
4204 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4205 string_dictionary_type = new TypeExpression (
4206 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4207 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4209 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4210 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4212 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4216 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4217 Field field = new Field (ctype, string_dictionary_type,
4218 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4219 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4220 if (!field.Define ())
4222 ctype.AddField (field);
4224 var init = new List<Expression> ();
4226 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4227 string value = null;
4229 foreach (SwitchLabel sl in case_labels) {
4231 if (sl.SectionStart)
4232 labels.Add (++counter, sl);
4234 if (sl == case_default || sl == case_null)
4237 value = (string) sl.Converted.GetValue ();
4238 var init_args = new List<Expression> (2);
4239 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4241 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4242 init_args.Add (sl.Converted);
4244 init.Add (new CollectionElementInitializer (init_args, loc));
4247 Arguments args = new Arguments (1);
4248 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4249 Expression initializer = new NewInitialize (string_dictionary_type, args,
4250 new CollectionOrObjectInitializers (init, loc), loc);
4252 switch_cache_field = new FieldExpr (field, loc);
4253 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4256 void DoEmitStringSwitch (EmitContext ec)
4258 Label l_initialized = ec.DefineLabel ();
4261 // Skip initialization when value is null
4263 value.EmitBranchable (ec, nullLabel, false);
4266 // Check if string dictionary is initialized and initialize
4268 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4269 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4270 string_dictionary.EmitStatement (ec);
4272 ec.MarkLabel (l_initialized);
4274 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4276 ResolveContext rc = new ResolveContext (ec.MemberContext);
4278 if (switch_cache_field.Type.IsGeneric) {
4279 Arguments get_value_args = new Arguments (2);
4280 get_value_args.Add (new Argument (value));
4281 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4282 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4283 if (get_item == null)
4287 // A value was not found, go to default case
4289 get_item.EmitBranchable (ec, defaultLabel, false);
4291 Arguments get_value_args = new Arguments (1);
4292 get_value_args.Add (new Argument (value));
4294 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4295 if (get_item == null)
4298 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4299 get_item_object.EmitAssign (ec, get_item, true, false);
4300 ec.Emit (OpCodes.Brfalse, defaultLabel);
4302 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4303 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4305 get_item_int.EmitStatement (ec);
4306 get_item_object.Release (ec);
4309 EmitTableSwitch (ec, string_switch_variable);
4310 string_switch_variable.Release (ec);
4314 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4316 void EmitShortSwitch (EmitContext ec)
4318 MethodSpec equal_method = null;
4319 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4320 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4323 if (equal_method != null) {
4324 value.EmitBranchable (ec, nullLabel, false);
4327 for (int i = 0; i < case_labels.Count; ++i) {
4328 var label = case_labels [i];
4329 if (label == case_default || label == case_null)
4332 var constant = label.Converted;
4334 if (equal_method != null) {
4338 var call = new CallEmitter ();
4339 call.EmitPredefined (ec, equal_method, new Arguments (0));
4340 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4344 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4345 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4351 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4354 ec.Emit (OpCodes.Br, defaultLabel);
4357 void EmitDispatch (EmitContext ec)
4359 if (value == null) {
4361 // Constant switch, we already done the work
4367 // Mark sequence point explicitly to switch
4369 ec.Mark (block.StartLocation);
4370 block.IsCompilerGenerated = true;
4372 if (string_dictionary != null) {
4373 DoEmitStringSwitch (ec);
4374 } else if (case_labels.Count < 4 || string_labels != null) {
4375 EmitShortSwitch (ec);
4377 EmitTableSwitch (ec, value);
4381 protected override void DoEmit (EmitContext ec)
4383 // Workaround broken flow-analysis
4384 block.HasUnreachableClosingBrace = true;
4387 // Setup the codegen context
4389 Label old_end = ec.LoopEnd;
4390 Switch old_switch = ec.Switch;
4392 ec.LoopEnd = ec.DefineLabel ();
4395 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4396 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4398 if (value != null) {
4401 unwrap.EmitCheck (ec);
4402 ec.Emit (OpCodes.Brfalse, nullLabel);
4403 value.EmitAssign (ec, new_expr, false, false);
4404 } else if (new_expr != value) {
4405 value.EmitAssign (ec, new_expr, false, false);
4411 // Restore context state.
4412 ec.MarkLabel (ec.LoopEnd);
4415 // Restore the previous context
4417 ec.LoopEnd = old_end;
4418 ec.Switch = old_switch;
4421 protected override void CloneTo (CloneContext clonectx, Statement t)
4423 Switch target = (Switch) t;
4425 target.Expr = Expr.Clone (clonectx);
4426 target.block = (ExplicitBlock) block.Clone (clonectx);
4429 public override object Accept (StructuralVisitor visitor)
4431 return visitor.Visit (this);
4435 // A place where execution can restart in an iterator
4436 public abstract class ResumableStatement : Statement
4439 protected Label resume_point;
4441 public Label PrepareForEmit (EmitContext ec)
4445 resume_point = ec.DefineLabel ();
4447 return resume_point;
4450 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4455 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4460 public abstract class TryFinallyBlock : ExceptionStatement
4462 protected Statement stmt;
4463 Label dispose_try_block;
4464 bool prepared_for_dispose, emitted_dispose;
4465 Method finally_host;
4467 protected TryFinallyBlock (Statement stmt, Location loc)
4475 public Statement Statement {
4483 protected abstract void EmitTryBody (EmitContext ec);
4484 public abstract void EmitFinallyBody (EmitContext ec);
4486 public override Label PrepareForDispose (EmitContext ec, Label end)
4488 if (!prepared_for_dispose) {
4489 prepared_for_dispose = true;
4490 dispose_try_block = ec.DefineLabel ();
4492 return dispose_try_block;
4495 protected sealed override void DoEmit (EmitContext ec)
4497 EmitTryBodyPrepare (ec);
4500 ec.BeginFinallyBlock ();
4502 Label start_finally = ec.DefineLabel ();
4503 if (resume_points != null) {
4504 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4506 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4507 ec.Emit (OpCodes.Brfalse_S, start_finally);
4508 ec.Emit (OpCodes.Endfinally);
4511 ec.MarkLabel (start_finally);
4513 if (finally_host != null) {
4514 finally_host.Define ();
4515 finally_host.Emit ();
4517 // Now it's safe to add, to close it properly and emit sequence points
4518 finally_host.Parent.AddMember (finally_host);
4520 var ce = new CallEmitter ();
4521 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4522 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4524 EmitFinallyBody (ec);
4527 ec.EndExceptionBlock ();
4530 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4532 if (emitted_dispose)
4535 emitted_dispose = true;
4537 Label end_of_try = ec.DefineLabel ();
4539 // Ensure that the only way we can get into this code is through a dispatcher
4540 if (have_dispatcher)
4541 ec.Emit (OpCodes.Br, end);
4543 ec.BeginExceptionBlock ();
4545 ec.MarkLabel (dispose_try_block);
4547 Label[] labels = null;
4548 for (int i = 0; i < resume_points.Count; ++i) {
4549 ResumableStatement s = resume_points[i];
4550 Label ret = s.PrepareForDispose (ec, end_of_try);
4551 if (ret.Equals (end_of_try) && labels == null)
4553 if (labels == null) {
4554 labels = new Label[resume_points.Count];
4555 for (int j = 0; j < i; ++j)
4556 labels[j] = end_of_try;
4561 if (labels != null) {
4563 for (j = 1; j < labels.Length; ++j)
4564 if (!labels[0].Equals (labels[j]))
4566 bool emit_dispatcher = j < labels.Length;
4568 if (emit_dispatcher) {
4569 ec.Emit (OpCodes.Ldloc, pc);
4570 ec.EmitInt (first_resume_pc);
4571 ec.Emit (OpCodes.Sub);
4572 ec.Emit (OpCodes.Switch, labels);
4575 foreach (ResumableStatement s in resume_points)
4576 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4579 ec.MarkLabel (end_of_try);
4581 ec.BeginFinallyBlock ();
4583 if (finally_host != null) {
4584 var ce = new CallEmitter ();
4585 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4586 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4588 EmitFinallyBody (ec);
4591 ec.EndExceptionBlock ();
4594 public override bool Resolve (BlockContext bc)
4597 // Finally block inside iterator is called from MoveNext and
4598 // Dispose methods that means we need to lift the block into
4599 // newly created host method to emit the body only once. The
4600 // original block then simply calls the newly generated method.
4602 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4603 var b = stmt as Block;
4604 if (b != null && b.Explicit.HasYield) {
4605 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4609 return base.Resolve (bc);
4614 // Base class for blocks using exception handling
4616 public abstract class ExceptionStatement : ResumableStatement
4621 protected List<ResumableStatement> resume_points;
4622 protected int first_resume_pc;
4624 protected ExceptionStatement (Location loc)
4629 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4631 StateMachineInitializer state_machine = null;
4632 if (resume_points != null) {
4633 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4635 ec.EmitInt ((int) IteratorStorey.State.Running);
4636 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4639 ec.BeginExceptionBlock ();
4641 if (resume_points != null) {
4642 ec.MarkLabel (resume_point);
4644 // For normal control flow, we want to fall-through the Switch
4645 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4646 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4647 ec.EmitInt (first_resume_pc);
4648 ec.Emit (OpCodes.Sub);
4650 Label[] labels = new Label[resume_points.Count];
4651 for (int i = 0; i < resume_points.Count; ++i)
4652 labels[i] = resume_points[i].PrepareForEmit (ec);
4653 ec.Emit (OpCodes.Switch, labels);
4657 public void SomeCodeFollows ()
4660 code_follows = true;
4664 public override bool Resolve (BlockContext ec)
4667 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4668 // So, ensure there's some IL code after this statement.
4669 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4670 ec.NeedReturnLabel ();
4675 public void AddResumePoint (ResumableStatement stmt, int pc)
4677 if (resume_points == null) {
4678 resume_points = new List<ResumableStatement> ();
4679 first_resume_pc = pc;
4682 if (pc != first_resume_pc + resume_points.Count)
4683 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4685 resume_points.Add (stmt);
4690 public class Lock : TryFinallyBlock
4693 TemporaryVariableReference expr_copy;
4694 TemporaryVariableReference lock_taken;
4696 public Lock (Expression expr, Statement stmt, Location loc)
4702 public Expression Expr {
4708 public override bool Resolve (BlockContext ec)
4710 expr = expr.Resolve (ec);
4714 if (!TypeSpec.IsReferenceType (expr.Type)) {
4715 ec.Report.Error (185, loc,
4716 "`{0}' is not a reference type as required by the lock statement",
4717 expr.Type.GetSignatureForError ());
4720 if (expr.Type.IsGenericParameter) {
4721 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4724 VariableReference lv = expr as VariableReference;
4727 locked = lv.IsLockedByStatement;
4728 lv.IsLockedByStatement = true;
4735 // Have to keep original lock value around to unlock same location
4736 // in the case of original value has changed or is null
4738 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4739 expr_copy.Resolve (ec);
4742 // Ensure Monitor methods are available
4744 if (ResolvePredefinedMethods (ec) > 1) {
4745 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4746 lock_taken.Resolve (ec);
4749 using (ec.Set (ResolveContext.Options.LockScope)) {
4750 ec.StartFlowBranching (this);
4751 Statement.Resolve (ec);
4752 ec.EndFlowBranching ();
4756 lv.IsLockedByStatement = locked;
4764 protected override void EmitTryBodyPrepare (EmitContext ec)
4766 expr_copy.EmitAssign (ec, expr);
4768 if (lock_taken != null) {
4770 // Initialize ref variable
4772 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4775 // Monitor.Enter (expr_copy)
4777 expr_copy.Emit (ec);
4778 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4781 base.EmitTryBodyPrepare (ec);
4784 protected override void EmitTryBody (EmitContext ec)
4787 // Monitor.Enter (expr_copy, ref lock_taken)
4789 if (lock_taken != null) {
4790 expr_copy.Emit (ec);
4791 lock_taken.LocalInfo.CreateBuilder (ec);
4792 lock_taken.AddressOf (ec, AddressOp.Load);
4793 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4796 Statement.Emit (ec);
4799 public override void EmitFinallyBody (EmitContext ec)
4802 // if (lock_taken) Monitor.Exit (expr_copy)
4804 Label skip = ec.DefineLabel ();
4806 if (lock_taken != null) {
4807 lock_taken.Emit (ec);
4808 ec.Emit (OpCodes.Brfalse_S, skip);
4811 expr_copy.Emit (ec);
4812 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4814 ec.Emit (OpCodes.Call, m);
4816 ec.MarkLabel (skip);
4819 int ResolvePredefinedMethods (ResolveContext rc)
4821 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4822 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4826 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4830 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4834 protected override void CloneTo (CloneContext clonectx, Statement t)
4836 Lock target = (Lock) t;
4838 target.expr = expr.Clone (clonectx);
4839 target.stmt = Statement.Clone (clonectx);
4842 public override object Accept (StructuralVisitor visitor)
4844 return visitor.Visit (this);
4849 public class Unchecked : Statement {
4852 public Unchecked (Block b, Location loc)
4859 public override bool Resolve (BlockContext ec)
4861 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4862 return Block.Resolve (ec);
4865 protected override void DoEmit (EmitContext ec)
4867 using (ec.With (EmitContext.Options.CheckedScope, false))
4871 protected override void CloneTo (CloneContext clonectx, Statement t)
4873 Unchecked target = (Unchecked) t;
4875 target.Block = clonectx.LookupBlock (Block);
4878 public override object Accept (StructuralVisitor visitor)
4880 return visitor.Visit (this);
4884 public class Checked : Statement {
4887 public Checked (Block b, Location loc)
4890 b.Unchecked = false;
4894 public override bool Resolve (BlockContext ec)
4896 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4897 return Block.Resolve (ec);
4900 protected override void DoEmit (EmitContext ec)
4902 using (ec.With (EmitContext.Options.CheckedScope, true))
4906 protected override void CloneTo (CloneContext clonectx, Statement t)
4908 Checked target = (Checked) t;
4910 target.Block = clonectx.LookupBlock (Block);
4913 public override object Accept (StructuralVisitor visitor)
4915 return visitor.Visit (this);
4919 public class Unsafe : Statement {
4922 public Unsafe (Block b, Location loc)
4925 Block.Unsafe = true;
4929 public override bool Resolve (BlockContext ec)
4931 if (ec.CurrentIterator != null)
4932 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4934 using (ec.Set (ResolveContext.Options.UnsafeScope))
4935 return Block.Resolve (ec);
4938 protected override void DoEmit (EmitContext ec)
4943 protected override void CloneTo (CloneContext clonectx, Statement t)
4945 Unsafe target = (Unsafe) t;
4947 target.Block = clonectx.LookupBlock (Block);
4950 public override object Accept (StructuralVisitor visitor)
4952 return visitor.Visit (this);
4959 public class Fixed : Statement
4961 abstract class Emitter : ShimExpression
4963 protected LocalVariable vi;
4965 protected Emitter (Expression expr, LocalVariable li)
4971 public abstract void EmitExit (EmitContext ec);
4974 class ExpressionEmitter : Emitter {
4975 public ExpressionEmitter (Expression converted, LocalVariable li) :
4976 base (converted, li)
4980 protected override Expression DoResolve (ResolveContext rc)
4982 throw new NotImplementedException ();
4985 public override void Emit (EmitContext ec) {
4987 // Store pointer in pinned location
4993 public override void EmitExit (EmitContext ec)
4996 ec.Emit (OpCodes.Conv_U);
5001 class StringEmitter : Emitter
5003 LocalVariable pinned_string;
5005 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5010 protected override Expression DoResolve (ResolveContext rc)
5012 pinned_string = new LocalVariable (vi.Block, "$pinned",
5013 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5015 pinned_string.Type = rc.BuiltinTypes.String;
5017 eclass = ExprClass.Variable;
5018 type = rc.BuiltinTypes.Int;
5022 public override void Emit (EmitContext ec)
5024 pinned_string.CreateBuilder (ec);
5027 pinned_string.EmitAssign (ec);
5029 // TODO: Should use Binary::Add
5030 pinned_string.Emit (ec);
5031 ec.Emit (OpCodes.Conv_I);
5033 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5037 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5038 //pe.InstanceExpression = pinned_string;
5039 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5041 ec.Emit (OpCodes.Add);
5045 public override void EmitExit (EmitContext ec)
5048 pinned_string.EmitAssign (ec);
5052 public class VariableDeclaration : BlockVariableDeclaration
5054 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5059 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5061 if (!Variable.Type.IsPointer && li == Variable) {
5062 bc.Report.Error (209, TypeExpression.Location,
5063 "The type of locals declared in a fixed statement must be a pointer type");
5068 // The rules for the possible declarators are pretty wise,
5069 // but the production on the grammar is more concise.
5071 // So we have to enforce these rules here.
5073 // We do not resolve before doing the case 1 test,
5074 // because the grammar is explicit in that the token &
5075 // is present, so we need to test for this particular case.
5078 if (initializer is Cast) {
5079 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5083 initializer = initializer.Resolve (bc);
5085 if (initializer == null)
5091 if (initializer.Type.IsArray) {
5092 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5095 // Provided that array_type is unmanaged,
5097 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5101 // and T* is implicitly convertible to the
5102 // pointer type given in the fixed statement.
5104 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5106 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5107 if (converted == null)
5111 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5113 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5114 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5115 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5116 new NullLiteral (loc),
5119 converted = converted.Resolve (bc);
5121 return new ExpressionEmitter (converted, li);
5127 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5128 return new StringEmitter (initializer, li, loc).Resolve (bc);
5131 // Case 3: fixed buffer
5132 if (initializer is FixedBufferPtr) {
5133 return new ExpressionEmitter (initializer, li);
5137 // Case 4: & object.
5139 bool already_fixed = true;
5140 Unary u = initializer as Unary;
5141 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5142 IVariableReference vr = u.Expr as IVariableReference;
5143 if (vr == null || !vr.IsFixed) {
5144 already_fixed = false;
5148 if (already_fixed) {
5149 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5152 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5153 return new ExpressionEmitter (initializer, li);
5158 VariableDeclaration decl;
5159 Statement statement;
5162 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5171 public Statement Statement {
5177 public BlockVariableDeclaration Variables {
5185 public override bool Resolve (BlockContext ec)
5187 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5188 if (!decl.Resolve (ec))
5192 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5193 bool ok = statement.Resolve (ec);
5194 bool flow_unreachable = ec.EndFlowBranching ();
5195 has_ret = flow_unreachable;
5200 protected override void DoEmit (EmitContext ec)
5202 decl.Variable.CreateBuilder (ec);
5203 decl.Initializer.Emit (ec);
5204 if (decl.Declarators != null) {
5205 foreach (var d in decl.Declarators) {
5206 d.Variable.CreateBuilder (ec);
5207 d.Initializer.Emit (ec);
5211 statement.Emit (ec);
5217 // Clear the pinned variable
5219 ((Emitter) decl.Initializer).EmitExit (ec);
5220 if (decl.Declarators != null) {
5221 foreach (var d in decl.Declarators) {
5222 ((Emitter)d.Initializer).EmitExit (ec);
5227 protected override void CloneTo (CloneContext clonectx, Statement t)
5229 Fixed target = (Fixed) t;
5231 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5232 target.statement = statement.Clone (clonectx);
5235 public override object Accept (StructuralVisitor visitor)
5237 return visitor.Visit (this);
5241 public class Catch : Statement
5245 FullNamedExpression type_expr;
5246 CompilerAssign assign;
5249 public Catch (Block block, Location loc)
5257 public Block Block {
5263 public TypeSpec CatchType {
5269 public bool IsGeneral {
5271 return type_expr == null;
5275 public FullNamedExpression TypeExpression {
5284 public LocalVariable Variable {
5295 protected override void DoEmit (EmitContext ec)
5298 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5300 ec.BeginCatchBlock (CatchType);
5303 li.CreateBuilder (ec);
5306 // Special case hoisted catch variable, we have to use a temporary variable
5307 // to pass via anonymous storey initialization with the value still on top
5310 if (li.HoistedVariant != null) {
5311 LocalTemporary lt = new LocalTemporary (li.Type);
5314 // switch to assigning from the temporary variable and not from top of the stack
5315 assign.UpdateSource (lt);
5318 ec.Emit (OpCodes.Pop);
5324 public override bool Resolve (BlockContext ec)
5326 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5327 if (type_expr != null) {
5328 type = type_expr.ResolveAsType (ec);
5332 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5333 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5334 } else if (li != null) {
5336 li.PrepareForFlowAnalysis (ec);
5338 // source variable is at the top of the stack
5339 Expression source = new EmptyExpression (li.Type);
5340 if (li.Type.IsGenericParameter)
5341 source = new UnboxCast (source, li.Type);
5344 // Uses Location.Null to hide from symbol file
5346 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5347 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5351 return Block.Resolve (ec);
5355 protected override void CloneTo (CloneContext clonectx, Statement t)
5357 Catch target = (Catch) t;
5359 if (type_expr != null)
5360 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5362 target.block = clonectx.LookupBlock (block);
5366 public class TryFinally : TryFinallyBlock
5370 public TryFinally (Statement stmt, Block fini, Location loc)
5376 public Block Finallyblock {
5382 public override bool Resolve (BlockContext ec)
5386 ec.StartFlowBranching (this);
5388 if (!stmt.Resolve (ec))
5392 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5394 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5395 if (!fini.Resolve (ec))
5399 ec.EndFlowBranching ();
5401 ok &= base.Resolve (ec);
5406 protected override void EmitTryBody (EmitContext ec)
5411 public override void EmitFinallyBody (EmitContext ec)
5416 protected override void CloneTo (CloneContext clonectx, Statement t)
5418 TryFinally target = (TryFinally) t;
5420 target.stmt = (Statement) stmt.Clone (clonectx);
5422 target.fini = clonectx.LookupBlock (fini);
5425 public override object Accept (StructuralVisitor visitor)
5427 return visitor.Visit (this);
5431 public class TryCatch : ExceptionStatement
5434 List<Catch> clauses;
5435 readonly bool inside_try_finally;
5437 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5441 this.clauses = catch_clauses;
5442 this.inside_try_finally = inside_try_finally;
5445 public List<Catch> Clauses {
5451 public bool IsTryCatchFinally {
5453 return inside_try_finally;
5457 public override bool Resolve (BlockContext ec)
5461 ec.StartFlowBranching (this);
5463 if (!Block.Resolve (ec))
5466 for (int i = 0; i < clauses.Count; ++i) {
5468 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5470 if (!c.Resolve (ec)) {
5475 TypeSpec resolved_type = c.CatchType;
5476 for (int ii = 0; ii < clauses.Count; ++ii) {
5480 if (clauses[ii].IsGeneral) {
5481 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5484 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5487 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5490 ec.Report.Warning (1058, 1, c.loc,
5491 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5499 var ct = clauses[ii].CatchType;
5503 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5504 ec.Report.Error (160, c.loc,
5505 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5506 ct.GetSignatureForError ());
5512 ec.EndFlowBranching ();
5514 return base.Resolve (ec) && ok;
5517 protected sealed override void DoEmit (EmitContext ec)
5519 if (!inside_try_finally)
5520 EmitTryBodyPrepare (ec);
5524 foreach (Catch c in clauses)
5527 if (!inside_try_finally)
5528 ec.EndExceptionBlock ();
5531 protected override void CloneTo (CloneContext clonectx, Statement t)
5533 TryCatch target = (TryCatch) t;
5535 target.Block = clonectx.LookupBlock (Block);
5536 if (clauses != null){
5537 target.clauses = new List<Catch> ();
5538 foreach (Catch c in clauses)
5539 target.clauses.Add ((Catch) c.Clone (clonectx));
5543 public override object Accept (StructuralVisitor visitor)
5545 return visitor.Visit (this);
5549 public class Using : TryFinallyBlock
5551 public class VariableDeclaration : BlockVariableDeclaration
5553 Statement dispose_call;
5555 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5560 public VariableDeclaration (LocalVariable li, Location loc)
5566 public VariableDeclaration (Expression expr)
5569 loc = expr.Location;
5575 public bool IsNested { get; private set; }
5579 public void EmitDispose (EmitContext ec)
5581 dispose_call.Emit (ec);
5584 public override bool Resolve (BlockContext bc)
5589 return base.Resolve (bc, false);
5592 public Expression ResolveExpression (BlockContext bc)
5594 var e = Initializer.Resolve (bc);
5598 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5599 Initializer = ResolveInitializer (bc, Variable, e);
5603 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5605 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5606 initializer = initializer.Resolve (bc);
5607 if (initializer == null)
5610 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5611 Arguments args = new Arguments (1);
5612 args.Add (new Argument (initializer));
5613 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5614 if (initializer == null)
5617 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5618 dispose_call = CreateDisposeCall (bc, var);
5619 dispose_call.Resolve (bc);
5621 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5624 if (li == Variable) {
5625 CheckIDiposableConversion (bc, li, initializer);
5626 dispose_call = CreateDisposeCall (bc, li);
5627 dispose_call.Resolve (bc);
5630 return base.ResolveInitializer (bc, li, initializer);
5633 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5637 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5638 if (type.IsNullableType) {
5639 // it's handled in CreateDisposeCall
5643 bc.Report.SymbolRelatedToPreviousError (type);
5644 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5645 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5646 type.GetSignatureForError ());
5652 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5654 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5656 var loc = lv.Location;
5658 var idt = bc.BuiltinTypes.IDisposable;
5659 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5661 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5662 dispose_mg.InstanceExpression = type.IsNullableType ?
5663 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5667 // Hide it from symbol file via null location
5669 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5671 // Add conditional call when disposing possible null variable
5672 if (!type.IsStruct || type.IsNullableType)
5673 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5678 public void ResolveDeclaratorInitializer (BlockContext bc)
5680 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5683 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5685 for (int i = declarators.Count - 1; i >= 0; --i) {
5686 var d = declarators [i];
5687 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5688 vd.Initializer = d.Initializer;
5690 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5691 vd.dispose_call.Resolve (bc);
5693 stmt = new Using (vd, stmt, d.Variable.Location);
5700 public override object Accept (StructuralVisitor visitor)
5702 return visitor.Visit (this);
5706 VariableDeclaration decl;
5708 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5714 public Using (Expression expr, Statement stmt, Location loc)
5717 this.decl = new VariableDeclaration (expr);
5722 public Expression Expr {
5724 return decl.Variable == null ? decl.Initializer : null;
5728 public BlockVariableDeclaration Variables {
5736 public override void Emit (EmitContext ec)
5739 // Don't emit sequence point it will be set on variable declaration
5744 protected override void EmitTryBodyPrepare (EmitContext ec)
5747 base.EmitTryBodyPrepare (ec);
5750 protected override void EmitTryBody (EmitContext ec)
5755 public override void EmitFinallyBody (EmitContext ec)
5757 decl.EmitDispose (ec);
5760 public override bool Resolve (BlockContext ec)
5762 VariableReference vr;
5763 bool vr_locked = false;
5765 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5766 if (decl.Variable == null) {
5767 vr = decl.ResolveExpression (ec) as VariableReference;
5769 vr_locked = vr.IsLockedByStatement;
5770 vr.IsLockedByStatement = true;
5773 if (decl.IsNested) {
5774 decl.ResolveDeclaratorInitializer (ec);
5776 if (!decl.Resolve (ec))
5779 if (decl.Declarators != null) {
5780 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5788 ec.StartFlowBranching (this);
5792 ec.EndFlowBranching ();
5795 vr.IsLockedByStatement = vr_locked;
5802 protected override void CloneTo (CloneContext clonectx, Statement t)
5804 Using target = (Using) t;
5806 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5807 target.stmt = stmt.Clone (clonectx);
5810 public override object Accept (StructuralVisitor visitor)
5812 return visitor.Visit (this);
5817 /// Implementation of the foreach C# statement
5819 public class Foreach : Statement
5821 abstract class IteratorStatement : Statement
5823 protected readonly Foreach for_each;
5825 protected IteratorStatement (Foreach @foreach)
5827 this.for_each = @foreach;
5828 this.loc = @foreach.expr.Location;
5831 protected override void CloneTo (CloneContext clonectx, Statement target)
5833 throw new NotImplementedException ();
5836 public override void Emit (EmitContext ec)
5838 if (ec.EmitAccurateDebugInfo) {
5839 ec.Emit (OpCodes.Nop);
5846 sealed class ArrayForeach : IteratorStatement
5848 TemporaryVariableReference[] lengths;
5849 Expression [] length_exprs;
5850 StatementExpression[] counter;
5851 TemporaryVariableReference[] variables;
5853 TemporaryVariableReference copy;
5855 public ArrayForeach (Foreach @foreach, int rank)
5858 counter = new StatementExpression[rank];
5859 variables = new TemporaryVariableReference[rank];
5860 length_exprs = new Expression [rank];
5863 // Only use temporary length variables when dealing with
5864 // multi-dimensional arrays
5867 lengths = new TemporaryVariableReference [rank];
5870 public override bool Resolve (BlockContext ec)
5872 Block variables_block = for_each.variable.Block;
5873 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5876 int rank = length_exprs.Length;
5877 Arguments list = new Arguments (rank);
5878 for (int i = 0; i < rank; i++) {
5879 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5881 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5882 counter[i].Resolve (ec);
5885 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5887 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5888 lengths[i].Resolve (ec);
5890 Arguments args = new Arguments (1);
5891 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5892 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5895 list.Add (new Argument (v));
5898 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5903 if (for_each.type is VarExpr) {
5904 // Infer implicitly typed local variable from foreach array type
5905 var_type = access.Type;
5907 var_type = for_each.type.ResolveAsType (ec);
5909 if (var_type == null)
5912 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5917 for_each.variable.Type = var_type;
5919 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5920 if (variable_ref == null)
5923 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5927 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5928 ec.CurrentBranching.CreateSibling ();
5930 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5931 if (!for_each.body.Resolve (ec))
5933 ec.EndFlowBranching ();
5935 // There's no direct control flow from the end of the embedded statement to the end of the loop
5936 ec.CurrentBranching.CurrentUsageVector.Goto ();
5938 ec.EndFlowBranching ();
5943 protected override void DoEmit (EmitContext ec)
5945 copy.EmitAssign (ec, for_each.expr);
5947 int rank = length_exprs.Length;
5948 Label[] test = new Label [rank];
5949 Label[] loop = new Label [rank];
5951 for (int i = 0; i < rank; i++) {
5952 test [i] = ec.DefineLabel ();
5953 loop [i] = ec.DefineLabel ();
5955 if (lengths != null)
5956 lengths [i].EmitAssign (ec, length_exprs [i]);
5959 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5960 for (int i = 0; i < rank; i++) {
5961 variables [i].EmitAssign (ec, zero);
5963 ec.Emit (OpCodes.Br, test [i]);
5964 ec.MarkLabel (loop [i]);
5967 for_each.body.Emit (ec);
5969 ec.MarkLabel (ec.LoopBegin);
5970 ec.Mark (for_each.expr.Location);
5972 for (int i = rank - 1; i >= 0; i--){
5973 counter [i].Emit (ec);
5975 ec.MarkLabel (test [i]);
5976 variables [i].Emit (ec);
5978 if (lengths != null)
5979 lengths [i].Emit (ec);
5981 length_exprs [i].Emit (ec);
5983 ec.Emit (OpCodes.Blt, loop [i]);
5986 ec.MarkLabel (ec.LoopEnd);
5990 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
5992 class RuntimeDispose : Using.VariableDeclaration
5994 public RuntimeDispose (LocalVariable lv, Location loc)
5999 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6001 // Defered to runtime check
6004 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6006 var idt = bc.BuiltinTypes.IDisposable;
6009 // Fabricates code like
6011 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6014 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6016 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6017 dispose_variable.CreateReferenceExpression (bc, loc),
6018 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6019 loc), new NullLiteral (loc));
6021 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6023 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6024 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6026 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6027 return new If (idisaposable_test, dispose, loc);
6031 LocalVariable variable;
6033 Statement statement;
6034 ExpressionStatement init;
6035 TemporaryVariableReference enumerator_variable;
6036 bool ambiguous_getenumerator_name;
6038 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6041 this.variable = var;
6045 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6047 rc.Report.SymbolRelatedToPreviousError (enumerator);
6048 rc.Report.Error (202, loc,
6049 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6050 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6053 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6056 // Option 1: Try to match by name GetEnumerator first
6058 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6059 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6061 var mg = mexpr as MethodGroupExpr;
6063 mg.InstanceExpression = expr;
6064 Arguments args = new Arguments (0);
6065 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6067 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6068 if (ambiguous_getenumerator_name)
6071 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6077 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6080 PredefinedMember<MethodSpec> iface_candidate = null;
6081 var ptypes = rc.Module.PredefinedTypes;
6082 var gen_ienumerable = ptypes.IEnumerableGeneric;
6083 if (!gen_ienumerable.Define ())
6084 gen_ienumerable = null;
6086 var ifaces = t.Interfaces;
6087 if (ifaces != null) {
6088 foreach (var iface in ifaces) {
6089 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6090 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6091 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6092 rc.Report.Error (1640, loc,
6093 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6094 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6099 // TODO: Cache this somehow
6100 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6101 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6106 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6107 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6112 if (iface_candidate == null) {
6113 if (expr.Type != InternalType.ErrorType) {
6114 rc.Report.Error (1579, loc,
6115 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6116 expr.Type.GetSignatureForError (), "GetEnumerator");
6122 var method = iface_candidate.Resolve (loc);
6126 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6127 mg.InstanceExpression = expr;
6131 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6133 var ms = MemberCache.FindMember (enumerator.ReturnType,
6134 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6135 BindingRestriction.InstanceOnly) as MethodSpec;
6137 if (ms == null || !ms.IsPublic) {
6138 Error_WrongEnumerator (rc, enumerator);
6142 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6145 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6147 var ps = MemberCache.FindMember (enumerator.ReturnType,
6148 MemberFilter.Property ("Current", null),
6149 BindingRestriction.InstanceOnly) as PropertySpec;
6151 if (ps == null || !ps.IsPublic) {
6152 Error_WrongEnumerator (rc, enumerator);
6159 public override bool Resolve (BlockContext ec)
6161 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6164 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6165 } else if (expr.Type.IsNullableType) {
6166 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6169 var get_enumerator_mg = ResolveGetEnumerator (ec);
6170 if (get_enumerator_mg == null) {
6174 var get_enumerator = get_enumerator_mg.BestCandidate;
6175 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6176 enumerator_variable.Resolve (ec);
6178 // Prepare bool MoveNext ()
6179 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6180 if (move_next_mg == null) {
6184 move_next_mg.InstanceExpression = enumerator_variable;
6186 // Prepare ~T~ Current { get; }
6187 var current_prop = ResolveCurrent (ec, get_enumerator);
6188 if (current_prop == null) {
6192 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6193 if (current_pe == null)
6196 VarExpr ve = for_each.type as VarExpr;
6200 // Source type is dynamic, set element type to dynamic too
6201 variable.Type = ec.BuiltinTypes.Dynamic;
6203 // Infer implicitly typed local variable from foreach enumerable type
6204 variable.Type = current_pe.Type;
6208 // Explicit cast of dynamic collection elements has to be done at runtime
6209 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6212 variable.Type = for_each.type.ResolveAsType (ec);
6214 if (variable.Type == null)
6217 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6218 if (current_pe == null)
6222 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6223 if (variable_ref == null)
6226 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6228 var init = new Invocation (get_enumerator_mg, null);
6230 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6231 for_each.body, Location.Null);
6233 var enum_type = enumerator_variable.Type;
6236 // Add Dispose method call when enumerator can be IDisposable
6238 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6239 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6241 // Runtime Dispose check
6243 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6244 vd.Initializer = init;
6245 statement = new Using (vd, statement, Location.Null);
6248 // No Dispose call needed
6250 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6251 this.init.Resolve (ec);
6255 // Static Dispose check
6257 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6258 vd.Initializer = init;
6259 statement = new Using (vd, statement, Location.Null);
6262 return statement.Resolve (ec);
6265 protected override void DoEmit (EmitContext ec)
6267 enumerator_variable.LocalInfo.CreateBuilder (ec);
6270 init.EmitStatement (ec);
6272 statement.Emit (ec);
6275 #region IErrorHandler Members
6277 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6279 ec.Report.SymbolRelatedToPreviousError (best);
6280 ec.Report.Warning (278, 2, expr.Location,
6281 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6282 expr.Type.GetSignatureForError (), "enumerable",
6283 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6285 ambiguous_getenumerator_name = true;
6289 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6294 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6299 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6308 LocalVariable variable;
6310 Statement statement;
6313 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6316 this.variable = var;
6318 this.statement = stmt;
6323 public Expression Expr {
6324 get { return expr; }
6327 public Statement Statement {
6328 get { return statement; }
6331 public Expression TypeExpression {
6332 get { return type; }
6335 public LocalVariable Variable {
6336 get { return variable; }
6339 public override bool Resolve (BlockContext ec)
6341 expr = expr.Resolve (ec);
6346 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6350 body.AddStatement (statement);
6352 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6353 statement = new ArrayForeach (this, 1);
6354 } else if (expr.Type is ArrayContainer) {
6355 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6357 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6358 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6359 expr.ExprClassName);
6363 statement = new CollectionForeach (this, variable, expr);
6366 return statement.Resolve (ec);
6369 protected override void DoEmit (EmitContext ec)
6371 variable.CreateBuilder (ec);
6373 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6374 ec.LoopBegin = ec.DefineLabel ();
6375 ec.LoopEnd = ec.DefineLabel ();
6377 statement.Emit (ec);
6379 ec.LoopBegin = old_begin;
6380 ec.LoopEnd = old_end;
6383 protected override void CloneTo (CloneContext clonectx, Statement t)
6385 Foreach target = (Foreach) t;
6387 target.type = type.Clone (clonectx);
6388 target.expr = expr.Clone (clonectx);
6389 target.body = (Block) body.Clone (clonectx);
6390 target.statement = statement.Clone (clonectx);
6393 public override object Accept (StructuralVisitor visitor)
6395 return visitor.Visit (this);