2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
53 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56 bool ok = Resolve (ec);
57 ec.KillFlowBranching ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
72 if (ec.StatementEpilogue != null) {
78 // This routine must be overrided in derived classes and make copies
79 // of all the data that might be modified if resolved
81 protected abstract void CloneTo (CloneContext clonectx, Statement target);
83 public Statement Clone (CloneContext clonectx)
85 Statement s = (Statement) this.MemberwiseClone ();
86 CloneTo (clonectx, s);
90 public virtual Expression CreateExpressionTree (ResolveContext ec)
92 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
96 public virtual object Accept (StructuralVisitor visitor)
98 return visitor.Visit (this);
102 public sealed class EmptyStatement : Statement
104 public EmptyStatement (Location loc)
109 public override bool Resolve (BlockContext ec)
114 public override bool ResolveUnreachable (BlockContext ec, bool warn)
119 public override void Emit (EmitContext ec)
123 protected override void DoEmit (EmitContext ec)
125 throw new NotSupportedException ();
128 protected override void CloneTo (CloneContext clonectx, Statement target)
133 public override object Accept (StructuralVisitor visitor)
135 return visitor.Visit (this);
139 public class If : Statement {
141 public Statement TrueStatement;
142 public Statement FalseStatement;
146 public If (Expression bool_expr, Statement true_statement, Location l)
147 : this (bool_expr, true_statement, null, l)
151 public If (Expression bool_expr,
152 Statement true_statement,
153 Statement false_statement,
156 this.expr = bool_expr;
157 TrueStatement = true_statement;
158 FalseStatement = false_statement;
162 public Expression Expr {
168 public override bool Resolve (BlockContext ec)
172 expr = expr.Resolve (ec);
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
183 if (!TrueStatement.Resolve (ec))
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
189 FalseStatement = null;
191 if (!TrueStatement.ResolveUnreachable (ec, true))
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206 ok &= TrueStatement.Resolve (ec);
208 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210 ec.CurrentBranching.CreateSibling ();
212 if (FalseStatement != null)
213 ok &= FalseStatement.Resolve (ec);
215 ec.EndFlowBranching ();
220 protected override void DoEmit (EmitContext ec)
222 Label false_target = ec.DefineLabel ();
226 // If we're a boolean constant, Resolve() already
227 // eliminated dead code for us.
229 Constant c = expr as Constant;
231 c.EmitSideEffect (ec);
233 if (!c.IsDefaultValue)
234 TrueStatement.Emit (ec);
235 else if (FalseStatement != null)
236 FalseStatement.Emit (ec);
241 expr.EmitBranchable (ec, false_target, false);
243 TrueStatement.Emit (ec);
245 if (FalseStatement != null){
246 bool branch_emitted = false;
248 end = ec.DefineLabel ();
250 ec.Emit (OpCodes.Br, end);
251 branch_emitted = true;
254 ec.MarkLabel (false_target);
255 FalseStatement.Emit (ec);
260 ec.MarkLabel (false_target);
264 protected override void CloneTo (CloneContext clonectx, Statement t)
268 target.expr = expr.Clone (clonectx);
269 target.TrueStatement = TrueStatement.Clone (clonectx);
270 if (FalseStatement != null)
271 target.FalseStatement = FalseStatement.Clone (clonectx);
274 public override object Accept (StructuralVisitor visitor)
276 return visitor.Visit (this);
280 public class Do : Statement {
281 public Expression expr;
282 public Statement EmbeddedStatement;
284 public Do (Statement statement, BooleanExpression bool_expr, Location l)
287 EmbeddedStatement = statement;
291 public override bool Resolve (BlockContext ec)
295 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
297 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
299 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
300 if (!EmbeddedStatement.Resolve (ec))
302 ec.EndFlowBranching ();
304 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
305 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
307 expr = expr.Resolve (ec);
310 else if (expr is Constant){
311 bool infinite = !((Constant) expr).IsDefaultValue;
313 ec.CurrentBranching.CurrentUsageVector.Goto ();
316 ec.EndFlowBranching ();
321 protected override void DoEmit (EmitContext ec)
323 Label loop = ec.DefineLabel ();
324 Label old_begin = ec.LoopBegin;
325 Label old_end = ec.LoopEnd;
327 ec.LoopBegin = ec.DefineLabel ();
328 ec.LoopEnd = ec.DefineLabel ();
331 EmbeddedStatement.Emit (ec);
332 ec.MarkLabel (ec.LoopBegin);
334 // Mark start of while condition
335 ec.Mark (expr.Location);
338 // Dead code elimination
340 if (expr is Constant) {
341 bool res = !((Constant) expr).IsDefaultValue;
343 expr.EmitSideEffect (ec);
345 ec.Emit (OpCodes.Br, loop);
347 expr.EmitBranchable (ec, loop, true);
350 ec.MarkLabel (ec.LoopEnd);
352 ec.LoopBegin = old_begin;
353 ec.LoopEnd = old_end;
356 protected override void CloneTo (CloneContext clonectx, Statement t)
360 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
361 target.expr = expr.Clone (clonectx);
364 public override object Accept (StructuralVisitor visitor)
366 return visitor.Visit (this);
370 public class While : Statement {
371 public Expression expr;
372 public Statement Statement;
373 bool infinite, empty;
375 public While (BooleanExpression bool_expr, Statement statement, Location l)
377 this.expr = bool_expr;
378 Statement = statement;
382 public override bool Resolve (BlockContext ec)
386 expr = expr.Resolve (ec);
391 // Inform whether we are infinite or not
393 if (expr is Constant){
394 bool value = !((Constant) expr).IsDefaultValue;
397 if (!Statement.ResolveUnreachable (ec, true))
405 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
407 ec.CurrentBranching.CreateSibling ();
409 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
410 if (!Statement.Resolve (ec))
412 ec.EndFlowBranching ();
414 // There's no direct control flow from the end of the embedded statement to the end of the loop
415 ec.CurrentBranching.CurrentUsageVector.Goto ();
417 ec.EndFlowBranching ();
422 protected override void DoEmit (EmitContext ec)
425 expr.EmitSideEffect (ec);
429 Label old_begin = ec.LoopBegin;
430 Label old_end = ec.LoopEnd;
432 ec.LoopBegin = ec.DefineLabel ();
433 ec.LoopEnd = ec.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr is Constant) {
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ec.MarkLabel (ec.LoopBegin);
442 if (ec.EmitAccurateDebugInfo)
443 ec.Emit (OpCodes.Nop);
445 expr.EmitSideEffect (ec);
447 ec.Emit (OpCodes.Br, ec.LoopBegin);
450 // Inform that we are infinite (ie, `we return'), only
451 // if we do not `break' inside the code.
453 ec.MarkLabel (ec.LoopEnd);
455 Label while_loop = ec.DefineLabel ();
457 ec.Emit (OpCodes.Br, ec.LoopBegin);
458 ec.MarkLabel (while_loop);
462 ec.MarkLabel (ec.LoopBegin);
464 ec.Mark (expr.Location);
465 expr.EmitBranchable (ec, while_loop, true);
467 ec.MarkLabel (ec.LoopEnd);
470 ec.LoopBegin = old_begin;
471 ec.LoopEnd = old_end;
474 protected override void CloneTo (CloneContext clonectx, Statement t)
476 While target = (While) t;
478 target.expr = expr.Clone (clonectx);
479 target.Statement = Statement.Clone (clonectx);
482 public override object Accept (StructuralVisitor visitor)
484 return visitor.Visit (this);
488 public class For : Statement
490 bool infinite, empty;
492 public For (Location l)
497 public Statement Initializer {
501 public Expression Condition {
505 public Statement Iterator {
509 public Statement Statement {
513 public override bool Resolve (BlockContext ec)
517 if (Initializer != null) {
518 if (!Initializer.Resolve (ec))
522 if (Condition != null) {
523 Condition = Condition.Resolve (ec);
524 if (Condition == null)
526 else if (Condition is Constant) {
527 bool value = !((Constant) Condition).IsDefaultValue;
530 if (!Statement.ResolveUnreachable (ec, true))
532 if ((Iterator != null) &&
533 !Iterator.ResolveUnreachable (ec, false))
543 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
545 ec.CurrentBranching.CreateSibling ();
547 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
549 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
550 if (!Statement.Resolve (ec))
552 ec.EndFlowBranching ();
554 if (Iterator != null){
555 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
556 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
559 if (!Iterator.Resolve (ec))
564 // There's no direct control flow from the end of the embedded statement to the end of the loop
565 ec.CurrentBranching.CurrentUsageVector.Goto ();
567 ec.EndFlowBranching ();
572 protected override void DoEmit (EmitContext ec)
574 if (Initializer != null)
575 Initializer.Emit (ec);
578 Condition.EmitSideEffect (ec);
582 Label old_begin = ec.LoopBegin;
583 Label old_end = ec.LoopEnd;
584 Label loop = ec.DefineLabel ();
585 Label test = ec.DefineLabel ();
587 ec.LoopBegin = ec.DefineLabel ();
588 ec.LoopEnd = ec.DefineLabel ();
590 ec.Emit (OpCodes.Br, test);
594 ec.MarkLabel (ec.LoopBegin);
599 // If test is null, there is no test, and we are just
602 if (Condition != null) {
603 ec.Mark (Condition.Location);
606 // The Resolve code already catches the case for
607 // Test == Constant (false) so we know that
610 if (Condition is Constant) {
611 Condition.EmitSideEffect (ec);
612 ec.Emit (OpCodes.Br, loop);
614 Condition.EmitBranchable (ec, loop, true);
618 ec.Emit (OpCodes.Br, loop);
619 ec.MarkLabel (ec.LoopEnd);
621 ec.LoopBegin = old_begin;
622 ec.LoopEnd = old_end;
625 protected override void CloneTo (CloneContext clonectx, Statement t)
627 For target = (For) t;
629 if (Initializer != null)
630 target.Initializer = Initializer.Clone (clonectx);
631 if (Condition != null)
632 target.Condition = Condition.Clone (clonectx);
633 if (Iterator != null)
634 target.Iterator = Iterator.Clone (clonectx);
635 target.Statement = Statement.Clone (clonectx);
638 public override object Accept (StructuralVisitor visitor)
640 return visitor.Visit (this);
644 public class StatementExpression : Statement
646 ExpressionStatement expr;
648 public StatementExpression (ExpressionStatement expr)
654 public StatementExpression (ExpressionStatement expr, Location loc)
660 public ExpressionStatement Expr {
666 protected override void CloneTo (CloneContext clonectx, Statement t)
668 StatementExpression target = (StatementExpression) t;
669 target.expr = (ExpressionStatement) expr.Clone (clonectx);
672 protected override void DoEmit (EmitContext ec)
674 expr.EmitStatement (ec);
677 public override bool Resolve (BlockContext ec)
679 expr = expr.ResolveStatement (ec);
683 public override object Accept (StructuralVisitor visitor)
685 return visitor.Visit (this);
689 public class StatementErrorExpression : Statement
691 readonly Expression expr;
693 public StatementErrorExpression (Expression expr)
698 public Expression Expr {
704 protected override void DoEmit (EmitContext ec)
706 throw new NotSupportedException ();
709 protected override void CloneTo (CloneContext clonectx, Statement target)
711 throw new NotImplementedException ();
714 public override object Accept (StructuralVisitor visitor)
716 return visitor.Visit (this);
721 // Simple version of statement list not requiring a block
723 public class StatementList : Statement
725 List<Statement> statements;
727 public StatementList (Statement first, Statement second)
729 statements = new List<Statement> () { first, second };
733 public IList<Statement> Statements {
740 public void Add (Statement statement)
742 statements.Add (statement);
745 public override bool Resolve (BlockContext ec)
747 foreach (var s in statements)
753 protected override void DoEmit (EmitContext ec)
755 foreach (var s in statements)
759 protected override void CloneTo (CloneContext clonectx, Statement target)
761 StatementList t = (StatementList) target;
763 t.statements = new List<Statement> (statements.Count);
764 foreach (Statement s in statements)
765 t.statements.Add (s.Clone (clonectx));
768 public override object Accept (StructuralVisitor visitor)
770 return visitor.Visit (this);
774 // A 'return' or a 'yield break'
775 public abstract class ExitStatement : Statement
777 protected bool unwind_protect;
778 protected abstract bool DoResolve (BlockContext ec);
780 public virtual void Error_FinallyClause (Report Report)
782 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
785 public sealed override bool Resolve (BlockContext ec)
787 var res = DoResolve (ec);
788 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
789 ec.CurrentBranching.CurrentUsageVector.Goto ();
795 /// Implements the return statement
797 public class Return : ExitStatement
801 public Return (Expression expr, Location l)
809 public Expression Expr {
820 protected override bool DoResolve (BlockContext ec)
823 if (ec.ReturnType.Kind == MemberKind.Void)
827 // Return must not be followed by an expression when
828 // the method return type is Task
830 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
831 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
832 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
834 // Extra trick not to emit ret/leave inside awaiter body
836 expr = EmptyExpression.Null;
841 if (ec.CurrentIterator != null) {
842 Error_ReturnFromIterator (ec);
844 ec.Report.Error (126, loc,
845 "An object of a type convertible to `{0}' is required for the return statement",
846 ec.ReturnType.GetSignatureForError ());
852 expr = expr.Resolve (ec);
853 TypeSpec block_return_type = ec.ReturnType;
855 AnonymousExpression am = ec.CurrentAnonymousMethod;
857 if (block_return_type.Kind == MemberKind.Void) {
858 ec.Report.Error (127, loc,
859 "`{0}': A return keyword must not be followed by any expression when method returns void",
860 ec.GetSignatureForError ());
866 Error_ReturnFromIterator (ec);
870 var async_block = am as AsyncInitializer;
871 if (async_block != null) {
873 var storey = (AsyncTaskStorey) am.Storey;
874 var async_type = storey.ReturnType;
876 if (async_type == null && async_block.ReturnTypeInference != null) {
877 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
881 // TODO: Better error message
882 if (async_type.Kind == MemberKind.Void) {
883 ec.Report.Error (127, loc,
884 "`{0}': A return keyword must not be followed by any expression when method returns void",
885 ec.GetSignatureForError ());
890 if (!async_type.IsGenericTask) {
891 if (this is ContextualReturn)
894 ec.Report.Error (1997, loc,
895 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
896 ec.GetSignatureForError ());
901 // The return type is actually Task<T> type argument
903 if (expr.Type == async_type) {
904 ec.Report.Error (4016, loc,
905 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
906 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
908 block_return_type = async_type.TypeArguments[0];
912 var l = am as AnonymousMethodBody;
913 if (l != null && l.ReturnTypeInference != null && expr != null) {
914 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
923 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
924 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
927 if (am != null && block_return_type == ec.ReturnType) {
928 ec.Report.Error (1662, loc,
929 "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",
930 am.ContainerType, am.GetSignatureForError ());
939 protected override void DoEmit (EmitContext ec)
944 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
945 if (async_body != null) {
946 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
948 // It's null for await without async
949 if (async_return != null) {
950 async_return.EmitAssign (ec);
954 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
962 if (unwind_protect || ec.EmitAccurateDebugInfo)
963 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
966 if (unwind_protect) {
967 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
968 } else if (ec.EmitAccurateDebugInfo) {
969 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
971 ec.Emit (OpCodes.Ret);
975 void Error_ReturnFromIterator (ResolveContext rc)
977 rc.Report.Error (1622, loc,
978 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
981 protected override void CloneTo (CloneContext clonectx, Statement t)
983 Return target = (Return) t;
984 // It's null for simple return;
986 target.expr = expr.Clone (clonectx);
989 public override object Accept (StructuralVisitor visitor)
991 return visitor.Visit (this);
995 public class Goto : Statement {
997 LabeledStatement label;
1000 public override bool Resolve (BlockContext ec)
1002 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1003 ec.CurrentBranching.CurrentUsageVector.Goto ();
1007 public Goto (string label, Location l)
1013 public string Target {
1014 get { return target; }
1017 public void SetResolvedTarget (LabeledStatement label)
1020 label.AddReference ();
1023 protected override void CloneTo (CloneContext clonectx, Statement target)
1028 protected override void DoEmit (EmitContext ec)
1031 throw new InternalErrorException ("goto emitted before target resolved");
1032 Label l = label.LabelTarget (ec);
1033 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1036 public override object Accept (StructuralVisitor visitor)
1038 return visitor.Visit (this);
1042 public class LabeledStatement : Statement {
1049 FlowBranching.UsageVector vectors;
1051 public LabeledStatement (string name, Block block, Location l)
1058 public Label LabelTarget (EmitContext ec)
1063 label = ec.DefineLabel ();
1068 public Block Block {
1074 public string Name {
1075 get { return name; }
1078 public bool IsDefined {
1079 get { return defined; }
1082 public bool HasBeenReferenced {
1083 get { return referenced; }
1086 public FlowBranching.UsageVector JumpOrigins {
1087 get { return vectors; }
1090 public void AddUsageVector (FlowBranching.UsageVector vector)
1092 vector = vector.Clone ();
1093 vector.Next = vectors;
1097 protected override void CloneTo (CloneContext clonectx, Statement target)
1102 public override bool Resolve (BlockContext ec)
1104 // this flow-branching will be terminated when the surrounding block ends
1105 ec.StartFlowBranching (this);
1109 protected override void DoEmit (EmitContext ec)
1111 if (!HasBeenReferenced)
1112 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1115 ec.MarkLabel (label);
1118 public void AddReference ()
1123 public override object Accept (StructuralVisitor visitor)
1125 return visitor.Visit (this);
1131 /// `goto default' statement
1133 public class GotoDefault : Statement {
1135 public GotoDefault (Location l)
1140 protected override void CloneTo (CloneContext clonectx, Statement target)
1145 public override bool Resolve (BlockContext ec)
1147 ec.CurrentBranching.CurrentUsageVector.Goto ();
1149 if (ec.Switch == null) {
1150 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1154 if (!ec.Switch.GotDefault) {
1155 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1162 protected override void DoEmit (EmitContext ec)
1164 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1167 public override object Accept (StructuralVisitor visitor)
1169 return visitor.Visit (this);
1174 /// `goto case' statement
1176 public class GotoCase : Statement {
1180 public GotoCase (Expression e, Location l)
1186 public Expression Expr {
1192 public override bool Resolve (BlockContext ec)
1194 if (ec.Switch == null){
1195 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1199 ec.CurrentBranching.CurrentUsageVector.Goto ();
1201 expr = expr.Resolve (ec);
1205 Constant c = expr as Constant;
1207 ec.Report.Error (150, expr.Location, "A constant value is expected");
1212 if (ec.Switch.IsNullable && c is NullLiteral) {
1215 TypeSpec type = ec.Switch.SwitchType;
1216 res = c.TryReduce (ec, type);
1218 c.Error_ValueCannotBeConverted (ec, type, true);
1222 if (!Convert.ImplicitStandardConversionExists (c, type))
1223 ec.Report.Warning (469, 2, loc,
1224 "The `goto case' value is not implicitly convertible to type `{0}'",
1225 TypeManager.CSharpName (type));
1229 sl = ec.Switch.ResolveGotoCase (ec, res);
1233 protected override void DoEmit (EmitContext ec)
1235 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1238 protected override void CloneTo (CloneContext clonectx, Statement t)
1240 GotoCase target = (GotoCase) t;
1242 target.expr = expr.Clone (clonectx);
1245 public override object Accept (StructuralVisitor visitor)
1247 return visitor.Visit (this);
1251 public class Throw : Statement {
1254 public Throw (Expression expr, Location l)
1260 public Expression Expr {
1266 public override bool Resolve (BlockContext ec)
1269 ec.CurrentBranching.CurrentUsageVector.Goto ();
1270 return ec.CurrentBranching.CheckRethrow (loc);
1273 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1274 ec.CurrentBranching.CurrentUsageVector.Goto ();
1279 var et = ec.BuiltinTypes.Exception;
1280 if (Convert.ImplicitConversionExists (ec, expr, et))
1281 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1283 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1288 protected override void DoEmit (EmitContext ec)
1291 ec.Emit (OpCodes.Rethrow);
1295 ec.Emit (OpCodes.Throw);
1299 protected override void CloneTo (CloneContext clonectx, Statement t)
1301 Throw target = (Throw) t;
1304 target.expr = expr.Clone (clonectx);
1307 public override object Accept (StructuralVisitor visitor)
1309 return visitor.Visit (this);
1313 public class Break : Statement {
1315 public Break (Location l)
1320 bool unwind_protect;
1322 public override bool Resolve (BlockContext ec)
1324 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1325 ec.CurrentBranching.CurrentUsageVector.Goto ();
1329 protected override void DoEmit (EmitContext ec)
1331 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1334 protected override void CloneTo (CloneContext clonectx, Statement t)
1339 public override object Accept (StructuralVisitor visitor)
1341 return visitor.Visit (this);
1345 public class Continue : Statement {
1347 public Continue (Location l)
1352 bool unwind_protect;
1354 public override bool Resolve (BlockContext ec)
1356 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1357 ec.CurrentBranching.CurrentUsageVector.Goto ();
1361 protected override void DoEmit (EmitContext ec)
1363 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1366 protected override void CloneTo (CloneContext clonectx, Statement t)
1371 public override object Accept (StructuralVisitor visitor)
1373 return visitor.Visit (this);
1377 public interface ILocalVariable
1379 void Emit (EmitContext ec);
1380 void EmitAssign (EmitContext ec);
1381 void EmitAddressOf (EmitContext ec);
1384 public interface INamedBlockVariable
1386 Block Block { get; }
1387 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1388 bool IsDeclared { get; }
1389 bool IsParameter { get; }
1390 Location Location { get; }
1393 public class BlockVariableDeclaration : Statement
1395 public class Declarator
1398 Expression initializer;
1400 public Declarator (LocalVariable li, Expression initializer)
1402 if (li.Type != null)
1403 throw new ArgumentException ("Expected null variable type");
1406 this.initializer = initializer;
1409 public Declarator (Declarator clone, Expression initializer)
1412 this.initializer = initializer;
1417 public LocalVariable Variable {
1423 public Expression Initializer {
1428 initializer = value;
1435 Expression initializer;
1436 protected FullNamedExpression type_expr;
1437 protected LocalVariable li;
1438 protected List<Declarator> declarators;
1441 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1443 this.type_expr = type;
1445 this.loc = type_expr.Location;
1448 protected BlockVariableDeclaration (LocalVariable li)
1455 public List<Declarator> Declarators {
1461 public Expression Initializer {
1466 initializer = value;
1470 public FullNamedExpression TypeExpression {
1476 public LocalVariable Variable {
1484 public void AddDeclarator (Declarator decl)
1486 if (declarators == null)
1487 declarators = new List<Declarator> ();
1489 declarators.Add (decl);
1492 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1494 if (bc.Report.Errors != 0)
1497 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1499 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1500 new MemberName (li.Name, li.Location), null);
1502 container.AddField (f);
1505 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1509 public override bool Resolve (BlockContext bc)
1511 return Resolve (bc, true);
1514 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1516 if (type == null && !li.IsCompilerGenerated) {
1517 var vexpr = type_expr as VarExpr;
1520 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1521 // same name exists or as a keyword when no type was found
1523 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1524 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1525 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1528 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1532 if (li.IsConstant) {
1533 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1537 if (Initializer == null) {
1538 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1542 if (declarators != null) {
1543 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1547 Initializer = Initializer.Resolve (bc);
1548 if (Initializer != null) {
1549 ((VarExpr) type_expr).InferType (bc, Initializer);
1550 type = type_expr.Type;
1552 // Set error type to indicate the var was placed correctly but could
1555 // var a = missing ();
1557 type = InternalType.ErrorType;
1562 type = type_expr.ResolveAsType (bc);
1566 if (li.IsConstant && !type.IsConstantCompatible) {
1567 Const.Error_InvalidConstantType (type, loc, bc.Report);
1572 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1577 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1579 CreateEvaluatorVariable (bc, li);
1581 li.PrepareForFlowAnalysis (bc);
1584 if (initializer != null) {
1585 initializer = ResolveInitializer (bc, li, initializer);
1586 // li.Variable.DefinitelyAssigned
1589 if (declarators != null) {
1590 foreach (var d in declarators) {
1591 d.Variable.Type = li.Type;
1593 CreateEvaluatorVariable (bc, d.Variable);
1595 d.Variable.PrepareForFlowAnalysis (bc);
1598 if (d.Initializer != null && resolveDeclaratorInitializers) {
1599 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1600 // d.Variable.DefinitelyAssigned
1608 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1610 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1611 return a.ResolveStatement (bc);
1614 protected override void DoEmit (EmitContext ec)
1616 li.CreateBuilder (ec);
1618 if (Initializer != null)
1619 ((ExpressionStatement) Initializer).EmitStatement (ec);
1621 if (declarators != null) {
1622 foreach (var d in declarators) {
1623 d.Variable.CreateBuilder (ec);
1624 if (d.Initializer != null)
1625 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1630 protected override void CloneTo (CloneContext clonectx, Statement target)
1632 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1634 if (type_expr != null)
1635 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1637 if (initializer != null)
1638 t.initializer = initializer.Clone (clonectx);
1640 if (declarators != null) {
1641 t.declarators = null;
1642 foreach (var d in declarators)
1643 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1647 public override object Accept (StructuralVisitor visitor)
1649 return visitor.Visit (this);
1653 public class BlockConstantDeclaration : BlockVariableDeclaration
1655 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1660 public override void Emit (EmitContext ec)
1662 // Nothing to emit, not even sequence point
1665 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1667 initializer = initializer.Resolve (bc);
1668 if (initializer == null)
1671 var c = initializer as Constant;
1673 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1677 c = c.ConvertImplicitly (li.Type);
1679 if (TypeSpec.IsReferenceType (li.Type))
1680 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1682 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1687 li.ConstantValue = c;
1691 public override object Accept (StructuralVisitor visitor)
1693 return visitor.Visit (this);
1698 // The information about a user-perceived local variable
1700 public class LocalVariable : INamedBlockVariable, ILocalVariable
1707 AddressTaken = 1 << 2,
1708 CompilerGenerated = 1 << 3,
1710 ForeachVariable = 1 << 5,
1711 FixedVariable = 1 << 6,
1712 UsingVariable = 1 << 7,
1713 // DefinitelyAssigned = 1 << 8,
1716 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1720 readonly string name;
1721 readonly Location loc;
1722 readonly Block block;
1724 Constant const_value;
1726 public VariableInfo VariableInfo;
1727 HoistedVariable hoisted_variant;
1729 LocalBuilder builder;
1731 public LocalVariable (Block block, string name, Location loc)
1738 public LocalVariable (Block block, string name, Flags flags, Location loc)
1739 : this (block, name, loc)
1745 // Used by variable declarators
1747 public LocalVariable (LocalVariable li, string name, Location loc)
1748 : this (li.block, name, li.flags, loc)
1754 public bool AddressTaken {
1756 return (flags & Flags.AddressTaken) != 0;
1760 public Block Block {
1766 public Constant ConstantValue {
1771 const_value = value;
1776 // Hoisted local variable variant
1778 public HoistedVariable HoistedVariant {
1780 return hoisted_variant;
1783 hoisted_variant = value;
1787 public bool IsDeclared {
1789 return type != null;
1793 public bool IsCompilerGenerated {
1795 return (flags & Flags.CompilerGenerated) != 0;
1799 public bool IsConstant {
1801 return (flags & Flags.Constant) != 0;
1805 public bool IsLocked {
1807 return (flags & Flags.IsLocked) != 0;
1810 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1814 public bool IsThis {
1816 return (flags & Flags.IsThis) != 0;
1820 public bool IsFixed {
1822 return (flags & Flags.FixedVariable) != 0;
1826 bool INamedBlockVariable.IsParameter {
1832 public bool IsReadonly {
1834 return (flags & Flags.ReadonlyMask) != 0;
1838 public Location Location {
1844 public string Name {
1850 public TypeSpec Type {
1861 public void CreateBuilder (EmitContext ec)
1863 if ((flags & Flags.Used) == 0) {
1864 if (VariableInfo == null) {
1865 // Missing flow analysis or wrong variable flags
1866 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1869 if (VariableInfo.IsEverAssigned)
1870 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1872 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1875 if (HoistedVariant != null)
1878 if (builder != null) {
1879 if ((flags & Flags.CompilerGenerated) != 0)
1882 // To avoid Used warning duplicates
1883 throw new InternalErrorException ("Already created variable `{0}'", name);
1887 // All fixed variabled are pinned, a slot has to be alocated
1889 builder = ec.DeclareLocal (Type, IsFixed);
1890 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1891 ec.DefineLocalVariable (name, builder);
1894 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1896 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1901 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1903 if (IsConstant && const_value != null)
1904 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1906 return new LocalVariableReference (this, loc);
1909 public void Emit (EmitContext ec)
1911 // TODO: Need something better for temporary variables
1912 if ((flags & Flags.CompilerGenerated) != 0)
1915 ec.Emit (OpCodes.Ldloc, builder);
1918 public void EmitAssign (EmitContext ec)
1920 // TODO: Need something better for temporary variables
1921 if ((flags & Flags.CompilerGenerated) != 0)
1924 ec.Emit (OpCodes.Stloc, builder);
1927 public void EmitAddressOf (EmitContext ec)
1929 ec.Emit (OpCodes.Ldloca, builder);
1932 public static string GetCompilerGeneratedName (Block block)
1934 // HACK: Debugger depends on the name semantics
1935 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1938 public string GetReadOnlyContext ()
1940 switch (flags & Flags.ReadonlyMask) {
1941 case Flags.FixedVariable:
1942 return "fixed variable";
1943 case Flags.ForeachVariable:
1944 return "foreach iteration variable";
1945 case Flags.UsingVariable:
1946 return "using variable";
1949 throw new InternalErrorException ("Variable is not readonly");
1952 public bool IsThisAssigned (BlockContext ec, Block block)
1954 if (VariableInfo == null)
1955 throw new Exception ();
1957 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1960 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1963 public bool IsAssigned (BlockContext ec)
1965 if (VariableInfo == null)
1966 throw new Exception ();
1968 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1971 public void PrepareForFlowAnalysis (BlockContext bc)
1974 // No need for definitely assigned check for these guys
1976 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1979 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1980 bc.FlowOffset += VariableInfo.Length;
1984 // Mark the variables as referenced in the user code
1986 public void SetIsUsed ()
1988 flags |= Flags.Used;
1991 public void SetHasAddressTaken ()
1993 flags |= (Flags.AddressTaken | Flags.Used);
1996 public override string ToString ()
1998 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2003 /// Block represents a C# block.
2007 /// This class is used in a number of places: either to represent
2008 /// explicit blocks that the programmer places or implicit blocks.
2010 /// Implicit blocks are used as labels or to introduce variable
2013 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2014 /// they contain extra information that is not necessary on normal blocks.
2016 public class Block : Statement {
2023 HasCapturedVariable = 64,
2024 HasCapturedThis = 1 << 7,
2025 IsExpressionTree = 1 << 8,
2026 CompilerGenerated = 1 << 9,
2027 HasAsyncModifier = 1 << 10,
2029 YieldBlock = 1 << 12,
2030 AwaitBlock = 1 << 13
2033 public Block Parent;
2034 public Location StartLocation;
2035 public Location EndLocation;
2037 public ExplicitBlock Explicit;
2038 public ParametersBlock ParametersBlock;
2040 protected Flags flags;
2043 // The statements in this block
2045 protected List<Statement> statements;
2047 protected List<Statement> scope_initializers;
2049 int? resolving_init_idx;
2055 public int ID = id++;
2057 static int clone_id_counter;
2061 // int assignable_slots;
2062 bool unreachable_shown;
2065 public Block (Block parent, Location start, Location end)
2066 : this (parent, 0, start, end)
2070 public Block (Block parent, Flags flags, Location start, Location end)
2072 if (parent != null) {
2073 // the appropriate constructors will fixup these fields
2074 ParametersBlock = parent.ParametersBlock;
2075 Explicit = parent.Explicit;
2078 this.Parent = parent;
2080 this.StartLocation = start;
2081 this.EndLocation = end;
2083 statements = new List<Statement> (4);
2085 this.original = this;
2090 public bool HasUnreachableClosingBrace {
2092 return (flags & Flags.HasRet) != 0;
2095 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2099 public Block Original {
2108 public bool IsCompilerGenerated {
2109 get { return (flags & Flags.CompilerGenerated) != 0; }
2110 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2113 public bool Unchecked {
2114 get { return (flags & Flags.Unchecked) != 0; }
2115 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2118 public bool Unsafe {
2119 get { return (flags & Flags.Unsafe) != 0; }
2120 set { flags |= Flags.Unsafe; }
2123 public List<Statement> Statements {
2124 get { return statements; }
2129 public Block CreateSwitchBlock (Location start)
2131 // FIXME: Only explicit block should be created
2132 var new_block = new Block (this, start, start);
2133 new_block.IsCompilerGenerated = true;
2137 public void SetEndLocation (Location loc)
2142 public void AddLabel (LabeledStatement target)
2144 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2147 public void AddLocalName (LocalVariable li)
2149 AddLocalName (li.Name, li);
2152 public void AddLocalName (string name, INamedBlockVariable li)
2154 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2157 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2159 if (reason == null) {
2160 Error_AlreadyDeclared (name, variable);
2164 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2165 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2166 "to `{0}', which is already used in a `{1}' scope to denote something else",
2170 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2172 var pi = variable as ParametersBlock.ParameterInfo;
2174 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2176 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2177 "A local variable named `{0}' is already defined in this scope", name);
2181 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2183 ParametersBlock.TopBlock.Report.Error (412, loc,
2184 "The type parameter name `{0}' is the same as local variable or parameter name",
2189 // It should be used by expressions which require to
2190 // register a statement during resolve process.
2192 public void AddScopeStatement (Statement s)
2194 if (scope_initializers == null)
2195 scope_initializers = new List<Statement> ();
2198 // Simple recursive helper, when resolve scope initializer another
2199 // new scope initializer can be added, this ensures it's initialized
2200 // before existing one. For now this can happen with expression trees
2201 // in base ctor initializer only
2203 if (resolving_init_idx.HasValue) {
2204 scope_initializers.Insert (resolving_init_idx.Value, s);
2205 ++resolving_init_idx;
2207 scope_initializers.Add (s);
2211 public void AddStatement (Statement s)
2216 public int AssignableSlots {
2218 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2220 // return assignable_slots;
2224 public LabeledStatement LookupLabel (string name)
2226 return ParametersBlock.TopBlock.GetLabel (name, this);
2229 public override bool Resolve (BlockContext ec)
2231 if ((flags & Flags.Resolved) != 0)
2234 Block prev_block = ec.CurrentBlock;
2237 ec.CurrentBlock = this;
2238 ec.StartFlowBranching (this);
2241 // Compiler generated scope statements
2243 if (scope_initializers != null) {
2244 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2245 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2248 resolving_init_idx = null;
2252 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2253 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2254 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2255 // responsible for handling the situation.
2257 int statement_count = statements.Count;
2258 for (int ix = 0; ix < statement_count; ix++){
2259 Statement s = statements [ix];
2262 // Warn if we detect unreachable code.
2265 if (s is EmptyStatement)
2268 if (!unreachable_shown && !(s is LabeledStatement)) {
2269 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2270 unreachable_shown = true;
2273 Block c_block = s as Block;
2274 if (c_block != null)
2275 c_block.unreachable = c_block.unreachable_shown = true;
2279 // Note that we're not using ResolveUnreachable() for unreachable
2280 // statements here. ResolveUnreachable() creates a temporary
2281 // flow branching and kills it afterwards. This leads to problems
2282 // if you have two unreachable statements where the first one
2283 // assigns a variable and the second one tries to access it.
2286 if (!s.Resolve (ec)) {
2288 if (ec.IsInProbingMode)
2291 statements [ix] = new EmptyStatement (s.loc);
2295 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2296 statements [ix] = new EmptyStatement (s.loc);
2298 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2299 if (unreachable && s is LabeledStatement)
2300 throw new InternalErrorException ("should not happen");
2303 while (ec.CurrentBranching is FlowBranchingLabeled)
2304 ec.EndFlowBranching ();
2306 bool flow_unreachable = ec.EndFlowBranching ();
2308 ec.CurrentBlock = prev_block;
2310 if (flow_unreachable)
2311 flags |= Flags.HasRet;
2313 // If we're a non-static `struct' constructor which doesn't have an
2314 // initializer, then we must initialize all of the struct's fields.
2315 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2318 flags |= Flags.Resolved;
2322 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2324 unreachable_shown = true;
2328 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2330 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2331 fb.CurrentUsageVector.IsUnreachable = true;
2332 bool ok = Resolve (ec);
2333 ec.KillFlowBranching ();
2338 protected override void DoEmit (EmitContext ec)
2340 for (int ix = 0; ix < statements.Count; ix++){
2341 statements [ix].Emit (ec);
2345 public override void Emit (EmitContext ec)
2347 if (scope_initializers != null)
2348 EmitScopeInitializers (ec);
2353 protected void EmitScopeInitializers (EmitContext ec)
2355 foreach (Statement s in scope_initializers)
2360 public override string ToString ()
2362 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2366 protected override void CloneTo (CloneContext clonectx, Statement t)
2368 Block target = (Block) t;
2370 target.clone_id = clone_id_counter++;
2373 clonectx.AddBlockMap (this, target);
2374 if (original != this)
2375 clonectx.AddBlockMap (original, target);
2377 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2378 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2381 target.Parent = clonectx.RemapBlockCopy (Parent);
2383 target.statements = new List<Statement> (statements.Count);
2384 foreach (Statement s in statements)
2385 target.statements.Add (s.Clone (clonectx));
2388 public override object Accept (StructuralVisitor visitor)
2390 return visitor.Visit (this);
2394 public class ExplicitBlock : Block
2396 protected AnonymousMethodStorey am_storey;
2398 public ExplicitBlock (Block parent, Location start, Location end)
2399 : this (parent, (Flags) 0, start, end)
2403 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2404 : base (parent, flags, start, end)
2406 this.Explicit = this;
2411 public AnonymousMethodStorey AnonymousMethodStorey {
2417 public bool HasAwait {
2419 return (flags & Flags.AwaitBlock) != 0;
2423 public bool HasCapturedThis {
2425 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2428 return (flags & Flags.HasCapturedThis) != 0;
2433 // Used to indicate that the block has reference to parent
2434 // block and cannot be made static when defining anonymous method
2436 public bool HasCapturedVariable {
2438 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2441 return (flags & Flags.HasCapturedVariable) != 0;
2445 public bool HasYield {
2447 return (flags & Flags.YieldBlock) != 0;
2454 // Creates anonymous method storey in current block
2456 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2459 // Return same story for iterator and async blocks unless we are
2460 // in nested anonymous method
2462 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2463 return ec.CurrentAnonymousMethod.Storey;
2466 // When referencing a variable inside iterator where all
2467 // variables will be captured anyway we don't need to create
2468 // another storey context
2470 if (ParametersBlock.StateMachine is IteratorStorey) {
2471 return ParametersBlock.StateMachine;
2474 if (am_storey == null) {
2475 MemberBase mc = ec.MemberContext as MemberBase;
2478 // Creates anonymous method storey for this block
2480 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2486 public override void Emit (EmitContext ec)
2488 if (am_storey != null) {
2489 DefineStoreyContainer (ec, am_storey);
2490 am_storey.EmitStoreyInstantiation (ec, this);
2493 if (scope_initializers != null)
2494 EmitScopeInitializers (ec);
2496 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2497 ec.Emit (OpCodes.Nop);
2508 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2509 ec.Emit (OpCodes.Nop);
2513 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2515 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2516 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2517 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2521 // Creates anonymous method storey
2523 storey.CreateContainer ();
2524 storey.DefineContainer ();
2526 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2529 // Only first storey in path will hold this reference. All children blocks will
2530 // reference it indirectly using $ref field
2532 for (Block b = Original.Explicit.Parent; b != null; b = b.Parent) {
2533 var s = b.Explicit.AnonymousMethodStorey;
2535 storey.HoistedThis = s.HoistedThis;
2541 // We are the first storey on path and this has to be hoisted
2543 if (storey.HoistedThis == null) {
2544 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2546 // ThisReferencesFromChildrenBlock holds all reference even if they
2547 // are not on this path. It saves some memory otherwise it'd have to
2548 // be in every explicit block. We run this check to see if the reference
2549 // is valid for this storey
2551 Block block_on_path = ref_block;
2552 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2554 if (block_on_path == null)
2557 if (storey.HoistedThis == null)
2558 storey.AddCapturedThisField (ec);
2560 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2561 if (b.AnonymousMethodStorey != null) {
2562 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2563 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2566 // Stop propagation inside same top block
2568 if (b.ParametersBlock == ParametersBlock.Original)
2571 b = b.ParametersBlock;
2574 var pb = b as ParametersBlock;
2575 if (pb != null && pb.StateMachine != null) {
2576 if (pb.StateMachine == storey)
2579 pb.StateMachine.AddParentStoreyReference (ec, storey);
2582 b.HasCapturedVariable = true;
2588 var ref_blocks = storey.ReferencesFromChildrenBlock;
2589 if (ref_blocks != null) {
2590 foreach (ExplicitBlock ref_block in ref_blocks) {
2591 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2592 if (b.AnonymousMethodStorey != null) {
2593 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2596 // Stop propagation inside same top block
2598 if (b.ParametersBlock == ParametersBlock.Original)
2601 b = b.ParametersBlock;
2604 var pb = b as ParametersBlock;
2605 if (pb != null && pb.StateMachine != null) {
2606 if (pb.StateMachine == storey)
2609 pb.StateMachine.AddParentStoreyReference (ec, storey);
2612 b.HasCapturedVariable = true;
2618 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2621 public void RegisterAsyncAwait ()
2624 while ((block.flags & Flags.AwaitBlock) == 0) {
2625 block.flags |= Flags.AwaitBlock;
2627 if (block is ParametersBlock)
2630 block = block.Parent.Explicit;
2634 public void RegisterIteratorYield ()
2637 while ((block.flags & Flags.YieldBlock) == 0) {
2638 block.flags |= Flags.YieldBlock;
2640 if (block.Parent == null)
2643 block = block.Parent.Explicit;
2647 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2649 tryBlock.statements = statements;
2650 statements = new List<Statement> (1);
2651 statements.Add (tf);
2656 // ParametersBlock was introduced to support anonymous methods
2657 // and lambda expressions
2659 public class ParametersBlock : ExplicitBlock
2661 public class ParameterInfo : INamedBlockVariable
2663 readonly ParametersBlock block;
2665 public VariableInfo VariableInfo;
2668 public ParameterInfo (ParametersBlock block, int index)
2676 public ParametersBlock Block {
2682 Block INamedBlockVariable.Block {
2688 public bool IsDeclared {
2694 public bool IsParameter {
2700 public bool IsLocked {
2709 public Location Location {
2711 return Parameter.Location;
2715 public Parameter Parameter {
2717 return block.Parameters [index];
2721 public TypeSpec ParameterType {
2723 return Parameter.Type;
2729 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2731 return new ParameterReference (this, loc);
2736 // Block is converted into an expression
2738 sealed class BlockScopeExpression : Expression
2741 readonly ParametersBlock block;
2743 public BlockScopeExpression (Expression child, ParametersBlock block)
2749 public override bool ContainsEmitWithAwait ()
2751 return child.ContainsEmitWithAwait ();
2754 public override Expression CreateExpressionTree (ResolveContext ec)
2756 throw new NotSupportedException ();
2759 protected override Expression DoResolve (ResolveContext ec)
2764 child = child.Resolve (ec);
2768 eclass = child.eclass;
2773 public override void Emit (EmitContext ec)
2775 block.EmitScopeInitializers (ec);
2780 protected ParametersCompiled parameters;
2781 protected ParameterInfo[] parameter_info;
2783 protected bool unreachable;
2784 protected ToplevelBlock top_block;
2785 protected StateMachine state_machine;
2787 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2788 : base (parent, 0, start, start)
2790 if (parameters == null)
2791 throw new ArgumentNullException ("parameters");
2793 this.parameters = parameters;
2794 ParametersBlock = this;
2796 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2798 this.top_block = parent.ParametersBlock.top_block;
2799 ProcessParameters ();
2802 protected ParametersBlock (ParametersCompiled parameters, Location start)
2803 : base (null, 0, start, start)
2805 if (parameters == null)
2806 throw new ArgumentNullException ("parameters");
2808 this.parameters = parameters;
2809 ParametersBlock = this;
2813 // It's supposed to be used by method body implementation of anonymous methods
2815 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2816 : base (null, 0, source.StartLocation, source.EndLocation)
2818 this.parameters = parameters;
2819 this.statements = source.statements;
2820 this.scope_initializers = source.scope_initializers;
2822 this.resolved = true;
2823 this.unreachable = source.unreachable;
2824 this.am_storey = source.am_storey;
2825 this.state_machine = source.state_machine;
2827 ParametersBlock = this;
2830 // Overwrite original for comparison purposes when linking cross references
2831 // between anonymous methods
2838 public bool IsAsync {
2840 return (flags & Flags.HasAsyncModifier) != 0;
2843 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2848 // Block has been converted to expression tree
2850 public bool IsExpressionTree {
2852 return (flags & Flags.IsExpressionTree) != 0;
2857 // The parameters for the block.
2859 public ParametersCompiled Parameters {
2865 public StateMachine StateMachine {
2867 return state_machine;
2871 public ToplevelBlock TopBlock {
2877 public bool Resolved {
2879 return (flags & Flags.Resolved) != 0;
2883 public int TemporaryLocalsCount { get; set; }
2888 // Check whether all `out' parameters have been assigned.
2890 public void CheckOutParameters (FlowBranching.UsageVector vector)
2892 if (vector.IsUnreachable)
2895 int n = parameter_info == null ? 0 : parameter_info.Length;
2897 for (int i = 0; i < n; i++) {
2898 VariableInfo var = parameter_info[i].VariableInfo;
2903 if (vector.IsAssigned (var, false))
2906 var p = parameter_info[i].Parameter;
2907 TopBlock.Report.Error (177, p.Location,
2908 "The out parameter `{0}' must be assigned to before control leaves the current method",
2913 public override Expression CreateExpressionTree (ResolveContext ec)
2915 if (statements.Count == 1) {
2916 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2917 if (scope_initializers != null)
2918 expr = new BlockScopeExpression (expr, this);
2923 return base.CreateExpressionTree (ec);
2926 public override void Emit (EmitContext ec)
2928 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2929 DefineStoreyContainer (ec, state_machine);
2930 state_machine.EmitStoreyInstantiation (ec, this);
2936 public void EmitEmbedded (EmitContext ec)
2938 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2939 DefineStoreyContainer (ec, state_machine);
2940 state_machine.EmitStoreyInstantiation (ec, this);
2946 public ParameterInfo GetParameterInfo (Parameter p)
2948 for (int i = 0; i < parameters.Count; ++i) {
2949 if (parameters[i] == p)
2950 return parameter_info[i];
2953 throw new ArgumentException ("Invalid parameter");
2956 public ParameterReference GetParameterReference (int index, Location loc)
2958 return new ParameterReference (parameter_info[index], loc);
2961 public Statement PerformClone ()
2963 CloneContext clonectx = new CloneContext ();
2964 return Clone (clonectx);
2967 protected void ProcessParameters ()
2969 if (parameters.Count == 0)
2972 parameter_info = new ParameterInfo[parameters.Count];
2973 for (int i = 0; i < parameter_info.Length; ++i) {
2974 var p = parameters.FixedParameters[i];
2978 // TODO: Should use Parameter only and more block there
2979 parameter_info[i] = new ParameterInfo (this, i);
2981 AddLocalName (p.Name, parameter_info[i]);
2985 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2992 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2993 flags |= Flags.IsExpressionTree;
2998 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2999 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3004 unreachable = top_level.End ();
3006 } catch (Exception e) {
3007 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3010 if (rc.CurrentBlock != null) {
3011 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3013 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3016 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3020 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3021 if (rc.CurrentAnonymousMethod == null) {
3022 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3023 if (md is StateMachineMethod) {
3026 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3031 // If an asynchronous body of F is either an expression classified as nothing, or a
3032 // statement block where no return statements have expressions, the inferred return type is Task
3035 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3036 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3037 am.ReturnTypeInference = null;
3038 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3043 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3044 rc.CurrentAnonymousMethod.GetSignatureForError ());
3052 void ResolveMeta (BlockContext ec)
3054 int orig_count = parameters.Count;
3056 for (int i = 0; i < orig_count; ++i) {
3057 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3059 if ((mod & Parameter.Modifier.OUT) == 0)
3062 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3063 parameter_info[i].VariableInfo = vi;
3064 ec.FlowOffset += vi.Length;
3068 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3070 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3071 var stateMachine = new IteratorStorey (iterator);
3073 state_machine = stateMachine;
3074 iterator.SetStateMachine (stateMachine);
3076 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3077 tlb.Original = this;
3078 tlb.IsCompilerGenerated = true;
3079 tlb.state_machine = stateMachine;
3080 tlb.AddStatement (new Return (iterator, iterator.Location));
3084 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3086 for (int i = 0; i < parameters.Count; i++) {
3087 Parameter p = parameters[i];
3088 Parameter.Modifier mod = p.ModFlags;
3089 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3090 host.Compiler.Report.Error (1988, p.Location,
3091 "Async methods cannot have ref or out parameters");
3095 if (p is ArglistParameter) {
3096 host.Compiler.Report.Error (4006, p.Location,
3097 "__arglist is not allowed in parameter list of async methods");
3101 if (parameters.Types[i].IsPointer) {
3102 host.Compiler.Report.Error (4005, p.Location,
3103 "Async methods cannot have unsafe parameters");
3109 host.Compiler.Report.Warning (1998, 1, loc,
3110 "Async block lacks `await' operator and will run synchronously");
3113 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3114 var initializer = new AsyncInitializer (this, host, block_type);
3115 initializer.Type = block_type;
3117 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3119 state_machine = stateMachine;
3120 initializer.SetStateMachine (stateMachine);
3122 var b = this is ToplevelBlock ?
3123 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3124 new ParametersBlock (Parent, parameters, Location.Null) {
3129 b.IsCompilerGenerated = true;
3130 b.state_machine = stateMachine;
3131 b.AddStatement (new StatementExpression (initializer));
3139 public class ToplevelBlock : ParametersBlock
3141 LocalVariable this_variable;
3142 CompilerContext compiler;
3143 Dictionary<string, object> names;
3144 Dictionary<string, object> labels;
3146 List<ExplicitBlock> this_references;
3148 public ToplevelBlock (CompilerContext ctx, Location loc)
3149 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3153 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3154 : base (parameters, start)
3156 this.compiler = ctx;
3158 flags |= Flags.HasRet;
3160 ProcessParameters ();
3164 // Recreates a top level block from parameters block. Used for
3165 // compiler generated methods where the original block comes from
3166 // explicit child block. This works for already resolved blocks
3167 // only to ensure we resolve them in the correct flow order
3169 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3170 : base (source, parameters)
3172 this.compiler = source.TopBlock.compiler;
3174 flags |= Flags.HasRet;
3177 public bool IsIterator {
3183 public Report Report {
3185 return compiler.Report;
3190 // Used by anonymous blocks to track references of `this' variable
3192 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3194 return this_references;
3199 // Returns the "this" instance variable of this block.
3200 // See AddThisVariable() for more information.
3202 public LocalVariable ThisVariable {
3204 return this_variable;
3208 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3211 names = new Dictionary<string, object> ();
3214 if (!names.TryGetValue (name, out value)) {
3215 names.Add (name, li);
3219 INamedBlockVariable existing = value as INamedBlockVariable;
3220 List<INamedBlockVariable> existing_list;
3221 if (existing != null) {
3222 existing_list = new List<INamedBlockVariable> ();
3223 existing_list.Add (existing);
3224 names[name] = existing_list;
3226 existing_list = (List<INamedBlockVariable>) value;
3230 // A collision checking between local names
3232 for (int i = 0; i < existing_list.Count; ++i) {
3233 existing = existing_list[i];
3234 Block b = existing.Block.Explicit;
3236 // Collision at same level
3237 if (li.Block.Explicit == b) {
3238 li.Block.Error_AlreadyDeclared (name, li);
3242 // Collision with parent
3243 Block parent = li.Block.Explicit;
3244 while ((parent = parent.Parent) != null) {
3246 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3247 i = existing_list.Count;
3252 if (!ignoreChildrenBlocks) {
3253 // Collision with children
3254 while ((b = b.Parent) != null) {
3255 if (li.Block.Explicit == b) {
3256 li.Block.Error_AlreadyDeclared (name, li, "child");
3257 i = existing_list.Count;
3264 existing_list.Add (li);
3267 public void AddLabel (string name, LabeledStatement label)
3270 labels = new Dictionary<string, object> ();
3273 if (!labels.TryGetValue (name, out value)) {
3274 labels.Add (name, label);
3278 LabeledStatement existing = value as LabeledStatement;
3279 List<LabeledStatement> existing_list;
3280 if (existing != null) {
3281 existing_list = new List<LabeledStatement> ();
3282 existing_list.Add (existing);
3283 labels[name] = existing_list;
3285 existing_list = (List<LabeledStatement>) value;
3289 // A collision checking between labels
3291 for (int i = 0; i < existing_list.Count; ++i) {
3292 existing = existing_list[i];
3293 Block b = existing.Block;
3295 // Collision at same level
3296 if (label.Block == b) {
3297 Report.SymbolRelatedToPreviousError (existing.loc, name);
3298 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3302 // Collision with parent
3304 while ((b = b.Parent) != null) {
3305 if (existing.Block == b) {
3306 Report.Error (158, label.loc,
3307 "The label `{0}' shadows another label by the same name in a contained scope", name);
3308 i = existing_list.Count;
3313 // Collision with with children
3315 while ((b = b.Parent) != null) {
3316 if (label.Block == b) {
3317 Report.Error (158, label.loc,
3318 "The label `{0}' shadows another label by the same name in a contained scope", name);
3319 i = existing_list.Count;
3325 existing_list.Add (label);
3328 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3330 if (this_references == null)
3331 this_references = new List<ExplicitBlock> ();
3333 if (!this_references.Contains (block))
3334 this_references.Add (block);
3337 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3339 this_references.Remove (block);
3343 // Creates an arguments set from all parameters, useful for method proxy calls
3345 public Arguments GetAllParametersArguments ()
3347 int count = parameters.Count;
3348 Arguments args = new Arguments (count);
3349 for (int i = 0; i < count; ++i) {
3350 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3351 args.Add (new Argument (arg_expr));
3358 // Lookup inside a block, the returned value can represent 3 states
3360 // true+variable: A local name was found and it's valid
3361 // false+variable: A local name was found in a child block only
3362 // false+null: No local name was found
3364 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3370 if (!names.TryGetValue (name, out value))
3373 variable = value as INamedBlockVariable;
3375 if (variable != null) {
3377 if (variable.Block == b.Original)
3381 } while (b != null);
3389 } while (b != null);
3391 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3392 for (int i = 0; i < list.Count; ++i) {
3395 if (variable.Block == b.Original)
3399 } while (b != null);
3407 } while (b != null);
3417 public LabeledStatement GetLabel (string name, Block block)
3423 if (!labels.TryGetValue (name, out value)) {
3427 var label = value as LabeledStatement;
3429 if (label != null) {
3430 if (label.Block == b.Original)
3433 // TODO: Temporary workaround for the switch block implicit label block
3434 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3437 List<LabeledStatement> list = (List<LabeledStatement>) value;
3438 for (int i = 0; i < list.Count; ++i) {
3440 if (label.Block == b.Original)
3443 // TODO: Temporary workaround for the switch block implicit label block
3444 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3453 // This is used by non-static `struct' constructors which do not have an
3454 // initializer - in this case, the constructor must initialize all of the
3455 // struct's fields. To do this, we add a "this" variable and use the flow
3456 // analysis code to ensure that it's been fully initialized before control
3457 // leaves the constructor.
3459 public void AddThisVariable (BlockContext bc)
3461 if (this_variable != null)
3462 throw new InternalErrorException (StartLocation.ToString ());
3464 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3465 this_variable.Type = bc.CurrentType;
3466 this_variable.PrepareForFlowAnalysis (bc);
3469 public bool IsThisAssigned (BlockContext ec)
3471 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3474 public override void Emit (EmitContext ec)
3476 if (Report.Errors > 0)
3482 if (IsCompilerGenerated) {
3483 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3491 // If `HasReturnLabel' is set, then we already emitted a
3492 // jump to the end of the method, so we must emit a `ret'
3495 // Unfortunately, System.Reflection.Emit automatically emits
3496 // a leave to the end of a finally block. This is a problem
3497 // if no code is following the try/finally block since we may
3498 // jump to a point after the end of the method.
3499 // As a workaround, we're always creating a return label in
3502 if (ec.HasReturnLabel || !unreachable) {
3503 if (ec.HasReturnLabel)
3504 ec.MarkLabel (ec.ReturnLabel);
3506 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3507 ec.Mark (EndLocation);
3509 if (ec.ReturnType.Kind != MemberKind.Void)
3510 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3512 ec.Emit (OpCodes.Ret);
3516 } catch (Exception e){
3517 Console.WriteLine ("Exception caught by the compiler while emitting:");
3518 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3520 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3527 public class SwitchLabel {
3530 readonly Location loc;
3535 // if expr == null, then it is the default case.
3537 public SwitchLabel (Expression expr, Location l)
3543 public bool IsDefault {
3545 return label == null;
3549 public Expression Label {
3555 public Location Location {
3561 public Constant Converted {
3570 public Label GetILLabel (EmitContext ec)
3572 if (il_label == null){
3573 il_label = ec.DefineLabel ();
3576 return il_label.Value;
3580 // Resolves the expression, reduces it to a literal if possible
3581 // and then converts it to the requested type.
3583 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3585 Expression e = label.Resolve (ec);
3590 Constant c = e as Constant;
3592 ec.Report.Error (150, loc, "A constant value is expected");
3596 if (allow_nullable && c is NullLiteral) {
3601 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3602 return converted != null;
3605 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3608 if (converted == null)
3611 label = converted.GetValueAsLiteral ();
3613 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3614 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3617 public SwitchLabel Clone (CloneContext clonectx)
3622 return new SwitchLabel (label.Clone (clonectx), loc);
3626 public class SwitchSection {
3627 public readonly List<SwitchLabel> Labels;
3628 public readonly Block Block;
3630 public SwitchSection (List<SwitchLabel> labels, Block block)
3636 public SwitchSection Clone (CloneContext clonectx)
3638 var cloned_labels = new List<SwitchLabel> ();
3640 foreach (SwitchLabel sl in Labels)
3641 cloned_labels.Add (sl.Clone (clonectx));
3643 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3647 public class Switch : Statement
3649 // structure used to hold blocks of keys while calculating table switch
3650 sealed class LabelsRange : IComparable<LabelsRange>
3652 public readonly long min;
3654 public readonly List<long> label_values;
3656 public LabelsRange (long value)
3659 label_values = new List<long> ();
3660 label_values.Add (value);
3663 public LabelsRange (long min, long max, ICollection<long> values)
3667 this.label_values = new List<long> (values);
3672 return max - min + 1;
3676 public bool AddValue (long value)
3678 var gap = value - min + 1;
3679 // Ensure the range has > 50% occupancy
3680 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3684 label_values.Add (value);
3688 public int CompareTo (LabelsRange other)
3690 int nLength = label_values.Count;
3691 int nLengthOther = other.label_values.Count;
3692 if (nLengthOther == nLength)
3693 return (int) (other.min - min);
3695 return nLength - nLengthOther;
3699 sealed class LabelMarker : Statement
3702 readonly List<SwitchLabel> labels;
3704 public LabelMarker (Switch s, List<SwitchLabel> labels)
3707 this.labels = labels;
3710 protected override void CloneTo (CloneContext clonectx, Statement target)
3714 protected override void DoEmit (EmitContext ec)
3716 foreach (var l in labels) {
3718 ec.MarkLabel (s.DefaultLabel);
3720 ec.MarkLabel (l.GetILLabel (ec));
3725 public List<SwitchSection> Sections;
3726 public Expression Expr;
3729 // Mapping of all labels to their SwitchLabels
3731 Dictionary<long, SwitchLabel> labels;
3732 Dictionary<string, SwitchLabel> string_labels;
3735 /// The governing switch type
3737 public TypeSpec SwitchType;
3742 Label default_target;
3744 Expression new_expr;
3747 SwitchSection constant_section;
3748 SwitchSection default_section;
3749 SwitchLabel null_section;
3751 Statement simple_stmt;
3752 VariableReference value;
3753 ExpressionStatement string_dictionary;
3754 FieldExpr switch_cache_field;
3755 ExplicitBlock block;
3758 // Nullable Types support
3760 Nullable.Unwrap unwrap;
3762 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3770 public ExplicitBlock Block {
3776 public Label DefaultLabel {
3778 return default_target;
3782 public bool GotDefault {
3784 return default_section != null;
3788 public bool IsNullable {
3790 return unwrap != null;
3795 // Determines the governing type for a switch. The returned
3796 // expression might be the expression from the switch, or an
3797 // expression that includes any potential conversions to
3799 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3801 switch (expr.Type.BuiltinType) {
3802 case BuiltinTypeSpec.Type.Byte:
3803 case BuiltinTypeSpec.Type.SByte:
3804 case BuiltinTypeSpec.Type.UShort:
3805 case BuiltinTypeSpec.Type.Short:
3806 case BuiltinTypeSpec.Type.UInt:
3807 case BuiltinTypeSpec.Type.Int:
3808 case BuiltinTypeSpec.Type.ULong:
3809 case BuiltinTypeSpec.Type.Long:
3810 case BuiltinTypeSpec.Type.Char:
3811 case BuiltinTypeSpec.Type.String:
3812 case BuiltinTypeSpec.Type.Bool:
3816 if (expr.Type.IsEnum)
3820 // Try to find a *user* defined implicit conversion.
3822 // If there is no implicit conversion, or if there are multiple
3823 // conversions, we have to report an error
3825 Expression converted = null;
3826 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3829 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3834 // Ignore over-worked ImplicitUserConversions that do
3835 // an implicit conversion in addition to the user conversion.
3837 if (!(e is UserCast))
3840 if (converted != null){
3841 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3850 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3852 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3868 // Performs the basic sanity checks on the switch statement
3869 // (looks for duplicate keys and non-constant expressions).
3871 // It also returns a hashtable with the keys that we will later
3872 // use to compute the switch tables
3874 bool CheckSwitch (ResolveContext ec)
3877 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3878 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3880 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3882 foreach (SwitchSection ss in Sections){
3883 foreach (SwitchLabel sl in ss.Labels){
3885 if (default_section != null){
3886 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3889 default_section = ss;
3893 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3899 if (string_labels != null) {
3900 string s = sl.Converted.GetValue () as string;
3904 string_labels.Add (s, sl);
3906 if (sl.Converted is NullLiteral) {
3909 labels.Add (sl.Converted.GetValueAsLong (), sl);
3912 } catch (ArgumentException) {
3913 if (string_labels != null)
3914 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3916 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3926 // This method emits code for a lookup-based switch statement (non-string)
3927 // Basically it groups the cases into blocks that are at least half full,
3928 // and then spits out individual lookup opcodes for each block.
3929 // It emits the longest blocks first, and short blocks are just
3930 // handled with direct compares.
3932 void EmitTableSwitch (EmitContext ec, Expression val)
3934 Label lbl_default = default_target;
3936 if (labels != null && labels.Count > 0) {
3937 List<LabelsRange> ranges;
3938 if (string_labels != null) {
3939 // We have done all hard work for string already
3940 // setup single range only
3941 ranges = new List<LabelsRange> (1);
3942 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3944 var element_keys = new long[labels.Count];
3945 labels.Keys.CopyTo (element_keys, 0);
3946 Array.Sort (element_keys);
3949 // Build possible ranges of switch labes to reduce number
3952 ranges = new List<LabelsRange> (element_keys.Length);
3953 var range = new LabelsRange (element_keys[0]);
3955 for (int i = 1; i < element_keys.Length; ++i) {
3956 var l = element_keys[i];
3957 if (range.AddValue (l))
3960 range = new LabelsRange (l);
3964 // sort the blocks so we can tackle the largest ones first
3968 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3970 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3971 LabelsRange kb = ranges[range_index];
3972 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3974 // Optimize small ranges using simple equality check
3975 if (kb.Range <= 2) {
3976 foreach (var key in kb.label_values) {
3977 SwitchLabel sl = labels[key];
3978 if (sl.Converted.IsDefaultValue) {
3979 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3982 sl.Converted.Emit (ec);
3983 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3987 // TODO: if all the keys in the block are the same and there are
3988 // no gaps/defaults then just use a range-check.
3989 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3990 // TODO: optimize constant/I4 cases
3992 // check block range (could be > 2^31)
3994 ec.EmitLong (kb.min);
3995 ec.Emit (OpCodes.Blt, lbl_default);
3998 ec.EmitLong (kb.max);
3999 ec.Emit (OpCodes.Bgt, lbl_default);
4004 ec.EmitLong (kb.min);
4005 ec.Emit (OpCodes.Sub);
4008 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4012 int first = (int) kb.min;
4015 ec.Emit (OpCodes.Sub);
4016 } else if (first < 0) {
4017 ec.EmitInt (-first);
4018 ec.Emit (OpCodes.Add);
4022 // first, build the list of labels for the switch
4024 long cJumps = kb.Range;
4025 Label[] switch_labels = new Label[cJumps];
4026 for (int iJump = 0; iJump < cJumps; iJump++) {
4027 var key = kb.label_values[iKey];
4028 if (key == kb.min + iJump) {
4029 switch_labels[iJump] = labels[key].GetILLabel (ec);
4032 switch_labels[iJump] = lbl_default;
4036 // emit the switch opcode
4037 ec.Emit (OpCodes.Switch, switch_labels);
4040 // mark the default for this block
4041 if (range_index != 0)
4042 ec.MarkLabel (lbl_default);
4045 // the last default just goes to the end
4046 if (ranges.Count > 0)
4047 ec.Emit (OpCodes.Br, lbl_default);
4050 // now emit the code for the sections
4051 bool found_default = false;
4053 foreach (SwitchSection ss in Sections) {
4054 foreach (SwitchLabel sl in ss.Labels) {
4056 ec.MarkLabel (lbl_default);
4057 found_default = true;
4058 if (null_section == null)
4059 ec.MarkLabel (null_target);
4060 } else if (sl.Converted.IsNull) {
4061 ec.MarkLabel (null_target);
4064 ec.MarkLabel (sl.GetILLabel (ec));
4070 if (!found_default) {
4071 ec.MarkLabel (lbl_default);
4072 if (null_section == null) {
4073 ec.MarkLabel (null_target);
4078 SwitchLabel FindLabel (Constant value)
4080 SwitchLabel sl = null;
4082 if (string_labels != null) {
4083 string s = value.GetValue () as string;
4085 if (null_section != null)
4087 else if (default_section != null)
4088 sl = default_section.Labels[0];
4090 string_labels.TryGetValue (s, out sl);
4093 if (value is NullLiteral) {
4096 labels.TryGetValue (value.GetValueAsLong (), out sl);
4103 SwitchSection FindSection (SwitchLabel label)
4105 foreach (SwitchSection ss in Sections){
4106 foreach (SwitchLabel sl in ss.Labels){
4115 public override bool Resolve (BlockContext ec)
4117 Expr = Expr.Resolve (ec);
4121 new_expr = SwitchGoverningType (ec, Expr);
4123 if (new_expr == null && Expr.Type.IsNullableType) {
4124 unwrap = Nullable.Unwrap.Create (Expr, false);
4128 new_expr = SwitchGoverningType (ec, unwrap);
4131 if (new_expr == null){
4132 ec.Report.Error (151, loc,
4133 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4134 TypeManager.CSharpName (Expr.Type));
4139 SwitchType = new_expr.Type;
4141 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4142 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4146 if (!CheckSwitch (ec))
4149 Switch old_switch = ec.Switch;
4151 ec.Switch.SwitchType = SwitchType;
4153 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4155 var constant = new_expr as Constant;
4156 if (constant != null) {
4158 SwitchLabel label = FindLabel (constant);
4160 constant_section = FindSection (label);
4162 if (constant_section == null)
4163 constant_section = default_section;
4166 // Store switch expression for comparission purposes
4168 value = new_expr as VariableReference;
4170 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4175 foreach (SwitchSection ss in Sections){
4177 ec.CurrentBranching.CreateSibling (
4178 null, FlowBranching.SiblingType.SwitchSection);
4182 if (is_constant && (ss != constant_section)) {
4183 // If we're a constant switch, we're only emitting
4184 // one single section - mark all the others as
4186 ec.CurrentBranching.CurrentUsageVector.Goto ();
4187 if (!ss.Block.ResolveUnreachable (ec, true)) {
4191 if (!ss.Block.Resolve (ec))
4196 if (default_section == null)
4197 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4199 ec.EndFlowBranching ();
4200 ec.Switch = old_switch;
4206 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4207 if (string_labels.Count < 7)
4208 ResolveSimpleSwitch (ec);
4210 ResolveStringSwitchMap (ec);
4211 } else if (labels.Count < 3 && !IsNullable) {
4212 ResolveSimpleSwitch (ec);
4219 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4221 var sl = FindLabel (value);
4224 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4231 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4233 void ResolveSimpleSwitch (BlockContext bc)
4235 simple_stmt = default_section != null ? default_section.Block : null;
4237 for (int i = Sections.Count - 1; i >= 0; --i) {
4238 var s = Sections[i];
4240 if (s == default_section) {
4241 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4245 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4247 Expression cond = null;
4248 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4249 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4252 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4259 // Compiler generated, hide from symbol file
4261 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4264 // It's null for empty switch
4265 if (simple_stmt != null)
4266 simple_stmt.Resolve (bc);
4270 // Converts string switch into string hashtable
4272 void ResolveStringSwitchMap (ResolveContext ec)
4274 FullNamedExpression string_dictionary_type;
4275 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4276 string_dictionary_type = new TypeExpression (
4277 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4278 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4280 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4281 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4283 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4287 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4288 Field field = new Field (ctype, string_dictionary_type,
4289 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4290 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4291 if (!field.Define ())
4293 ctype.AddField (field);
4295 var init = new List<Expression> ();
4297 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4298 string value = null;
4299 foreach (SwitchSection section in Sections) {
4300 bool contains_label = false;
4301 foreach (SwitchLabel sl in section.Labels) {
4302 if (sl.IsDefault || sl.Converted.IsNull)
4305 if (!contains_label) {
4306 labels.Add (counter, sl);
4307 contains_label = true;
4310 value = (string) sl.Converted.GetValue ();
4311 var init_args = new List<Expression> (2);
4312 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4314 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4315 init_args.Add (sl.Converted);
4317 init.Add (new CollectionElementInitializer (init_args, loc));
4321 // Don't add empty sections
4327 Arguments args = new Arguments (1);
4328 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4329 Expression initializer = new NewInitialize (string_dictionary_type, args,
4330 new CollectionOrObjectInitializers (init, loc), loc);
4332 switch_cache_field = new FieldExpr (field, loc);
4333 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4336 void DoEmitStringSwitch (EmitContext ec)
4338 Label l_initialized = ec.DefineLabel ();
4341 // Skip initialization when value is null
4343 value.EmitBranchable (ec, null_target, false);
4346 // Check if string dictionary is initialized and initialize
4348 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4349 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4350 string_dictionary.EmitStatement (ec);
4352 ec.MarkLabel (l_initialized);
4354 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4356 ResolveContext rc = new ResolveContext (ec.MemberContext);
4358 if (switch_cache_field.Type.IsGeneric) {
4359 Arguments get_value_args = new Arguments (2);
4360 get_value_args.Add (new Argument (value));
4361 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4362 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4363 if (get_item == null)
4367 // A value was not found, go to default case
4369 get_item.EmitBranchable (ec, default_target, false);
4371 Arguments get_value_args = new Arguments (1);
4372 get_value_args.Add (new Argument (value));
4374 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4375 if (get_item == null)
4378 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4379 get_item_object.EmitAssign (ec, get_item, true, false);
4380 ec.Emit (OpCodes.Brfalse, default_target);
4382 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4383 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4385 get_item_int.EmitStatement (ec);
4386 get_item_object.Release (ec);
4389 EmitTableSwitch (ec, string_switch_variable);
4390 string_switch_variable.Release (ec);
4393 protected override void DoEmit (EmitContext ec)
4395 // Workaround broken flow-analysis
4396 block.HasUnreachableClosingBrace = true;
4399 // Needed to emit anonymous storey initialization
4400 // Otherwise it does not contain any statements for now
4404 default_target = ec.DefineLabel ();
4405 null_target = ec.DefineLabel ();
4408 unwrap.EmitCheck (ec);
4409 ec.Emit (OpCodes.Brfalse, null_target);
4410 value.EmitAssign (ec, new_expr, false, false);
4411 } else if (new_expr != value && !is_constant) {
4412 value.EmitAssign (ec, new_expr, false, false);
4416 // Setup the codegen context
4418 Label old_end = ec.LoopEnd;
4419 Switch old_switch = ec.Switch;
4421 ec.LoopEnd = ec.DefineLabel ();
4426 if (constant_section != null)
4427 constant_section.Block.Emit (ec);
4428 } else if (string_dictionary != null) {
4429 DoEmitStringSwitch (ec);
4430 } else if (simple_stmt != null) {
4431 simple_stmt.Emit (ec);
4433 EmitTableSwitch (ec, value);
4436 // Restore context state.
4437 ec.MarkLabel (ec.LoopEnd);
4440 // Restore the previous context
4442 ec.LoopEnd = old_end;
4443 ec.Switch = old_switch;
4446 protected override void CloneTo (CloneContext clonectx, Statement t)
4448 Switch target = (Switch) t;
4450 target.Expr = Expr.Clone (clonectx);
4451 target.Sections = new List<SwitchSection> ();
4452 foreach (SwitchSection ss in Sections){
4453 target.Sections.Add (ss.Clone (clonectx));
4457 public override object Accept (StructuralVisitor visitor)
4459 return visitor.Visit (this);
4463 // A place where execution can restart in an iterator
4464 public abstract class ResumableStatement : Statement
4467 protected Label resume_point;
4469 public Label PrepareForEmit (EmitContext ec)
4473 resume_point = ec.DefineLabel ();
4475 return resume_point;
4478 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4483 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4488 public abstract class TryFinallyBlock : ExceptionStatement
4490 protected Statement stmt;
4491 Label dispose_try_block;
4492 bool prepared_for_dispose, emitted_dispose;
4493 Method finally_host;
4495 protected TryFinallyBlock (Statement stmt, Location loc)
4503 public Statement Statement {
4511 protected abstract void EmitTryBody (EmitContext ec);
4512 public abstract void EmitFinallyBody (EmitContext ec);
4514 public override Label PrepareForDispose (EmitContext ec, Label end)
4516 if (!prepared_for_dispose) {
4517 prepared_for_dispose = true;
4518 dispose_try_block = ec.DefineLabel ();
4520 return dispose_try_block;
4523 protected sealed override void DoEmit (EmitContext ec)
4525 EmitTryBodyPrepare (ec);
4528 ec.BeginFinallyBlock ();
4530 Label start_finally = ec.DefineLabel ();
4531 if (resume_points != null) {
4532 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4534 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4535 ec.Emit (OpCodes.Brfalse_S, start_finally);
4536 ec.Emit (OpCodes.Endfinally);
4539 ec.MarkLabel (start_finally);
4541 if (finally_host != null) {
4542 var ce = new CallEmitter ();
4543 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4544 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4546 EmitFinallyBody (ec);
4549 ec.EndExceptionBlock ();
4552 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4554 if (emitted_dispose)
4557 emitted_dispose = true;
4559 Label end_of_try = ec.DefineLabel ();
4561 // Ensure that the only way we can get into this code is through a dispatcher
4562 if (have_dispatcher)
4563 ec.Emit (OpCodes.Br, end);
4565 ec.BeginExceptionBlock ();
4567 ec.MarkLabel (dispose_try_block);
4569 Label[] labels = null;
4570 for (int i = 0; i < resume_points.Count; ++i) {
4571 ResumableStatement s = resume_points[i];
4572 Label ret = s.PrepareForDispose (ec, end_of_try);
4573 if (ret.Equals (end_of_try) && labels == null)
4575 if (labels == null) {
4576 labels = new Label[resume_points.Count];
4577 for (int j = 0; j < i; ++j)
4578 labels[j] = end_of_try;
4583 if (labels != null) {
4585 for (j = 1; j < labels.Length; ++j)
4586 if (!labels[0].Equals (labels[j]))
4588 bool emit_dispatcher = j < labels.Length;
4590 if (emit_dispatcher) {
4591 ec.Emit (OpCodes.Ldloc, pc);
4592 ec.EmitInt (first_resume_pc);
4593 ec.Emit (OpCodes.Sub);
4594 ec.Emit (OpCodes.Switch, labels);
4597 foreach (ResumableStatement s in resume_points)
4598 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4601 ec.MarkLabel (end_of_try);
4603 ec.BeginFinallyBlock ();
4605 if (finally_host != null) {
4606 var ce = new CallEmitter ();
4607 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4608 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4610 EmitFinallyBody (ec);
4613 ec.EndExceptionBlock ();
4616 public override bool Resolve (BlockContext bc)
4619 // Finally block inside iterator is called from MoveNext and
4620 // Dispose methods that means we need to lift the block into
4621 // newly created host method to emit the body only once. The
4622 // original block then simply calls the newly generated method.
4624 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4625 var b = stmt as Block;
4626 if (b != null && b.Explicit.HasYield) {
4627 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4631 return base.Resolve (bc);
4636 // Base class for blocks using exception handling
4638 public abstract class ExceptionStatement : ResumableStatement
4643 protected List<ResumableStatement> resume_points;
4644 protected int first_resume_pc;
4646 protected ExceptionStatement (Location loc)
4651 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4653 StateMachineInitializer state_machine = null;
4654 if (resume_points != null) {
4655 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4657 ec.EmitInt ((int) IteratorStorey.State.Running);
4658 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4661 ec.BeginExceptionBlock ();
4663 if (resume_points != null) {
4664 ec.MarkLabel (resume_point);
4666 // For normal control flow, we want to fall-through the Switch
4667 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4668 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4669 ec.EmitInt (first_resume_pc);
4670 ec.Emit (OpCodes.Sub);
4672 Label[] labels = new Label[resume_points.Count];
4673 for (int i = 0; i < resume_points.Count; ++i)
4674 labels[i] = resume_points[i].PrepareForEmit (ec);
4675 ec.Emit (OpCodes.Switch, labels);
4679 public void SomeCodeFollows ()
4682 code_follows = true;
4686 public override bool Resolve (BlockContext ec)
4689 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4690 // So, ensure there's some IL code after this statement.
4691 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4692 ec.NeedReturnLabel ();
4697 public void AddResumePoint (ResumableStatement stmt, int pc)
4699 if (resume_points == null) {
4700 resume_points = new List<ResumableStatement> ();
4701 first_resume_pc = pc;
4704 if (pc != first_resume_pc + resume_points.Count)
4705 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4707 resume_points.Add (stmt);
4712 public class Lock : TryFinallyBlock
4715 TemporaryVariableReference expr_copy;
4716 TemporaryVariableReference lock_taken;
4718 public Lock (Expression expr, Statement stmt, Location loc)
4724 public Expression Expr {
4730 public override bool Resolve (BlockContext ec)
4732 expr = expr.Resolve (ec);
4736 if (!TypeSpec.IsReferenceType (expr.Type)) {
4737 ec.Report.Error (185, loc,
4738 "`{0}' is not a reference type as required by the lock statement",
4739 expr.Type.GetSignatureForError ());
4742 if (expr.Type.IsGenericParameter) {
4743 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4746 VariableReference lv = expr as VariableReference;
4749 locked = lv.IsLockedByStatement;
4750 lv.IsLockedByStatement = true;
4756 using (ec.Set (ResolveContext.Options.LockScope)) {
4757 ec.StartFlowBranching (this);
4758 Statement.Resolve (ec);
4759 ec.EndFlowBranching ();
4763 lv.IsLockedByStatement = locked;
4769 // Have to keep original lock value around to unlock same location
4770 // in the case the original has changed or is null
4772 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4773 expr_copy.Resolve (ec);
4776 // Ensure Monitor methods are available
4778 if (ResolvePredefinedMethods (ec) > 1) {
4779 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4780 lock_taken.Resolve (ec);
4786 protected override void EmitTryBodyPrepare (EmitContext ec)
4788 expr_copy.EmitAssign (ec, expr);
4790 if (lock_taken != null) {
4792 // Initialize ref variable
4794 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4797 // Monitor.Enter (expr_copy)
4799 expr_copy.Emit (ec);
4800 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4803 base.EmitTryBodyPrepare (ec);
4806 protected override void EmitTryBody (EmitContext ec)
4809 // Monitor.Enter (expr_copy, ref lock_taken)
4811 if (lock_taken != null) {
4812 expr_copy.Emit (ec);
4813 lock_taken.LocalInfo.CreateBuilder (ec);
4814 lock_taken.AddressOf (ec, AddressOp.Load);
4815 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4818 Statement.Emit (ec);
4821 public override void EmitFinallyBody (EmitContext ec)
4824 // if (lock_taken) Monitor.Exit (expr_copy)
4826 Label skip = ec.DefineLabel ();
4828 if (lock_taken != null) {
4829 lock_taken.Emit (ec);
4830 ec.Emit (OpCodes.Brfalse_S, skip);
4833 expr_copy.Emit (ec);
4834 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4836 ec.Emit (OpCodes.Call, m);
4838 ec.MarkLabel (skip);
4841 int ResolvePredefinedMethods (ResolveContext rc)
4843 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4844 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4848 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4852 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4856 protected override void CloneTo (CloneContext clonectx, Statement t)
4858 Lock target = (Lock) t;
4860 target.expr = expr.Clone (clonectx);
4861 target.stmt = Statement.Clone (clonectx);
4864 public override object Accept (StructuralVisitor visitor)
4866 return visitor.Visit (this);
4871 public class Unchecked : Statement {
4874 public Unchecked (Block b, Location loc)
4881 public override bool Resolve (BlockContext ec)
4883 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4884 return Block.Resolve (ec);
4887 protected override void DoEmit (EmitContext ec)
4889 using (ec.With (EmitContext.Options.CheckedScope, false))
4893 protected override void CloneTo (CloneContext clonectx, Statement t)
4895 Unchecked target = (Unchecked) t;
4897 target.Block = clonectx.LookupBlock (Block);
4900 public override object Accept (StructuralVisitor visitor)
4902 return visitor.Visit (this);
4906 public class Checked : Statement {
4909 public Checked (Block b, Location loc)
4912 b.Unchecked = false;
4916 public override bool Resolve (BlockContext ec)
4918 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4919 return Block.Resolve (ec);
4922 protected override void DoEmit (EmitContext ec)
4924 using (ec.With (EmitContext.Options.CheckedScope, true))
4928 protected override void CloneTo (CloneContext clonectx, Statement t)
4930 Checked target = (Checked) t;
4932 target.Block = clonectx.LookupBlock (Block);
4935 public override object Accept (StructuralVisitor visitor)
4937 return visitor.Visit (this);
4941 public class Unsafe : Statement {
4944 public Unsafe (Block b, Location loc)
4947 Block.Unsafe = true;
4951 public override bool Resolve (BlockContext ec)
4953 if (ec.CurrentIterator != null)
4954 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4956 using (ec.Set (ResolveContext.Options.UnsafeScope))
4957 return Block.Resolve (ec);
4960 protected override void DoEmit (EmitContext ec)
4965 protected override void CloneTo (CloneContext clonectx, Statement t)
4967 Unsafe target = (Unsafe) t;
4969 target.Block = clonectx.LookupBlock (Block);
4972 public override object Accept (StructuralVisitor visitor)
4974 return visitor.Visit (this);
4981 public class Fixed : Statement
4983 abstract class Emitter : ShimExpression
4985 protected LocalVariable vi;
4987 protected Emitter (Expression expr, LocalVariable li)
4993 public abstract void EmitExit (EmitContext ec);
4996 class ExpressionEmitter : Emitter {
4997 public ExpressionEmitter (Expression converted, LocalVariable li) :
4998 base (converted, li)
5002 protected override Expression DoResolve (ResolveContext rc)
5004 throw new NotImplementedException ();
5007 public override void Emit (EmitContext ec) {
5009 // Store pointer in pinned location
5015 public override void EmitExit (EmitContext ec)
5018 ec.Emit (OpCodes.Conv_U);
5023 class StringEmitter : Emitter
5025 LocalVariable pinned_string;
5027 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5032 protected override Expression DoResolve (ResolveContext rc)
5034 pinned_string = new LocalVariable (vi.Block, "$pinned",
5035 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5037 pinned_string.Type = rc.BuiltinTypes.String;
5039 eclass = ExprClass.Variable;
5040 type = rc.BuiltinTypes.Int;
5044 public override void Emit (EmitContext ec)
5046 pinned_string.CreateBuilder (ec);
5049 pinned_string.EmitAssign (ec);
5051 // TODO: Should use Binary::Add
5052 pinned_string.Emit (ec);
5053 ec.Emit (OpCodes.Conv_I);
5055 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5059 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5060 //pe.InstanceExpression = pinned_string;
5061 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5063 ec.Emit (OpCodes.Add);
5067 public override void EmitExit (EmitContext ec)
5070 pinned_string.EmitAssign (ec);
5074 public class VariableDeclaration : BlockVariableDeclaration
5076 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5081 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5083 if (!Variable.Type.IsPointer && li == Variable) {
5084 bc.Report.Error (209, TypeExpression.Location,
5085 "The type of locals declared in a fixed statement must be a pointer type");
5090 // The rules for the possible declarators are pretty wise,
5091 // but the production on the grammar is more concise.
5093 // So we have to enforce these rules here.
5095 // We do not resolve before doing the case 1 test,
5096 // because the grammar is explicit in that the token &
5097 // is present, so we need to test for this particular case.
5100 if (initializer is Cast) {
5101 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5105 initializer = initializer.Resolve (bc);
5107 if (initializer == null)
5113 if (initializer.Type.IsArray) {
5114 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5117 // Provided that array_type is unmanaged,
5119 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5123 // and T* is implicitly convertible to the
5124 // pointer type given in the fixed statement.
5126 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5128 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5129 if (converted == null)
5133 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5135 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5136 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
5137 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
5138 new NullLiteral (loc),
5141 converted = converted.Resolve (bc);
5143 return new ExpressionEmitter (converted, li);
5149 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5150 return new StringEmitter (initializer, li, loc).Resolve (bc);
5153 // Case 3: fixed buffer
5154 if (initializer is FixedBufferPtr) {
5155 return new ExpressionEmitter (initializer, li);
5159 // Case 4: & object.
5161 bool already_fixed = true;
5162 Unary u = initializer as Unary;
5163 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5164 IVariableReference vr = u.Expr as IVariableReference;
5165 if (vr == null || !vr.IsFixed) {
5166 already_fixed = false;
5170 if (already_fixed) {
5171 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5174 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5175 return new ExpressionEmitter (initializer, li);
5180 VariableDeclaration decl;
5181 Statement statement;
5184 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5193 public Statement Statement {
5199 public BlockVariableDeclaration Variables {
5207 public override bool Resolve (BlockContext ec)
5209 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5210 if (!decl.Resolve (ec))
5214 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5215 bool ok = statement.Resolve (ec);
5216 bool flow_unreachable = ec.EndFlowBranching ();
5217 has_ret = flow_unreachable;
5222 protected override void DoEmit (EmitContext ec)
5224 decl.Variable.CreateBuilder (ec);
5225 decl.Initializer.Emit (ec);
5226 if (decl.Declarators != null) {
5227 foreach (var d in decl.Declarators) {
5228 d.Variable.CreateBuilder (ec);
5229 d.Initializer.Emit (ec);
5233 statement.Emit (ec);
5239 // Clear the pinned variable
5241 ((Emitter) decl.Initializer).EmitExit (ec);
5242 if (decl.Declarators != null) {
5243 foreach (var d in decl.Declarators) {
5244 ((Emitter)d.Initializer).EmitExit (ec);
5249 protected override void CloneTo (CloneContext clonectx, Statement t)
5251 Fixed target = (Fixed) t;
5253 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5254 target.statement = statement.Clone (clonectx);
5257 public override object Accept (StructuralVisitor visitor)
5259 return visitor.Visit (this);
5263 public class Catch : Statement
5267 FullNamedExpression type_expr;
5268 CompilerAssign assign;
5271 public Catch (Block block, Location loc)
5279 public Block Block {
5285 public TypeSpec CatchType {
5291 public bool IsGeneral {
5293 return type_expr == null;
5297 public FullNamedExpression TypeExpression {
5306 public LocalVariable Variable {
5317 protected override void DoEmit (EmitContext ec)
5320 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5322 ec.BeginCatchBlock (CatchType);
5325 li.CreateBuilder (ec);
5328 // Special case hoisted catch variable, we have to use a temporary variable
5329 // to pass via anonymous storey initialization with the value still on top
5332 if (li.HoistedVariant != null) {
5333 LocalTemporary lt = new LocalTemporary (li.Type);
5336 // switch to assigning from the temporary variable and not from top of the stack
5337 assign.UpdateSource (lt);
5340 ec.Emit (OpCodes.Pop);
5346 public override bool Resolve (BlockContext ec)
5348 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5349 if (type_expr != null) {
5350 type = type_expr.ResolveAsType (ec);
5354 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5355 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5356 } else if (li != null) {
5358 li.PrepareForFlowAnalysis (ec);
5360 // source variable is at the top of the stack
5361 Expression source = new EmptyExpression (li.Type);
5362 if (li.Type.IsGenericParameter)
5363 source = new UnboxCast (source, li.Type);
5366 // Uses Location.Null to hide from symbol file
5368 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5369 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5373 return Block.Resolve (ec);
5377 protected override void CloneTo (CloneContext clonectx, Statement t)
5379 Catch target = (Catch) t;
5381 if (type_expr != null)
5382 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5384 target.block = clonectx.LookupBlock (block);
5388 public class TryFinally : TryFinallyBlock
5392 public TryFinally (Statement stmt, Block fini, Location loc)
5398 public Block Finallyblock {
5404 public override bool Resolve (BlockContext ec)
5408 ec.StartFlowBranching (this);
5410 if (!stmt.Resolve (ec))
5414 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5416 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5417 if (!fini.Resolve (ec))
5421 ec.EndFlowBranching ();
5423 ok &= base.Resolve (ec);
5428 protected override void EmitTryBody (EmitContext ec)
5433 public override void EmitFinallyBody (EmitContext ec)
5438 protected override void CloneTo (CloneContext clonectx, Statement t)
5440 TryFinally target = (TryFinally) t;
5442 target.stmt = (Statement) stmt.Clone (clonectx);
5444 target.fini = clonectx.LookupBlock (fini);
5447 public override object Accept (StructuralVisitor visitor)
5449 return visitor.Visit (this);
5453 public class TryCatch : ExceptionStatement
5456 List<Catch> clauses;
5457 readonly bool inside_try_finally;
5459 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5463 this.clauses = catch_clauses;
5464 this.inside_try_finally = inside_try_finally;
5467 public List<Catch> Clauses {
5473 public bool IsTryCatchFinally {
5475 return inside_try_finally;
5479 public override bool Resolve (BlockContext ec)
5483 ec.StartFlowBranching (this);
5485 if (!Block.Resolve (ec))
5488 for (int i = 0; i < clauses.Count; ++i) {
5490 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5492 if (!c.Resolve (ec)) {
5497 TypeSpec resolved_type = c.CatchType;
5498 for (int ii = 0; ii < clauses.Count; ++ii) {
5502 if (clauses[ii].IsGeneral) {
5503 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5506 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5509 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5512 ec.Report.Warning (1058, 1, c.loc,
5513 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5521 var ct = clauses[ii].CatchType;
5525 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5526 ec.Report.Error (160, c.loc,
5527 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5528 ct.GetSignatureForError ());
5534 ec.EndFlowBranching ();
5536 return base.Resolve (ec) && ok;
5539 protected sealed override void DoEmit (EmitContext ec)
5541 if (!inside_try_finally)
5542 EmitTryBodyPrepare (ec);
5546 foreach (Catch c in clauses)
5549 if (!inside_try_finally)
5550 ec.EndExceptionBlock ();
5553 protected override void CloneTo (CloneContext clonectx, Statement t)
5555 TryCatch target = (TryCatch) t;
5557 target.Block = clonectx.LookupBlock (Block);
5558 if (clauses != null){
5559 target.clauses = new List<Catch> ();
5560 foreach (Catch c in clauses)
5561 target.clauses.Add ((Catch) c.Clone (clonectx));
5565 public override object Accept (StructuralVisitor visitor)
5567 return visitor.Visit (this);
5571 public class Using : TryFinallyBlock
5573 public class VariableDeclaration : BlockVariableDeclaration
5575 Statement dispose_call;
5577 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5582 public VariableDeclaration (LocalVariable li, Location loc)
5588 public VariableDeclaration (Expression expr)
5591 loc = expr.Location;
5597 public bool IsNested { get; private set; }
5601 public void EmitDispose (EmitContext ec)
5603 dispose_call.Emit (ec);
5606 public override bool Resolve (BlockContext bc)
5611 return base.Resolve (bc, false);
5614 public Expression ResolveExpression (BlockContext bc)
5616 var e = Initializer.Resolve (bc);
5620 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5621 Initializer = ResolveInitializer (bc, Variable, e);
5625 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5627 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5628 initializer = initializer.Resolve (bc);
5629 if (initializer == null)
5632 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5633 Arguments args = new Arguments (1);
5634 args.Add (new Argument (initializer));
5635 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5636 if (initializer == null)
5639 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5640 dispose_call = CreateDisposeCall (bc, var);
5641 dispose_call.Resolve (bc);
5643 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5646 if (li == Variable) {
5647 CheckIDiposableConversion (bc, li, initializer);
5648 dispose_call = CreateDisposeCall (bc, li);
5649 dispose_call.Resolve (bc);
5652 return base.ResolveInitializer (bc, li, initializer);
5655 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5659 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5660 if (type.IsNullableType) {
5661 // it's handled in CreateDisposeCall
5665 bc.Report.SymbolRelatedToPreviousError (type);
5666 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5667 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5668 type.GetSignatureForError ());
5674 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5676 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5678 var loc = lv.Location;
5680 var idt = bc.BuiltinTypes.IDisposable;
5681 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5683 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5684 dispose_mg.InstanceExpression = type.IsNullableType ?
5685 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5689 // Hide it from symbol file via null location
5691 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5693 // Add conditional call when disposing possible null variable
5694 if (!type.IsStruct || type.IsNullableType)
5695 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, dispose.loc);
5700 public void ResolveDeclaratorInitializer (BlockContext bc)
5702 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5705 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5707 for (int i = declarators.Count - 1; i >= 0; --i) {
5708 var d = declarators [i];
5709 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5710 vd.Initializer = d.Initializer;
5712 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5713 vd.dispose_call.Resolve (bc);
5715 stmt = new Using (vd, stmt, d.Variable.Location);
5722 public override object Accept (StructuralVisitor visitor)
5724 return visitor.Visit (this);
5728 VariableDeclaration decl;
5730 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5736 public Using (Expression expr, Statement stmt, Location loc)
5739 this.decl = new VariableDeclaration (expr);
5744 public Expression Expr {
5746 return decl.Variable == null ? decl.Initializer : null;
5750 public BlockVariableDeclaration Variables {
5758 public override void Emit (EmitContext ec)
5761 // Don't emit sequence point it will be set on variable declaration
5766 protected override void EmitTryBodyPrepare (EmitContext ec)
5769 base.EmitTryBodyPrepare (ec);
5772 protected override void EmitTryBody (EmitContext ec)
5777 public override void EmitFinallyBody (EmitContext ec)
5779 decl.EmitDispose (ec);
5782 public override bool Resolve (BlockContext ec)
5784 VariableReference vr;
5785 bool vr_locked = false;
5787 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5788 if (decl.Variable == null) {
5789 vr = decl.ResolveExpression (ec) as VariableReference;
5791 vr_locked = vr.IsLockedByStatement;
5792 vr.IsLockedByStatement = true;
5795 if (decl.IsNested) {
5796 decl.ResolveDeclaratorInitializer (ec);
5798 if (!decl.Resolve (ec))
5801 if (decl.Declarators != null) {
5802 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5810 ec.StartFlowBranching (this);
5814 ec.EndFlowBranching ();
5817 vr.IsLockedByStatement = vr_locked;
5824 protected override void CloneTo (CloneContext clonectx, Statement t)
5826 Using target = (Using) t;
5828 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5829 target.stmt = stmt.Clone (clonectx);
5832 public override object Accept (StructuralVisitor visitor)
5834 return visitor.Visit (this);
5839 /// Implementation of the foreach C# statement
5841 public class Foreach : Statement
5843 abstract class IteratorStatement : Statement
5845 protected readonly Foreach for_each;
5847 protected IteratorStatement (Foreach @foreach)
5849 this.for_each = @foreach;
5850 this.loc = @foreach.expr.Location;
5853 protected override void CloneTo (CloneContext clonectx, Statement target)
5855 throw new NotImplementedException ();
5858 public override void Emit (EmitContext ec)
5860 if (ec.EmitAccurateDebugInfo) {
5861 ec.Emit (OpCodes.Nop);
5868 sealed class ArrayForeach : IteratorStatement
5870 TemporaryVariableReference[] lengths;
5871 Expression [] length_exprs;
5872 StatementExpression[] counter;
5873 TemporaryVariableReference[] variables;
5875 TemporaryVariableReference copy;
5877 public ArrayForeach (Foreach @foreach, int rank)
5880 counter = new StatementExpression[rank];
5881 variables = new TemporaryVariableReference[rank];
5882 length_exprs = new Expression [rank];
5885 // Only use temporary length variables when dealing with
5886 // multi-dimensional arrays
5889 lengths = new TemporaryVariableReference [rank];
5892 public override bool Resolve (BlockContext ec)
5894 Block variables_block = for_each.variable.Block;
5895 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5898 int rank = length_exprs.Length;
5899 Arguments list = new Arguments (rank);
5900 for (int i = 0; i < rank; i++) {
5901 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5903 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5904 counter[i].Resolve (ec);
5907 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5909 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5910 lengths[i].Resolve (ec);
5912 Arguments args = new Arguments (1);
5913 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5914 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5917 list.Add (new Argument (v));
5920 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5925 if (for_each.type is VarExpr) {
5926 // Infer implicitly typed local variable from foreach array type
5927 var_type = access.Type;
5929 var_type = for_each.type.ResolveAsType (ec);
5931 if (var_type == null)
5934 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5939 for_each.variable.Type = var_type;
5941 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5942 if (variable_ref == null)
5945 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.variable.Location));
5949 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5950 ec.CurrentBranching.CreateSibling ();
5952 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5953 if (!for_each.body.Resolve (ec))
5955 ec.EndFlowBranching ();
5957 // There's no direct control flow from the end of the embedded statement to the end of the loop
5958 ec.CurrentBranching.CurrentUsageVector.Goto ();
5960 ec.EndFlowBranching ();
5965 protected override void DoEmit (EmitContext ec)
5967 copy.EmitAssign (ec, for_each.expr);
5969 int rank = length_exprs.Length;
5970 Label[] test = new Label [rank];
5971 Label[] loop = new Label [rank];
5973 for (int i = 0; i < rank; i++) {
5974 test [i] = ec.DefineLabel ();
5975 loop [i] = ec.DefineLabel ();
5977 if (lengths != null)
5978 lengths [i].EmitAssign (ec, length_exprs [i]);
5981 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5982 for (int i = 0; i < rank; i++) {
5983 variables [i].EmitAssign (ec, zero);
5985 ec.Emit (OpCodes.Br, test [i]);
5986 ec.MarkLabel (loop [i]);
5989 for_each.body.Emit (ec);
5991 ec.MarkLabel (ec.LoopBegin);
5992 ec.Mark (for_each.expr.Location);
5994 for (int i = rank - 1; i >= 0; i--){
5995 counter [i].Emit (ec);
5997 ec.MarkLabel (test [i]);
5998 variables [i].Emit (ec);
6000 if (lengths != null)
6001 lengths [i].Emit (ec);
6003 length_exprs [i].Emit (ec);
6005 ec.Emit (OpCodes.Blt, loop [i]);
6008 ec.MarkLabel (ec.LoopEnd);
6012 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6014 class RuntimeDispose : Using.VariableDeclaration
6016 public RuntimeDispose (LocalVariable lv, Location loc)
6021 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6023 // Defered to runtime check
6026 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6028 var idt = bc.BuiltinTypes.IDisposable;
6031 // Fabricates code like
6033 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6036 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6038 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6039 dispose_variable.CreateReferenceExpression (bc, loc),
6040 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6041 loc), new NullLiteral (loc), loc);
6043 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6045 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6046 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6048 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6049 return new If (idisaposable_test, dispose, loc);
6053 LocalVariable variable;
6055 Statement statement;
6056 ExpressionStatement init;
6057 TemporaryVariableReference enumerator_variable;
6058 bool ambiguous_getenumerator_name;
6060 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6063 this.variable = var;
6067 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6069 rc.Report.SymbolRelatedToPreviousError (enumerator);
6070 rc.Report.Error (202, loc,
6071 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6072 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6075 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6078 // Option 1: Try to match by name GetEnumerator first
6080 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6081 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6083 var mg = mexpr as MethodGroupExpr;
6085 mg.InstanceExpression = expr;
6086 Arguments args = new Arguments (0);
6087 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
6089 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6090 if (ambiguous_getenumerator_name)
6093 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6099 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6102 PredefinedMember<MethodSpec> iface_candidate = null;
6103 var ptypes = rc.Module.PredefinedTypes;
6104 var gen_ienumerable = ptypes.IEnumerableGeneric;
6105 if (!gen_ienumerable.Define ())
6106 gen_ienumerable = null;
6109 var ifaces = t.Interfaces;
6110 if (ifaces != null) {
6111 foreach (var iface in ifaces) {
6112 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6113 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6114 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6115 rc.Report.Error (1640, loc,
6116 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6117 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6122 // TODO: Cache this somehow
6123 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6124 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6129 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6130 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6135 if (t.IsGenericParameter)
6140 } while (t != null);
6142 if (iface_candidate == null) {
6143 if (expr.Type != InternalType.ErrorType) {
6144 rc.Report.Error (1579, loc,
6145 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6146 expr.Type.GetSignatureForError (), "GetEnumerator");
6152 var method = iface_candidate.Resolve (loc);
6156 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6157 mg.InstanceExpression = expr;
6161 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6163 var ms = MemberCache.FindMember (enumerator.ReturnType,
6164 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6165 BindingRestriction.InstanceOnly) as MethodSpec;
6167 if (ms == null || !ms.IsPublic) {
6168 Error_WrongEnumerator (rc, enumerator);
6172 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6175 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6177 var ps = MemberCache.FindMember (enumerator.ReturnType,
6178 MemberFilter.Property ("Current", null),
6179 BindingRestriction.InstanceOnly) as PropertySpec;
6181 if (ps == null || !ps.IsPublic) {
6182 Error_WrongEnumerator (rc, enumerator);
6189 public override bool Resolve (BlockContext ec)
6191 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6194 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6195 } else if (expr.Type.IsNullableType) {
6196 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6199 var get_enumerator_mg = ResolveGetEnumerator (ec);
6200 if (get_enumerator_mg == null) {
6204 var get_enumerator = get_enumerator_mg.BestCandidate;
6205 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6206 enumerator_variable.Resolve (ec);
6208 // Prepare bool MoveNext ()
6209 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6210 if (move_next_mg == null) {
6214 move_next_mg.InstanceExpression = enumerator_variable;
6216 // Prepare ~T~ Current { get; }
6217 var current_prop = ResolveCurrent (ec, get_enumerator);
6218 if (current_prop == null) {
6222 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6223 if (current_pe == null)
6226 VarExpr ve = for_each.type as VarExpr;
6230 // Source type is dynamic, set element type to dynamic too
6231 variable.Type = ec.BuiltinTypes.Dynamic;
6233 // Infer implicitly typed local variable from foreach enumerable type
6234 variable.Type = current_pe.Type;
6238 // Explicit cast of dynamic collection elements has to be done at runtime
6239 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6242 variable.Type = for_each.type.ResolveAsType (ec);
6244 if (variable.Type == null)
6247 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6248 if (current_pe == null)
6252 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6253 if (variable_ref == null)
6256 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), variable.Location));
6258 var init = new Invocation (get_enumerator_mg, null);
6260 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6261 for_each.body, Location.Null);
6263 var enum_type = enumerator_variable.Type;
6266 // Add Dispose method call when enumerator can be IDisposable
6268 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6269 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6271 // Runtime Dispose check
6273 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6274 vd.Initializer = init;
6275 statement = new Using (vd, statement, Location.Null);
6278 // No Dispose call needed
6280 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6281 this.init.Resolve (ec);
6285 // Static Dispose check
6287 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6288 vd.Initializer = init;
6289 statement = new Using (vd, statement, Location.Null);
6292 return statement.Resolve (ec);
6295 protected override void DoEmit (EmitContext ec)
6297 enumerator_variable.LocalInfo.CreateBuilder (ec);
6300 init.EmitStatement (ec);
6302 statement.Emit (ec);
6305 #region IErrorHandler Members
6307 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6309 ec.Report.SymbolRelatedToPreviousError (best);
6310 ec.Report.Warning (278, 2, expr.Location,
6311 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6312 expr.Type.GetSignatureForError (), "enumerable",
6313 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6315 ambiguous_getenumerator_name = true;
6319 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6324 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6329 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6338 LocalVariable variable;
6340 Statement statement;
6343 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6346 this.variable = var;
6348 this.statement = stmt;
6353 public Expression Expr {
6354 get { return expr; }
6357 public Statement Statement {
6358 get { return statement; }
6361 public Expression TypeExpression {
6362 get { return type; }
6365 public LocalVariable Variable {
6366 get { return variable; }
6369 public override bool Resolve (BlockContext ec)
6371 expr = expr.Resolve (ec);
6376 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6380 body.AddStatement (statement);
6382 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6383 statement = new ArrayForeach (this, 1);
6384 } else if (expr.Type is ArrayContainer) {
6385 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6387 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6388 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6389 expr.ExprClassName);
6393 statement = new CollectionForeach (this, variable, expr);
6396 return statement.Resolve (ec);
6399 protected override void DoEmit (EmitContext ec)
6401 variable.CreateBuilder (ec);
6403 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6404 ec.LoopBegin = ec.DefineLabel ();
6405 ec.LoopEnd = ec.DefineLabel ();
6407 statement.Emit (ec);
6409 ec.LoopBegin = old_begin;
6410 ec.LoopEnd = old_end;
6413 protected override void CloneTo (CloneContext clonectx, Statement t)
6415 Foreach target = (Foreach) t;
6417 target.type = type.Clone (clonectx);
6418 target.expr = expr.Clone (clonectx);
6419 target.body = (Block) body.Clone (clonectx);
6420 target.statement = statement.Clone (clonectx);
6423 public override object Accept (StructuralVisitor visitor)
6425 return visitor.Visit (this);