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)
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 && l.ReturnTypeInference != null && expr != null) {
935 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
944 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
945 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
948 if (am != null && block_return_type == ec.ReturnType) {
949 ec.Report.Error (1662, loc,
950 "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",
951 am.ContainerType, am.GetSignatureForError ());
960 protected override void DoEmit (EmitContext ec)
965 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
966 if (async_body != null) {
967 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
969 // It's null for await without async
970 if (async_return != null) {
971 async_return.EmitAssign (ec);
976 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
982 if (unwind_protect || ec.EmitAccurateDebugInfo)
983 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
986 if (unwind_protect) {
987 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
988 } else if (ec.EmitAccurateDebugInfo) {
989 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
991 ec.Emit (OpCodes.Ret);
995 void Error_ReturnFromIterator (ResolveContext rc)
997 rc.Report.Error (1622, loc,
998 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1001 protected override void CloneTo (CloneContext clonectx, Statement t)
1003 Return target = (Return) t;
1004 // It's null for simple return;
1006 target.expr = expr.Clone (clonectx);
1009 public override object Accept (StructuralVisitor visitor)
1011 return visitor.Visit (this);
1015 public class Goto : Statement {
1017 LabeledStatement label;
1018 bool unwind_protect;
1020 public override bool Resolve (BlockContext ec)
1022 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1023 ec.CurrentBranching.CurrentUsageVector.Goto ();
1027 public Goto (string label, Location l)
1033 public string Target {
1034 get { return target; }
1037 public void SetResolvedTarget (LabeledStatement label)
1040 label.AddReference ();
1043 protected override void CloneTo (CloneContext clonectx, Statement target)
1048 protected override void DoEmit (EmitContext ec)
1051 throw new InternalErrorException ("goto emitted before target resolved");
1052 Label l = label.LabelTarget (ec);
1053 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1056 public override object Accept (StructuralVisitor visitor)
1058 return visitor.Visit (this);
1062 public class LabeledStatement : Statement {
1069 FlowBranching.UsageVector vectors;
1071 public LabeledStatement (string name, Block block, Location l)
1078 public Label LabelTarget (EmitContext ec)
1083 label = ec.DefineLabel ();
1088 public Block Block {
1094 public string Name {
1095 get { return name; }
1098 public bool IsDefined {
1099 get { return defined; }
1102 public bool HasBeenReferenced {
1103 get { return referenced; }
1106 public FlowBranching.UsageVector JumpOrigins {
1107 get { return vectors; }
1110 public void AddUsageVector (FlowBranching.UsageVector vector)
1112 vector = vector.Clone ();
1113 vector.Next = vectors;
1117 protected override void CloneTo (CloneContext clonectx, Statement target)
1122 public override bool Resolve (BlockContext ec)
1124 // this flow-branching will be terminated when the surrounding block ends
1125 ec.StartFlowBranching (this);
1129 protected override void DoEmit (EmitContext ec)
1131 if (!HasBeenReferenced)
1132 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1135 ec.MarkLabel (label);
1138 public void AddReference ()
1143 public override object Accept (StructuralVisitor visitor)
1145 return visitor.Visit (this);
1151 /// `goto default' statement
1153 public class GotoDefault : Statement {
1155 public GotoDefault (Location l)
1160 protected override void CloneTo (CloneContext clonectx, Statement target)
1165 public override bool Resolve (BlockContext ec)
1167 ec.CurrentBranching.CurrentUsageVector.Goto ();
1169 if (ec.Switch == null) {
1170 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1174 if (ec.Switch.DefaultLabel == null) {
1175 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1182 protected override void DoEmit (EmitContext ec)
1184 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1187 public override object Accept (StructuralVisitor visitor)
1189 return visitor.Visit (this);
1194 /// `goto case' statement
1196 public class GotoCase : Statement {
1200 public GotoCase (Expression e, Location l)
1206 public Expression Expr {
1212 public override bool Resolve (BlockContext ec)
1214 if (ec.Switch == null){
1215 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1219 ec.CurrentBranching.CurrentUsageVector.Goto ();
1221 expr = expr.Resolve (ec);
1225 Constant c = expr as Constant;
1227 ec.Report.Error (150, expr.Location, "A constant value is expected");
1232 if (ec.Switch.IsNullable && c is NullLiteral) {
1235 TypeSpec type = ec.Switch.SwitchType;
1236 res = c.Reduce (ec, type);
1238 c.Error_ValueCannotBeConverted (ec, type, true);
1242 if (!Convert.ImplicitStandardConversionExists (c, type))
1243 ec.Report.Warning (469, 2, loc,
1244 "The `goto case' value is not implicitly convertible to type `{0}'",
1245 TypeManager.CSharpName (type));
1249 sl = ec.Switch.ResolveGotoCase (ec, res);
1253 protected override void DoEmit (EmitContext ec)
1255 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1258 protected override void CloneTo (CloneContext clonectx, Statement t)
1260 GotoCase target = (GotoCase) t;
1262 target.expr = expr.Clone (clonectx);
1265 public override object Accept (StructuralVisitor visitor)
1267 return visitor.Visit (this);
1271 public class Throw : Statement {
1274 public Throw (Expression expr, Location l)
1280 public Expression Expr {
1286 public override bool Resolve (BlockContext ec)
1289 ec.CurrentBranching.CurrentUsageVector.Goto ();
1290 return ec.CurrentBranching.CheckRethrow (loc);
1293 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1294 ec.CurrentBranching.CurrentUsageVector.Goto ();
1299 var et = ec.BuiltinTypes.Exception;
1300 if (Convert.ImplicitConversionExists (ec, expr, et))
1301 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1303 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1308 protected override void DoEmit (EmitContext ec)
1311 ec.Emit (OpCodes.Rethrow);
1315 ec.Emit (OpCodes.Throw);
1319 protected override void CloneTo (CloneContext clonectx, Statement t)
1321 Throw target = (Throw) t;
1324 target.expr = expr.Clone (clonectx);
1327 public override object Accept (StructuralVisitor visitor)
1329 return visitor.Visit (this);
1333 public class Break : Statement {
1335 public Break (Location l)
1340 bool unwind_protect;
1342 public override bool Resolve (BlockContext ec)
1344 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1345 ec.CurrentBranching.CurrentUsageVector.Goto ();
1349 protected override void DoEmit (EmitContext ec)
1351 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1354 protected override void CloneTo (CloneContext clonectx, Statement t)
1359 public override object Accept (StructuralVisitor visitor)
1361 return visitor.Visit (this);
1365 public class Continue : Statement {
1367 public Continue (Location l)
1372 bool unwind_protect;
1374 public override bool Resolve (BlockContext ec)
1376 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1377 ec.CurrentBranching.CurrentUsageVector.Goto ();
1381 protected override void DoEmit (EmitContext ec)
1383 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1386 protected override void CloneTo (CloneContext clonectx, Statement t)
1391 public override object Accept (StructuralVisitor visitor)
1393 return visitor.Visit (this);
1397 public interface ILocalVariable
1399 void Emit (EmitContext ec);
1400 void EmitAssign (EmitContext ec);
1401 void EmitAddressOf (EmitContext ec);
1404 public interface INamedBlockVariable
1406 Block Block { get; }
1407 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1408 bool IsDeclared { get; }
1409 bool IsParameter { get; }
1410 Location Location { get; }
1413 public class BlockVariableDeclaration : Statement
1415 public class Declarator
1418 Expression initializer;
1420 public Declarator (LocalVariable li, Expression initializer)
1422 if (li.Type != null)
1423 throw new ArgumentException ("Expected null variable type");
1426 this.initializer = initializer;
1429 public Declarator (Declarator clone, Expression initializer)
1432 this.initializer = initializer;
1437 public LocalVariable Variable {
1443 public Expression Initializer {
1448 initializer = value;
1455 Expression initializer;
1456 protected FullNamedExpression type_expr;
1457 protected LocalVariable li;
1458 protected List<Declarator> declarators;
1461 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1463 this.type_expr = type;
1465 this.loc = type_expr.Location;
1468 protected BlockVariableDeclaration (LocalVariable li)
1475 public List<Declarator> Declarators {
1481 public Expression Initializer {
1486 initializer = value;
1490 public FullNamedExpression TypeExpression {
1496 public LocalVariable Variable {
1504 public void AddDeclarator (Declarator decl)
1506 if (declarators == null)
1507 declarators = new List<Declarator> ();
1509 declarators.Add (decl);
1512 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1514 if (bc.Report.Errors != 0)
1517 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1519 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1520 new MemberName (li.Name, li.Location), null);
1522 container.AddField (f);
1525 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1529 public override bool Resolve (BlockContext bc)
1531 return Resolve (bc, true);
1534 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1536 if (type == null && !li.IsCompilerGenerated) {
1537 var vexpr = type_expr as VarExpr;
1540 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1541 // same name exists or as a keyword when no type was found
1543 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1544 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1545 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1548 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1552 if (li.IsConstant) {
1553 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1557 if (Initializer == null) {
1558 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1562 if (declarators != null) {
1563 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1567 Initializer = Initializer.Resolve (bc);
1568 if (Initializer != null) {
1569 ((VarExpr) type_expr).InferType (bc, Initializer);
1570 type = type_expr.Type;
1572 // Set error type to indicate the var was placed correctly but could
1575 // var a = missing ();
1577 type = InternalType.ErrorType;
1582 type = type_expr.ResolveAsType (bc);
1586 if (li.IsConstant && !type.IsConstantCompatible) {
1587 Const.Error_InvalidConstantType (type, loc, bc.Report);
1592 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1597 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1599 CreateEvaluatorVariable (bc, li);
1601 li.PrepareForFlowAnalysis (bc);
1604 if (initializer != null) {
1605 initializer = ResolveInitializer (bc, li, initializer);
1606 // li.Variable.DefinitelyAssigned
1609 if (declarators != null) {
1610 foreach (var d in declarators) {
1611 d.Variable.Type = li.Type;
1613 CreateEvaluatorVariable (bc, d.Variable);
1615 d.Variable.PrepareForFlowAnalysis (bc);
1618 if (d.Initializer != null && resolveDeclaratorInitializers) {
1619 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1620 // d.Variable.DefinitelyAssigned
1628 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1630 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1631 return a.ResolveStatement (bc);
1634 protected override void DoEmit (EmitContext ec)
1636 li.CreateBuilder (ec);
1638 if (Initializer != null)
1639 ((ExpressionStatement) Initializer).EmitStatement (ec);
1641 if (declarators != null) {
1642 foreach (var d in declarators) {
1643 d.Variable.CreateBuilder (ec);
1644 if (d.Initializer != null) {
1645 ec.Mark (d.Variable.Location);
1646 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1652 protected override void CloneTo (CloneContext clonectx, Statement target)
1654 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1656 if (type_expr != null)
1657 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1659 if (initializer != null)
1660 t.initializer = initializer.Clone (clonectx);
1662 if (declarators != null) {
1663 t.declarators = null;
1664 foreach (var d in declarators)
1665 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1669 public override object Accept (StructuralVisitor visitor)
1671 return visitor.Visit (this);
1675 public class BlockConstantDeclaration : BlockVariableDeclaration
1677 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1682 public override void Emit (EmitContext ec)
1684 // Nothing to emit, not even sequence point
1687 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1689 initializer = initializer.Resolve (bc);
1690 if (initializer == null)
1693 var c = initializer as Constant;
1695 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1699 c = c.ConvertImplicitly (li.Type);
1701 if (TypeSpec.IsReferenceType (li.Type))
1702 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1704 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1709 li.ConstantValue = c;
1713 public override object Accept (StructuralVisitor visitor)
1715 return visitor.Visit (this);
1720 // The information about a user-perceived local variable
1722 public class LocalVariable : INamedBlockVariable, ILocalVariable
1729 AddressTaken = 1 << 2,
1730 CompilerGenerated = 1 << 3,
1732 ForeachVariable = 1 << 5,
1733 FixedVariable = 1 << 6,
1734 UsingVariable = 1 << 7,
1735 // DefinitelyAssigned = 1 << 8,
1738 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1742 readonly string name;
1743 readonly Location loc;
1744 readonly Block block;
1746 Constant const_value;
1748 public VariableInfo VariableInfo;
1749 HoistedVariable hoisted_variant;
1751 LocalBuilder builder;
1753 public LocalVariable (Block block, string name, Location loc)
1760 public LocalVariable (Block block, string name, Flags flags, Location loc)
1761 : this (block, name, loc)
1767 // Used by variable declarators
1769 public LocalVariable (LocalVariable li, string name, Location loc)
1770 : this (li.block, name, li.flags, loc)
1776 public bool AddressTaken {
1778 return (flags & Flags.AddressTaken) != 0;
1782 public Block Block {
1788 public Constant ConstantValue {
1793 const_value = value;
1798 // Hoisted local variable variant
1800 public HoistedVariable HoistedVariant {
1802 return hoisted_variant;
1805 hoisted_variant = value;
1809 public bool IsDeclared {
1811 return type != null;
1815 public bool IsCompilerGenerated {
1817 return (flags & Flags.CompilerGenerated) != 0;
1821 public bool IsConstant {
1823 return (flags & Flags.Constant) != 0;
1827 public bool IsLocked {
1829 return (flags & Flags.IsLocked) != 0;
1832 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1836 public bool IsThis {
1838 return (flags & Flags.IsThis) != 0;
1842 public bool IsFixed {
1844 return (flags & Flags.FixedVariable) != 0;
1848 bool INamedBlockVariable.IsParameter {
1854 public bool IsReadonly {
1856 return (flags & Flags.ReadonlyMask) != 0;
1860 public Location Location {
1866 public string Name {
1872 public TypeSpec Type {
1883 public void CreateBuilder (EmitContext ec)
1885 if ((flags & Flags.Used) == 0) {
1886 if (VariableInfo == null) {
1887 // Missing flow analysis or wrong variable flags
1888 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1891 if (VariableInfo.IsEverAssigned)
1892 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1894 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1897 if (HoistedVariant != null)
1900 if (builder != null) {
1901 if ((flags & Flags.CompilerGenerated) != 0)
1904 // To avoid Used warning duplicates
1905 throw new InternalErrorException ("Already created variable `{0}'", name);
1909 // All fixed variabled are pinned, a slot has to be alocated
1911 builder = ec.DeclareLocal (Type, IsFixed);
1912 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1913 ec.DefineLocalVariable (name, builder);
1916 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1918 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1923 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1925 if (IsConstant && const_value != null)
1926 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1928 return new LocalVariableReference (this, loc);
1931 public void Emit (EmitContext ec)
1933 // TODO: Need something better for temporary variables
1934 if ((flags & Flags.CompilerGenerated) != 0)
1937 ec.Emit (OpCodes.Ldloc, builder);
1940 public void EmitAssign (EmitContext ec)
1942 // TODO: Need something better for temporary variables
1943 if ((flags & Flags.CompilerGenerated) != 0)
1946 ec.Emit (OpCodes.Stloc, builder);
1949 public void EmitAddressOf (EmitContext ec)
1951 ec.Emit (OpCodes.Ldloca, builder);
1954 public static string GetCompilerGeneratedName (Block block)
1956 // HACK: Debugger depends on the name semantics
1957 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1960 public string GetReadOnlyContext ()
1962 switch (flags & Flags.ReadonlyMask) {
1963 case Flags.FixedVariable:
1964 return "fixed variable";
1965 case Flags.ForeachVariable:
1966 return "foreach iteration variable";
1967 case Flags.UsingVariable:
1968 return "using variable";
1971 throw new InternalErrorException ("Variable is not readonly");
1974 public bool IsThisAssigned (BlockContext ec, Block block)
1976 if (VariableInfo == null)
1977 throw new Exception ();
1979 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1982 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1985 public bool IsAssigned (BlockContext ec)
1987 if (VariableInfo == null)
1988 throw new Exception ();
1990 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1993 public void PrepareForFlowAnalysis (BlockContext bc)
1996 // No need for definitely assigned check for these guys
1998 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2001 VariableInfo = new VariableInfo (this, bc.FlowOffset);
2002 bc.FlowOffset += VariableInfo.Length;
2006 // Mark the variables as referenced in the user code
2008 public void SetIsUsed ()
2010 flags |= Flags.Used;
2013 public void SetHasAddressTaken ()
2015 flags |= (Flags.AddressTaken | Flags.Used);
2018 public override string ToString ()
2020 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2025 /// Block represents a C# block.
2029 /// This class is used in a number of places: either to represent
2030 /// explicit blocks that the programmer places or implicit blocks.
2032 /// Implicit blocks are used as labels or to introduce variable
2035 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2036 /// they contain extra information that is not necessary on normal blocks.
2038 public class Block : Statement {
2045 HasCapturedVariable = 64,
2046 HasCapturedThis = 1 << 7,
2047 IsExpressionTree = 1 << 8,
2048 CompilerGenerated = 1 << 9,
2049 HasAsyncModifier = 1 << 10,
2051 YieldBlock = 1 << 12,
2052 AwaitBlock = 1 << 13
2055 public Block Parent;
2056 public Location StartLocation;
2057 public Location EndLocation;
2059 public ExplicitBlock Explicit;
2060 public ParametersBlock ParametersBlock;
2062 protected Flags flags;
2065 // The statements in this block
2067 protected List<Statement> statements;
2069 protected List<Statement> scope_initializers;
2071 int? resolving_init_idx;
2077 public int ID = id++;
2079 static int clone_id_counter;
2083 // int assignable_slots;
2085 public Block (Block parent, Location start, Location end)
2086 : this (parent, 0, start, end)
2090 public Block (Block parent, Flags flags, Location start, Location end)
2092 if (parent != null) {
2093 // the appropriate constructors will fixup these fields
2094 ParametersBlock = parent.ParametersBlock;
2095 Explicit = parent.Explicit;
2098 this.Parent = parent;
2100 this.StartLocation = start;
2101 this.EndLocation = end;
2103 statements = new List<Statement> (4);
2105 this.original = this;
2110 public bool HasUnreachableClosingBrace {
2112 return (flags & Flags.HasRet) != 0;
2115 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2119 public Block Original {
2128 public bool IsCompilerGenerated {
2129 get { return (flags & Flags.CompilerGenerated) != 0; }
2130 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2133 public bool Unchecked {
2134 get { return (flags & Flags.Unchecked) != 0; }
2135 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2138 public bool Unsafe {
2139 get { return (flags & Flags.Unsafe) != 0; }
2140 set { flags |= Flags.Unsafe; }
2143 public List<Statement> Statements {
2144 get { return statements; }
2149 public void SetEndLocation (Location loc)
2154 public void AddLabel (LabeledStatement target)
2156 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2159 public void AddLocalName (LocalVariable li)
2161 AddLocalName (li.Name, li);
2164 public void AddLocalName (string name, INamedBlockVariable li)
2166 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2169 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2171 if (reason == null) {
2172 Error_AlreadyDeclared (name, variable);
2176 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2177 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2178 "to `{0}', which is already used in a `{1}' scope to denote something else",
2182 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2184 var pi = variable as ParametersBlock.ParameterInfo;
2186 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2188 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2189 "A local variable named `{0}' is already defined in this scope", name);
2193 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2195 ParametersBlock.TopBlock.Report.Error (412, loc,
2196 "The type parameter name `{0}' is the same as local variable or parameter name",
2201 // It should be used by expressions which require to
2202 // register a statement during resolve process.
2204 public void AddScopeStatement (Statement s)
2206 if (scope_initializers == null)
2207 scope_initializers = new List<Statement> ();
2210 // Simple recursive helper, when resolve scope initializer another
2211 // new scope initializer can be added, this ensures it's initialized
2212 // before existing one. For now this can happen with expression trees
2213 // in base ctor initializer only
2215 if (resolving_init_idx.HasValue) {
2216 scope_initializers.Insert (resolving_init_idx.Value, s);
2217 ++resolving_init_idx;
2219 scope_initializers.Add (s);
2223 public void AddStatement (Statement s)
2228 public int AssignableSlots {
2230 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2232 // return assignable_slots;
2236 public LabeledStatement LookupLabel (string name)
2238 return ParametersBlock.TopBlock.GetLabel (name, this);
2241 public override bool Resolve (BlockContext ec)
2243 if ((flags & Flags.Resolved) != 0)
2246 Block prev_block = ec.CurrentBlock;
2248 bool unreachable = ec.IsUnreachable;
2249 bool prev_unreachable = unreachable;
2251 ec.CurrentBlock = this;
2252 ec.StartFlowBranching (this);
2255 // Compiler generated scope statements
2257 if (scope_initializers != null) {
2258 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2259 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2262 resolving_init_idx = null;
2266 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2267 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2268 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2269 // responsible for handling the situation.
2271 int statement_count = statements.Count;
2272 for (int ix = 0; ix < statement_count; ix++){
2273 Statement s = statements [ix];
2276 // Warn if we detect unreachable code.
2279 if (s is EmptyStatement)
2282 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2283 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2284 ec.UnreachableReported = true;
2289 // Note that we're not using ResolveUnreachable() for unreachable
2290 // statements here. ResolveUnreachable() creates a temporary
2291 // flow branching and kills it afterwards. This leads to problems
2292 // if you have two unreachable statements where the first one
2293 // assigns a variable and the second one tries to access it.
2296 if (!s.Resolve (ec)) {
2298 if (!ec.IsInProbingMode)
2299 statements [ix] = new EmptyStatement (s.loc);
2304 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2305 statements [ix] = new EmptyStatement (s.loc);
2307 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2309 ec.IsUnreachable = true;
2310 } else if (ec.IsUnreachable)
2311 ec.IsUnreachable = false;
2314 if (unreachable != prev_unreachable) {
2315 ec.IsUnreachable = prev_unreachable;
2316 ec.UnreachableReported = false;
2319 while (ec.CurrentBranching is FlowBranchingLabeled)
2320 ec.EndFlowBranching ();
2322 bool flow_unreachable = ec.EndFlowBranching ();
2324 ec.CurrentBlock = prev_block;
2326 if (flow_unreachable)
2327 flags |= Flags.HasRet;
2329 // If we're a non-static `struct' constructor which doesn't have an
2330 // initializer, then we must initialize all of the struct's fields.
2331 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2334 flags |= Flags.Resolved;
2338 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2340 bool unreachable = false;
2341 if (warn && !ec.UnreachableReported) {
2342 ec.UnreachableReported = true;
2344 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2347 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2348 fb.CurrentUsageVector.IsUnreachable = true;
2349 bool ok = Resolve (ec);
2350 ec.KillFlowBranching ();
2353 ec.UnreachableReported = false;
2358 protected override void DoEmit (EmitContext ec)
2360 for (int ix = 0; ix < statements.Count; ix++){
2361 statements [ix].Emit (ec);
2365 public override void Emit (EmitContext ec)
2367 if (scope_initializers != null)
2368 EmitScopeInitializers (ec);
2373 protected void EmitScopeInitializers (EmitContext ec)
2375 foreach (Statement s in scope_initializers)
2380 public override string ToString ()
2382 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2386 protected override void CloneTo (CloneContext clonectx, Statement t)
2388 Block target = (Block) t;
2390 target.clone_id = clone_id_counter++;
2393 clonectx.AddBlockMap (this, target);
2394 if (original != this)
2395 clonectx.AddBlockMap (original, target);
2397 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2398 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2401 target.Parent = clonectx.RemapBlockCopy (Parent);
2403 target.statements = new List<Statement> (statements.Count);
2404 foreach (Statement s in statements)
2405 target.statements.Add (s.Clone (clonectx));
2408 public override object Accept (StructuralVisitor visitor)
2410 return visitor.Visit (this);
2414 public class ExplicitBlock : Block
2416 protected AnonymousMethodStorey am_storey;
2418 public ExplicitBlock (Block parent, Location start, Location end)
2419 : this (parent, (Flags) 0, start, end)
2423 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2424 : base (parent, flags, start, end)
2426 this.Explicit = this;
2431 public AnonymousMethodStorey AnonymousMethodStorey {
2437 public bool HasAwait {
2439 return (flags & Flags.AwaitBlock) != 0;
2443 public bool HasCapturedThis {
2445 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2448 return (flags & Flags.HasCapturedThis) != 0;
2453 // Used to indicate that the block has reference to parent
2454 // block and cannot be made static when defining anonymous method
2456 public bool HasCapturedVariable {
2458 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2461 return (flags & Flags.HasCapturedVariable) != 0;
2465 public bool HasYield {
2467 return (flags & Flags.YieldBlock) != 0;
2474 // Creates anonymous method storey in current block
2476 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2479 // Return same story for iterator and async blocks unless we are
2480 // in nested anonymous method
2482 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2483 return ec.CurrentAnonymousMethod.Storey;
2485 if (am_storey == null) {
2486 MemberBase mc = ec.MemberContext as MemberBase;
2489 // Creates anonymous method storey for this block
2491 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2497 public override void Emit (EmitContext ec)
2499 if (am_storey != null) {
2500 DefineStoreyContainer (ec, am_storey);
2501 am_storey.EmitStoreyInstantiation (ec, this);
2504 if (scope_initializers != null)
2505 EmitScopeInitializers (ec);
2507 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2508 ec.Emit (OpCodes.Nop);
2519 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2520 ec.Emit (OpCodes.Nop);
2524 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2526 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2527 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2528 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2532 // Creates anonymous method storey
2534 storey.CreateContainer ();
2535 storey.DefineContainer ();
2537 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2540 // Only first storey in path will hold this reference. All children blocks will
2541 // reference it indirectly using $ref field
2543 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2544 if (b.Parent != null) {
2545 var s = b.Parent.Explicit.AnonymousMethodStorey;
2547 storey.HoistedThis = s.HoistedThis;
2552 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2553 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2555 if (storey.HoistedThis != null)
2561 // We are the first storey on path and 'this' has to be hoisted
2563 if (storey.HoistedThis == null) {
2564 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2566 // ThisReferencesFromChildrenBlock holds all reference even if they
2567 // are not on this path. It saves some memory otherwise it'd have to
2568 // be in every explicit block. We run this check to see if the reference
2569 // is valid for this storey
2571 Block block_on_path = ref_block;
2572 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2574 if (block_on_path == null)
2577 if (storey.HoistedThis == null) {
2578 storey.AddCapturedThisField (ec);
2581 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2582 if (b.AnonymousMethodStorey != null) {
2583 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2584 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2587 // Stop propagation inside same top block
2589 if (b.ParametersBlock == ParametersBlock.Original)
2592 b = b.ParametersBlock;
2595 var pb = b as ParametersBlock;
2596 if (pb != null && pb.StateMachine != null) {
2597 if (pb.StateMachine == storey)
2600 pb.StateMachine.AddParentStoreyReference (ec, storey);
2603 b.HasCapturedVariable = true;
2609 var ref_blocks = storey.ReferencesFromChildrenBlock;
2610 if (ref_blocks != null) {
2611 foreach (ExplicitBlock ref_block in ref_blocks) {
2612 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2613 if (b.AnonymousMethodStorey != null) {
2614 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2617 // Stop propagation inside same top block
2619 if (b.ParametersBlock == ParametersBlock.Original)
2622 b = b.ParametersBlock;
2625 var pb = b as ParametersBlock;
2626 if (pb != null && pb.StateMachine != null) {
2627 if (pb.StateMachine == storey)
2630 pb.StateMachine.AddParentStoreyReference (ec, storey);
2633 b.HasCapturedVariable = true;
2639 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2642 public void RegisterAsyncAwait ()
2645 while ((block.flags & Flags.AwaitBlock) == 0) {
2646 block.flags |= Flags.AwaitBlock;
2648 if (block is ParametersBlock)
2651 block = block.Parent.Explicit;
2655 public void RegisterIteratorYield ()
2658 while ((block.flags & Flags.YieldBlock) == 0) {
2659 block.flags |= Flags.YieldBlock;
2661 if (block.Parent == null)
2664 block = block.Parent.Explicit;
2668 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2670 tryBlock.statements = statements;
2671 statements = new List<Statement> (1);
2672 statements.Add (tf);
2677 // ParametersBlock was introduced to support anonymous methods
2678 // and lambda expressions
2680 public class ParametersBlock : ExplicitBlock
2682 public class ParameterInfo : INamedBlockVariable
2684 readonly ParametersBlock block;
2686 public VariableInfo VariableInfo;
2689 public ParameterInfo (ParametersBlock block, int index)
2697 public ParametersBlock Block {
2703 Block INamedBlockVariable.Block {
2709 public bool IsDeclared {
2715 public bool IsParameter {
2721 public bool IsLocked {
2730 public Location Location {
2732 return Parameter.Location;
2736 public Parameter Parameter {
2738 return block.Parameters [index];
2742 public TypeSpec ParameterType {
2744 return Parameter.Type;
2750 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2752 return new ParameterReference (this, loc);
2757 // Block is converted into an expression
2759 sealed class BlockScopeExpression : Expression
2762 readonly ParametersBlock block;
2764 public BlockScopeExpression (Expression child, ParametersBlock block)
2770 public override bool ContainsEmitWithAwait ()
2772 return child.ContainsEmitWithAwait ();
2775 public override Expression CreateExpressionTree (ResolveContext ec)
2777 throw new NotSupportedException ();
2780 protected override Expression DoResolve (ResolveContext ec)
2785 child = child.Resolve (ec);
2789 eclass = child.eclass;
2794 public override void Emit (EmitContext ec)
2796 block.EmitScopeInitializers (ec);
2801 protected ParametersCompiled parameters;
2802 protected ParameterInfo[] parameter_info;
2804 protected bool unreachable;
2805 protected ToplevelBlock top_block;
2806 protected StateMachine state_machine;
2808 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2809 : base (parent, 0, start, start)
2811 if (parameters == null)
2812 throw new ArgumentNullException ("parameters");
2814 this.parameters = parameters;
2815 ParametersBlock = this;
2817 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2819 this.top_block = parent.ParametersBlock.top_block;
2820 ProcessParameters ();
2823 protected ParametersBlock (ParametersCompiled parameters, Location start)
2824 : base (null, 0, start, start)
2826 if (parameters == null)
2827 throw new ArgumentNullException ("parameters");
2829 this.parameters = parameters;
2830 ParametersBlock = this;
2834 // It's supposed to be used by method body implementation of anonymous methods
2836 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2837 : base (null, 0, source.StartLocation, source.EndLocation)
2839 this.parameters = parameters;
2840 this.statements = source.statements;
2841 this.scope_initializers = source.scope_initializers;
2843 this.resolved = true;
2844 this.unreachable = source.unreachable;
2845 this.am_storey = source.am_storey;
2846 this.state_machine = source.state_machine;
2848 ParametersBlock = this;
2851 // Overwrite original for comparison purposes when linking cross references
2852 // between anonymous methods
2854 Original = source.Original;
2859 public bool IsAsync {
2861 return (flags & Flags.HasAsyncModifier) != 0;
2864 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2869 // Block has been converted to expression tree
2871 public bool IsExpressionTree {
2873 return (flags & Flags.IsExpressionTree) != 0;
2878 // The parameters for the block.
2880 public ParametersCompiled Parameters {
2886 public StateMachine StateMachine {
2888 return state_machine;
2892 public ToplevelBlock TopBlock {
2898 public bool Resolved {
2900 return (flags & Flags.Resolved) != 0;
2904 public int TemporaryLocalsCount { get; set; }
2909 // Check whether all `out' parameters have been assigned.
2911 public void CheckOutParameters (FlowBranching.UsageVector vector)
2913 if (vector.IsUnreachable)
2916 int n = parameter_info == null ? 0 : parameter_info.Length;
2918 for (int i = 0; i < n; i++) {
2919 VariableInfo var = parameter_info[i].VariableInfo;
2924 if (vector.IsAssigned (var, false))
2927 var p = parameter_info[i].Parameter;
2928 TopBlock.Report.Error (177, p.Location,
2929 "The out parameter `{0}' must be assigned to before control leaves the current method",
2934 public override Expression CreateExpressionTree (ResolveContext ec)
2936 if (statements.Count == 1) {
2937 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2938 if (scope_initializers != null)
2939 expr = new BlockScopeExpression (expr, this);
2944 return base.CreateExpressionTree (ec);
2947 public override void Emit (EmitContext ec)
2949 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2950 DefineStoreyContainer (ec, state_machine);
2951 state_machine.EmitStoreyInstantiation (ec, this);
2957 public void EmitEmbedded (EmitContext ec)
2959 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2960 DefineStoreyContainer (ec, state_machine);
2961 state_machine.EmitStoreyInstantiation (ec, this);
2967 public ParameterInfo GetParameterInfo (Parameter p)
2969 for (int i = 0; i < parameters.Count; ++i) {
2970 if (parameters[i] == p)
2971 return parameter_info[i];
2974 throw new ArgumentException ("Invalid parameter");
2977 public ParameterReference GetParameterReference (int index, Location loc)
2979 return new ParameterReference (parameter_info[index], loc);
2982 public Statement PerformClone ()
2984 CloneContext clonectx = new CloneContext ();
2985 return Clone (clonectx);
2988 protected void ProcessParameters ()
2990 if (parameters.Count == 0)
2993 parameter_info = new ParameterInfo[parameters.Count];
2994 for (int i = 0; i < parameter_info.Length; ++i) {
2995 var p = parameters.FixedParameters[i];
2999 // TODO: Should use Parameter only and more block there
3000 parameter_info[i] = new ParameterInfo (this, i);
3002 AddLocalName (p.Name, parameter_info[i]);
3006 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3013 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3014 flags |= Flags.IsExpressionTree;
3019 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3020 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3025 unreachable = top_level.End ();
3027 } catch (Exception e) {
3028 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3031 if (rc.CurrentBlock != null) {
3032 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3034 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3037 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3041 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3042 if (rc.CurrentAnonymousMethod == null) {
3043 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3044 if (md is StateMachineMethod) {
3047 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3052 // If an asynchronous body of F is either an expression classified as nothing, or a
3053 // statement block where no return statements have expressions, the inferred return type is Task
3056 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3057 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3058 am.ReturnTypeInference = null;
3059 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3064 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3065 rc.CurrentAnonymousMethod.GetSignatureForError ());
3073 void ResolveMeta (BlockContext ec)
3075 int orig_count = parameters.Count;
3077 for (int i = 0; i < orig_count; ++i) {
3078 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3080 if ((mod & Parameter.Modifier.OUT) == 0)
3083 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3084 parameter_info[i].VariableInfo = vi;
3085 ec.FlowOffset += vi.Length;
3089 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3091 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3092 var stateMachine = new IteratorStorey (iterator);
3094 state_machine = stateMachine;
3095 iterator.SetStateMachine (stateMachine);
3097 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3098 tlb.Original = this;
3099 tlb.IsCompilerGenerated = true;
3100 tlb.state_machine = stateMachine;
3101 tlb.AddStatement (new Return (iterator, iterator.Location));
3105 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3107 for (int i = 0; i < parameters.Count; i++) {
3108 Parameter p = parameters[i];
3109 Parameter.Modifier mod = p.ModFlags;
3110 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3111 host.Compiler.Report.Error (1988, p.Location,
3112 "Async methods cannot have ref or out parameters");
3116 if (p is ArglistParameter) {
3117 host.Compiler.Report.Error (4006, p.Location,
3118 "__arglist is not allowed in parameter list of async methods");
3122 if (parameters.Types[i].IsPointer) {
3123 host.Compiler.Report.Error (4005, p.Location,
3124 "Async methods cannot have unsafe parameters");
3130 host.Compiler.Report.Warning (1998, 1, loc,
3131 "Async block lacks `await' operator and will run synchronously");
3134 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3135 var initializer = new AsyncInitializer (this, host, block_type);
3136 initializer.Type = block_type;
3138 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3140 state_machine = stateMachine;
3141 initializer.SetStateMachine (stateMachine);
3143 var b = this is ToplevelBlock ?
3144 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3145 new ParametersBlock (Parent, parameters, Location.Null) {
3150 b.IsCompilerGenerated = true;
3151 b.state_machine = stateMachine;
3152 b.AddStatement (new StatementExpression (initializer));
3160 public class ToplevelBlock : ParametersBlock
3162 LocalVariable this_variable;
3163 CompilerContext compiler;
3164 Dictionary<string, object> names;
3165 Dictionary<string, object> labels;
3167 List<ExplicitBlock> this_references;
3169 public ToplevelBlock (CompilerContext ctx, Location loc)
3170 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3174 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3175 : base (parameters, start)
3177 this.compiler = ctx;
3179 flags |= Flags.HasRet;
3181 ProcessParameters ();
3185 // Recreates a top level block from parameters block. Used for
3186 // compiler generated methods where the original block comes from
3187 // explicit child block. This works for already resolved blocks
3188 // only to ensure we resolve them in the correct flow order
3190 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3191 : base (source, parameters)
3193 this.compiler = source.TopBlock.compiler;
3195 flags |= Flags.HasRet;
3198 public bool IsIterator {
3204 public Report Report {
3206 return compiler.Report;
3211 // Used by anonymous blocks to track references of `this' variable
3213 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3215 return this_references;
3220 // Returns the "this" instance variable of this block.
3221 // See AddThisVariable() for more information.
3223 public LocalVariable ThisVariable {
3225 return this_variable;
3229 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3232 names = new Dictionary<string, object> ();
3235 if (!names.TryGetValue (name, out value)) {
3236 names.Add (name, li);
3240 INamedBlockVariable existing = value as INamedBlockVariable;
3241 List<INamedBlockVariable> existing_list;
3242 if (existing != null) {
3243 existing_list = new List<INamedBlockVariable> ();
3244 existing_list.Add (existing);
3245 names[name] = existing_list;
3247 existing_list = (List<INamedBlockVariable>) value;
3251 // A collision checking between local names
3253 var variable_block = li.Block.Explicit;
3254 for (int i = 0; i < existing_list.Count; ++i) {
3255 existing = existing_list[i];
3256 Block b = existing.Block.Explicit;
3258 // Collision at same level
3259 if (variable_block == b) {
3260 li.Block.Error_AlreadyDeclared (name, li);
3264 // Collision with parent
3265 Block parent = variable_block;
3266 while ((parent = parent.Parent) != null) {
3268 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3269 i = existing_list.Count;
3274 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3275 // Collision with children
3276 while ((b = b.Parent) != null) {
3277 if (variable_block == b) {
3278 li.Block.Error_AlreadyDeclared (name, li, "child");
3279 i = existing_list.Count;
3286 existing_list.Add (li);
3289 public void AddLabel (string name, LabeledStatement label)
3292 labels = new Dictionary<string, object> ();
3295 if (!labels.TryGetValue (name, out value)) {
3296 labels.Add (name, label);
3300 LabeledStatement existing = value as LabeledStatement;
3301 List<LabeledStatement> existing_list;
3302 if (existing != null) {
3303 existing_list = new List<LabeledStatement> ();
3304 existing_list.Add (existing);
3305 labels[name] = existing_list;
3307 existing_list = (List<LabeledStatement>) value;
3311 // A collision checking between labels
3313 for (int i = 0; i < existing_list.Count; ++i) {
3314 existing = existing_list[i];
3315 Block b = existing.Block;
3317 // Collision at same level
3318 if (label.Block == b) {
3319 Report.SymbolRelatedToPreviousError (existing.loc, name);
3320 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3324 // Collision with parent
3326 while ((b = b.Parent) != null) {
3327 if (existing.Block == b) {
3328 Report.Error (158, label.loc,
3329 "The label `{0}' shadows another label by the same name in a contained scope", name);
3330 i = existing_list.Count;
3335 // Collision with with children
3337 while ((b = b.Parent) != null) {
3338 if (label.Block == b) {
3339 Report.Error (158, label.loc,
3340 "The label `{0}' shadows another label by the same name in a contained scope", name);
3341 i = existing_list.Count;
3347 existing_list.Add (label);
3350 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3352 if (this_references == null)
3353 this_references = new List<ExplicitBlock> ();
3355 if (!this_references.Contains (block))
3356 this_references.Add (block);
3359 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3361 this_references.Remove (block);
3365 // Creates an arguments set from all parameters, useful for method proxy calls
3367 public Arguments GetAllParametersArguments ()
3369 int count = parameters.Count;
3370 Arguments args = new Arguments (count);
3371 for (int i = 0; i < count; ++i) {
3372 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3373 args.Add (new Argument (arg_expr));
3380 // Lookup inside a block, the returned value can represent 3 states
3382 // true+variable: A local name was found and it's valid
3383 // false+variable: A local name was found in a child block only
3384 // false+null: No local name was found
3386 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3392 if (!names.TryGetValue (name, out value))
3395 variable = value as INamedBlockVariable;
3397 if (variable != null) {
3399 if (variable.Block == b.Original)
3403 } while (b != null);
3411 } while (b != null);
3413 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3414 for (int i = 0; i < list.Count; ++i) {
3417 if (variable.Block == b.Original)
3421 } while (b != null);
3429 } while (b != null);
3439 public LabeledStatement GetLabel (string name, Block block)
3445 if (!labels.TryGetValue (name, out value)) {
3449 var label = value as LabeledStatement;
3451 if (label != null) {
3452 if (label.Block == b.Original)
3455 List<LabeledStatement> list = (List<LabeledStatement>) value;
3456 for (int i = 0; i < list.Count; ++i) {
3458 if (label.Block == b.Original)
3467 // This is used by non-static `struct' constructors which do not have an
3468 // initializer - in this case, the constructor must initialize all of the
3469 // struct's fields. To do this, we add a "this" variable and use the flow
3470 // analysis code to ensure that it's been fully initialized before control
3471 // leaves the constructor.
3473 public void AddThisVariable (BlockContext bc)
3475 if (this_variable != null)
3476 throw new InternalErrorException (StartLocation.ToString ());
3478 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3479 this_variable.Type = bc.CurrentType;
3480 this_variable.PrepareForFlowAnalysis (bc);
3483 public bool IsThisAssigned (BlockContext ec)
3485 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3488 public override void Emit (EmitContext ec)
3490 if (Report.Errors > 0)
3496 if (IsCompilerGenerated) {
3497 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3505 // If `HasReturnLabel' is set, then we already emitted a
3506 // jump to the end of the method, so we must emit a `ret'
3509 // Unfortunately, System.Reflection.Emit automatically emits
3510 // a leave to the end of a finally block. This is a problem
3511 // if no code is following the try/finally block since we may
3512 // jump to a point after the end of the method.
3513 // As a workaround, we're always creating a return label in
3516 if (ec.HasReturnLabel || !unreachable) {
3517 if (ec.HasReturnLabel)
3518 ec.MarkLabel (ec.ReturnLabel);
3520 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3521 ec.Mark (EndLocation);
3523 if (ec.ReturnType.Kind != MemberKind.Void)
3524 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3526 ec.Emit (OpCodes.Ret);
3530 } catch (Exception e){
3531 Console.WriteLine ("Exception caught by the compiler while emitting:");
3532 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3534 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3541 public class SwitchLabel : Statement
3549 // if expr == null, then it is the default case.
3551 public SwitchLabel (Expression expr, Location l)
3557 public bool IsDefault {
3559 return label == null;
3563 public Expression Label {
3569 public Location Location {
3575 public Constant Converted {
3584 public bool SectionStart { get; set; }
3586 public Label GetILLabel (EmitContext ec)
3588 if (il_label == null){
3589 il_label = ec.DefineLabel ();
3592 return il_label.Value;
3595 protected override void DoEmit (EmitContext ec)
3597 ec.MarkLabel (GetILLabel (ec));
3600 public override bool Resolve (BlockContext bc)
3602 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3604 return base.Resolve (bc);
3608 // Resolves the expression, reduces it to a literal if possible
3609 // and then converts it to the requested type.
3611 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3613 Expression e = label.Resolve (ec);
3618 Constant c = e as Constant;
3620 ec.Report.Error (150, loc, "A constant value is expected");
3624 if (allow_nullable && c is NullLiteral) {
3629 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3630 return converted != null;
3633 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3636 if (converted == null)
3639 label = converted.GetValueAsLiteral ();
3641 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3642 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3645 protected override void CloneTo (CloneContext clonectx, Statement target)
3647 var t = (SwitchLabel) target;
3649 t.label = label.Clone (clonectx);
3652 public override object Accept (StructuralVisitor visitor)
3654 return visitor.Visit (this);
3658 public class Switch : Statement
3660 // structure used to hold blocks of keys while calculating table switch
3661 sealed class LabelsRange : IComparable<LabelsRange>
3663 public readonly long min;
3665 public readonly List<long> label_values;
3667 public LabelsRange (long value)
3670 label_values = new List<long> ();
3671 label_values.Add (value);
3674 public LabelsRange (long min, long max, ICollection<long> values)
3678 this.label_values = new List<long> (values);
3683 return max - min + 1;
3687 public bool AddValue (long value)
3689 var gap = value - min + 1;
3690 // Ensure the range has > 50% occupancy
3691 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3695 label_values.Add (value);
3699 public int CompareTo (LabelsRange other)
3701 int nLength = label_values.Count;
3702 int nLengthOther = other.label_values.Count;
3703 if (nLengthOther == nLength)
3704 return (int) (other.min - min);
3706 return nLength - nLengthOther;
3710 sealed class DispatchStatement : Statement
3712 readonly Switch body;
3714 public DispatchStatement (Switch body)
3719 protected override void CloneTo (CloneContext clonectx, Statement target)
3721 throw new NotImplementedException ();
3724 protected override void DoEmit (EmitContext ec)
3726 body.EmitDispatch (ec);
3730 public Expression Expr;
3733 // Mapping of all labels to their SwitchLabels
3735 Dictionary<long, SwitchLabel> labels;
3736 Dictionary<string, SwitchLabel> string_labels;
3737 List<SwitchLabel> case_labels;
3740 /// The governing switch type
3742 public TypeSpec SwitchType;
3744 Expression new_expr;
3746 SwitchLabel case_null;
3747 SwitchLabel case_default;
3749 Label defaultLabel, nullLabel;
3750 VariableReference value;
3751 ExpressionStatement string_dictionary;
3752 FieldExpr switch_cache_field;
3753 ExplicitBlock block;
3756 // Nullable Types support
3758 Nullable.Unwrap unwrap;
3760 public Switch (Expression e, ExplicitBlock block, Location l)
3767 public ExplicitBlock Block {
3773 public SwitchLabel DefaultLabel {
3775 return case_default;
3779 public bool IsNullable {
3781 return unwrap != null;
3786 // Determines the governing type for a switch. The returned
3787 // expression might be the expression from the switch, or an
3788 // expression that includes any potential conversions to
3790 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3792 switch (expr.Type.BuiltinType) {
3793 case BuiltinTypeSpec.Type.Byte:
3794 case BuiltinTypeSpec.Type.SByte:
3795 case BuiltinTypeSpec.Type.UShort:
3796 case BuiltinTypeSpec.Type.Short:
3797 case BuiltinTypeSpec.Type.UInt:
3798 case BuiltinTypeSpec.Type.Int:
3799 case BuiltinTypeSpec.Type.ULong:
3800 case BuiltinTypeSpec.Type.Long:
3801 case BuiltinTypeSpec.Type.Char:
3802 case BuiltinTypeSpec.Type.String:
3803 case BuiltinTypeSpec.Type.Bool:
3807 if (expr.Type.IsEnum)
3811 // Try to find a *user* defined implicit conversion.
3813 // If there is no implicit conversion, or if there are multiple
3814 // conversions, we have to report an error
3816 Expression converted = null;
3817 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3820 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3825 // Ignore over-worked ImplicitUserConversions that do
3826 // an implicit conversion in addition to the user conversion.
3828 if (!(e is UserCast))
3831 if (converted != null){
3832 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3841 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3843 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3859 // Performs the basic sanity checks on the switch statement
3860 // (looks for duplicate keys and non-constant expressions).
3862 // It also returns a hashtable with the keys that we will later
3863 // use to compute the switch tables
3865 bool ResolveLabels (ResolveContext ec, Constant value)
3868 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3869 string_labels = new Dictionary<string, SwitchLabel> ();
3871 labels = new Dictionary<long, SwitchLabel> ();
3874 case_labels = new List<SwitchLabel> ();
3875 int default_label_index = -1;
3876 bool constant_label_found = false;
3878 for (int i = 0; i < block.Statements.Count; ++i) {
3879 var s = block.Statements[i];
3881 var sl = s as SwitchLabel;
3886 case_labels.Add (sl);
3889 if (case_default != null) {
3890 sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3894 default_label_index = i;
3899 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3905 if (string_labels != null) {
3906 string string_value = sl.Converted.GetValue () as string;
3907 if (string_value == null)
3910 string_labels.Add (string_value, sl);
3912 if (sl.Converted is NullLiteral) {
3915 labels.Add (sl.Converted.GetValueAsLong (), sl);
3918 } catch (ArgumentException) {
3919 if (string_labels != null)
3920 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3922 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3927 if (value != null) {
3928 var constant_label = constant_label_found ? null : FindLabel (value);
3929 if (constant_label == null || constant_label != sl)
3930 block.Statements[i] = new EmptyStatement (s.loc);
3932 constant_label_found = true;
3936 if (value != null && constant_label_found && default_label_index >= 0)
3937 block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3943 // This method emits code for a lookup-based switch statement (non-string)
3944 // Basically it groups the cases into blocks that are at least half full,
3945 // and then spits out individual lookup opcodes for each block.
3946 // It emits the longest blocks first, and short blocks are just
3947 // handled with direct compares.
3949 void EmitTableSwitch (EmitContext ec, Expression val)
3951 if (labels != null && labels.Count > 0) {
3952 List<LabelsRange> ranges;
3953 if (string_labels != null) {
3954 // We have done all hard work for string already
3955 // setup single range only
3956 ranges = new List<LabelsRange> (1);
3957 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3959 var element_keys = new long[labels.Count];
3960 labels.Keys.CopyTo (element_keys, 0);
3961 Array.Sort (element_keys);
3964 // Build possible ranges of switch labes to reduce number
3967 ranges = new List<LabelsRange> (element_keys.Length);
3968 var range = new LabelsRange (element_keys[0]);
3970 for (int i = 1; i < element_keys.Length; ++i) {
3971 var l = element_keys[i];
3972 if (range.AddValue (l))
3975 range = new LabelsRange (l);
3979 // sort the blocks so we can tackle the largest ones first
3983 Label lbl_default = defaultLabel;
3984 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3986 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3987 LabelsRange kb = ranges[range_index];
3988 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
3990 // Optimize small ranges using simple equality check
3991 if (kb.Range <= 2) {
3992 foreach (var key in kb.label_values) {
3993 SwitchLabel sl = labels[key];
3994 if (sl == case_default || sl == case_null)
3997 if (sl.Converted.IsZeroInteger) {
3998 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4001 sl.Converted.Emit (ec);
4002 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4006 // TODO: if all the keys in the block are the same and there are
4007 // no gaps/defaults then just use a range-check.
4008 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4009 // TODO: optimize constant/I4 cases
4011 // check block range (could be > 2^31)
4013 ec.EmitLong (kb.min);
4014 ec.Emit (OpCodes.Blt, lbl_default);
4017 ec.EmitLong (kb.max);
4018 ec.Emit (OpCodes.Bgt, lbl_default);
4023 ec.EmitLong (kb.min);
4024 ec.Emit (OpCodes.Sub);
4027 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4031 int first = (int) kb.min;
4034 ec.Emit (OpCodes.Sub);
4035 } else if (first < 0) {
4036 ec.EmitInt (-first);
4037 ec.Emit (OpCodes.Add);
4041 // first, build the list of labels for the switch
4043 long cJumps = kb.Range;
4044 Label[] switch_labels = new Label[cJumps];
4045 for (int iJump = 0; iJump < cJumps; iJump++) {
4046 var key = kb.label_values[iKey];
4047 if (key == kb.min + iJump) {
4048 switch_labels[iJump] = labels[key].GetILLabel (ec);
4051 switch_labels[iJump] = lbl_default;
4055 // emit the switch opcode
4056 ec.Emit (OpCodes.Switch, switch_labels);
4059 // mark the default for this block
4060 if (range_index != 0)
4061 ec.MarkLabel (lbl_default);
4064 // the last default just goes to the end
4065 if (ranges.Count > 0)
4066 ec.Emit (OpCodes.Br, lbl_default);
4070 SwitchLabel FindLabel (Constant value)
4072 SwitchLabel sl = null;
4074 if (string_labels != null) {
4075 string s = value.GetValue () as string;
4077 if (case_null != null)
4079 else if (case_default != null)
4082 string_labels.TryGetValue (s, out sl);
4085 if (value is NullLiteral) {
4088 labels.TryGetValue (value.GetValueAsLong (), out sl);
4095 public override bool Resolve (BlockContext ec)
4097 Expr = Expr.Resolve (ec);
4101 new_expr = SwitchGoverningType (ec, Expr);
4103 if (new_expr == null && Expr.Type.IsNullableType) {
4104 unwrap = Nullable.Unwrap.Create (Expr, false);
4108 new_expr = SwitchGoverningType (ec, unwrap);
4111 if (new_expr == null){
4112 ec.Report.Error (151, loc,
4113 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4114 TypeManager.CSharpName (Expr.Type));
4119 SwitchType = new_expr.Type;
4121 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4122 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4126 if (block.Statements.Count == 0)
4129 var constant = new_expr as Constant;
4131 if (!ResolveLabels (ec, constant))
4135 // Don't need extra variable for constant switch or switch with
4136 // only default case
4138 if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4140 // Store switch expression for comparison purposes
4142 value = new_expr as VariableReference;
4143 if (value == null) {
4144 // Create temporary variable inside switch scope
4145 var current_block = ec.CurrentBlock;
4146 ec.CurrentBlock = Block;
4147 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4149 ec.CurrentBlock = current_block;
4153 Switch old_switch = ec.Switch;
4155 ec.Switch.SwitchType = SwitchType;
4157 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4159 ec.CurrentBranching.CurrentUsageVector.Goto ();
4161 var ok = block.Resolve (ec);
4163 if (case_default == null)
4164 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4166 ec.EndFlowBranching ();
4167 ec.Switch = old_switch;
4172 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4173 ResolveStringSwitchMap (ec);
4177 // Needed to emit anonymous storey initialization before
4178 // any generated switch dispatch
4180 block.AddScopeStatement (new DispatchStatement (this));
4185 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4187 var sl = FindLabel (value);
4190 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4197 // Converts string switch into string hashtable
4199 void ResolveStringSwitchMap (ResolveContext ec)
4201 FullNamedExpression string_dictionary_type;
4202 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4203 string_dictionary_type = new TypeExpression (
4204 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4205 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4207 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4208 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4210 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4214 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4215 Field field = new Field (ctype, string_dictionary_type,
4216 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4217 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4218 if (!field.Define ())
4220 ctype.AddField (field);
4222 var init = new List<Expression> ();
4224 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4225 string value = null;
4227 foreach (SwitchLabel sl in case_labels) {
4229 if (sl.SectionStart)
4230 labels.Add (++counter, sl);
4232 if (sl == case_default || sl == case_null)
4235 value = (string) sl.Converted.GetValue ();
4236 var init_args = new List<Expression> (2);
4237 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4239 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4240 init_args.Add (sl.Converted);
4242 init.Add (new CollectionElementInitializer (init_args, loc));
4245 Arguments args = new Arguments (1);
4246 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4247 Expression initializer = new NewInitialize (string_dictionary_type, args,
4248 new CollectionOrObjectInitializers (init, loc), loc);
4250 switch_cache_field = new FieldExpr (field, loc);
4251 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4254 void DoEmitStringSwitch (EmitContext ec)
4256 Label l_initialized = ec.DefineLabel ();
4259 // Skip initialization when value is null
4261 value.EmitBranchable (ec, nullLabel, false);
4264 // Check if string dictionary is initialized and initialize
4266 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4267 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4268 string_dictionary.EmitStatement (ec);
4270 ec.MarkLabel (l_initialized);
4272 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4274 ResolveContext rc = new ResolveContext (ec.MemberContext);
4276 if (switch_cache_field.Type.IsGeneric) {
4277 Arguments get_value_args = new Arguments (2);
4278 get_value_args.Add (new Argument (value));
4279 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4280 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4281 if (get_item == null)
4285 // A value was not found, go to default case
4287 get_item.EmitBranchable (ec, defaultLabel, false);
4289 Arguments get_value_args = new Arguments (1);
4290 get_value_args.Add (new Argument (value));
4292 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4293 if (get_item == null)
4296 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4297 get_item_object.EmitAssign (ec, get_item, true, false);
4298 ec.Emit (OpCodes.Brfalse, defaultLabel);
4300 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4301 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4303 get_item_int.EmitStatement (ec);
4304 get_item_object.Release (ec);
4307 EmitTableSwitch (ec, string_switch_variable);
4308 string_switch_variable.Release (ec);
4312 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4314 void EmitShortSwitch (EmitContext ec)
4316 MethodSpec equal_method = null;
4317 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4318 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4321 if (equal_method != null) {
4322 value.EmitBranchable (ec, nullLabel, false);
4325 for (int i = 0; i < case_labels.Count; ++i) {
4326 var label = case_labels [i];
4327 if (label == case_default || label == case_null)
4330 var constant = label.Converted;
4332 if (equal_method != null) {
4336 var call = new CallEmitter ();
4337 call.EmitPredefined (ec, equal_method, new Arguments (0));
4338 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4342 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4343 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4349 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4352 ec.Emit (OpCodes.Br, defaultLabel);
4355 void EmitDispatch (EmitContext ec)
4357 if (value == null) {
4359 // Constant switch, we already done the work
4365 // Mark sequence point explicitly to switch
4367 ec.Mark (block.StartLocation);
4368 block.IsCompilerGenerated = true;
4370 if (string_dictionary != null) {
4371 DoEmitStringSwitch (ec);
4372 } else if (case_labels.Count < 4 || string_labels != null) {
4373 EmitShortSwitch (ec);
4375 EmitTableSwitch (ec, value);
4379 protected override void DoEmit (EmitContext ec)
4381 // Workaround broken flow-analysis
4382 block.HasUnreachableClosingBrace = true;
4385 // Setup the codegen context
4387 Label old_end = ec.LoopEnd;
4388 Switch old_switch = ec.Switch;
4390 ec.LoopEnd = ec.DefineLabel ();
4393 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4394 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4396 if (value != null) {
4399 unwrap.EmitCheck (ec);
4400 ec.Emit (OpCodes.Brfalse, nullLabel);
4401 value.EmitAssign (ec, new_expr, false, false);
4402 } else if (new_expr != value) {
4403 value.EmitAssign (ec, new_expr, false, false);
4409 // Restore context state.
4410 ec.MarkLabel (ec.LoopEnd);
4413 // Restore the previous context
4415 ec.LoopEnd = old_end;
4416 ec.Switch = old_switch;
4419 protected override void CloneTo (CloneContext clonectx, Statement t)
4421 Switch target = (Switch) t;
4423 target.Expr = Expr.Clone (clonectx);
4424 target.block = (ExplicitBlock) block.Clone (clonectx);
4427 public override object Accept (StructuralVisitor visitor)
4429 return visitor.Visit (this);
4433 // A place where execution can restart in an iterator
4434 public abstract class ResumableStatement : Statement
4437 protected Label resume_point;
4439 public Label PrepareForEmit (EmitContext ec)
4443 resume_point = ec.DefineLabel ();
4445 return resume_point;
4448 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4453 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4458 public abstract class TryFinallyBlock : ExceptionStatement
4460 protected Statement stmt;
4461 Label dispose_try_block;
4462 bool prepared_for_dispose, emitted_dispose;
4463 Method finally_host;
4465 protected TryFinallyBlock (Statement stmt, Location loc)
4473 public Statement Statement {
4481 protected abstract void EmitTryBody (EmitContext ec);
4482 public abstract void EmitFinallyBody (EmitContext ec);
4484 public override Label PrepareForDispose (EmitContext ec, Label end)
4486 if (!prepared_for_dispose) {
4487 prepared_for_dispose = true;
4488 dispose_try_block = ec.DefineLabel ();
4490 return dispose_try_block;
4493 protected sealed override void DoEmit (EmitContext ec)
4495 EmitTryBodyPrepare (ec);
4498 ec.BeginFinallyBlock ();
4500 Label start_finally = ec.DefineLabel ();
4501 if (resume_points != null) {
4502 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4504 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4505 ec.Emit (OpCodes.Brfalse_S, start_finally);
4506 ec.Emit (OpCodes.Endfinally);
4509 ec.MarkLabel (start_finally);
4511 if (finally_host != null) {
4512 finally_host.Define ();
4513 finally_host.Emit ();
4515 // Now it's safe to add, to close it properly and emit sequence points
4516 finally_host.Parent.AddMember (finally_host);
4518 var ce = new CallEmitter ();
4519 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4520 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4522 EmitFinallyBody (ec);
4525 ec.EndExceptionBlock ();
4528 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4530 if (emitted_dispose)
4533 emitted_dispose = true;
4535 Label end_of_try = ec.DefineLabel ();
4537 // Ensure that the only way we can get into this code is through a dispatcher
4538 if (have_dispatcher)
4539 ec.Emit (OpCodes.Br, end);
4541 ec.BeginExceptionBlock ();
4543 ec.MarkLabel (dispose_try_block);
4545 Label[] labels = null;
4546 for (int i = 0; i < resume_points.Count; ++i) {
4547 ResumableStatement s = resume_points[i];
4548 Label ret = s.PrepareForDispose (ec, end_of_try);
4549 if (ret.Equals (end_of_try) && labels == null)
4551 if (labels == null) {
4552 labels = new Label[resume_points.Count];
4553 for (int j = 0; j < i; ++j)
4554 labels[j] = end_of_try;
4559 if (labels != null) {
4561 for (j = 1; j < labels.Length; ++j)
4562 if (!labels[0].Equals (labels[j]))
4564 bool emit_dispatcher = j < labels.Length;
4566 if (emit_dispatcher) {
4567 ec.Emit (OpCodes.Ldloc, pc);
4568 ec.EmitInt (first_resume_pc);
4569 ec.Emit (OpCodes.Sub);
4570 ec.Emit (OpCodes.Switch, labels);
4573 foreach (ResumableStatement s in resume_points)
4574 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4577 ec.MarkLabel (end_of_try);
4579 ec.BeginFinallyBlock ();
4581 if (finally_host != null) {
4582 var ce = new CallEmitter ();
4583 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4584 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4586 EmitFinallyBody (ec);
4589 ec.EndExceptionBlock ();
4592 public override bool Resolve (BlockContext bc)
4595 // Finally block inside iterator is called from MoveNext and
4596 // Dispose methods that means we need to lift the block into
4597 // newly created host method to emit the body only once. The
4598 // original block then simply calls the newly generated method.
4600 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4601 var b = stmt as Block;
4602 if (b != null && b.Explicit.HasYield) {
4603 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4607 return base.Resolve (bc);
4612 // Base class for blocks using exception handling
4614 public abstract class ExceptionStatement : ResumableStatement
4619 protected List<ResumableStatement> resume_points;
4620 protected int first_resume_pc;
4622 protected ExceptionStatement (Location loc)
4627 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4629 StateMachineInitializer state_machine = null;
4630 if (resume_points != null) {
4631 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4633 ec.EmitInt ((int) IteratorStorey.State.Running);
4634 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4637 ec.BeginExceptionBlock ();
4639 if (resume_points != null) {
4640 ec.MarkLabel (resume_point);
4642 // For normal control flow, we want to fall-through the Switch
4643 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4644 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4645 ec.EmitInt (first_resume_pc);
4646 ec.Emit (OpCodes.Sub);
4648 Label[] labels = new Label[resume_points.Count];
4649 for (int i = 0; i < resume_points.Count; ++i)
4650 labels[i] = resume_points[i].PrepareForEmit (ec);
4651 ec.Emit (OpCodes.Switch, labels);
4655 public void SomeCodeFollows ()
4658 code_follows = true;
4662 public override bool Resolve (BlockContext ec)
4665 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4666 // So, ensure there's some IL code after this statement.
4667 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4668 ec.NeedReturnLabel ();
4673 public void AddResumePoint (ResumableStatement stmt, int pc)
4675 if (resume_points == null) {
4676 resume_points = new List<ResumableStatement> ();
4677 first_resume_pc = pc;
4680 if (pc != first_resume_pc + resume_points.Count)
4681 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4683 resume_points.Add (stmt);
4688 public class Lock : TryFinallyBlock
4691 TemporaryVariableReference expr_copy;
4692 TemporaryVariableReference lock_taken;
4694 public Lock (Expression expr, Statement stmt, Location loc)
4700 public Expression Expr {
4706 public override bool Resolve (BlockContext ec)
4708 expr = expr.Resolve (ec);
4712 if (!TypeSpec.IsReferenceType (expr.Type)) {
4713 ec.Report.Error (185, loc,
4714 "`{0}' is not a reference type as required by the lock statement",
4715 expr.Type.GetSignatureForError ());
4718 if (expr.Type.IsGenericParameter) {
4719 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4722 VariableReference lv = expr as VariableReference;
4725 locked = lv.IsLockedByStatement;
4726 lv.IsLockedByStatement = true;
4733 // Have to keep original lock value around to unlock same location
4734 // in the case of original value has changed or is null
4736 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4737 expr_copy.Resolve (ec);
4740 // Ensure Monitor methods are available
4742 if (ResolvePredefinedMethods (ec) > 1) {
4743 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4744 lock_taken.Resolve (ec);
4747 using (ec.Set (ResolveContext.Options.LockScope)) {
4748 ec.StartFlowBranching (this);
4749 Statement.Resolve (ec);
4750 ec.EndFlowBranching ();
4754 lv.IsLockedByStatement = locked;
4762 protected override void EmitTryBodyPrepare (EmitContext ec)
4764 expr_copy.EmitAssign (ec, expr);
4766 if (lock_taken != null) {
4768 // Initialize ref variable
4770 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4773 // Monitor.Enter (expr_copy)
4775 expr_copy.Emit (ec);
4776 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4779 base.EmitTryBodyPrepare (ec);
4782 protected override void EmitTryBody (EmitContext ec)
4785 // Monitor.Enter (expr_copy, ref lock_taken)
4787 if (lock_taken != null) {
4788 expr_copy.Emit (ec);
4789 lock_taken.LocalInfo.CreateBuilder (ec);
4790 lock_taken.AddressOf (ec, AddressOp.Load);
4791 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4794 Statement.Emit (ec);
4797 public override void EmitFinallyBody (EmitContext ec)
4800 // if (lock_taken) Monitor.Exit (expr_copy)
4802 Label skip = ec.DefineLabel ();
4804 if (lock_taken != null) {
4805 lock_taken.Emit (ec);
4806 ec.Emit (OpCodes.Brfalse_S, skip);
4809 expr_copy.Emit (ec);
4810 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4812 ec.Emit (OpCodes.Call, m);
4814 ec.MarkLabel (skip);
4817 int ResolvePredefinedMethods (ResolveContext rc)
4819 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4820 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4824 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4828 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4832 protected override void CloneTo (CloneContext clonectx, Statement t)
4834 Lock target = (Lock) t;
4836 target.expr = expr.Clone (clonectx);
4837 target.stmt = Statement.Clone (clonectx);
4840 public override object Accept (StructuralVisitor visitor)
4842 return visitor.Visit (this);
4847 public class Unchecked : Statement {
4850 public Unchecked (Block b, Location loc)
4857 public override bool Resolve (BlockContext ec)
4859 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4860 return Block.Resolve (ec);
4863 protected override void DoEmit (EmitContext ec)
4865 using (ec.With (EmitContext.Options.CheckedScope, false))
4869 protected override void CloneTo (CloneContext clonectx, Statement t)
4871 Unchecked target = (Unchecked) t;
4873 target.Block = clonectx.LookupBlock (Block);
4876 public override object Accept (StructuralVisitor visitor)
4878 return visitor.Visit (this);
4882 public class Checked : Statement {
4885 public Checked (Block b, Location loc)
4888 b.Unchecked = false;
4892 public override bool Resolve (BlockContext ec)
4894 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4895 return Block.Resolve (ec);
4898 protected override void DoEmit (EmitContext ec)
4900 using (ec.With (EmitContext.Options.CheckedScope, true))
4904 protected override void CloneTo (CloneContext clonectx, Statement t)
4906 Checked target = (Checked) t;
4908 target.Block = clonectx.LookupBlock (Block);
4911 public override object Accept (StructuralVisitor visitor)
4913 return visitor.Visit (this);
4917 public class Unsafe : Statement {
4920 public Unsafe (Block b, Location loc)
4923 Block.Unsafe = true;
4927 public override bool Resolve (BlockContext ec)
4929 if (ec.CurrentIterator != null)
4930 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4932 using (ec.Set (ResolveContext.Options.UnsafeScope))
4933 return Block.Resolve (ec);
4936 protected override void DoEmit (EmitContext ec)
4941 protected override void CloneTo (CloneContext clonectx, Statement t)
4943 Unsafe target = (Unsafe) t;
4945 target.Block = clonectx.LookupBlock (Block);
4948 public override object Accept (StructuralVisitor visitor)
4950 return visitor.Visit (this);
4957 public class Fixed : Statement
4959 abstract class Emitter : ShimExpression
4961 protected LocalVariable vi;
4963 protected Emitter (Expression expr, LocalVariable li)
4969 public abstract void EmitExit (EmitContext ec);
4972 class ExpressionEmitter : Emitter {
4973 public ExpressionEmitter (Expression converted, LocalVariable li) :
4974 base (converted, li)
4978 protected override Expression DoResolve (ResolveContext rc)
4980 throw new NotImplementedException ();
4983 public override void Emit (EmitContext ec) {
4985 // Store pointer in pinned location
4991 public override void EmitExit (EmitContext ec)
4994 ec.Emit (OpCodes.Conv_U);
4999 class StringEmitter : Emitter
5001 LocalVariable pinned_string;
5003 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5008 protected override Expression DoResolve (ResolveContext rc)
5010 pinned_string = new LocalVariable (vi.Block, "$pinned",
5011 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5013 pinned_string.Type = rc.BuiltinTypes.String;
5015 eclass = ExprClass.Variable;
5016 type = rc.BuiltinTypes.Int;
5020 public override void Emit (EmitContext ec)
5022 pinned_string.CreateBuilder (ec);
5025 pinned_string.EmitAssign (ec);
5027 // TODO: Should use Binary::Add
5028 pinned_string.Emit (ec);
5029 ec.Emit (OpCodes.Conv_I);
5031 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5035 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5036 //pe.InstanceExpression = pinned_string;
5037 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5039 ec.Emit (OpCodes.Add);
5043 public override void EmitExit (EmitContext ec)
5046 pinned_string.EmitAssign (ec);
5050 public class VariableDeclaration : BlockVariableDeclaration
5052 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5057 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5059 if (!Variable.Type.IsPointer && li == Variable) {
5060 bc.Report.Error (209, TypeExpression.Location,
5061 "The type of locals declared in a fixed statement must be a pointer type");
5066 // The rules for the possible declarators are pretty wise,
5067 // but the production on the grammar is more concise.
5069 // So we have to enforce these rules here.
5071 // We do not resolve before doing the case 1 test,
5072 // because the grammar is explicit in that the token &
5073 // is present, so we need to test for this particular case.
5076 if (initializer is Cast) {
5077 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5081 initializer = initializer.Resolve (bc);
5083 if (initializer == null)
5089 if (initializer.Type.IsArray) {
5090 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5093 // Provided that array_type is unmanaged,
5095 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5099 // and T* is implicitly convertible to the
5100 // pointer type given in the fixed statement.
5102 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5104 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5105 if (converted == null)
5109 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5111 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5112 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5113 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5114 new NullLiteral (loc),
5117 converted = converted.Resolve (bc);
5119 return new ExpressionEmitter (converted, li);
5125 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5126 return new StringEmitter (initializer, li, loc).Resolve (bc);
5129 // Case 3: fixed buffer
5130 if (initializer is FixedBufferPtr) {
5131 return new ExpressionEmitter (initializer, li);
5135 // Case 4: & object.
5137 bool already_fixed = true;
5138 Unary u = initializer as Unary;
5139 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5140 IVariableReference vr = u.Expr as IVariableReference;
5141 if (vr == null || !vr.IsFixed) {
5142 already_fixed = false;
5146 if (already_fixed) {
5147 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5150 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5151 return new ExpressionEmitter (initializer, li);
5156 VariableDeclaration decl;
5157 Statement statement;
5160 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5169 public Statement Statement {
5175 public BlockVariableDeclaration Variables {
5183 public override bool Resolve (BlockContext ec)
5185 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5186 if (!decl.Resolve (ec))
5190 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5191 bool ok = statement.Resolve (ec);
5192 bool flow_unreachable = ec.EndFlowBranching ();
5193 has_ret = flow_unreachable;
5198 protected override void DoEmit (EmitContext ec)
5200 decl.Variable.CreateBuilder (ec);
5201 decl.Initializer.Emit (ec);
5202 if (decl.Declarators != null) {
5203 foreach (var d in decl.Declarators) {
5204 d.Variable.CreateBuilder (ec);
5205 d.Initializer.Emit (ec);
5209 statement.Emit (ec);
5215 // Clear the pinned variable
5217 ((Emitter) decl.Initializer).EmitExit (ec);
5218 if (decl.Declarators != null) {
5219 foreach (var d in decl.Declarators) {
5220 ((Emitter)d.Initializer).EmitExit (ec);
5225 protected override void CloneTo (CloneContext clonectx, Statement t)
5227 Fixed target = (Fixed) t;
5229 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5230 target.statement = statement.Clone (clonectx);
5233 public override object Accept (StructuralVisitor visitor)
5235 return visitor.Visit (this);
5239 public class Catch : Statement
5243 FullNamedExpression type_expr;
5244 CompilerAssign assign;
5247 public Catch (Block block, Location loc)
5255 public Block Block {
5261 public TypeSpec CatchType {
5267 public bool IsGeneral {
5269 return type_expr == null;
5273 public FullNamedExpression TypeExpression {
5282 public LocalVariable Variable {
5293 protected override void DoEmit (EmitContext ec)
5296 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5298 ec.BeginCatchBlock (CatchType);
5301 li.CreateBuilder (ec);
5304 // Special case hoisted catch variable, we have to use a temporary variable
5305 // to pass via anonymous storey initialization with the value still on top
5308 if (li.HoistedVariant != null) {
5309 LocalTemporary lt = new LocalTemporary (li.Type);
5312 // switch to assigning from the temporary variable and not from top of the stack
5313 assign.UpdateSource (lt);
5316 ec.Emit (OpCodes.Pop);
5322 public override bool Resolve (BlockContext ec)
5324 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5325 if (type_expr != null) {
5326 type = type_expr.ResolveAsType (ec);
5330 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5331 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5332 } else if (li != null) {
5334 li.PrepareForFlowAnalysis (ec);
5336 // source variable is at the top of the stack
5337 Expression source = new EmptyExpression (li.Type);
5338 if (li.Type.IsGenericParameter)
5339 source = new UnboxCast (source, li.Type);
5342 // Uses Location.Null to hide from symbol file
5344 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5345 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5349 return Block.Resolve (ec);
5353 protected override void CloneTo (CloneContext clonectx, Statement t)
5355 Catch target = (Catch) t;
5357 if (type_expr != null)
5358 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5360 target.block = clonectx.LookupBlock (block);
5364 public class TryFinally : TryFinallyBlock
5368 public TryFinally (Statement stmt, Block fini, Location loc)
5374 public Block Finallyblock {
5380 public override bool Resolve (BlockContext ec)
5384 ec.StartFlowBranching (this);
5386 if (!stmt.Resolve (ec))
5390 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5392 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5393 if (!fini.Resolve (ec))
5397 ec.EndFlowBranching ();
5399 ok &= base.Resolve (ec);
5404 protected override void EmitTryBody (EmitContext ec)
5409 public override void EmitFinallyBody (EmitContext ec)
5414 protected override void CloneTo (CloneContext clonectx, Statement t)
5416 TryFinally target = (TryFinally) t;
5418 target.stmt = (Statement) stmt.Clone (clonectx);
5420 target.fini = clonectx.LookupBlock (fini);
5423 public override object Accept (StructuralVisitor visitor)
5425 return visitor.Visit (this);
5429 public class TryCatch : ExceptionStatement
5432 List<Catch> clauses;
5433 readonly bool inside_try_finally;
5435 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5439 this.clauses = catch_clauses;
5440 this.inside_try_finally = inside_try_finally;
5443 public List<Catch> Clauses {
5449 public bool IsTryCatchFinally {
5451 return inside_try_finally;
5455 public override bool Resolve (BlockContext ec)
5459 ec.StartFlowBranching (this);
5461 if (!Block.Resolve (ec))
5464 for (int i = 0; i < clauses.Count; ++i) {
5466 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5468 if (!c.Resolve (ec)) {
5473 TypeSpec resolved_type = c.CatchType;
5474 for (int ii = 0; ii < clauses.Count; ++ii) {
5478 if (clauses[ii].IsGeneral) {
5479 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5482 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5485 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5488 ec.Report.Warning (1058, 1, c.loc,
5489 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5497 var ct = clauses[ii].CatchType;
5501 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5502 ec.Report.Error (160, c.loc,
5503 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5504 ct.GetSignatureForError ());
5510 ec.EndFlowBranching ();
5512 return base.Resolve (ec) && ok;
5515 protected sealed override void DoEmit (EmitContext ec)
5517 if (!inside_try_finally)
5518 EmitTryBodyPrepare (ec);
5522 foreach (Catch c in clauses)
5525 if (!inside_try_finally)
5526 ec.EndExceptionBlock ();
5529 protected override void CloneTo (CloneContext clonectx, Statement t)
5531 TryCatch target = (TryCatch) t;
5533 target.Block = clonectx.LookupBlock (Block);
5534 if (clauses != null){
5535 target.clauses = new List<Catch> ();
5536 foreach (Catch c in clauses)
5537 target.clauses.Add ((Catch) c.Clone (clonectx));
5541 public override object Accept (StructuralVisitor visitor)
5543 return visitor.Visit (this);
5547 public class Using : TryFinallyBlock
5549 public class VariableDeclaration : BlockVariableDeclaration
5551 Statement dispose_call;
5553 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5558 public VariableDeclaration (LocalVariable li, Location loc)
5564 public VariableDeclaration (Expression expr)
5567 loc = expr.Location;
5573 public bool IsNested { get; private set; }
5577 public void EmitDispose (EmitContext ec)
5579 dispose_call.Emit (ec);
5582 public override bool Resolve (BlockContext bc)
5587 return base.Resolve (bc, false);
5590 public Expression ResolveExpression (BlockContext bc)
5592 var e = Initializer.Resolve (bc);
5596 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5597 Initializer = ResolveInitializer (bc, Variable, e);
5601 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5603 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5604 initializer = initializer.Resolve (bc);
5605 if (initializer == null)
5608 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5609 Arguments args = new Arguments (1);
5610 args.Add (new Argument (initializer));
5611 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5612 if (initializer == null)
5615 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5616 dispose_call = CreateDisposeCall (bc, var);
5617 dispose_call.Resolve (bc);
5619 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5622 if (li == Variable) {
5623 CheckIDiposableConversion (bc, li, initializer);
5624 dispose_call = CreateDisposeCall (bc, li);
5625 dispose_call.Resolve (bc);
5628 return base.ResolveInitializer (bc, li, initializer);
5631 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5635 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5636 if (type.IsNullableType) {
5637 // it's handled in CreateDisposeCall
5641 bc.Report.SymbolRelatedToPreviousError (type);
5642 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5643 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5644 type.GetSignatureForError ());
5650 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5652 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5654 var loc = lv.Location;
5656 var idt = bc.BuiltinTypes.IDisposable;
5657 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5659 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5660 dispose_mg.InstanceExpression = type.IsNullableType ?
5661 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5665 // Hide it from symbol file via null location
5667 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5669 // Add conditional call when disposing possible null variable
5670 if (!type.IsStruct || type.IsNullableType)
5671 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5676 public void ResolveDeclaratorInitializer (BlockContext bc)
5678 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5681 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5683 for (int i = declarators.Count - 1; i >= 0; --i) {
5684 var d = declarators [i];
5685 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5686 vd.Initializer = d.Initializer;
5688 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5689 vd.dispose_call.Resolve (bc);
5691 stmt = new Using (vd, stmt, d.Variable.Location);
5698 public override object Accept (StructuralVisitor visitor)
5700 return visitor.Visit (this);
5704 VariableDeclaration decl;
5706 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5712 public Using (Expression expr, Statement stmt, Location loc)
5715 this.decl = new VariableDeclaration (expr);
5720 public Expression Expr {
5722 return decl.Variable == null ? decl.Initializer : null;
5726 public BlockVariableDeclaration Variables {
5734 public override void Emit (EmitContext ec)
5737 // Don't emit sequence point it will be set on variable declaration
5742 protected override void EmitTryBodyPrepare (EmitContext ec)
5745 base.EmitTryBodyPrepare (ec);
5748 protected override void EmitTryBody (EmitContext ec)
5753 public override void EmitFinallyBody (EmitContext ec)
5755 decl.EmitDispose (ec);
5758 public override bool Resolve (BlockContext ec)
5760 VariableReference vr;
5761 bool vr_locked = false;
5763 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5764 if (decl.Variable == null) {
5765 vr = decl.ResolveExpression (ec) as VariableReference;
5767 vr_locked = vr.IsLockedByStatement;
5768 vr.IsLockedByStatement = true;
5771 if (decl.IsNested) {
5772 decl.ResolveDeclaratorInitializer (ec);
5774 if (!decl.Resolve (ec))
5777 if (decl.Declarators != null) {
5778 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5786 ec.StartFlowBranching (this);
5790 ec.EndFlowBranching ();
5793 vr.IsLockedByStatement = vr_locked;
5800 protected override void CloneTo (CloneContext clonectx, Statement t)
5802 Using target = (Using) t;
5804 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5805 target.stmt = stmt.Clone (clonectx);
5808 public override object Accept (StructuralVisitor visitor)
5810 return visitor.Visit (this);
5815 /// Implementation of the foreach C# statement
5817 public class Foreach : Statement
5819 abstract class IteratorStatement : Statement
5821 protected readonly Foreach for_each;
5823 protected IteratorStatement (Foreach @foreach)
5825 this.for_each = @foreach;
5826 this.loc = @foreach.expr.Location;
5829 protected override void CloneTo (CloneContext clonectx, Statement target)
5831 throw new NotImplementedException ();
5834 public override void Emit (EmitContext ec)
5836 if (ec.EmitAccurateDebugInfo) {
5837 ec.Emit (OpCodes.Nop);
5844 sealed class ArrayForeach : IteratorStatement
5846 TemporaryVariableReference[] lengths;
5847 Expression [] length_exprs;
5848 StatementExpression[] counter;
5849 TemporaryVariableReference[] variables;
5851 TemporaryVariableReference copy;
5853 public ArrayForeach (Foreach @foreach, int rank)
5856 counter = new StatementExpression[rank];
5857 variables = new TemporaryVariableReference[rank];
5858 length_exprs = new Expression [rank];
5861 // Only use temporary length variables when dealing with
5862 // multi-dimensional arrays
5865 lengths = new TemporaryVariableReference [rank];
5868 public override bool Resolve (BlockContext ec)
5870 Block variables_block = for_each.variable.Block;
5871 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5874 int rank = length_exprs.Length;
5875 Arguments list = new Arguments (rank);
5876 for (int i = 0; i < rank; i++) {
5877 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5879 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5880 counter[i].Resolve (ec);
5883 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5885 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5886 lengths[i].Resolve (ec);
5888 Arguments args = new Arguments (1);
5889 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5890 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5893 list.Add (new Argument (v));
5896 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5901 if (for_each.type is VarExpr) {
5902 // Infer implicitly typed local variable from foreach array type
5903 var_type = access.Type;
5905 var_type = for_each.type.ResolveAsType (ec);
5907 if (var_type == null)
5910 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5915 for_each.variable.Type = var_type;
5917 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5918 if (variable_ref == null)
5921 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5925 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5926 ec.CurrentBranching.CreateSibling ();
5928 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5929 if (!for_each.body.Resolve (ec))
5931 ec.EndFlowBranching ();
5933 // There's no direct control flow from the end of the embedded statement to the end of the loop
5934 ec.CurrentBranching.CurrentUsageVector.Goto ();
5936 ec.EndFlowBranching ();
5941 protected override void DoEmit (EmitContext ec)
5943 copy.EmitAssign (ec, for_each.expr);
5945 int rank = length_exprs.Length;
5946 Label[] test = new Label [rank];
5947 Label[] loop = new Label [rank];
5949 for (int i = 0; i < rank; i++) {
5950 test [i] = ec.DefineLabel ();
5951 loop [i] = ec.DefineLabel ();
5953 if (lengths != null)
5954 lengths [i].EmitAssign (ec, length_exprs [i]);
5957 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5958 for (int i = 0; i < rank; i++) {
5959 variables [i].EmitAssign (ec, zero);
5961 ec.Emit (OpCodes.Br, test [i]);
5962 ec.MarkLabel (loop [i]);
5965 for_each.body.Emit (ec);
5967 ec.MarkLabel (ec.LoopBegin);
5968 ec.Mark (for_each.expr.Location);
5970 for (int i = rank - 1; i >= 0; i--){
5971 counter [i].Emit (ec);
5973 ec.MarkLabel (test [i]);
5974 variables [i].Emit (ec);
5976 if (lengths != null)
5977 lengths [i].Emit (ec);
5979 length_exprs [i].Emit (ec);
5981 ec.Emit (OpCodes.Blt, loop [i]);
5984 ec.MarkLabel (ec.LoopEnd);
5988 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
5990 class RuntimeDispose : Using.VariableDeclaration
5992 public RuntimeDispose (LocalVariable lv, Location loc)
5997 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5999 // Defered to runtime check
6002 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6004 var idt = bc.BuiltinTypes.IDisposable;
6007 // Fabricates code like
6009 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6012 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6014 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6015 dispose_variable.CreateReferenceExpression (bc, loc),
6016 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6017 loc), new NullLiteral (loc));
6019 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6021 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6022 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6024 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6025 return new If (idisaposable_test, dispose, loc);
6029 LocalVariable variable;
6031 Statement statement;
6032 ExpressionStatement init;
6033 TemporaryVariableReference enumerator_variable;
6034 bool ambiguous_getenumerator_name;
6036 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6039 this.variable = var;
6043 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6045 rc.Report.SymbolRelatedToPreviousError (enumerator);
6046 rc.Report.Error (202, loc,
6047 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6048 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6051 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6054 // Option 1: Try to match by name GetEnumerator first
6056 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6057 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6059 var mg = mexpr as MethodGroupExpr;
6061 mg.InstanceExpression = expr;
6062 Arguments args = new Arguments (0);
6063 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6065 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6066 if (ambiguous_getenumerator_name)
6069 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6075 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6078 PredefinedMember<MethodSpec> iface_candidate = null;
6079 var ptypes = rc.Module.PredefinedTypes;
6080 var gen_ienumerable = ptypes.IEnumerableGeneric;
6081 if (!gen_ienumerable.Define ())
6082 gen_ienumerable = null;
6084 var ifaces = t.Interfaces;
6085 if (ifaces != null) {
6086 foreach (var iface in ifaces) {
6087 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6088 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6089 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6090 rc.Report.Error (1640, loc,
6091 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6092 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6097 // TODO: Cache this somehow
6098 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6099 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6104 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6105 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6110 if (iface_candidate == null) {
6111 if (expr.Type != InternalType.ErrorType) {
6112 rc.Report.Error (1579, loc,
6113 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6114 expr.Type.GetSignatureForError (), "GetEnumerator");
6120 var method = iface_candidate.Resolve (loc);
6124 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6125 mg.InstanceExpression = expr;
6129 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6131 var ms = MemberCache.FindMember (enumerator.ReturnType,
6132 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6133 BindingRestriction.InstanceOnly) as MethodSpec;
6135 if (ms == null || !ms.IsPublic) {
6136 Error_WrongEnumerator (rc, enumerator);
6140 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6143 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6145 var ps = MemberCache.FindMember (enumerator.ReturnType,
6146 MemberFilter.Property ("Current", null),
6147 BindingRestriction.InstanceOnly) as PropertySpec;
6149 if (ps == null || !ps.IsPublic) {
6150 Error_WrongEnumerator (rc, enumerator);
6157 public override bool Resolve (BlockContext ec)
6159 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6162 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6163 } else if (expr.Type.IsNullableType) {
6164 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6167 var get_enumerator_mg = ResolveGetEnumerator (ec);
6168 if (get_enumerator_mg == null) {
6172 var get_enumerator = get_enumerator_mg.BestCandidate;
6173 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6174 enumerator_variable.Resolve (ec);
6176 // Prepare bool MoveNext ()
6177 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6178 if (move_next_mg == null) {
6182 move_next_mg.InstanceExpression = enumerator_variable;
6184 // Prepare ~T~ Current { get; }
6185 var current_prop = ResolveCurrent (ec, get_enumerator);
6186 if (current_prop == null) {
6190 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6191 if (current_pe == null)
6194 VarExpr ve = for_each.type as VarExpr;
6198 // Source type is dynamic, set element type to dynamic too
6199 variable.Type = ec.BuiltinTypes.Dynamic;
6201 // Infer implicitly typed local variable from foreach enumerable type
6202 variable.Type = current_pe.Type;
6206 // Explicit cast of dynamic collection elements has to be done at runtime
6207 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6210 variable.Type = for_each.type.ResolveAsType (ec);
6212 if (variable.Type == null)
6215 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6216 if (current_pe == null)
6220 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6221 if (variable_ref == null)
6224 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6226 var init = new Invocation (get_enumerator_mg, null);
6228 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6229 for_each.body, Location.Null);
6231 var enum_type = enumerator_variable.Type;
6234 // Add Dispose method call when enumerator can be IDisposable
6236 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6237 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6239 // Runtime Dispose check
6241 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6242 vd.Initializer = init;
6243 statement = new Using (vd, statement, Location.Null);
6246 // No Dispose call needed
6248 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6249 this.init.Resolve (ec);
6253 // Static Dispose check
6255 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6256 vd.Initializer = init;
6257 statement = new Using (vd, statement, Location.Null);
6260 return statement.Resolve (ec);
6263 protected override void DoEmit (EmitContext ec)
6265 enumerator_variable.LocalInfo.CreateBuilder (ec);
6268 init.EmitStatement (ec);
6270 statement.Emit (ec);
6273 #region IErrorHandler Members
6275 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6277 ec.Report.SymbolRelatedToPreviousError (best);
6278 ec.Report.Warning (278, 2, expr.Location,
6279 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6280 expr.Type.GetSignatureForError (), "enumerable",
6281 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6283 ambiguous_getenumerator_name = true;
6287 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6292 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6297 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6306 LocalVariable variable;
6308 Statement statement;
6311 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6314 this.variable = var;
6316 this.statement = stmt;
6321 public Expression Expr {
6322 get { return expr; }
6325 public Statement Statement {
6326 get { return statement; }
6329 public Expression TypeExpression {
6330 get { return type; }
6333 public LocalVariable Variable {
6334 get { return variable; }
6337 public override bool Resolve (BlockContext ec)
6339 expr = expr.Resolve (ec);
6344 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6348 body.AddStatement (statement);
6350 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6351 statement = new ArrayForeach (this, 1);
6352 } else if (expr.Type is ArrayContainer) {
6353 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6355 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6356 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6357 expr.ExprClassName);
6361 statement = new CollectionForeach (this, variable, expr);
6364 return statement.Resolve (ec);
6367 protected override void DoEmit (EmitContext ec)
6369 variable.CreateBuilder (ec);
6371 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6372 ec.LoopBegin = ec.DefineLabel ();
6373 ec.LoopEnd = ec.DefineLabel ();
6375 statement.Emit (ec);
6377 ec.LoopBegin = old_begin;
6378 ec.LoopEnd = old_end;
6381 protected override void CloneTo (CloneContext clonectx, Statement t)
6383 Foreach target = (Foreach) t;
6385 target.type = type.Clone (clonectx);
6386 target.expr = expr.Clone (clonectx);
6387 target.body = (Block) body.Clone (clonectx);
6388 target.statement = statement.Clone (clonectx);
6391 public override object Accept (StructuralVisitor visitor)
6393 return visitor.Visit (this);