2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
52 bool unreachable = false;
53 if (warn && !ec.UnreachableReported) {
54 ec.UnreachableReported = true;
56 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
59 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
60 bool ok = Resolve (ec);
61 ec.KillFlowBranching ();
64 ec.UnreachableReported = false;
71 /// Return value indicates whether all code paths emitted return.
73 protected abstract void DoEmit (EmitContext ec);
75 public virtual void Emit (EmitContext ec)
80 if (ec.StatementEpilogue != null) {
86 // This routine must be overrided in derived classes and make copies
87 // of all the data that might be modified if resolved
89 protected abstract void CloneTo (CloneContext clonectx, Statement target);
91 public Statement Clone (CloneContext clonectx)
93 Statement s = (Statement) this.MemberwiseClone ();
94 CloneTo (clonectx, s);
98 public virtual Expression CreateExpressionTree (ResolveContext ec)
100 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
104 public virtual object Accept (StructuralVisitor visitor)
106 return visitor.Visit (this);
110 public sealed class EmptyStatement : Statement
112 public EmptyStatement (Location loc)
117 public override bool Resolve (BlockContext ec)
122 public override bool ResolveUnreachable (BlockContext ec, bool warn)
127 public override void Emit (EmitContext ec)
131 protected override void DoEmit (EmitContext ec)
133 throw new NotSupportedException ();
136 protected override void CloneTo (CloneContext clonectx, Statement target)
141 public override object Accept (StructuralVisitor visitor)
143 return visitor.Visit (this);
147 public class If : Statement {
149 public Statement TrueStatement;
150 public Statement FalseStatement;
154 public If (Expression bool_expr, Statement true_statement, Location l)
155 : this (bool_expr, true_statement, null, l)
159 public If (Expression bool_expr,
160 Statement true_statement,
161 Statement false_statement,
164 this.expr = bool_expr;
165 TrueStatement = true_statement;
166 FalseStatement = false_statement;
170 public Expression Expr {
176 public override bool Resolve (BlockContext ec)
180 expr = expr.Resolve (ec);
185 // Dead code elimination
187 if (expr is Constant) {
188 bool take = !((Constant) expr).IsDefaultValue;
191 if (!TrueStatement.Resolve (ec))
194 if ((FalseStatement != null) &&
195 !FalseStatement.ResolveUnreachable (ec, true))
197 FalseStatement = null;
199 if (!TrueStatement.ResolveUnreachable (ec, true))
201 TrueStatement = null;
203 if ((FalseStatement != null) &&
204 !FalseStatement.Resolve (ec))
212 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
214 ok &= TrueStatement.Resolve (ec);
216 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
218 ec.CurrentBranching.CreateSibling ();
220 if (FalseStatement != null)
221 ok &= FalseStatement.Resolve (ec);
223 ec.EndFlowBranching ();
228 protected override void DoEmit (EmitContext ec)
230 Label false_target = ec.DefineLabel ();
234 // If we're a boolean constant, Resolve() already
235 // eliminated dead code for us.
237 Constant c = expr as Constant;
239 c.EmitSideEffect (ec);
241 if (!c.IsDefaultValue)
242 TrueStatement.Emit (ec);
243 else if (FalseStatement != null)
244 FalseStatement.Emit (ec);
249 expr.EmitBranchable (ec, false_target, false);
251 TrueStatement.Emit (ec);
253 if (FalseStatement != null){
254 bool branch_emitted = false;
256 end = ec.DefineLabel ();
258 ec.Emit (OpCodes.Br, end);
259 branch_emitted = true;
262 ec.MarkLabel (false_target);
263 FalseStatement.Emit (ec);
268 ec.MarkLabel (false_target);
272 protected override void CloneTo (CloneContext clonectx, Statement t)
276 target.expr = expr.Clone (clonectx);
277 target.TrueStatement = TrueStatement.Clone (clonectx);
278 if (FalseStatement != null)
279 target.FalseStatement = FalseStatement.Clone (clonectx);
282 public override object Accept (StructuralVisitor visitor)
284 return visitor.Visit (this);
288 public class Do : Statement {
289 public Expression expr;
290 public Statement EmbeddedStatement;
292 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
295 EmbeddedStatement = statement;
297 WhileLocation = whileLocation;
300 public Location WhileLocation {
304 public override bool Resolve (BlockContext ec)
308 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
310 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
312 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
313 if (!EmbeddedStatement.Resolve (ec))
315 ec.EndFlowBranching ();
317 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
318 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
320 expr = expr.Resolve (ec);
323 else if (expr is Constant){
324 bool infinite = !((Constant) expr).IsDefaultValue;
326 ec.CurrentBranching.CurrentUsageVector.Goto ();
329 ec.EndFlowBranching ();
334 protected override void DoEmit (EmitContext ec)
336 Label loop = ec.DefineLabel ();
337 Label old_begin = ec.LoopBegin;
338 Label old_end = ec.LoopEnd;
340 ec.LoopBegin = ec.DefineLabel ();
341 ec.LoopEnd = ec.DefineLabel ();
344 EmbeddedStatement.Emit (ec);
345 ec.MarkLabel (ec.LoopBegin);
347 // Mark start of while condition
348 ec.Mark (WhileLocation);
351 // Dead code elimination
353 if (expr is Constant) {
354 bool res = !((Constant) expr).IsDefaultValue;
356 expr.EmitSideEffect (ec);
358 ec.Emit (OpCodes.Br, loop);
360 expr.EmitBranchable (ec, loop, true);
363 ec.MarkLabel (ec.LoopEnd);
365 ec.LoopBegin = old_begin;
366 ec.LoopEnd = old_end;
369 protected override void CloneTo (CloneContext clonectx, Statement t)
373 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
374 target.expr = expr.Clone (clonectx);
377 public override object Accept (StructuralVisitor visitor)
379 return visitor.Visit (this);
383 public class While : Statement {
384 public Expression expr;
385 public Statement Statement;
386 bool infinite, empty;
388 public While (BooleanExpression bool_expr, Statement statement, Location l)
390 this.expr = bool_expr;
391 Statement = statement;
395 public override bool Resolve (BlockContext ec)
399 expr = expr.Resolve (ec);
404 // Inform whether we are infinite or not
406 if (expr is Constant){
407 bool value = !((Constant) expr).IsDefaultValue;
410 if (!Statement.ResolveUnreachable (ec, true))
418 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
420 ec.CurrentBranching.CreateSibling ();
422 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
423 if (!Statement.Resolve (ec))
425 ec.EndFlowBranching ();
427 // There's no direct control flow from the end of the embedded statement to the end of the loop
428 ec.CurrentBranching.CurrentUsageVector.Goto ();
430 ec.EndFlowBranching ();
435 protected override void DoEmit (EmitContext ec)
438 expr.EmitSideEffect (ec);
442 Label old_begin = ec.LoopBegin;
443 Label old_end = ec.LoopEnd;
445 ec.LoopBegin = ec.DefineLabel ();
446 ec.LoopEnd = ec.DefineLabel ();
449 // Inform whether we are infinite or not
451 if (expr is Constant) {
452 // expr is 'true', since the 'empty' case above handles the 'false' case
453 ec.MarkLabel (ec.LoopBegin);
455 if (ec.EmitAccurateDebugInfo)
456 ec.Emit (OpCodes.Nop);
458 expr.EmitSideEffect (ec);
460 ec.Emit (OpCodes.Br, ec.LoopBegin);
463 // Inform that we are infinite (ie, `we return'), only
464 // if we do not `break' inside the code.
466 ec.MarkLabel (ec.LoopEnd);
468 Label while_loop = ec.DefineLabel ();
470 ec.Emit (OpCodes.Br, ec.LoopBegin);
471 ec.MarkLabel (while_loop);
475 ec.MarkLabel (ec.LoopBegin);
478 expr.EmitBranchable (ec, while_loop, true);
480 ec.MarkLabel (ec.LoopEnd);
483 ec.LoopBegin = old_begin;
484 ec.LoopEnd = old_end;
487 protected override void CloneTo (CloneContext clonectx, Statement t)
489 While target = (While) t;
491 target.expr = expr.Clone (clonectx);
492 target.Statement = Statement.Clone (clonectx);
495 public override object Accept (StructuralVisitor visitor)
497 return visitor.Visit (this);
501 public class For : Statement
503 bool infinite, empty;
505 public For (Location l)
510 public Statement Initializer {
514 public Expression Condition {
518 public Statement Iterator {
522 public Statement Statement {
526 public override bool Resolve (BlockContext ec)
530 if (Initializer != null) {
531 if (!Initializer.Resolve (ec))
535 if (Condition != null) {
536 Condition = Condition.Resolve (ec);
537 if (Condition == null)
539 else if (Condition is Constant) {
540 bool value = !((Constant) Condition).IsDefaultValue;
543 if (!Statement.ResolveUnreachable (ec, true))
545 if ((Iterator != null) &&
546 !Iterator.ResolveUnreachable (ec, false))
556 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
558 ec.CurrentBranching.CreateSibling ();
560 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
562 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
563 if (!Statement.Resolve (ec))
565 ec.EndFlowBranching ();
567 if (Iterator != null){
568 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
569 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
572 if (!Iterator.Resolve (ec))
577 // There's no direct control flow from the end of the embedded statement to the end of the loop
578 ec.CurrentBranching.CurrentUsageVector.Goto ();
580 ec.EndFlowBranching ();
585 protected override void DoEmit (EmitContext ec)
587 if (Initializer != null)
588 Initializer.Emit (ec);
591 Condition.EmitSideEffect (ec);
595 Label old_begin = ec.LoopBegin;
596 Label old_end = ec.LoopEnd;
597 Label loop = ec.DefineLabel ();
598 Label test = ec.DefineLabel ();
600 ec.LoopBegin = ec.DefineLabel ();
601 ec.LoopEnd = ec.DefineLabel ();
603 ec.Emit (OpCodes.Br, test);
607 ec.MarkLabel (ec.LoopBegin);
612 // If test is null, there is no test, and we are just
615 if (Condition != null) {
616 ec.Mark (Condition.Location);
619 // The Resolve code already catches the case for
620 // Test == Constant (false) so we know that
623 if (Condition is Constant) {
624 Condition.EmitSideEffect (ec);
625 ec.Emit (OpCodes.Br, loop);
627 Condition.EmitBranchable (ec, loop, true);
631 ec.Emit (OpCodes.Br, loop);
632 ec.MarkLabel (ec.LoopEnd);
634 ec.LoopBegin = old_begin;
635 ec.LoopEnd = old_end;
638 protected override void CloneTo (CloneContext clonectx, Statement t)
640 For target = (For) t;
642 if (Initializer != null)
643 target.Initializer = Initializer.Clone (clonectx);
644 if (Condition != null)
645 target.Condition = Condition.Clone (clonectx);
646 if (Iterator != null)
647 target.Iterator = Iterator.Clone (clonectx);
648 target.Statement = Statement.Clone (clonectx);
651 public override object Accept (StructuralVisitor visitor)
653 return visitor.Visit (this);
657 public class StatementExpression : Statement
659 ExpressionStatement expr;
661 public StatementExpression (ExpressionStatement expr)
667 public StatementExpression (ExpressionStatement expr, Location loc)
673 public ExpressionStatement Expr {
679 protected override void CloneTo (CloneContext clonectx, Statement t)
681 StatementExpression target = (StatementExpression) t;
682 target.expr = (ExpressionStatement) expr.Clone (clonectx);
685 protected override void DoEmit (EmitContext ec)
687 expr.EmitStatement (ec);
690 public override bool Resolve (BlockContext ec)
692 expr = expr.ResolveStatement (ec);
696 public override object Accept (StructuralVisitor visitor)
698 return visitor.Visit (this);
702 public class StatementErrorExpression : Statement
704 readonly Expression expr;
706 public StatementErrorExpression (Expression expr)
711 public Expression Expr {
717 protected override void DoEmit (EmitContext ec)
719 throw new NotSupportedException ();
722 protected override void CloneTo (CloneContext clonectx, Statement target)
724 throw new NotImplementedException ();
727 public override object Accept (StructuralVisitor visitor)
729 return visitor.Visit (this);
734 // Simple version of statement list not requiring a block
736 public class StatementList : Statement
738 List<Statement> statements;
740 public StatementList (Statement first, Statement second)
742 statements = new List<Statement> () { first, second };
746 public IList<Statement> Statements {
753 public void Add (Statement statement)
755 statements.Add (statement);
758 public override bool Resolve (BlockContext ec)
760 foreach (var s in statements)
766 protected override void DoEmit (EmitContext ec)
768 foreach (var s in statements)
772 protected override void CloneTo (CloneContext clonectx, Statement target)
774 StatementList t = (StatementList) target;
776 t.statements = new List<Statement> (statements.Count);
777 foreach (Statement s in statements)
778 t.statements.Add (s.Clone (clonectx));
781 public override object Accept (StructuralVisitor visitor)
783 return visitor.Visit (this);
787 // A 'return' or a 'yield break'
788 public abstract class ExitStatement : Statement
790 protected bool unwind_protect;
791 protected abstract bool DoResolve (BlockContext ec);
793 public virtual void Error_FinallyClause (Report Report)
795 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
798 public sealed override bool Resolve (BlockContext ec)
800 var res = DoResolve (ec);
801 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
802 ec.CurrentBranching.CurrentUsageVector.Goto ();
808 /// Implements the return statement
810 public class Return : ExitStatement
814 public Return (Expression expr, Location l)
822 public Expression Expr {
833 protected override bool DoResolve (BlockContext ec)
836 if (ec.ReturnType.Kind == MemberKind.Void)
840 // Return must not be followed by an expression when
841 // the method return type is Task
843 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
844 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
845 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
847 // Extra trick not to emit ret/leave inside awaiter body
849 expr = EmptyExpression.Null;
854 if (ec.CurrentIterator != null) {
855 Error_ReturnFromIterator (ec);
857 ec.Report.Error (126, loc,
858 "An object of a type convertible to `{0}' is required for the return statement",
859 ec.ReturnType.GetSignatureForError ());
865 expr = expr.Resolve (ec);
866 TypeSpec block_return_type = ec.ReturnType;
868 AnonymousExpression am = ec.CurrentAnonymousMethod;
870 if (block_return_type.Kind == MemberKind.Void) {
871 ec.Report.Error (127, loc,
872 "`{0}': A return keyword must not be followed by any expression when method returns void",
873 ec.GetSignatureForError ());
879 Error_ReturnFromIterator (ec);
883 var async_block = am as AsyncInitializer;
884 if (async_block != null) {
886 var storey = (AsyncTaskStorey) am.Storey;
887 var async_type = storey.ReturnType;
889 if (async_type == null && async_block.ReturnTypeInference != null) {
890 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
894 if (async_type.Kind == MemberKind.Void) {
895 ec.Report.Error (127, loc,
896 "`{0}': A return keyword must not be followed by any expression when method returns void",
897 ec.GetSignatureForError ());
902 if (!async_type.IsGenericTask) {
903 if (this is ContextualReturn)
906 ec.Report.Error (1997, loc,
907 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
908 ec.GetSignatureForError ());
913 // The return type is actually Task<T> type argument
915 if (expr.Type == async_type) {
916 ec.Report.Error (4016, loc,
917 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
918 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
920 block_return_type = async_type.TypeArguments[0];
924 // Same error code as .NET but better error message
925 if (block_return_type.Kind == MemberKind.Void) {
926 ec.Report.Error (127, loc,
927 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
928 am.GetSignatureForError ());
933 var l = am as AnonymousMethodBody;
934 if (l != null && l.ReturnTypeInference != null && expr != null) {
935 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
944 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
945 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
948 if (am != null && block_return_type == ec.ReturnType) {
949 ec.Report.Error (1662, loc,
950 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
951 am.ContainerType, am.GetSignatureForError ());
960 protected override void DoEmit (EmitContext ec)
965 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
966 if (async_body != null) {
967 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
969 // It's null for await without async
970 if (async_return != null) {
971 async_return.EmitAssign (ec);
976 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
982 if (unwind_protect || ec.EmitAccurateDebugInfo)
983 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
986 if (unwind_protect) {
987 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
988 } else if (ec.EmitAccurateDebugInfo) {
989 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
991 ec.Emit (OpCodes.Ret);
995 void Error_ReturnFromIterator (ResolveContext rc)
997 rc.Report.Error (1622, loc,
998 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1001 protected override void CloneTo (CloneContext clonectx, Statement t)
1003 Return target = (Return) t;
1004 // It's null for simple return;
1006 target.expr = expr.Clone (clonectx);
1009 public override object Accept (StructuralVisitor visitor)
1011 return visitor.Visit (this);
1015 public class Goto : Statement {
1017 LabeledStatement label;
1018 bool unwind_protect;
1020 public override bool Resolve (BlockContext ec)
1022 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1023 ec.CurrentBranching.CurrentUsageVector.Goto ();
1027 public Goto (string label, Location l)
1033 public string Target {
1034 get { return target; }
1037 public void SetResolvedTarget (LabeledStatement label)
1040 label.AddReference ();
1043 protected override void CloneTo (CloneContext clonectx, Statement target)
1048 protected override void DoEmit (EmitContext ec)
1051 throw new InternalErrorException ("goto emitted before target resolved");
1052 Label l = label.LabelTarget (ec);
1053 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1056 public override object Accept (StructuralVisitor visitor)
1058 return visitor.Visit (this);
1062 public class LabeledStatement : Statement {
1069 FlowBranching.UsageVector vectors;
1071 public LabeledStatement (string name, Block block, Location l)
1078 public Label LabelTarget (EmitContext ec)
1083 label = ec.DefineLabel ();
1088 public Block Block {
1094 public string Name {
1095 get { return name; }
1098 public bool IsDefined {
1099 get { return defined; }
1102 public bool HasBeenReferenced {
1103 get { return referenced; }
1106 public FlowBranching.UsageVector JumpOrigins {
1107 get { return vectors; }
1110 public void AddUsageVector (FlowBranching.UsageVector vector)
1112 vector = vector.Clone ();
1113 vector.Next = vectors;
1117 protected override void CloneTo (CloneContext clonectx, Statement target)
1122 public override bool Resolve (BlockContext ec)
1124 // this flow-branching will be terminated when the surrounding block ends
1125 ec.StartFlowBranching (this);
1129 protected override void DoEmit (EmitContext ec)
1131 if (!HasBeenReferenced)
1132 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1135 ec.MarkLabel (label);
1138 public void AddReference ()
1143 public override object Accept (StructuralVisitor visitor)
1145 return visitor.Visit (this);
1151 /// `goto default' statement
1153 public class GotoDefault : Statement {
1155 public GotoDefault (Location l)
1160 protected override void CloneTo (CloneContext clonectx, Statement target)
1165 public override bool Resolve (BlockContext ec)
1167 ec.CurrentBranching.CurrentUsageVector.Goto ();
1169 if (ec.Switch == null) {
1170 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1174 if (!ec.Switch.GotDefault) {
1175 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1182 protected override void DoEmit (EmitContext ec)
1184 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1187 public override object Accept (StructuralVisitor visitor)
1189 return visitor.Visit (this);
1194 /// `goto case' statement
1196 public class GotoCase : Statement {
1200 public GotoCase (Expression e, Location l)
1206 public Expression Expr {
1212 public override bool Resolve (BlockContext ec)
1214 if (ec.Switch == null){
1215 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1219 ec.CurrentBranching.CurrentUsageVector.Goto ();
1221 expr = expr.Resolve (ec);
1225 Constant c = expr as Constant;
1227 ec.Report.Error (150, expr.Location, "A constant value is expected");
1232 if (ec.Switch.IsNullable && c is NullLiteral) {
1235 TypeSpec type = ec.Switch.SwitchType;
1236 res = c.Reduce (ec, type);
1238 c.Error_ValueCannotBeConverted (ec, type, true);
1242 if (!Convert.ImplicitStandardConversionExists (c, type))
1243 ec.Report.Warning (469, 2, loc,
1244 "The `goto case' value is not implicitly convertible to type `{0}'",
1245 TypeManager.CSharpName (type));
1249 sl = ec.Switch.ResolveGotoCase (ec, res);
1253 protected override void DoEmit (EmitContext ec)
1255 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1258 protected override void CloneTo (CloneContext clonectx, Statement t)
1260 GotoCase target = (GotoCase) t;
1262 target.expr = expr.Clone (clonectx);
1265 public override object Accept (StructuralVisitor visitor)
1267 return visitor.Visit (this);
1271 public class Throw : Statement {
1274 public Throw (Expression expr, Location l)
1280 public Expression Expr {
1286 public override bool Resolve (BlockContext ec)
1289 ec.CurrentBranching.CurrentUsageVector.Goto ();
1290 return ec.CurrentBranching.CheckRethrow (loc);
1293 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1294 ec.CurrentBranching.CurrentUsageVector.Goto ();
1299 var et = ec.BuiltinTypes.Exception;
1300 if (Convert.ImplicitConversionExists (ec, expr, et))
1301 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1303 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1308 protected override void DoEmit (EmitContext ec)
1311 ec.Emit (OpCodes.Rethrow);
1315 ec.Emit (OpCodes.Throw);
1319 protected override void CloneTo (CloneContext clonectx, Statement t)
1321 Throw target = (Throw) t;
1324 target.expr = expr.Clone (clonectx);
1327 public override object Accept (StructuralVisitor visitor)
1329 return visitor.Visit (this);
1333 public class Break : Statement {
1335 public Break (Location l)
1340 bool unwind_protect;
1342 public override bool Resolve (BlockContext ec)
1344 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1345 ec.CurrentBranching.CurrentUsageVector.Goto ();
1349 protected override void DoEmit (EmitContext ec)
1351 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1354 protected override void CloneTo (CloneContext clonectx, Statement t)
1359 public override object Accept (StructuralVisitor visitor)
1361 return visitor.Visit (this);
1365 public class Continue : Statement {
1367 public Continue (Location l)
1372 bool unwind_protect;
1374 public override bool Resolve (BlockContext ec)
1376 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1377 ec.CurrentBranching.CurrentUsageVector.Goto ();
1381 protected override void DoEmit (EmitContext ec)
1383 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1386 protected override void CloneTo (CloneContext clonectx, Statement t)
1391 public override object Accept (StructuralVisitor visitor)
1393 return visitor.Visit (this);
1397 public interface ILocalVariable
1399 void Emit (EmitContext ec);
1400 void EmitAssign (EmitContext ec);
1401 void EmitAddressOf (EmitContext ec);
1404 public interface INamedBlockVariable
1406 Block Block { get; }
1407 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1408 bool IsDeclared { get; }
1409 bool IsParameter { get; }
1410 Location Location { get; }
1413 public class BlockVariableDeclaration : Statement
1415 public class Declarator
1418 Expression initializer;
1420 public Declarator (LocalVariable li, Expression initializer)
1422 if (li.Type != null)
1423 throw new ArgumentException ("Expected null variable type");
1426 this.initializer = initializer;
1429 public Declarator (Declarator clone, Expression initializer)
1432 this.initializer = initializer;
1437 public LocalVariable Variable {
1443 public Expression Initializer {
1448 initializer = value;
1455 Expression initializer;
1456 protected FullNamedExpression type_expr;
1457 protected LocalVariable li;
1458 protected List<Declarator> declarators;
1461 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1463 this.type_expr = type;
1465 this.loc = type_expr.Location;
1468 protected BlockVariableDeclaration (LocalVariable li)
1475 public List<Declarator> Declarators {
1481 public Expression Initializer {
1486 initializer = value;
1490 public FullNamedExpression TypeExpression {
1496 public LocalVariable Variable {
1504 public void AddDeclarator (Declarator decl)
1506 if (declarators == null)
1507 declarators = new List<Declarator> ();
1509 declarators.Add (decl);
1512 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1514 if (bc.Report.Errors != 0)
1517 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1519 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1520 new MemberName (li.Name, li.Location), null);
1522 container.AddField (f);
1525 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1529 public override bool Resolve (BlockContext bc)
1531 return Resolve (bc, true);
1534 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1536 if (type == null && !li.IsCompilerGenerated) {
1537 var vexpr = type_expr as VarExpr;
1540 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1541 // same name exists or as a keyword when no type was found
1543 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1544 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1545 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1548 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1552 if (li.IsConstant) {
1553 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1557 if (Initializer == null) {
1558 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1562 if (declarators != null) {
1563 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1567 Initializer = Initializer.Resolve (bc);
1568 if (Initializer != null) {
1569 ((VarExpr) type_expr).InferType (bc, Initializer);
1570 type = type_expr.Type;
1572 // Set error type to indicate the var was placed correctly but could
1575 // var a = missing ();
1577 type = InternalType.ErrorType;
1582 type = type_expr.ResolveAsType (bc);
1586 if (li.IsConstant && !type.IsConstantCompatible) {
1587 Const.Error_InvalidConstantType (type, loc, bc.Report);
1592 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1597 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1599 CreateEvaluatorVariable (bc, li);
1601 li.PrepareForFlowAnalysis (bc);
1604 if (initializer != null) {
1605 initializer = ResolveInitializer (bc, li, initializer);
1606 // li.Variable.DefinitelyAssigned
1609 if (declarators != null) {
1610 foreach (var d in declarators) {
1611 d.Variable.Type = li.Type;
1613 CreateEvaluatorVariable (bc, d.Variable);
1615 d.Variable.PrepareForFlowAnalysis (bc);
1618 if (d.Initializer != null && resolveDeclaratorInitializers) {
1619 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1620 // d.Variable.DefinitelyAssigned
1628 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1630 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1631 return a.ResolveStatement (bc);
1634 protected override void DoEmit (EmitContext ec)
1636 li.CreateBuilder (ec);
1638 if (Initializer != null)
1639 ((ExpressionStatement) Initializer).EmitStatement (ec);
1641 if (declarators != null) {
1642 foreach (var d in declarators) {
1643 d.Variable.CreateBuilder (ec);
1644 if (d.Initializer != null) {
1645 ec.Mark (d.Variable.Location);
1646 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1652 protected override void CloneTo (CloneContext clonectx, Statement target)
1654 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1656 if (type_expr != null)
1657 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1659 if (initializer != null)
1660 t.initializer = initializer.Clone (clonectx);
1662 if (declarators != null) {
1663 t.declarators = null;
1664 foreach (var d in declarators)
1665 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1669 public override object Accept (StructuralVisitor visitor)
1671 return visitor.Visit (this);
1675 public class BlockConstantDeclaration : BlockVariableDeclaration
1677 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1682 public override void Emit (EmitContext ec)
1684 // Nothing to emit, not even sequence point
1687 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1689 initializer = initializer.Resolve (bc);
1690 if (initializer == null)
1693 var c = initializer as Constant;
1695 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1699 c = c.ConvertImplicitly (li.Type);
1701 if (TypeSpec.IsReferenceType (li.Type))
1702 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1704 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1709 li.ConstantValue = c;
1713 public override object Accept (StructuralVisitor visitor)
1715 return visitor.Visit (this);
1720 // The information about a user-perceived local variable
1722 public class LocalVariable : INamedBlockVariable, ILocalVariable
1729 AddressTaken = 1 << 2,
1730 CompilerGenerated = 1 << 3,
1732 ForeachVariable = 1 << 5,
1733 FixedVariable = 1 << 6,
1734 UsingVariable = 1 << 7,
1735 // DefinitelyAssigned = 1 << 8,
1738 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1742 readonly string name;
1743 readonly Location loc;
1744 readonly Block block;
1746 Constant const_value;
1748 public VariableInfo VariableInfo;
1749 HoistedVariable hoisted_variant;
1751 LocalBuilder builder;
1753 public LocalVariable (Block block, string name, Location loc)
1760 public LocalVariable (Block block, string name, Flags flags, Location loc)
1761 : this (block, name, loc)
1767 // Used by variable declarators
1769 public LocalVariable (LocalVariable li, string name, Location loc)
1770 : this (li.block, name, li.flags, loc)
1776 public bool AddressTaken {
1778 return (flags & Flags.AddressTaken) != 0;
1782 public Block Block {
1788 public Constant ConstantValue {
1793 const_value = value;
1798 // Hoisted local variable variant
1800 public HoistedVariable HoistedVariant {
1802 return hoisted_variant;
1805 hoisted_variant = value;
1809 public bool IsDeclared {
1811 return type != null;
1815 public bool IsCompilerGenerated {
1817 return (flags & Flags.CompilerGenerated) != 0;
1821 public bool IsConstant {
1823 return (flags & Flags.Constant) != 0;
1827 public bool IsLocked {
1829 return (flags & Flags.IsLocked) != 0;
1832 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1836 public bool IsThis {
1838 return (flags & Flags.IsThis) != 0;
1842 public bool IsFixed {
1844 return (flags & Flags.FixedVariable) != 0;
1848 bool INamedBlockVariable.IsParameter {
1854 public bool IsReadonly {
1856 return (flags & Flags.ReadonlyMask) != 0;
1860 public Location Location {
1866 public string Name {
1872 public TypeSpec Type {
1883 public void CreateBuilder (EmitContext ec)
1885 if ((flags & Flags.Used) == 0) {
1886 if (VariableInfo == null) {
1887 // Missing flow analysis or wrong variable flags
1888 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1891 if (VariableInfo.IsEverAssigned)
1892 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1894 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1897 if (HoistedVariant != null)
1900 if (builder != null) {
1901 if ((flags & Flags.CompilerGenerated) != 0)
1904 // To avoid Used warning duplicates
1905 throw new InternalErrorException ("Already created variable `{0}'", name);
1909 // All fixed variabled are pinned, a slot has to be alocated
1911 builder = ec.DeclareLocal (Type, IsFixed);
1912 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1913 ec.DefineLocalVariable (name, builder);
1916 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1918 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1923 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1925 if (IsConstant && const_value != null)
1926 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1928 return new LocalVariableReference (this, loc);
1931 public void Emit (EmitContext ec)
1933 // TODO: Need something better for temporary variables
1934 if ((flags & Flags.CompilerGenerated) != 0)
1937 ec.Emit (OpCodes.Ldloc, builder);
1940 public void EmitAssign (EmitContext ec)
1942 // TODO: Need something better for temporary variables
1943 if ((flags & Flags.CompilerGenerated) != 0)
1946 ec.Emit (OpCodes.Stloc, builder);
1949 public void EmitAddressOf (EmitContext ec)
1951 ec.Emit (OpCodes.Ldloca, builder);
1954 public static string GetCompilerGeneratedName (Block block)
1956 // HACK: Debugger depends on the name semantics
1957 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1960 public string GetReadOnlyContext ()
1962 switch (flags & Flags.ReadonlyMask) {
1963 case Flags.FixedVariable:
1964 return "fixed variable";
1965 case Flags.ForeachVariable:
1966 return "foreach iteration variable";
1967 case Flags.UsingVariable:
1968 return "using variable";
1971 throw new InternalErrorException ("Variable is not readonly");
1974 public bool IsThisAssigned (BlockContext ec, Block block)
1976 if (VariableInfo == null)
1977 throw new Exception ();
1979 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1982 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1985 public bool IsAssigned (BlockContext ec)
1987 if (VariableInfo == null)
1988 throw new Exception ();
1990 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1993 public void PrepareForFlowAnalysis (BlockContext bc)
1996 // No need for definitely assigned check for these guys
1998 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2001 VariableInfo = new VariableInfo (this, bc.FlowOffset);
2002 bc.FlowOffset += VariableInfo.Length;
2006 // Mark the variables as referenced in the user code
2008 public void SetIsUsed ()
2010 flags |= Flags.Used;
2013 public void SetHasAddressTaken ()
2015 flags |= (Flags.AddressTaken | Flags.Used);
2018 public override string ToString ()
2020 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2025 /// Block represents a C# block.
2029 /// This class is used in a number of places: either to represent
2030 /// explicit blocks that the programmer places or implicit blocks.
2032 /// Implicit blocks are used as labels or to introduce variable
2035 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2036 /// they contain extra information that is not necessary on normal blocks.
2038 public class Block : Statement {
2045 HasCapturedVariable = 64,
2046 HasCapturedThis = 1 << 7,
2047 IsExpressionTree = 1 << 8,
2048 CompilerGenerated = 1 << 9,
2049 HasAsyncModifier = 1 << 10,
2051 YieldBlock = 1 << 12,
2052 AwaitBlock = 1 << 13
2055 public Block Parent;
2056 public Location StartLocation;
2057 public Location EndLocation;
2059 public ExplicitBlock Explicit;
2060 public ParametersBlock ParametersBlock;
2062 protected Flags flags;
2065 // The statements in this block
2067 protected List<Statement> statements;
2069 protected List<Statement> scope_initializers;
2071 int? resolving_init_idx;
2077 public int ID = id++;
2079 static int clone_id_counter;
2083 // int assignable_slots;
2085 public Block (Block parent, Location start, Location end)
2086 : this (parent, 0, start, end)
2090 public Block (Block parent, Flags flags, Location start, Location end)
2092 if (parent != null) {
2093 // the appropriate constructors will fixup these fields
2094 ParametersBlock = parent.ParametersBlock;
2095 Explicit = parent.Explicit;
2098 this.Parent = parent;
2100 this.StartLocation = start;
2101 this.EndLocation = end;
2103 statements = new List<Statement> (4);
2105 this.original = this;
2110 public bool HasUnreachableClosingBrace {
2112 return (flags & Flags.HasRet) != 0;
2115 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2119 public Block Original {
2128 public bool IsCompilerGenerated {
2129 get { return (flags & Flags.CompilerGenerated) != 0; }
2130 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2133 public bool Unchecked {
2134 get { return (flags & Flags.Unchecked) != 0; }
2135 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2138 public bool Unsafe {
2139 get { return (flags & Flags.Unsafe) != 0; }
2140 set { flags |= Flags.Unsafe; }
2143 public List<Statement> Statements {
2144 get { return statements; }
2149 public Block CreateSwitchBlock (Location start)
2151 // FIXME: Only explicit block should be created
2152 var new_block = new Block (this, start, start);
2153 new_block.IsCompilerGenerated = true;
2157 public void SetEndLocation (Location loc)
2162 public void AddLabel (LabeledStatement target)
2164 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2167 public void AddLocalName (LocalVariable li)
2169 AddLocalName (li.Name, li);
2172 public void AddLocalName (string name, INamedBlockVariable li)
2174 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2177 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2179 if (reason == null) {
2180 Error_AlreadyDeclared (name, variable);
2184 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2185 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2186 "to `{0}', which is already used in a `{1}' scope to denote something else",
2190 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2192 var pi = variable as ParametersBlock.ParameterInfo;
2194 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2196 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2197 "A local variable named `{0}' is already defined in this scope", name);
2201 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2203 ParametersBlock.TopBlock.Report.Error (412, loc,
2204 "The type parameter name `{0}' is the same as local variable or parameter name",
2209 // It should be used by expressions which require to
2210 // register a statement during resolve process.
2212 public void AddScopeStatement (Statement s)
2214 if (scope_initializers == null)
2215 scope_initializers = new List<Statement> ();
2218 // Simple recursive helper, when resolve scope initializer another
2219 // new scope initializer can be added, this ensures it's initialized
2220 // before existing one. For now this can happen with expression trees
2221 // in base ctor initializer only
2223 if (resolving_init_idx.HasValue) {
2224 scope_initializers.Insert (resolving_init_idx.Value, s);
2225 ++resolving_init_idx;
2227 scope_initializers.Add (s);
2231 public void AddStatement (Statement s)
2236 public int AssignableSlots {
2238 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2240 // return assignable_slots;
2244 public LabeledStatement LookupLabel (string name)
2246 return ParametersBlock.TopBlock.GetLabel (name, this);
2249 public override bool Resolve (BlockContext ec)
2251 if ((flags & Flags.Resolved) != 0)
2254 Block prev_block = ec.CurrentBlock;
2256 bool unreachable = ec.IsUnreachable;
2257 bool prev_unreachable = unreachable;
2259 ec.CurrentBlock = this;
2260 ec.StartFlowBranching (this);
2263 // Compiler generated scope statements
2265 if (scope_initializers != null) {
2266 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2267 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2270 resolving_init_idx = null;
2274 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2275 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2276 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2277 // responsible for handling the situation.
2279 int statement_count = statements.Count;
2280 for (int ix = 0; ix < statement_count; ix++){
2281 Statement s = statements [ix];
2284 // Warn if we detect unreachable code.
2287 if (s is EmptyStatement)
2290 if (!ec.UnreachableReported && !(s is LabeledStatement)) {
2291 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2292 ec.UnreachableReported = true;
2297 // Note that we're not using ResolveUnreachable() for unreachable
2298 // statements here. ResolveUnreachable() creates a temporary
2299 // flow branching and kills it afterwards. This leads to problems
2300 // if you have two unreachable statements where the first one
2301 // assigns a variable and the second one tries to access it.
2304 if (!s.Resolve (ec)) {
2306 if (ec.IsInProbingMode)
2309 statements [ix] = new EmptyStatement (s.loc);
2313 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2314 statements [ix] = new EmptyStatement (s.loc);
2316 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2318 ec.IsUnreachable = true;
2319 } else if (ec.IsUnreachable)
2320 ec.IsUnreachable = false;
2323 if (unreachable != prev_unreachable) {
2324 ec.IsUnreachable = prev_unreachable;
2325 ec.UnreachableReported = false;
2328 while (ec.CurrentBranching is FlowBranchingLabeled)
2329 ec.EndFlowBranching ();
2331 bool flow_unreachable = ec.EndFlowBranching ();
2333 ec.CurrentBlock = prev_block;
2335 if (flow_unreachable)
2336 flags |= Flags.HasRet;
2338 // If we're a non-static `struct' constructor which doesn't have an
2339 // initializer, then we must initialize all of the struct's fields.
2340 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2343 flags |= Flags.Resolved;
2347 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2349 bool unreachable = false;
2350 if (warn && !ec.UnreachableReported) {
2351 ec.UnreachableReported = true;
2353 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2356 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2357 fb.CurrentUsageVector.IsUnreachable = true;
2358 bool ok = Resolve (ec);
2359 ec.KillFlowBranching ();
2362 ec.UnreachableReported = false;
2367 protected override void DoEmit (EmitContext ec)
2369 for (int ix = 0; ix < statements.Count; ix++){
2370 statements [ix].Emit (ec);
2374 public override void Emit (EmitContext ec)
2376 if (scope_initializers != null)
2377 EmitScopeInitializers (ec);
2382 protected void EmitScopeInitializers (EmitContext ec)
2384 foreach (Statement s in scope_initializers)
2389 public override string ToString ()
2391 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2395 protected override void CloneTo (CloneContext clonectx, Statement t)
2397 Block target = (Block) t;
2399 target.clone_id = clone_id_counter++;
2402 clonectx.AddBlockMap (this, target);
2403 if (original != this)
2404 clonectx.AddBlockMap (original, target);
2406 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2407 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2410 target.Parent = clonectx.RemapBlockCopy (Parent);
2412 target.statements = new List<Statement> (statements.Count);
2413 foreach (Statement s in statements)
2414 target.statements.Add (s.Clone (clonectx));
2417 public override object Accept (StructuralVisitor visitor)
2419 return visitor.Visit (this);
2423 public class ExplicitBlock : Block
2425 protected AnonymousMethodStorey am_storey;
2427 public ExplicitBlock (Block parent, Location start, Location end)
2428 : this (parent, (Flags) 0, start, end)
2432 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2433 : base (parent, flags, start, end)
2435 this.Explicit = this;
2440 public AnonymousMethodStorey AnonymousMethodStorey {
2446 public bool HasAwait {
2448 return (flags & Flags.AwaitBlock) != 0;
2452 public bool HasCapturedThis {
2454 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2457 return (flags & Flags.HasCapturedThis) != 0;
2462 // Used to indicate that the block has reference to parent
2463 // block and cannot be made static when defining anonymous method
2465 public bool HasCapturedVariable {
2467 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2470 return (flags & Flags.HasCapturedVariable) != 0;
2474 public bool HasYield {
2476 return (flags & Flags.YieldBlock) != 0;
2483 // Creates anonymous method storey in current block
2485 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2488 // Return same story for iterator and async blocks unless we are
2489 // in nested anonymous method
2491 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2492 return ec.CurrentAnonymousMethod.Storey;
2494 if (am_storey == null) {
2495 MemberBase mc = ec.MemberContext as MemberBase;
2498 // Creates anonymous method storey for this block
2500 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2506 public override void Emit (EmitContext ec)
2508 if (am_storey != null) {
2509 DefineStoreyContainer (ec, am_storey);
2510 am_storey.EmitStoreyInstantiation (ec, this);
2513 if (scope_initializers != null)
2514 EmitScopeInitializers (ec);
2516 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2517 ec.Emit (OpCodes.Nop);
2528 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2529 ec.Emit (OpCodes.Nop);
2533 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2535 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2536 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2537 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2541 // Creates anonymous method storey
2543 storey.CreateContainer ();
2544 storey.DefineContainer ();
2546 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2549 // Only first storey in path will hold this reference. All children blocks will
2550 // reference it indirectly using $ref field
2552 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2553 if (b.Parent != null) {
2554 var s = b.Parent.Explicit.AnonymousMethodStorey;
2556 storey.HoistedThis = s.HoistedThis;
2561 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2562 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2568 // We are the first storey on path and this has to be hoisted
2570 if (storey.HoistedThis == null) {
2571 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2573 // ThisReferencesFromChildrenBlock holds all reference even if they
2574 // are not on this path. It saves some memory otherwise it'd have to
2575 // be in every explicit block. We run this check to see if the reference
2576 // is valid for this storey
2578 Block block_on_path = ref_block;
2579 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2581 if (block_on_path == null)
2584 if (storey.HoistedThis == null)
2585 storey.AddCapturedThisField (ec);
2587 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2588 if (b.AnonymousMethodStorey != null) {
2589 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2590 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2593 // Stop propagation inside same top block
2595 if (b.ParametersBlock == ParametersBlock.Original)
2598 b = b.ParametersBlock;
2601 var pb = b as ParametersBlock;
2602 if (pb != null && pb.StateMachine != null) {
2603 if (pb.StateMachine == storey)
2606 pb.StateMachine.AddParentStoreyReference (ec, storey);
2609 b.HasCapturedVariable = true;
2615 var ref_blocks = storey.ReferencesFromChildrenBlock;
2616 if (ref_blocks != null) {
2617 foreach (ExplicitBlock ref_block in ref_blocks) {
2618 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2619 if (b.AnonymousMethodStorey != null) {
2620 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2623 // Stop propagation inside same top block
2625 if (b.ParametersBlock == ParametersBlock.Original)
2628 b = b.ParametersBlock;
2631 var pb = b as ParametersBlock;
2632 if (pb != null && pb.StateMachine != null) {
2633 if (pb.StateMachine == storey)
2636 pb.StateMachine.AddParentStoreyReference (ec, storey);
2639 b.HasCapturedVariable = true;
2645 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2648 public void RegisterAsyncAwait ()
2651 while ((block.flags & Flags.AwaitBlock) == 0) {
2652 block.flags |= Flags.AwaitBlock;
2654 if (block is ParametersBlock)
2657 block = block.Parent.Explicit;
2661 public void RegisterIteratorYield ()
2664 while ((block.flags & Flags.YieldBlock) == 0) {
2665 block.flags |= Flags.YieldBlock;
2667 if (block.Parent == null)
2670 block = block.Parent.Explicit;
2674 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2676 tryBlock.statements = statements;
2677 statements = new List<Statement> (1);
2678 statements.Add (tf);
2683 // ParametersBlock was introduced to support anonymous methods
2684 // and lambda expressions
2686 public class ParametersBlock : ExplicitBlock
2688 public class ParameterInfo : INamedBlockVariable
2690 readonly ParametersBlock block;
2692 public VariableInfo VariableInfo;
2695 public ParameterInfo (ParametersBlock block, int index)
2703 public ParametersBlock Block {
2709 Block INamedBlockVariable.Block {
2715 public bool IsDeclared {
2721 public bool IsParameter {
2727 public bool IsLocked {
2736 public Location Location {
2738 return Parameter.Location;
2742 public Parameter Parameter {
2744 return block.Parameters [index];
2748 public TypeSpec ParameterType {
2750 return Parameter.Type;
2756 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2758 return new ParameterReference (this, loc);
2763 // Block is converted into an expression
2765 sealed class BlockScopeExpression : Expression
2768 readonly ParametersBlock block;
2770 public BlockScopeExpression (Expression child, ParametersBlock block)
2776 public override bool ContainsEmitWithAwait ()
2778 return child.ContainsEmitWithAwait ();
2781 public override Expression CreateExpressionTree (ResolveContext ec)
2783 throw new NotSupportedException ();
2786 protected override Expression DoResolve (ResolveContext ec)
2791 child = child.Resolve (ec);
2795 eclass = child.eclass;
2800 public override void Emit (EmitContext ec)
2802 block.EmitScopeInitializers (ec);
2807 protected ParametersCompiled parameters;
2808 protected ParameterInfo[] parameter_info;
2810 protected bool unreachable;
2811 protected ToplevelBlock top_block;
2812 protected StateMachine state_machine;
2814 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2815 : base (parent, 0, start, start)
2817 if (parameters == null)
2818 throw new ArgumentNullException ("parameters");
2820 this.parameters = parameters;
2821 ParametersBlock = this;
2823 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2825 this.top_block = parent.ParametersBlock.top_block;
2826 ProcessParameters ();
2829 protected ParametersBlock (ParametersCompiled parameters, Location start)
2830 : base (null, 0, start, start)
2832 if (parameters == null)
2833 throw new ArgumentNullException ("parameters");
2835 this.parameters = parameters;
2836 ParametersBlock = this;
2840 // It's supposed to be used by method body implementation of anonymous methods
2842 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2843 : base (null, 0, source.StartLocation, source.EndLocation)
2845 this.parameters = parameters;
2846 this.statements = source.statements;
2847 this.scope_initializers = source.scope_initializers;
2849 this.resolved = true;
2850 this.unreachable = source.unreachable;
2851 this.am_storey = source.am_storey;
2852 this.state_machine = source.state_machine;
2854 ParametersBlock = this;
2857 // Overwrite original for comparison purposes when linking cross references
2858 // between anonymous methods
2860 Original = source.Original;
2865 public bool IsAsync {
2867 return (flags & Flags.HasAsyncModifier) != 0;
2870 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2875 // Block has been converted to expression tree
2877 public bool IsExpressionTree {
2879 return (flags & Flags.IsExpressionTree) != 0;
2884 // The parameters for the block.
2886 public ParametersCompiled Parameters {
2892 public StateMachine StateMachine {
2894 return state_machine;
2898 public ToplevelBlock TopBlock {
2904 public bool Resolved {
2906 return (flags & Flags.Resolved) != 0;
2910 public int TemporaryLocalsCount { get; set; }
2915 // Check whether all `out' parameters have been assigned.
2917 public void CheckOutParameters (FlowBranching.UsageVector vector)
2919 if (vector.IsUnreachable)
2922 int n = parameter_info == null ? 0 : parameter_info.Length;
2924 for (int i = 0; i < n; i++) {
2925 VariableInfo var = parameter_info[i].VariableInfo;
2930 if (vector.IsAssigned (var, false))
2933 var p = parameter_info[i].Parameter;
2934 TopBlock.Report.Error (177, p.Location,
2935 "The out parameter `{0}' must be assigned to before control leaves the current method",
2940 public override Expression CreateExpressionTree (ResolveContext ec)
2942 if (statements.Count == 1) {
2943 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2944 if (scope_initializers != null)
2945 expr = new BlockScopeExpression (expr, this);
2950 return base.CreateExpressionTree (ec);
2953 public override void Emit (EmitContext ec)
2955 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2956 DefineStoreyContainer (ec, state_machine);
2957 state_machine.EmitStoreyInstantiation (ec, this);
2963 public void EmitEmbedded (EmitContext ec)
2965 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2966 DefineStoreyContainer (ec, state_machine);
2967 state_machine.EmitStoreyInstantiation (ec, this);
2973 public ParameterInfo GetParameterInfo (Parameter p)
2975 for (int i = 0; i < parameters.Count; ++i) {
2976 if (parameters[i] == p)
2977 return parameter_info[i];
2980 throw new ArgumentException ("Invalid parameter");
2983 public ParameterReference GetParameterReference (int index, Location loc)
2985 return new ParameterReference (parameter_info[index], loc);
2988 public Statement PerformClone ()
2990 CloneContext clonectx = new CloneContext ();
2991 return Clone (clonectx);
2994 protected void ProcessParameters ()
2996 if (parameters.Count == 0)
2999 parameter_info = new ParameterInfo[parameters.Count];
3000 for (int i = 0; i < parameter_info.Length; ++i) {
3001 var p = parameters.FixedParameters[i];
3005 // TODO: Should use Parameter only and more block there
3006 parameter_info[i] = new ParameterInfo (this, i);
3008 AddLocalName (p.Name, parameter_info[i]);
3012 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3019 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3020 flags |= Flags.IsExpressionTree;
3025 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3026 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3031 unreachable = top_level.End ();
3033 } catch (Exception e) {
3034 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3037 if (rc.CurrentBlock != null) {
3038 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3040 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3043 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3047 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3048 if (rc.CurrentAnonymousMethod == null) {
3049 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3050 if (md is StateMachineMethod) {
3053 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3058 // If an asynchronous body of F is either an expression classified as nothing, or a
3059 // statement block where no return statements have expressions, the inferred return type is Task
3062 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3063 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3064 am.ReturnTypeInference = null;
3065 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3070 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3071 rc.CurrentAnonymousMethod.GetSignatureForError ());
3079 void ResolveMeta (BlockContext ec)
3081 int orig_count = parameters.Count;
3083 for (int i = 0; i < orig_count; ++i) {
3084 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3086 if ((mod & Parameter.Modifier.OUT) == 0)
3089 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3090 parameter_info[i].VariableInfo = vi;
3091 ec.FlowOffset += vi.Length;
3095 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3097 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3098 var stateMachine = new IteratorStorey (iterator);
3100 state_machine = stateMachine;
3101 iterator.SetStateMachine (stateMachine);
3103 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3104 tlb.Original = this;
3105 tlb.IsCompilerGenerated = true;
3106 tlb.state_machine = stateMachine;
3107 tlb.AddStatement (new Return (iterator, iterator.Location));
3111 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, Location loc)
3113 for (int i = 0; i < parameters.Count; i++) {
3114 Parameter p = parameters[i];
3115 Parameter.Modifier mod = p.ModFlags;
3116 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3117 host.Compiler.Report.Error (1988, p.Location,
3118 "Async methods cannot have ref or out parameters");
3122 if (p is ArglistParameter) {
3123 host.Compiler.Report.Error (4006, p.Location,
3124 "__arglist is not allowed in parameter list of async methods");
3128 if (parameters.Types[i].IsPointer) {
3129 host.Compiler.Report.Error (4005, p.Location,
3130 "Async methods cannot have unsafe parameters");
3136 host.Compiler.Report.Warning (1998, 1, loc,
3137 "Async block lacks `await' operator and will run synchronously");
3140 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3141 var initializer = new AsyncInitializer (this, host, block_type);
3142 initializer.Type = block_type;
3144 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3146 state_machine = stateMachine;
3147 initializer.SetStateMachine (stateMachine);
3149 var b = this is ToplevelBlock ?
3150 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3151 new ParametersBlock (Parent, parameters, Location.Null) {
3156 b.IsCompilerGenerated = true;
3157 b.state_machine = stateMachine;
3158 b.AddStatement (new StatementExpression (initializer));
3166 public class ToplevelBlock : ParametersBlock
3168 LocalVariable this_variable;
3169 CompilerContext compiler;
3170 Dictionary<string, object> names;
3171 Dictionary<string, object> labels;
3173 List<ExplicitBlock> this_references;
3175 public ToplevelBlock (CompilerContext ctx, Location loc)
3176 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3180 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3181 : base (parameters, start)
3183 this.compiler = ctx;
3185 flags |= Flags.HasRet;
3187 ProcessParameters ();
3191 // Recreates a top level block from parameters block. Used for
3192 // compiler generated methods where the original block comes from
3193 // explicit child block. This works for already resolved blocks
3194 // only to ensure we resolve them in the correct flow order
3196 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3197 : base (source, parameters)
3199 this.compiler = source.TopBlock.compiler;
3201 flags |= Flags.HasRet;
3204 public bool IsIterator {
3210 public Report Report {
3212 return compiler.Report;
3217 // Used by anonymous blocks to track references of `this' variable
3219 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3221 return this_references;
3226 // Returns the "this" instance variable of this block.
3227 // See AddThisVariable() for more information.
3229 public LocalVariable ThisVariable {
3231 return this_variable;
3235 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3238 names = new Dictionary<string, object> ();
3241 if (!names.TryGetValue (name, out value)) {
3242 names.Add (name, li);
3246 INamedBlockVariable existing = value as INamedBlockVariable;
3247 List<INamedBlockVariable> existing_list;
3248 if (existing != null) {
3249 existing_list = new List<INamedBlockVariable> ();
3250 existing_list.Add (existing);
3251 names[name] = existing_list;
3253 existing_list = (List<INamedBlockVariable>) value;
3257 // A collision checking between local names
3259 for (int i = 0; i < existing_list.Count; ++i) {
3260 existing = existing_list[i];
3261 Block b = existing.Block.Explicit;
3263 // Collision at same level
3264 if (li.Block.Explicit == b) {
3265 li.Block.Error_AlreadyDeclared (name, li);
3269 // Collision with parent
3270 Block parent = li.Block.Explicit;
3271 while ((parent = parent.Parent) != null) {
3273 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3274 i = existing_list.Count;
3279 if (!ignoreChildrenBlocks) {
3280 // Collision with children
3281 while ((b = b.Parent) != null) {
3282 if (li.Block.Explicit == b) {
3283 li.Block.Error_AlreadyDeclared (name, li, "child");
3284 i = existing_list.Count;
3291 existing_list.Add (li);
3294 public void AddLabel (string name, LabeledStatement label)
3297 labels = new Dictionary<string, object> ();
3300 if (!labels.TryGetValue (name, out value)) {
3301 labels.Add (name, label);
3305 LabeledStatement existing = value as LabeledStatement;
3306 List<LabeledStatement> existing_list;
3307 if (existing != null) {
3308 existing_list = new List<LabeledStatement> ();
3309 existing_list.Add (existing);
3310 labels[name] = existing_list;
3312 existing_list = (List<LabeledStatement>) value;
3316 // A collision checking between labels
3318 for (int i = 0; i < existing_list.Count; ++i) {
3319 existing = existing_list[i];
3320 Block b = existing.Block;
3322 // Collision at same level
3323 if (label.Block == b) {
3324 Report.SymbolRelatedToPreviousError (existing.loc, name);
3325 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3329 // Collision with parent
3331 while ((b = b.Parent) != null) {
3332 if (existing.Block == b) {
3333 Report.Error (158, label.loc,
3334 "The label `{0}' shadows another label by the same name in a contained scope", name);
3335 i = existing_list.Count;
3340 // Collision with with children
3342 while ((b = b.Parent) != null) {
3343 if (label.Block == b) {
3344 Report.Error (158, label.loc,
3345 "The label `{0}' shadows another label by the same name in a contained scope", name);
3346 i = existing_list.Count;
3352 existing_list.Add (label);
3355 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3357 if (this_references == null)
3358 this_references = new List<ExplicitBlock> ();
3360 if (!this_references.Contains (block))
3361 this_references.Add (block);
3364 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3366 this_references.Remove (block);
3370 // Creates an arguments set from all parameters, useful for method proxy calls
3372 public Arguments GetAllParametersArguments ()
3374 int count = parameters.Count;
3375 Arguments args = new Arguments (count);
3376 for (int i = 0; i < count; ++i) {
3377 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3378 args.Add (new Argument (arg_expr));
3385 // Lookup inside a block, the returned value can represent 3 states
3387 // true+variable: A local name was found and it's valid
3388 // false+variable: A local name was found in a child block only
3389 // false+null: No local name was found
3391 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3397 if (!names.TryGetValue (name, out value))
3400 variable = value as INamedBlockVariable;
3402 if (variable != null) {
3404 if (variable.Block == b.Original)
3408 } while (b != null);
3416 } while (b != null);
3418 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3419 for (int i = 0; i < list.Count; ++i) {
3422 if (variable.Block == b.Original)
3426 } while (b != null);
3434 } while (b != null);
3444 public LabeledStatement GetLabel (string name, Block block)
3450 if (!labels.TryGetValue (name, out value)) {
3454 var label = value as LabeledStatement;
3456 if (label != null) {
3457 if (label.Block == b.Original)
3460 // TODO: Temporary workaround for the switch block implicit label block
3461 if (label.Block.IsCompilerGenerated && (label.Block.Parent == b.Original || label.Block == b.Original.Parent))
3464 List<LabeledStatement> list = (List<LabeledStatement>) value;
3465 for (int i = 0; i < list.Count; ++i) {
3467 if (label.Block == b.Original)
3470 // TODO: Temporary workaround for the switch block implicit label block
3471 if (label.Block.IsCompilerGenerated && (label.Block.Parent == b.Original || label.Block == b.Original.Parent))
3480 // This is used by non-static `struct' constructors which do not have an
3481 // initializer - in this case, the constructor must initialize all of the
3482 // struct's fields. To do this, we add a "this" variable and use the flow
3483 // analysis code to ensure that it's been fully initialized before control
3484 // leaves the constructor.
3486 public void AddThisVariable (BlockContext bc)
3488 if (this_variable != null)
3489 throw new InternalErrorException (StartLocation.ToString ());
3491 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3492 this_variable.Type = bc.CurrentType;
3493 this_variable.PrepareForFlowAnalysis (bc);
3496 public bool IsThisAssigned (BlockContext ec)
3498 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3501 public override void Emit (EmitContext ec)
3503 if (Report.Errors > 0)
3509 if (IsCompilerGenerated) {
3510 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3518 // If `HasReturnLabel' is set, then we already emitted a
3519 // jump to the end of the method, so we must emit a `ret'
3522 // Unfortunately, System.Reflection.Emit automatically emits
3523 // a leave to the end of a finally block. This is a problem
3524 // if no code is following the try/finally block since we may
3525 // jump to a point after the end of the method.
3526 // As a workaround, we're always creating a return label in
3529 if (ec.HasReturnLabel || !unreachable) {
3530 if (ec.HasReturnLabel)
3531 ec.MarkLabel (ec.ReturnLabel);
3533 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3534 ec.Mark (EndLocation);
3536 if (ec.ReturnType.Kind != MemberKind.Void)
3537 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3539 ec.Emit (OpCodes.Ret);
3543 } catch (Exception e){
3544 Console.WriteLine ("Exception caught by the compiler while emitting:");
3545 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3547 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3554 public class SwitchLabel {
3557 readonly Location loc;
3562 // if expr == null, then it is the default case.
3564 public SwitchLabel (Expression expr, Location l)
3570 public bool IsDefault {
3572 return label == null;
3576 public Expression Label {
3582 public Location Location {
3588 public Constant Converted {
3597 public Label GetILLabel (EmitContext ec)
3599 if (il_label == null){
3600 il_label = ec.DefineLabel ();
3603 return il_label.Value;
3607 // Resolves the expression, reduces it to a literal if possible
3608 // and then converts it to the requested type.
3610 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3612 Expression e = label.Resolve (ec);
3617 Constant c = e as Constant;
3619 ec.Report.Error (150, loc, "A constant value is expected");
3623 if (allow_nullable && c is NullLiteral) {
3628 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3629 return converted != null;
3632 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3635 if (converted == null)
3638 label = converted.GetValueAsLiteral ();
3640 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3641 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3644 public SwitchLabel Clone (CloneContext clonectx)
3649 return new SwitchLabel (label.Clone (clonectx), loc);
3653 public class SwitchSection {
3654 public readonly List<SwitchLabel> Labels;
3655 public readonly Block Block;
3657 public SwitchSection (List<SwitchLabel> labels, Block block)
3663 public SwitchSection Clone (CloneContext clonectx)
3665 var cloned_labels = new List<SwitchLabel> ();
3667 foreach (SwitchLabel sl in Labels)
3668 cloned_labels.Add (sl.Clone (clonectx));
3670 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3674 public class Switch : Statement
3676 // structure used to hold blocks of keys while calculating table switch
3677 sealed class LabelsRange : IComparable<LabelsRange>
3679 public readonly long min;
3681 public readonly List<long> label_values;
3683 public LabelsRange (long value)
3686 label_values = new List<long> ();
3687 label_values.Add (value);
3690 public LabelsRange (long min, long max, ICollection<long> values)
3694 this.label_values = new List<long> (values);
3699 return max - min + 1;
3703 public bool AddValue (long value)
3705 var gap = value - min + 1;
3706 // Ensure the range has > 50% occupancy
3707 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3711 label_values.Add (value);
3715 public int CompareTo (LabelsRange other)
3717 int nLength = label_values.Count;
3718 int nLengthOther = other.label_values.Count;
3719 if (nLengthOther == nLength)
3720 return (int) (other.min - min);
3722 return nLength - nLengthOther;
3726 sealed class LabelMarker : Statement
3729 readonly List<SwitchLabel> labels;
3731 public LabelMarker (Switch s, List<SwitchLabel> labels)
3734 this.labels = labels;
3737 protected override void CloneTo (CloneContext clonectx, Statement target)
3741 protected override void DoEmit (EmitContext ec)
3743 foreach (var l in labels) {
3745 ec.MarkLabel (s.DefaultLabel);
3747 ec.MarkLabel (l.GetILLabel (ec));
3752 public List<SwitchSection> Sections;
3753 public Expression Expr;
3756 // Mapping of all labels to their SwitchLabels
3758 Dictionary<long, SwitchLabel> labels;
3759 Dictionary<string, SwitchLabel> string_labels;
3762 /// The governing switch type
3764 public TypeSpec SwitchType;
3769 Label default_target;
3771 Expression new_expr;
3774 SwitchSection constant_section;
3775 SwitchSection default_section;
3776 SwitchLabel null_section;
3778 Statement simple_stmt;
3779 VariableReference value;
3780 ExpressionStatement string_dictionary;
3781 FieldExpr switch_cache_field;
3782 ExplicitBlock block;
3785 // Nullable Types support
3787 Nullable.Unwrap unwrap;
3789 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3797 public ExplicitBlock Block {
3803 public Label DefaultLabel {
3805 return default_target;
3809 public bool GotDefault {
3811 return default_section != null;
3815 public bool IsNullable {
3817 return unwrap != null;
3822 // Determines the governing type for a switch. The returned
3823 // expression might be the expression from the switch, or an
3824 // expression that includes any potential conversions to
3826 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3828 switch (expr.Type.BuiltinType) {
3829 case BuiltinTypeSpec.Type.Byte:
3830 case BuiltinTypeSpec.Type.SByte:
3831 case BuiltinTypeSpec.Type.UShort:
3832 case BuiltinTypeSpec.Type.Short:
3833 case BuiltinTypeSpec.Type.UInt:
3834 case BuiltinTypeSpec.Type.Int:
3835 case BuiltinTypeSpec.Type.ULong:
3836 case BuiltinTypeSpec.Type.Long:
3837 case BuiltinTypeSpec.Type.Char:
3838 case BuiltinTypeSpec.Type.String:
3839 case BuiltinTypeSpec.Type.Bool:
3843 if (expr.Type.IsEnum)
3847 // Try to find a *user* defined implicit conversion.
3849 // If there is no implicit conversion, or if there are multiple
3850 // conversions, we have to report an error
3852 Expression converted = null;
3853 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3856 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3861 // Ignore over-worked ImplicitUserConversions that do
3862 // an implicit conversion in addition to the user conversion.
3864 if (!(e is UserCast))
3867 if (converted != null){
3868 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3877 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3879 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3895 // Performs the basic sanity checks on the switch statement
3896 // (looks for duplicate keys and non-constant expressions).
3898 // It also returns a hashtable with the keys that we will later
3899 // use to compute the switch tables
3901 bool CheckSwitch (ResolveContext ec)
3904 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3905 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3907 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3909 foreach (SwitchSection ss in Sections){
3910 foreach (SwitchLabel sl in ss.Labels){
3912 if (default_section != null){
3913 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3916 default_section = ss;
3920 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3926 if (string_labels != null) {
3927 string s = sl.Converted.GetValue () as string;
3931 string_labels.Add (s, sl);
3933 if (sl.Converted is NullLiteral) {
3936 labels.Add (sl.Converted.GetValueAsLong (), sl);
3939 } catch (ArgumentException) {
3940 if (string_labels != null)
3941 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3943 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3953 // This method emits code for a lookup-based switch statement (non-string)
3954 // Basically it groups the cases into blocks that are at least half full,
3955 // and then spits out individual lookup opcodes for each block.
3956 // It emits the longest blocks first, and short blocks are just
3957 // handled with direct compares.
3959 void EmitTableSwitch (EmitContext ec, Expression val)
3961 Label lbl_default = default_target;
3963 if (labels != null && labels.Count > 0) {
3964 List<LabelsRange> ranges;
3965 if (string_labels != null) {
3966 // We have done all hard work for string already
3967 // setup single range only
3968 ranges = new List<LabelsRange> (1);
3969 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3971 var element_keys = new long[labels.Count];
3972 labels.Keys.CopyTo (element_keys, 0);
3973 Array.Sort (element_keys);
3976 // Build possible ranges of switch labes to reduce number
3979 ranges = new List<LabelsRange> (element_keys.Length);
3980 var range = new LabelsRange (element_keys[0]);
3982 for (int i = 1; i < element_keys.Length; ++i) {
3983 var l = element_keys[i];
3984 if (range.AddValue (l))
3987 range = new LabelsRange (l);
3991 // sort the blocks so we can tackle the largest ones first
3995 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3997 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3998 LabelsRange kb = ranges[range_index];
3999 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
4001 // Optimize small ranges using simple equality check
4002 if (kb.Range <= 2) {
4003 foreach (var key in kb.label_values) {
4004 SwitchLabel sl = labels[key];
4005 if (sl.Converted.IsDefaultValue) {
4006 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4009 sl.Converted.Emit (ec);
4010 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4014 // TODO: if all the keys in the block are the same and there are
4015 // no gaps/defaults then just use a range-check.
4016 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4017 // TODO: optimize constant/I4 cases
4019 // check block range (could be > 2^31)
4021 ec.EmitLong (kb.min);
4022 ec.Emit (OpCodes.Blt, lbl_default);
4025 ec.EmitLong (kb.max);
4026 ec.Emit (OpCodes.Bgt, lbl_default);
4031 ec.EmitLong (kb.min);
4032 ec.Emit (OpCodes.Sub);
4035 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4039 int first = (int) kb.min;
4042 ec.Emit (OpCodes.Sub);
4043 } else if (first < 0) {
4044 ec.EmitInt (-first);
4045 ec.Emit (OpCodes.Add);
4049 // first, build the list of labels for the switch
4051 long cJumps = kb.Range;
4052 Label[] switch_labels = new Label[cJumps];
4053 for (int iJump = 0; iJump < cJumps; iJump++) {
4054 var key = kb.label_values[iKey];
4055 if (key == kb.min + iJump) {
4056 switch_labels[iJump] = labels[key].GetILLabel (ec);
4059 switch_labels[iJump] = lbl_default;
4063 // emit the switch opcode
4064 ec.Emit (OpCodes.Switch, switch_labels);
4067 // mark the default for this block
4068 if (range_index != 0)
4069 ec.MarkLabel (lbl_default);
4072 // the last default just goes to the end
4073 if (ranges.Count > 0)
4074 ec.Emit (OpCodes.Br, lbl_default);
4077 // now emit the code for the sections
4078 bool found_default = false;
4080 foreach (SwitchSection ss in Sections) {
4081 foreach (SwitchLabel sl in ss.Labels) {
4083 ec.MarkLabel (lbl_default);
4084 found_default = true;
4085 if (null_section == null)
4086 ec.MarkLabel (null_target);
4087 } else if (sl.Converted.IsNull) {
4088 ec.MarkLabel (null_target);
4091 ec.MarkLabel (sl.GetILLabel (ec));
4097 if (!found_default) {
4098 ec.MarkLabel (lbl_default);
4099 if (null_section == null) {
4100 ec.MarkLabel (null_target);
4105 SwitchLabel FindLabel (Constant value)
4107 SwitchLabel sl = null;
4109 if (string_labels != null) {
4110 string s = value.GetValue () as string;
4112 if (null_section != null)
4114 else if (default_section != null)
4115 sl = default_section.Labels[0];
4117 string_labels.TryGetValue (s, out sl);
4120 if (value is NullLiteral) {
4123 labels.TryGetValue (value.GetValueAsLong (), out sl);
4130 SwitchSection FindSection (SwitchLabel label)
4132 foreach (SwitchSection ss in Sections){
4133 foreach (SwitchLabel sl in ss.Labels){
4142 public override bool Resolve (BlockContext ec)
4144 Expr = Expr.Resolve (ec);
4148 new_expr = SwitchGoverningType (ec, Expr);
4150 if (new_expr == null && Expr.Type.IsNullableType) {
4151 unwrap = Nullable.Unwrap.Create (Expr, false);
4155 new_expr = SwitchGoverningType (ec, unwrap);
4158 if (new_expr == null){
4159 ec.Report.Error (151, loc,
4160 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4161 TypeManager.CSharpName (Expr.Type));
4166 SwitchType = new_expr.Type;
4168 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4169 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4173 if (!CheckSwitch (ec))
4176 Switch old_switch = ec.Switch;
4178 ec.Switch.SwitchType = SwitchType;
4180 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4182 var constant = new_expr as Constant;
4183 if (constant != null) {
4185 SwitchLabel label = FindLabel (constant);
4187 constant_section = FindSection (label);
4189 if (constant_section == null)
4190 constant_section = default_section;
4193 // Store switch expression for comparission purposes
4195 value = new_expr as VariableReference;
4197 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4202 foreach (SwitchSection ss in Sections){
4204 ec.CurrentBranching.CreateSibling (
4205 null, FlowBranching.SiblingType.SwitchSection);
4209 if (is_constant && (ss != constant_section)) {
4210 // If we're a constant switch, we're only emitting
4211 // one single section - mark all the others as
4213 ec.CurrentBranching.CurrentUsageVector.Goto ();
4214 if (!ss.Block.ResolveUnreachable (ec, true)) {
4218 if (!ss.Block.Resolve (ec))
4223 if (default_section == null)
4224 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4226 ec.EndFlowBranching ();
4227 ec.Switch = old_switch;
4233 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4234 if (string_labels.Count < 7)
4235 ResolveSimpleSwitch (ec);
4237 ResolveStringSwitchMap (ec);
4238 } else if (labels.Count < 3 && !IsNullable) {
4239 ResolveSimpleSwitch (ec);
4246 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4248 var sl = FindLabel (value);
4251 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4258 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4260 void ResolveSimpleSwitch (BlockContext bc)
4262 simple_stmt = default_section != null ? default_section.Block : null;
4264 for (int i = Sections.Count - 1; i >= 0; --i) {
4265 var s = Sections[i];
4267 if (s == default_section) {
4268 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4272 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4274 Expression cond = null;
4275 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4276 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted);
4279 cond = new Binary (Binary.Operator.LogicalOr, cond, e);
4286 // Compiler generated, hide from symbol file
4288 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4291 // It's null for empty switch
4292 if (simple_stmt != null)
4293 simple_stmt.Resolve (bc);
4297 // Converts string switch into string hashtable
4299 void ResolveStringSwitchMap (ResolveContext ec)
4301 FullNamedExpression string_dictionary_type;
4302 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4303 string_dictionary_type = new TypeExpression (
4304 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4305 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4307 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4308 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4310 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4314 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4315 Field field = new Field (ctype, string_dictionary_type,
4316 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4317 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4318 if (!field.Define ())
4320 ctype.AddField (field);
4322 var init = new List<Expression> ();
4324 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4325 string value = null;
4326 foreach (SwitchSection section in Sections) {
4327 bool contains_label = false;
4328 foreach (SwitchLabel sl in section.Labels) {
4329 if (sl.IsDefault || sl.Converted.IsNull)
4332 if (!contains_label) {
4333 labels.Add (counter, sl);
4334 contains_label = true;
4337 value = (string) sl.Converted.GetValue ();
4338 var init_args = new List<Expression> (2);
4339 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4341 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4342 init_args.Add (sl.Converted);
4344 init.Add (new CollectionElementInitializer (init_args, loc));
4348 // Don't add empty sections
4354 Arguments args = new Arguments (1);
4355 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4356 Expression initializer = new NewInitialize (string_dictionary_type, args,
4357 new CollectionOrObjectInitializers (init, loc), loc);
4359 switch_cache_field = new FieldExpr (field, loc);
4360 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4363 void DoEmitStringSwitch (EmitContext ec)
4365 Label l_initialized = ec.DefineLabel ();
4368 // Skip initialization when value is null
4370 value.EmitBranchable (ec, null_target, false);
4373 // Check if string dictionary is initialized and initialize
4375 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4376 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4377 string_dictionary.EmitStatement (ec);
4379 ec.MarkLabel (l_initialized);
4381 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4383 ResolveContext rc = new ResolveContext (ec.MemberContext);
4385 if (switch_cache_field.Type.IsGeneric) {
4386 Arguments get_value_args = new Arguments (2);
4387 get_value_args.Add (new Argument (value));
4388 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4389 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4390 if (get_item == null)
4394 // A value was not found, go to default case
4396 get_item.EmitBranchable (ec, default_target, false);
4398 Arguments get_value_args = new Arguments (1);
4399 get_value_args.Add (new Argument (value));
4401 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4402 if (get_item == null)
4405 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4406 get_item_object.EmitAssign (ec, get_item, true, false);
4407 ec.Emit (OpCodes.Brfalse, default_target);
4409 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4410 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4412 get_item_int.EmitStatement (ec);
4413 get_item_object.Release (ec);
4416 EmitTableSwitch (ec, string_switch_variable);
4417 string_switch_variable.Release (ec);
4420 protected override void DoEmit (EmitContext ec)
4422 // Workaround broken flow-analysis
4423 block.HasUnreachableClosingBrace = true;
4426 // Needed to emit anonymous storey initialization
4427 // Otherwise it does not contain any statements for now
4431 default_target = ec.DefineLabel ();
4432 null_target = ec.DefineLabel ();
4435 unwrap.EmitCheck (ec);
4436 ec.Emit (OpCodes.Brfalse, null_target);
4437 value.EmitAssign (ec, new_expr, false, false);
4438 } else if (new_expr != value && !is_constant) {
4439 value.EmitAssign (ec, new_expr, false, false);
4443 // Setup the codegen context
4445 Label old_end = ec.LoopEnd;
4446 Switch old_switch = ec.Switch;
4448 ec.LoopEnd = ec.DefineLabel ();
4453 if (constant_section != null)
4454 constant_section.Block.Emit (ec);
4455 } else if (string_dictionary != null) {
4456 DoEmitStringSwitch (ec);
4457 } else if (simple_stmt != null) {
4458 simple_stmt.Emit (ec);
4460 EmitTableSwitch (ec, value);
4463 // Restore context state.
4464 ec.MarkLabel (ec.LoopEnd);
4467 // Restore the previous context
4469 ec.LoopEnd = old_end;
4470 ec.Switch = old_switch;
4473 protected override void CloneTo (CloneContext clonectx, Statement t)
4475 Switch target = (Switch) t;
4477 target.Expr = Expr.Clone (clonectx);
4478 target.Sections = new List<SwitchSection> ();
4479 foreach (SwitchSection ss in Sections){
4480 target.Sections.Add (ss.Clone (clonectx));
4484 public override object Accept (StructuralVisitor visitor)
4486 return visitor.Visit (this);
4490 // A place where execution can restart in an iterator
4491 public abstract class ResumableStatement : Statement
4494 protected Label resume_point;
4496 public Label PrepareForEmit (EmitContext ec)
4500 resume_point = ec.DefineLabel ();
4502 return resume_point;
4505 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4510 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4515 public abstract class TryFinallyBlock : ExceptionStatement
4517 protected Statement stmt;
4518 Label dispose_try_block;
4519 bool prepared_for_dispose, emitted_dispose;
4520 Method finally_host;
4522 protected TryFinallyBlock (Statement stmt, Location loc)
4530 public Statement Statement {
4538 protected abstract void EmitTryBody (EmitContext ec);
4539 public abstract void EmitFinallyBody (EmitContext ec);
4541 public override Label PrepareForDispose (EmitContext ec, Label end)
4543 if (!prepared_for_dispose) {
4544 prepared_for_dispose = true;
4545 dispose_try_block = ec.DefineLabel ();
4547 return dispose_try_block;
4550 protected sealed override void DoEmit (EmitContext ec)
4552 EmitTryBodyPrepare (ec);
4555 ec.BeginFinallyBlock ();
4557 Label start_finally = ec.DefineLabel ();
4558 if (resume_points != null) {
4559 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4561 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4562 ec.Emit (OpCodes.Brfalse_S, start_finally);
4563 ec.Emit (OpCodes.Endfinally);
4566 ec.MarkLabel (start_finally);
4568 if (finally_host != null) {
4569 finally_host.Define ();
4570 finally_host.Emit ();
4572 // Now it's safe to add, to close it properly and emit sequence points
4573 finally_host.Parent.AddMember (finally_host);
4575 var ce = new CallEmitter ();
4576 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4577 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4579 EmitFinallyBody (ec);
4582 ec.EndExceptionBlock ();
4585 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4587 if (emitted_dispose)
4590 emitted_dispose = true;
4592 Label end_of_try = ec.DefineLabel ();
4594 // Ensure that the only way we can get into this code is through a dispatcher
4595 if (have_dispatcher)
4596 ec.Emit (OpCodes.Br, end);
4598 ec.BeginExceptionBlock ();
4600 ec.MarkLabel (dispose_try_block);
4602 Label[] labels = null;
4603 for (int i = 0; i < resume_points.Count; ++i) {
4604 ResumableStatement s = resume_points[i];
4605 Label ret = s.PrepareForDispose (ec, end_of_try);
4606 if (ret.Equals (end_of_try) && labels == null)
4608 if (labels == null) {
4609 labels = new Label[resume_points.Count];
4610 for (int j = 0; j < i; ++j)
4611 labels[j] = end_of_try;
4616 if (labels != null) {
4618 for (j = 1; j < labels.Length; ++j)
4619 if (!labels[0].Equals (labels[j]))
4621 bool emit_dispatcher = j < labels.Length;
4623 if (emit_dispatcher) {
4624 ec.Emit (OpCodes.Ldloc, pc);
4625 ec.EmitInt (first_resume_pc);
4626 ec.Emit (OpCodes.Sub);
4627 ec.Emit (OpCodes.Switch, labels);
4630 foreach (ResumableStatement s in resume_points)
4631 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4634 ec.MarkLabel (end_of_try);
4636 ec.BeginFinallyBlock ();
4638 if (finally_host != null) {
4639 var ce = new CallEmitter ();
4640 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4641 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4643 EmitFinallyBody (ec);
4646 ec.EndExceptionBlock ();
4649 public override bool Resolve (BlockContext bc)
4652 // Finally block inside iterator is called from MoveNext and
4653 // Dispose methods that means we need to lift the block into
4654 // newly created host method to emit the body only once. The
4655 // original block then simply calls the newly generated method.
4657 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4658 var b = stmt as Block;
4659 if (b != null && b.Explicit.HasYield) {
4660 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4664 return base.Resolve (bc);
4669 // Base class for blocks using exception handling
4671 public abstract class ExceptionStatement : ResumableStatement
4676 protected List<ResumableStatement> resume_points;
4677 protected int first_resume_pc;
4679 protected ExceptionStatement (Location loc)
4684 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4686 StateMachineInitializer state_machine = null;
4687 if (resume_points != null) {
4688 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4690 ec.EmitInt ((int) IteratorStorey.State.Running);
4691 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4694 ec.BeginExceptionBlock ();
4696 if (resume_points != null) {
4697 ec.MarkLabel (resume_point);
4699 // For normal control flow, we want to fall-through the Switch
4700 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4701 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4702 ec.EmitInt (first_resume_pc);
4703 ec.Emit (OpCodes.Sub);
4705 Label[] labels = new Label[resume_points.Count];
4706 for (int i = 0; i < resume_points.Count; ++i)
4707 labels[i] = resume_points[i].PrepareForEmit (ec);
4708 ec.Emit (OpCodes.Switch, labels);
4712 public void SomeCodeFollows ()
4715 code_follows = true;
4719 public override bool Resolve (BlockContext ec)
4722 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4723 // So, ensure there's some IL code after this statement.
4724 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4725 ec.NeedReturnLabel ();
4730 public void AddResumePoint (ResumableStatement stmt, int pc)
4732 if (resume_points == null) {
4733 resume_points = new List<ResumableStatement> ();
4734 first_resume_pc = pc;
4737 if (pc != first_resume_pc + resume_points.Count)
4738 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4740 resume_points.Add (stmt);
4745 public class Lock : TryFinallyBlock
4748 TemporaryVariableReference expr_copy;
4749 TemporaryVariableReference lock_taken;
4751 public Lock (Expression expr, Statement stmt, Location loc)
4757 public Expression Expr {
4763 public override bool Resolve (BlockContext ec)
4765 expr = expr.Resolve (ec);
4769 if (!TypeSpec.IsReferenceType (expr.Type)) {
4770 ec.Report.Error (185, loc,
4771 "`{0}' is not a reference type as required by the lock statement",
4772 expr.Type.GetSignatureForError ());
4775 if (expr.Type.IsGenericParameter) {
4776 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4779 VariableReference lv = expr as VariableReference;
4782 locked = lv.IsLockedByStatement;
4783 lv.IsLockedByStatement = true;
4790 // Have to keep original lock value around to unlock same location
4791 // in the case of original value has changed or is null
4793 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4794 expr_copy.Resolve (ec);
4797 // Ensure Monitor methods are available
4799 if (ResolvePredefinedMethods (ec) > 1) {
4800 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4801 lock_taken.Resolve (ec);
4804 using (ec.Set (ResolveContext.Options.LockScope)) {
4805 ec.StartFlowBranching (this);
4806 Statement.Resolve (ec);
4807 ec.EndFlowBranching ();
4811 lv.IsLockedByStatement = locked;
4819 protected override void EmitTryBodyPrepare (EmitContext ec)
4821 expr_copy.EmitAssign (ec, expr);
4823 if (lock_taken != null) {
4825 // Initialize ref variable
4827 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4830 // Monitor.Enter (expr_copy)
4832 expr_copy.Emit (ec);
4833 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4836 base.EmitTryBodyPrepare (ec);
4839 protected override void EmitTryBody (EmitContext ec)
4842 // Monitor.Enter (expr_copy, ref lock_taken)
4844 if (lock_taken != null) {
4845 expr_copy.Emit (ec);
4846 lock_taken.LocalInfo.CreateBuilder (ec);
4847 lock_taken.AddressOf (ec, AddressOp.Load);
4848 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4851 Statement.Emit (ec);
4854 public override void EmitFinallyBody (EmitContext ec)
4857 // if (lock_taken) Monitor.Exit (expr_copy)
4859 Label skip = ec.DefineLabel ();
4861 if (lock_taken != null) {
4862 lock_taken.Emit (ec);
4863 ec.Emit (OpCodes.Brfalse_S, skip);
4866 expr_copy.Emit (ec);
4867 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4869 ec.Emit (OpCodes.Call, m);
4871 ec.MarkLabel (skip);
4874 int ResolvePredefinedMethods (ResolveContext rc)
4876 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4877 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4881 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4885 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4889 protected override void CloneTo (CloneContext clonectx, Statement t)
4891 Lock target = (Lock) t;
4893 target.expr = expr.Clone (clonectx);
4894 target.stmt = Statement.Clone (clonectx);
4897 public override object Accept (StructuralVisitor visitor)
4899 return visitor.Visit (this);
4904 public class Unchecked : Statement {
4907 public Unchecked (Block b, Location loc)
4914 public override bool Resolve (BlockContext ec)
4916 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4917 return Block.Resolve (ec);
4920 protected override void DoEmit (EmitContext ec)
4922 using (ec.With (EmitContext.Options.CheckedScope, false))
4926 protected override void CloneTo (CloneContext clonectx, Statement t)
4928 Unchecked target = (Unchecked) t;
4930 target.Block = clonectx.LookupBlock (Block);
4933 public override object Accept (StructuralVisitor visitor)
4935 return visitor.Visit (this);
4939 public class Checked : Statement {
4942 public Checked (Block b, Location loc)
4945 b.Unchecked = false;
4949 public override bool Resolve (BlockContext ec)
4951 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4952 return Block.Resolve (ec);
4955 protected override void DoEmit (EmitContext ec)
4957 using (ec.With (EmitContext.Options.CheckedScope, true))
4961 protected override void CloneTo (CloneContext clonectx, Statement t)
4963 Checked target = (Checked) t;
4965 target.Block = clonectx.LookupBlock (Block);
4968 public override object Accept (StructuralVisitor visitor)
4970 return visitor.Visit (this);
4974 public class Unsafe : Statement {
4977 public Unsafe (Block b, Location loc)
4980 Block.Unsafe = true;
4984 public override bool Resolve (BlockContext ec)
4986 if (ec.CurrentIterator != null)
4987 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4989 using (ec.Set (ResolveContext.Options.UnsafeScope))
4990 return Block.Resolve (ec);
4993 protected override void DoEmit (EmitContext ec)
4998 protected override void CloneTo (CloneContext clonectx, Statement t)
5000 Unsafe target = (Unsafe) t;
5002 target.Block = clonectx.LookupBlock (Block);
5005 public override object Accept (StructuralVisitor visitor)
5007 return visitor.Visit (this);
5014 public class Fixed : Statement
5016 abstract class Emitter : ShimExpression
5018 protected LocalVariable vi;
5020 protected Emitter (Expression expr, LocalVariable li)
5026 public abstract void EmitExit (EmitContext ec);
5029 class ExpressionEmitter : Emitter {
5030 public ExpressionEmitter (Expression converted, LocalVariable li) :
5031 base (converted, li)
5035 protected override Expression DoResolve (ResolveContext rc)
5037 throw new NotImplementedException ();
5040 public override void Emit (EmitContext ec) {
5042 // Store pointer in pinned location
5048 public override void EmitExit (EmitContext ec)
5051 ec.Emit (OpCodes.Conv_U);
5056 class StringEmitter : Emitter
5058 LocalVariable pinned_string;
5060 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5065 protected override Expression DoResolve (ResolveContext rc)
5067 pinned_string = new LocalVariable (vi.Block, "$pinned",
5068 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5070 pinned_string.Type = rc.BuiltinTypes.String;
5072 eclass = ExprClass.Variable;
5073 type = rc.BuiltinTypes.Int;
5077 public override void Emit (EmitContext ec)
5079 pinned_string.CreateBuilder (ec);
5082 pinned_string.EmitAssign (ec);
5084 // TODO: Should use Binary::Add
5085 pinned_string.Emit (ec);
5086 ec.Emit (OpCodes.Conv_I);
5088 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5092 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5093 //pe.InstanceExpression = pinned_string;
5094 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5096 ec.Emit (OpCodes.Add);
5100 public override void EmitExit (EmitContext ec)
5103 pinned_string.EmitAssign (ec);
5107 public class VariableDeclaration : BlockVariableDeclaration
5109 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5114 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5116 if (!Variable.Type.IsPointer && li == Variable) {
5117 bc.Report.Error (209, TypeExpression.Location,
5118 "The type of locals declared in a fixed statement must be a pointer type");
5123 // The rules for the possible declarators are pretty wise,
5124 // but the production on the grammar is more concise.
5126 // So we have to enforce these rules here.
5128 // We do not resolve before doing the case 1 test,
5129 // because the grammar is explicit in that the token &
5130 // is present, so we need to test for this particular case.
5133 if (initializer is Cast) {
5134 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5138 initializer = initializer.Resolve (bc);
5140 if (initializer == null)
5146 if (initializer.Type.IsArray) {
5147 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5150 // Provided that array_type is unmanaged,
5152 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5156 // and T* is implicitly convertible to the
5157 // pointer type given in the fixed statement.
5159 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5161 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5162 if (converted == null)
5166 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5168 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5169 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5170 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5171 new NullLiteral (loc),
5174 converted = converted.Resolve (bc);
5176 return new ExpressionEmitter (converted, li);
5182 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5183 return new StringEmitter (initializer, li, loc).Resolve (bc);
5186 // Case 3: fixed buffer
5187 if (initializer is FixedBufferPtr) {
5188 return new ExpressionEmitter (initializer, li);
5192 // Case 4: & object.
5194 bool already_fixed = true;
5195 Unary u = initializer as Unary;
5196 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5197 IVariableReference vr = u.Expr as IVariableReference;
5198 if (vr == null || !vr.IsFixed) {
5199 already_fixed = false;
5203 if (already_fixed) {
5204 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5207 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5208 return new ExpressionEmitter (initializer, li);
5213 VariableDeclaration decl;
5214 Statement statement;
5217 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5226 public Statement Statement {
5232 public BlockVariableDeclaration Variables {
5240 public override bool Resolve (BlockContext ec)
5242 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5243 if (!decl.Resolve (ec))
5247 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5248 bool ok = statement.Resolve (ec);
5249 bool flow_unreachable = ec.EndFlowBranching ();
5250 has_ret = flow_unreachable;
5255 protected override void DoEmit (EmitContext ec)
5257 decl.Variable.CreateBuilder (ec);
5258 decl.Initializer.Emit (ec);
5259 if (decl.Declarators != null) {
5260 foreach (var d in decl.Declarators) {
5261 d.Variable.CreateBuilder (ec);
5262 d.Initializer.Emit (ec);
5266 statement.Emit (ec);
5272 // Clear the pinned variable
5274 ((Emitter) decl.Initializer).EmitExit (ec);
5275 if (decl.Declarators != null) {
5276 foreach (var d in decl.Declarators) {
5277 ((Emitter)d.Initializer).EmitExit (ec);
5282 protected override void CloneTo (CloneContext clonectx, Statement t)
5284 Fixed target = (Fixed) t;
5286 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5287 target.statement = statement.Clone (clonectx);
5290 public override object Accept (StructuralVisitor visitor)
5292 return visitor.Visit (this);
5296 public class Catch : Statement
5300 FullNamedExpression type_expr;
5301 CompilerAssign assign;
5304 public Catch (Block block, Location loc)
5312 public Block Block {
5318 public TypeSpec CatchType {
5324 public bool IsGeneral {
5326 return type_expr == null;
5330 public FullNamedExpression TypeExpression {
5339 public LocalVariable Variable {
5350 protected override void DoEmit (EmitContext ec)
5353 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5355 ec.BeginCatchBlock (CatchType);
5358 li.CreateBuilder (ec);
5361 // Special case hoisted catch variable, we have to use a temporary variable
5362 // to pass via anonymous storey initialization with the value still on top
5365 if (li.HoistedVariant != null) {
5366 LocalTemporary lt = new LocalTemporary (li.Type);
5369 // switch to assigning from the temporary variable and not from top of the stack
5370 assign.UpdateSource (lt);
5373 ec.Emit (OpCodes.Pop);
5379 public override bool Resolve (BlockContext ec)
5381 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5382 if (type_expr != null) {
5383 type = type_expr.ResolveAsType (ec);
5387 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5388 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5389 } else if (li != null) {
5391 li.PrepareForFlowAnalysis (ec);
5393 // source variable is at the top of the stack
5394 Expression source = new EmptyExpression (li.Type);
5395 if (li.Type.IsGenericParameter)
5396 source = new UnboxCast (source, li.Type);
5399 // Uses Location.Null to hide from symbol file
5401 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5402 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5406 return Block.Resolve (ec);
5410 protected override void CloneTo (CloneContext clonectx, Statement t)
5412 Catch target = (Catch) t;
5414 if (type_expr != null)
5415 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5417 target.block = clonectx.LookupBlock (block);
5421 public class TryFinally : TryFinallyBlock
5425 public TryFinally (Statement stmt, Block fini, Location loc)
5431 public Block Finallyblock {
5437 public override bool Resolve (BlockContext ec)
5441 ec.StartFlowBranching (this);
5443 if (!stmt.Resolve (ec))
5447 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5449 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5450 if (!fini.Resolve (ec))
5454 ec.EndFlowBranching ();
5456 ok &= base.Resolve (ec);
5461 protected override void EmitTryBody (EmitContext ec)
5466 public override void EmitFinallyBody (EmitContext ec)
5471 protected override void CloneTo (CloneContext clonectx, Statement t)
5473 TryFinally target = (TryFinally) t;
5475 target.stmt = (Statement) stmt.Clone (clonectx);
5477 target.fini = clonectx.LookupBlock (fini);
5480 public override object Accept (StructuralVisitor visitor)
5482 return visitor.Visit (this);
5486 public class TryCatch : ExceptionStatement
5489 List<Catch> clauses;
5490 readonly bool inside_try_finally;
5492 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5496 this.clauses = catch_clauses;
5497 this.inside_try_finally = inside_try_finally;
5500 public List<Catch> Clauses {
5506 public bool IsTryCatchFinally {
5508 return inside_try_finally;
5512 public override bool Resolve (BlockContext ec)
5516 ec.StartFlowBranching (this);
5518 if (!Block.Resolve (ec))
5521 for (int i = 0; i < clauses.Count; ++i) {
5523 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5525 if (!c.Resolve (ec)) {
5530 TypeSpec resolved_type = c.CatchType;
5531 for (int ii = 0; ii < clauses.Count; ++ii) {
5535 if (clauses[ii].IsGeneral) {
5536 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5539 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5542 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5545 ec.Report.Warning (1058, 1, c.loc,
5546 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5554 var ct = clauses[ii].CatchType;
5558 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5559 ec.Report.Error (160, c.loc,
5560 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5561 ct.GetSignatureForError ());
5567 ec.EndFlowBranching ();
5569 return base.Resolve (ec) && ok;
5572 protected sealed override void DoEmit (EmitContext ec)
5574 if (!inside_try_finally)
5575 EmitTryBodyPrepare (ec);
5579 foreach (Catch c in clauses)
5582 if (!inside_try_finally)
5583 ec.EndExceptionBlock ();
5586 protected override void CloneTo (CloneContext clonectx, Statement t)
5588 TryCatch target = (TryCatch) t;
5590 target.Block = clonectx.LookupBlock (Block);
5591 if (clauses != null){
5592 target.clauses = new List<Catch> ();
5593 foreach (Catch c in clauses)
5594 target.clauses.Add ((Catch) c.Clone (clonectx));
5598 public override object Accept (StructuralVisitor visitor)
5600 return visitor.Visit (this);
5604 public class Using : TryFinallyBlock
5606 public class VariableDeclaration : BlockVariableDeclaration
5608 Statement dispose_call;
5610 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5615 public VariableDeclaration (LocalVariable li, Location loc)
5621 public VariableDeclaration (Expression expr)
5624 loc = expr.Location;
5630 public bool IsNested { get; private set; }
5634 public void EmitDispose (EmitContext ec)
5636 dispose_call.Emit (ec);
5639 public override bool Resolve (BlockContext bc)
5644 return base.Resolve (bc, false);
5647 public Expression ResolveExpression (BlockContext bc)
5649 var e = Initializer.Resolve (bc);
5653 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5654 Initializer = ResolveInitializer (bc, Variable, e);
5658 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5660 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5661 initializer = initializer.Resolve (bc);
5662 if (initializer == null)
5665 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5666 Arguments args = new Arguments (1);
5667 args.Add (new Argument (initializer));
5668 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5669 if (initializer == null)
5672 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5673 dispose_call = CreateDisposeCall (bc, var);
5674 dispose_call.Resolve (bc);
5676 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5679 if (li == Variable) {
5680 CheckIDiposableConversion (bc, li, initializer);
5681 dispose_call = CreateDisposeCall (bc, li);
5682 dispose_call.Resolve (bc);
5685 return base.ResolveInitializer (bc, li, initializer);
5688 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5692 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5693 if (type.IsNullableType) {
5694 // it's handled in CreateDisposeCall
5698 bc.Report.SymbolRelatedToPreviousError (type);
5699 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5700 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5701 type.GetSignatureForError ());
5707 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5709 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5711 var loc = lv.Location;
5713 var idt = bc.BuiltinTypes.IDisposable;
5714 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5716 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5717 dispose_mg.InstanceExpression = type.IsNullableType ?
5718 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5722 // Hide it from symbol file via null location
5724 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5726 // Add conditional call when disposing possible null variable
5727 if (!type.IsStruct || type.IsNullableType)
5728 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5733 public void ResolveDeclaratorInitializer (BlockContext bc)
5735 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5738 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5740 for (int i = declarators.Count - 1; i >= 0; --i) {
5741 var d = declarators [i];
5742 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5743 vd.Initializer = d.Initializer;
5745 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5746 vd.dispose_call.Resolve (bc);
5748 stmt = new Using (vd, stmt, d.Variable.Location);
5755 public override object Accept (StructuralVisitor visitor)
5757 return visitor.Visit (this);
5761 VariableDeclaration decl;
5763 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5769 public Using (Expression expr, Statement stmt, Location loc)
5772 this.decl = new VariableDeclaration (expr);
5777 public Expression Expr {
5779 return decl.Variable == null ? decl.Initializer : null;
5783 public BlockVariableDeclaration Variables {
5791 public override void Emit (EmitContext ec)
5794 // Don't emit sequence point it will be set on variable declaration
5799 protected override void EmitTryBodyPrepare (EmitContext ec)
5802 base.EmitTryBodyPrepare (ec);
5805 protected override void EmitTryBody (EmitContext ec)
5810 public override void EmitFinallyBody (EmitContext ec)
5812 decl.EmitDispose (ec);
5815 public override bool Resolve (BlockContext ec)
5817 VariableReference vr;
5818 bool vr_locked = false;
5820 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5821 if (decl.Variable == null) {
5822 vr = decl.ResolveExpression (ec) as VariableReference;
5824 vr_locked = vr.IsLockedByStatement;
5825 vr.IsLockedByStatement = true;
5828 if (decl.IsNested) {
5829 decl.ResolveDeclaratorInitializer (ec);
5831 if (!decl.Resolve (ec))
5834 if (decl.Declarators != null) {
5835 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5843 ec.StartFlowBranching (this);
5847 ec.EndFlowBranching ();
5850 vr.IsLockedByStatement = vr_locked;
5857 protected override void CloneTo (CloneContext clonectx, Statement t)
5859 Using target = (Using) t;
5861 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5862 target.stmt = stmt.Clone (clonectx);
5865 public override object Accept (StructuralVisitor visitor)
5867 return visitor.Visit (this);
5872 /// Implementation of the foreach C# statement
5874 public class Foreach : Statement
5876 abstract class IteratorStatement : Statement
5878 protected readonly Foreach for_each;
5880 protected IteratorStatement (Foreach @foreach)
5882 this.for_each = @foreach;
5883 this.loc = @foreach.expr.Location;
5886 protected override void CloneTo (CloneContext clonectx, Statement target)
5888 throw new NotImplementedException ();
5891 public override void Emit (EmitContext ec)
5893 if (ec.EmitAccurateDebugInfo) {
5894 ec.Emit (OpCodes.Nop);
5901 sealed class ArrayForeach : IteratorStatement
5903 TemporaryVariableReference[] lengths;
5904 Expression [] length_exprs;
5905 StatementExpression[] counter;
5906 TemporaryVariableReference[] variables;
5908 TemporaryVariableReference copy;
5910 public ArrayForeach (Foreach @foreach, int rank)
5913 counter = new StatementExpression[rank];
5914 variables = new TemporaryVariableReference[rank];
5915 length_exprs = new Expression [rank];
5918 // Only use temporary length variables when dealing with
5919 // multi-dimensional arrays
5922 lengths = new TemporaryVariableReference [rank];
5925 public override bool Resolve (BlockContext ec)
5927 Block variables_block = for_each.variable.Block;
5928 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5931 int rank = length_exprs.Length;
5932 Arguments list = new Arguments (rank);
5933 for (int i = 0; i < rank; i++) {
5934 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5936 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5937 counter[i].Resolve (ec);
5940 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5942 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5943 lengths[i].Resolve (ec);
5945 Arguments args = new Arguments (1);
5946 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5947 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5950 list.Add (new Argument (v));
5953 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5958 if (for_each.type is VarExpr) {
5959 // Infer implicitly typed local variable from foreach array type
5960 var_type = access.Type;
5962 var_type = for_each.type.ResolveAsType (ec);
5964 if (var_type == null)
5967 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5972 for_each.variable.Type = var_type;
5974 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5975 if (variable_ref == null)
5978 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5982 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5983 ec.CurrentBranching.CreateSibling ();
5985 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5986 if (!for_each.body.Resolve (ec))
5988 ec.EndFlowBranching ();
5990 // There's no direct control flow from the end of the embedded statement to the end of the loop
5991 ec.CurrentBranching.CurrentUsageVector.Goto ();
5993 ec.EndFlowBranching ();
5998 protected override void DoEmit (EmitContext ec)
6000 copy.EmitAssign (ec, for_each.expr);
6002 int rank = length_exprs.Length;
6003 Label[] test = new Label [rank];
6004 Label[] loop = new Label [rank];
6006 for (int i = 0; i < rank; i++) {
6007 test [i] = ec.DefineLabel ();
6008 loop [i] = ec.DefineLabel ();
6010 if (lengths != null)
6011 lengths [i].EmitAssign (ec, length_exprs [i]);
6014 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
6015 for (int i = 0; i < rank; i++) {
6016 variables [i].EmitAssign (ec, zero);
6018 ec.Emit (OpCodes.Br, test [i]);
6019 ec.MarkLabel (loop [i]);
6022 for_each.body.Emit (ec);
6024 ec.MarkLabel (ec.LoopBegin);
6025 ec.Mark (for_each.expr.Location);
6027 for (int i = rank - 1; i >= 0; i--){
6028 counter [i].Emit (ec);
6030 ec.MarkLabel (test [i]);
6031 variables [i].Emit (ec);
6033 if (lengths != null)
6034 lengths [i].Emit (ec);
6036 length_exprs [i].Emit (ec);
6038 ec.Emit (OpCodes.Blt, loop [i]);
6041 ec.MarkLabel (ec.LoopEnd);
6045 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6047 class RuntimeDispose : Using.VariableDeclaration
6049 public RuntimeDispose (LocalVariable lv, Location loc)
6054 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6056 // Defered to runtime check
6059 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6061 var idt = bc.BuiltinTypes.IDisposable;
6064 // Fabricates code like
6066 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6069 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6071 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6072 dispose_variable.CreateReferenceExpression (bc, loc),
6073 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6074 loc), new NullLiteral (loc));
6076 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6078 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6079 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6081 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6082 return new If (idisaposable_test, dispose, loc);
6086 LocalVariable variable;
6088 Statement statement;
6089 ExpressionStatement init;
6090 TemporaryVariableReference enumerator_variable;
6091 bool ambiguous_getenumerator_name;
6093 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6096 this.variable = var;
6100 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6102 rc.Report.SymbolRelatedToPreviousError (enumerator);
6103 rc.Report.Error (202, loc,
6104 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6105 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6108 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6111 // Option 1: Try to match by name GetEnumerator first
6113 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6114 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6116 var mg = mexpr as MethodGroupExpr;
6118 mg.InstanceExpression = expr;
6119 Arguments args = new Arguments (0);
6120 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
6122 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6123 if (ambiguous_getenumerator_name)
6126 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6132 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6135 PredefinedMember<MethodSpec> iface_candidate = null;
6136 var ptypes = rc.Module.PredefinedTypes;
6137 var gen_ienumerable = ptypes.IEnumerableGeneric;
6138 if (!gen_ienumerable.Define ())
6139 gen_ienumerable = null;
6142 var ifaces = t.Interfaces;
6143 if (ifaces != null) {
6144 foreach (var iface in ifaces) {
6145 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6146 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6147 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6148 rc.Report.Error (1640, loc,
6149 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6150 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6155 // TODO: Cache this somehow
6156 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6157 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6162 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6163 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6168 if (t.IsGenericParameter)
6173 } while (t != null);
6175 if (iface_candidate == null) {
6176 if (expr.Type != InternalType.ErrorType) {
6177 rc.Report.Error (1579, loc,
6178 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6179 expr.Type.GetSignatureForError (), "GetEnumerator");
6185 var method = iface_candidate.Resolve (loc);
6189 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6190 mg.InstanceExpression = expr;
6194 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6196 var ms = MemberCache.FindMember (enumerator.ReturnType,
6197 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6198 BindingRestriction.InstanceOnly) as MethodSpec;
6200 if (ms == null || !ms.IsPublic) {
6201 Error_WrongEnumerator (rc, enumerator);
6205 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6208 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6210 var ps = MemberCache.FindMember (enumerator.ReturnType,
6211 MemberFilter.Property ("Current", null),
6212 BindingRestriction.InstanceOnly) as PropertySpec;
6214 if (ps == null || !ps.IsPublic) {
6215 Error_WrongEnumerator (rc, enumerator);
6222 public override bool Resolve (BlockContext ec)
6224 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6227 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6228 } else if (expr.Type.IsNullableType) {
6229 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6232 var get_enumerator_mg = ResolveGetEnumerator (ec);
6233 if (get_enumerator_mg == null) {
6237 var get_enumerator = get_enumerator_mg.BestCandidate;
6238 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6239 enumerator_variable.Resolve (ec);
6241 // Prepare bool MoveNext ()
6242 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6243 if (move_next_mg == null) {
6247 move_next_mg.InstanceExpression = enumerator_variable;
6249 // Prepare ~T~ Current { get; }
6250 var current_prop = ResolveCurrent (ec, get_enumerator);
6251 if (current_prop == null) {
6255 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6256 if (current_pe == null)
6259 VarExpr ve = for_each.type as VarExpr;
6263 // Source type is dynamic, set element type to dynamic too
6264 variable.Type = ec.BuiltinTypes.Dynamic;
6266 // Infer implicitly typed local variable from foreach enumerable type
6267 variable.Type = current_pe.Type;
6271 // Explicit cast of dynamic collection elements has to be done at runtime
6272 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6275 variable.Type = for_each.type.ResolveAsType (ec);
6277 if (variable.Type == null)
6280 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6281 if (current_pe == null)
6285 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6286 if (variable_ref == null)
6289 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6291 var init = new Invocation (get_enumerator_mg, null);
6293 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6294 for_each.body, Location.Null);
6296 var enum_type = enumerator_variable.Type;
6299 // Add Dispose method call when enumerator can be IDisposable
6301 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6302 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6304 // Runtime Dispose check
6306 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6307 vd.Initializer = init;
6308 statement = new Using (vd, statement, Location.Null);
6311 // No Dispose call needed
6313 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6314 this.init.Resolve (ec);
6318 // Static Dispose check
6320 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6321 vd.Initializer = init;
6322 statement = new Using (vd, statement, Location.Null);
6325 return statement.Resolve (ec);
6328 protected override void DoEmit (EmitContext ec)
6330 enumerator_variable.LocalInfo.CreateBuilder (ec);
6333 init.EmitStatement (ec);
6335 statement.Emit (ec);
6338 #region IErrorHandler Members
6340 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6342 ec.Report.SymbolRelatedToPreviousError (best);
6343 ec.Report.Warning (278, 2, expr.Location,
6344 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6345 expr.Type.GetSignatureForError (), "enumerable",
6346 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6348 ambiguous_getenumerator_name = true;
6352 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6357 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6362 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6371 LocalVariable variable;
6373 Statement statement;
6376 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6379 this.variable = var;
6381 this.statement = stmt;
6386 public Expression Expr {
6387 get { return expr; }
6390 public Statement Statement {
6391 get { return statement; }
6394 public Expression TypeExpression {
6395 get { return type; }
6398 public LocalVariable Variable {
6399 get { return variable; }
6402 public override bool Resolve (BlockContext ec)
6404 expr = expr.Resolve (ec);
6409 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6413 body.AddStatement (statement);
6415 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6416 statement = new ArrayForeach (this, 1);
6417 } else if (expr.Type is ArrayContainer) {
6418 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6420 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6421 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6422 expr.ExprClassName);
6426 statement = new CollectionForeach (this, variable, expr);
6429 return statement.Resolve (ec);
6432 protected override void DoEmit (EmitContext ec)
6434 variable.CreateBuilder (ec);
6436 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6437 ec.LoopBegin = ec.DefineLabel ();
6438 ec.LoopEnd = ec.DefineLabel ();
6440 statement.Emit (ec);
6442 ec.LoopBegin = old_begin;
6443 ec.LoopEnd = old_end;
6446 protected override void CloneTo (CloneContext clonectx, Statement t)
6448 Foreach target = (Foreach) t;
6450 target.type = type.Clone (clonectx);
6451 target.expr = expr.Clone (clonectx);
6452 target.body = (Block) body.Clone (clonectx);
6453 target.statement = statement.Clone (clonectx);
6456 public override object Accept (StructuralVisitor visitor)
6458 return visitor.Visit (this);