2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
53 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56 bool ok = Resolve (ec);
57 ec.KillFlowBranching ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
74 // This routine must be overrided in derived classes and make copies
75 // of all the data that might be modified if resolved
77 protected abstract void CloneTo (CloneContext clonectx, Statement target);
79 public Statement Clone (CloneContext clonectx)
81 Statement s = (Statement) this.MemberwiseClone ();
82 CloneTo (clonectx, s);
86 public virtual Expression CreateExpressionTree (ResolveContext ec)
88 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
92 public virtual object Accept (StructuralVisitor visitor)
94 return visitor.Visit (this);
98 public sealed class EmptyStatement : Statement
100 public EmptyStatement (Location loc)
105 public override bool Resolve (BlockContext ec)
110 public override bool ResolveUnreachable (BlockContext ec, bool warn)
115 public override void Emit (EmitContext ec)
119 protected override void DoEmit (EmitContext ec)
121 throw new NotSupportedException ();
124 protected override void CloneTo (CloneContext clonectx, Statement target)
129 public override object Accept (StructuralVisitor visitor)
131 return visitor.Visit (this);
135 public class If : Statement {
137 public Statement TrueStatement;
138 public Statement FalseStatement;
142 public If (Expression bool_expr, Statement true_statement, Location l)
143 : this (bool_expr, true_statement, null, l)
147 public If (Expression bool_expr,
148 Statement true_statement,
149 Statement false_statement,
152 this.expr = bool_expr;
153 TrueStatement = true_statement;
154 FalseStatement = false_statement;
158 public Expression Expr {
164 public override bool Resolve (BlockContext ec)
168 expr = expr.Resolve (ec);
173 // Dead code elimination
175 if (expr is Constant) {
176 bool take = !((Constant) expr).IsDefaultValue;
179 if (!TrueStatement.Resolve (ec))
182 if ((FalseStatement != null) &&
183 !FalseStatement.ResolveUnreachable (ec, true))
185 FalseStatement = null;
187 if (!TrueStatement.ResolveUnreachable (ec, true))
189 TrueStatement = null;
191 if ((FalseStatement != null) &&
192 !FalseStatement.Resolve (ec))
200 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
202 ok &= TrueStatement.Resolve (ec);
204 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
206 ec.CurrentBranching.CreateSibling ();
208 if (FalseStatement != null)
209 ok &= FalseStatement.Resolve (ec);
211 ec.EndFlowBranching ();
216 protected override void DoEmit (EmitContext ec)
218 Label false_target = ec.DefineLabel ();
222 // If we're a boolean constant, Resolve() already
223 // eliminated dead code for us.
225 Constant c = expr as Constant;
227 c.EmitSideEffect (ec);
229 if (!c.IsDefaultValue)
230 TrueStatement.Emit (ec);
231 else if (FalseStatement != null)
232 FalseStatement.Emit (ec);
237 expr.EmitBranchable (ec, false_target, false);
239 TrueStatement.Emit (ec);
241 if (FalseStatement != null){
242 bool branch_emitted = false;
244 end = ec.DefineLabel ();
246 ec.Emit (OpCodes.Br, end);
247 branch_emitted = true;
250 ec.MarkLabel (false_target);
251 FalseStatement.Emit (ec);
256 ec.MarkLabel (false_target);
260 protected override void CloneTo (CloneContext clonectx, Statement t)
264 target.expr = expr.Clone (clonectx);
265 target.TrueStatement = TrueStatement.Clone (clonectx);
266 if (FalseStatement != null)
267 target.FalseStatement = FalseStatement.Clone (clonectx);
270 public override object Accept (StructuralVisitor visitor)
272 return visitor.Visit (this);
276 public class Do : Statement {
277 public Expression expr;
278 public Statement EmbeddedStatement;
280 public Do (Statement statement, BooleanExpression bool_expr, Location l)
283 EmbeddedStatement = statement;
287 public override bool Resolve (BlockContext ec)
291 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
293 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
295 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
296 if (!EmbeddedStatement.Resolve (ec))
298 ec.EndFlowBranching ();
300 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
301 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
303 expr = expr.Resolve (ec);
306 else if (expr is Constant){
307 bool infinite = !((Constant) expr).IsDefaultValue;
309 ec.CurrentBranching.CurrentUsageVector.Goto ();
312 ec.EndFlowBranching ();
317 protected override void DoEmit (EmitContext ec)
319 Label loop = ec.DefineLabel ();
320 Label old_begin = ec.LoopBegin;
321 Label old_end = ec.LoopEnd;
323 ec.LoopBegin = ec.DefineLabel ();
324 ec.LoopEnd = ec.DefineLabel ();
327 EmbeddedStatement.Emit (ec);
328 ec.MarkLabel (ec.LoopBegin);
331 // Dead code elimination
333 if (expr is Constant){
334 bool res = !((Constant) expr).IsDefaultValue;
336 expr.EmitSideEffect (ec);
338 ec.Emit (OpCodes.Br, loop);
340 expr.EmitBranchable (ec, loop, true);
342 ec.MarkLabel (ec.LoopEnd);
344 ec.LoopBegin = old_begin;
345 ec.LoopEnd = old_end;
348 protected override void CloneTo (CloneContext clonectx, Statement t)
352 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
353 target.expr = expr.Clone (clonectx);
356 public override object Accept (StructuralVisitor visitor)
358 return visitor.Visit (this);
362 public class While : Statement {
363 public Expression expr;
364 public Statement Statement;
365 bool infinite, empty;
367 public While (BooleanExpression bool_expr, Statement statement, Location l)
369 this.expr = bool_expr;
370 Statement = statement;
374 public override bool Resolve (BlockContext ec)
378 expr = expr.Resolve (ec);
383 // Inform whether we are infinite or not
385 if (expr is Constant){
386 bool value = !((Constant) expr).IsDefaultValue;
389 if (!Statement.ResolveUnreachable (ec, true))
397 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
399 ec.CurrentBranching.CreateSibling ();
401 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
402 if (!Statement.Resolve (ec))
404 ec.EndFlowBranching ();
406 // There's no direct control flow from the end of the embedded statement to the end of the loop
407 ec.CurrentBranching.CurrentUsageVector.Goto ();
409 ec.EndFlowBranching ();
414 protected override void DoEmit (EmitContext ec)
417 expr.EmitSideEffect (ec);
421 Label old_begin = ec.LoopBegin;
422 Label old_end = ec.LoopEnd;
424 ec.LoopBegin = ec.DefineLabel ();
425 ec.LoopEnd = ec.DefineLabel ();
428 // Inform whether we are infinite or not
430 if (expr is Constant){
431 // expr is 'true', since the 'empty' case above handles the 'false' case
432 ec.MarkLabel (ec.LoopBegin);
433 expr.EmitSideEffect (ec);
435 ec.Emit (OpCodes.Br, ec.LoopBegin);
438 // Inform that we are infinite (ie, `we return'), only
439 // if we do not `break' inside the code.
441 ec.MarkLabel (ec.LoopEnd);
443 Label while_loop = ec.DefineLabel ();
445 ec.Emit (OpCodes.Br, ec.LoopBegin);
446 ec.MarkLabel (while_loop);
450 ec.MarkLabel (ec.LoopBegin);
453 expr.EmitBranchable (ec, while_loop, true);
455 ec.MarkLabel (ec.LoopEnd);
458 ec.LoopBegin = old_begin;
459 ec.LoopEnd = old_end;
462 public override void Emit (EmitContext ec)
467 protected override void CloneTo (CloneContext clonectx, Statement t)
469 While target = (While) t;
471 target.expr = expr.Clone (clonectx);
472 target.Statement = Statement.Clone (clonectx);
475 public override object Accept (StructuralVisitor visitor)
477 return visitor.Visit (this);
481 public class For : Statement
483 bool infinite, empty;
485 public For (Location l)
490 public Statement Initializer {
494 public Expression Condition {
498 public Statement Iterator {
502 public Statement Statement {
506 public override bool Resolve (BlockContext ec)
510 if (Initializer != null) {
511 if (!Initializer.Resolve (ec))
515 if (Condition != null) {
516 Condition = Condition.Resolve (ec);
517 if (Condition == null)
519 else if (Condition is Constant) {
520 bool value = !((Constant) Condition).IsDefaultValue;
523 if (!Statement.ResolveUnreachable (ec, true))
525 if ((Iterator != null) &&
526 !Iterator.ResolveUnreachable (ec, false))
536 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
538 ec.CurrentBranching.CreateSibling ();
540 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
542 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
543 if (!Statement.Resolve (ec))
545 ec.EndFlowBranching ();
547 if (Iterator != null){
548 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
549 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
552 if (!Iterator.Resolve (ec))
557 // There's no direct control flow from the end of the embedded statement to the end of the loop
558 ec.CurrentBranching.CurrentUsageVector.Goto ();
560 ec.EndFlowBranching ();
565 protected override void DoEmit (EmitContext ec)
567 if (Initializer != null)
568 Initializer.Emit (ec);
571 Condition.EmitSideEffect (ec);
575 Label old_begin = ec.LoopBegin;
576 Label old_end = ec.LoopEnd;
577 Label loop = ec.DefineLabel ();
578 Label test = ec.DefineLabel ();
580 ec.LoopBegin = ec.DefineLabel ();
581 ec.LoopEnd = ec.DefineLabel ();
583 ec.Emit (OpCodes.Br, test);
587 ec.MarkLabel (ec.LoopBegin);
592 // If test is null, there is no test, and we are just
595 if (Condition != null){
597 // The Resolve code already catches the case for
598 // Test == Constant (false) so we know that
601 if (Condition is Constant) {
602 Condition.EmitSideEffect (ec);
603 ec.Emit (OpCodes.Br, loop);
605 Condition.EmitBranchable (ec, loop, true);
609 ec.Emit (OpCodes.Br, loop);
610 ec.MarkLabel (ec.LoopEnd);
612 ec.LoopBegin = old_begin;
613 ec.LoopEnd = old_end;
616 protected override void CloneTo (CloneContext clonectx, Statement t)
618 For target = (For) t;
620 if (Initializer != null)
621 target.Initializer = Initializer.Clone (clonectx);
622 if (Condition != null)
623 target.Condition = Condition.Clone (clonectx);
624 if (Iterator != null)
625 target.Iterator = Iterator.Clone (clonectx);
626 target.Statement = Statement.Clone (clonectx);
629 public override object Accept (StructuralVisitor visitor)
631 return visitor.Visit (this);
635 public class StatementExpression : Statement
637 ExpressionStatement expr;
639 public StatementExpression (ExpressionStatement expr)
645 public ExpressionStatement Expr {
651 protected override void CloneTo (CloneContext clonectx, Statement t)
653 StatementExpression target = (StatementExpression) t;
654 target.expr = (ExpressionStatement) expr.Clone (clonectx);
657 protected override void DoEmit (EmitContext ec)
659 expr.EmitStatement (ec);
662 public override bool Resolve (BlockContext ec)
664 expr = expr.ResolveStatement (ec);
668 public override object Accept (StructuralVisitor visitor)
670 return visitor.Visit (this);
674 public class StatementErrorExpression : Statement
676 readonly Expression expr;
678 public StatementErrorExpression (Expression expr)
683 public Expression Expr {
689 protected override void DoEmit (EmitContext ec)
691 throw new NotSupportedException ();
694 protected override void CloneTo (CloneContext clonectx, Statement target)
696 throw new NotImplementedException ();
699 public override object Accept (StructuralVisitor visitor)
701 return visitor.Visit (this);
706 // Simple version of statement list not requiring a block
708 public class StatementList : Statement
710 List<Statement> statements;
712 public StatementList (Statement first, Statement second)
714 statements = new List<Statement> () { first, second };
718 public IList<Statement> Statements {
725 public void Add (Statement statement)
727 statements.Add (statement);
730 public override bool Resolve (BlockContext ec)
732 foreach (var s in statements)
738 protected override void DoEmit (EmitContext ec)
740 foreach (var s in statements)
744 protected override void CloneTo (CloneContext clonectx, Statement target)
746 StatementList t = (StatementList) target;
748 t.statements = new List<Statement> (statements.Count);
749 foreach (Statement s in statements)
750 t.statements.Add (s.Clone (clonectx));
753 public override object Accept (StructuralVisitor visitor)
755 return visitor.Visit (this);
759 // A 'return' or a 'yield break'
760 public abstract class ExitStatement : Statement
762 protected bool unwind_protect;
763 protected abstract bool DoResolve (BlockContext ec);
765 public virtual void Error_FinallyClause (Report Report)
767 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
770 public sealed override bool Resolve (BlockContext ec)
775 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
776 ec.CurrentBranching.CurrentUsageVector.Goto ();
782 /// Implements the return statement
784 public class Return : ExitStatement
788 public Return (Expression expr, Location l)
796 public Expression Expr {
807 protected override bool DoResolve (BlockContext ec)
810 if (ec.ReturnType.Kind == MemberKind.Void)
814 // Return must not be followed by an expression when
815 // the method return type is Task
817 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
818 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
819 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
821 // Extra trick not to emit ret/leave inside awaiter body
823 expr = EmptyExpression.Null;
828 if (ec.CurrentIterator != null) {
829 Error_ReturnFromIterator (ec);
831 ec.Report.Error (126, loc,
832 "An object of a type convertible to `{0}' is required for the return statement",
833 ec.ReturnType.GetSignatureForError ());
839 expr = expr.Resolve (ec);
840 TypeSpec block_return_type = ec.ReturnType;
842 AnonymousExpression am = ec.CurrentAnonymousMethod;
844 if (block_return_type.Kind == MemberKind.Void) {
845 ec.Report.Error (127, loc,
846 "`{0}': A return keyword must not be followed by any expression when method returns void",
847 ec.GetSignatureForError ());
851 Error_ReturnFromIterator (ec);
855 var async_block = am as AsyncInitializer;
856 if (async_block != null) {
858 var storey = (AsyncTaskStorey) am.Storey;
859 var async_type = storey.ReturnType;
861 if (async_type == null && async_block.ReturnTypeInference != null) {
862 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
866 if (!async_type.IsGenericTask) {
867 if (this is ContextualReturn)
870 ec.Report.Error (1997, loc,
871 "`{0}': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>",
872 ec.GetSignatureForError ());
877 // The return type is actually Task<T> type argument
879 block_return_type = async_type.TypeArguments[0];
882 var l = am as AnonymousMethodBody;
883 if (l != null && l.ReturnTypeInference != null && expr != null) {
884 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
893 if (expr.Type != block_return_type) {
894 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
897 if (am != null && block_return_type == ec.ReturnType) {
898 ec.Report.Error (1662, loc,
899 "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",
900 am.ContainerType, am.GetSignatureForError ());
909 protected override void DoEmit (EmitContext ec)
914 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
915 if (async_body != null) {
916 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
918 // It's null for await without async
919 if (async_return != null) {
920 async_return.EmitAssign (ec);
922 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
929 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
933 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
935 ec.Emit (OpCodes.Ret);
938 void Error_ReturnFromIterator (ResolveContext rc)
940 rc.Report.Error (1622, loc,
941 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
944 protected override void CloneTo (CloneContext clonectx, Statement t)
946 Return target = (Return) t;
947 // It's null for simple return;
949 target.expr = expr.Clone (clonectx);
952 public override object Accept (StructuralVisitor visitor)
954 return visitor.Visit (this);
958 public class Goto : Statement {
960 LabeledStatement label;
963 public override bool Resolve (BlockContext ec)
965 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
966 ec.CurrentBranching.CurrentUsageVector.Goto ();
970 public Goto (string label, Location l)
976 public string Target {
977 get { return target; }
980 public void SetResolvedTarget (LabeledStatement label)
983 label.AddReference ();
986 protected override void CloneTo (CloneContext clonectx, Statement target)
991 protected override void DoEmit (EmitContext ec)
994 throw new InternalErrorException ("goto emitted before target resolved");
995 Label l = label.LabelTarget (ec);
996 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
999 public override object Accept (StructuralVisitor visitor)
1001 return visitor.Visit (this);
1005 public class LabeledStatement : Statement {
1012 FlowBranching.UsageVector vectors;
1014 public LabeledStatement (string name, Block block, Location l)
1021 public Label LabelTarget (EmitContext ec)
1026 label = ec.DefineLabel ();
1031 public Block Block {
1037 public string Name {
1038 get { return name; }
1041 public bool IsDefined {
1042 get { return defined; }
1045 public bool HasBeenReferenced {
1046 get { return referenced; }
1049 public FlowBranching.UsageVector JumpOrigins {
1050 get { return vectors; }
1053 public void AddUsageVector (FlowBranching.UsageVector vector)
1055 vector = vector.Clone ();
1056 vector.Next = vectors;
1060 protected override void CloneTo (CloneContext clonectx, Statement target)
1065 public override bool Resolve (BlockContext ec)
1067 // this flow-branching will be terminated when the surrounding block ends
1068 ec.StartFlowBranching (this);
1072 protected override void DoEmit (EmitContext ec)
1074 if (!HasBeenReferenced)
1075 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1078 ec.MarkLabel (label);
1081 public void AddReference ()
1086 public override object Accept (StructuralVisitor visitor)
1088 return visitor.Visit (this);
1094 /// `goto default' statement
1096 public class GotoDefault : Statement {
1098 public GotoDefault (Location l)
1103 protected override void CloneTo (CloneContext clonectx, Statement target)
1108 public override bool Resolve (BlockContext ec)
1110 ec.CurrentBranching.CurrentUsageVector.Goto ();
1112 if (ec.Switch == null) {
1113 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1117 if (!ec.Switch.GotDefault) {
1118 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1125 protected override void DoEmit (EmitContext ec)
1127 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1130 public override object Accept (StructuralVisitor visitor)
1132 return visitor.Visit (this);
1137 /// `goto case' statement
1139 public class GotoCase : Statement {
1143 public GotoCase (Expression e, Location l)
1149 public Expression Expr {
1155 public override bool Resolve (BlockContext ec)
1157 if (ec.Switch == null){
1158 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1162 ec.CurrentBranching.CurrentUsageVector.Goto ();
1164 expr = expr.Resolve (ec);
1168 Constant c = expr as Constant;
1170 ec.Report.Error (150, expr.Location, "A constant value is expected");
1175 if (ec.Switch.IsNullable && c is NullLiteral) {
1178 TypeSpec type = ec.Switch.SwitchType;
1179 res = c.TryReduce (ec, type, c.Location);
1181 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1185 if (!Convert.ImplicitStandardConversionExists (c, type))
1186 ec.Report.Warning (469, 2, loc,
1187 "The `goto case' value is not implicitly convertible to type `{0}'",
1188 TypeManager.CSharpName (type));
1192 sl = ec.Switch.ResolveGotoCase (ec, res);
1196 protected override void DoEmit (EmitContext ec)
1198 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1201 protected override void CloneTo (CloneContext clonectx, Statement t)
1203 GotoCase target = (GotoCase) t;
1205 target.expr = expr.Clone (clonectx);
1208 public override object Accept (StructuralVisitor visitor)
1210 return visitor.Visit (this);
1214 public class Throw : Statement {
1217 public Throw (Expression expr, Location l)
1223 public Expression Expr {
1229 public override bool Resolve (BlockContext ec)
1232 ec.CurrentBranching.CurrentUsageVector.Goto ();
1233 return ec.CurrentBranching.CheckRethrow (loc);
1236 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1237 ec.CurrentBranching.CurrentUsageVector.Goto ();
1242 var et = ec.BuiltinTypes.Exception;
1243 if (Convert.ImplicitConversionExists (ec, expr, et))
1244 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1246 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1251 protected override void DoEmit (EmitContext ec)
1254 ec.Emit (OpCodes.Rethrow);
1258 ec.Emit (OpCodes.Throw);
1262 protected override void CloneTo (CloneContext clonectx, Statement t)
1264 Throw target = (Throw) t;
1267 target.expr = expr.Clone (clonectx);
1270 public override object Accept (StructuralVisitor visitor)
1272 return visitor.Visit (this);
1276 public class Break : Statement {
1278 public Break (Location l)
1283 bool unwind_protect;
1285 public override bool Resolve (BlockContext ec)
1287 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1288 ec.CurrentBranching.CurrentUsageVector.Goto ();
1292 protected override void DoEmit (EmitContext ec)
1294 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1297 protected override void CloneTo (CloneContext clonectx, Statement t)
1302 public override object Accept (StructuralVisitor visitor)
1304 return visitor.Visit (this);
1308 public class Continue : Statement {
1310 public Continue (Location l)
1315 bool unwind_protect;
1317 public override bool Resolve (BlockContext ec)
1319 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1320 ec.CurrentBranching.CurrentUsageVector.Goto ();
1324 protected override void DoEmit (EmitContext ec)
1326 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1329 protected override void CloneTo (CloneContext clonectx, Statement t)
1334 public override object Accept (StructuralVisitor visitor)
1336 return visitor.Visit (this);
1340 public interface ILocalVariable
1342 void Emit (EmitContext ec);
1343 void EmitAssign (EmitContext ec);
1344 void EmitAddressOf (EmitContext ec);
1347 public interface INamedBlockVariable
1349 Block Block { get; }
1350 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1351 bool IsDeclared { get; }
1352 bool IsParameter { get; }
1353 Location Location { get; }
1356 public class BlockVariableDeclaration : Statement
1358 public class Declarator
1361 Expression initializer;
1363 public Declarator (LocalVariable li, Expression initializer)
1365 if (li.Type != null)
1366 throw new ArgumentException ("Expected null variable type");
1369 this.initializer = initializer;
1372 public Declarator (Declarator clone, Expression initializer)
1375 this.initializer = initializer;
1380 public LocalVariable Variable {
1386 public Expression Initializer {
1391 initializer = value;
1398 Expression initializer;
1399 protected FullNamedExpression type_expr;
1400 protected LocalVariable li;
1401 protected List<Declarator> declarators;
1403 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1405 this.type_expr = type;
1407 this.loc = type_expr.Location;
1410 protected BlockVariableDeclaration (LocalVariable li)
1417 public List<Declarator> Declarators {
1423 public Expression Initializer {
1428 initializer = value;
1432 public FullNamedExpression TypeExpression {
1438 public LocalVariable Variable {
1446 public void AddDeclarator (Declarator decl)
1448 if (declarators == null)
1449 declarators = new List<Declarator> ();
1451 declarators.Add (decl);
1454 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1456 if (bc.Report.Errors != 0)
1459 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1461 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1462 new MemberName (li.Name, li.Location), null);
1464 container.AddField (f);
1467 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1471 public override bool Resolve (BlockContext bc)
1473 return Resolve (bc, true);
1476 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1478 if (li.Type == null) {
1479 TypeSpec type = null;
1480 var vexpr = type_expr as VarExpr;
1483 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1484 // same name exists or as a keyword when no type was found
1486 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1487 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1488 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1491 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1495 if (li.IsConstant) {
1496 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1500 if (Initializer == null) {
1501 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1505 if (declarators != null) {
1506 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1510 Initializer = Initializer.Resolve (bc);
1511 if (Initializer != null) {
1512 ((VarExpr) type_expr).InferType (bc, Initializer);
1513 type = type_expr.Type;
1515 // Set error type to indicate the var was placed correctly but could
1518 // var a = missing ();
1520 type = InternalType.ErrorType;
1525 type = type_expr.ResolveAsType (bc);
1529 if (li.IsConstant && !type.IsConstantCompatible) {
1530 Const.Error_InvalidConstantType (type, loc, bc.Report);
1535 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1540 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1542 CreateEvaluatorVariable (bc, li);
1544 li.PrepareForFlowAnalysis (bc);
1547 if (initializer != null) {
1548 initializer = ResolveInitializer (bc, li, initializer);
1549 // li.Variable.DefinitelyAssigned
1552 if (declarators != null) {
1553 foreach (var d in declarators) {
1554 d.Variable.Type = li.Type;
1556 CreateEvaluatorVariable (bc, d.Variable);
1558 d.Variable.PrepareForFlowAnalysis (bc);
1561 if (d.Initializer != null && resolveDeclaratorInitializers) {
1562 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1563 // d.Variable.DefinitelyAssigned
1571 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1573 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1574 return a.ResolveStatement (bc);
1577 protected override void DoEmit (EmitContext ec)
1582 li.CreateBuilder (ec);
1584 if (Initializer != null)
1585 ((ExpressionStatement) Initializer).EmitStatement (ec);
1587 if (declarators != null) {
1588 foreach (var d in declarators) {
1589 d.Variable.CreateBuilder (ec);
1590 if (d.Initializer != null)
1591 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1596 protected override void CloneTo (CloneContext clonectx, Statement target)
1598 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1600 if (type_expr != null)
1601 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1603 if (initializer != null)
1604 t.initializer = initializer.Clone (clonectx);
1606 if (declarators != null) {
1607 t.declarators = null;
1608 foreach (var d in declarators)
1609 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1613 public override object Accept (StructuralVisitor visitor)
1615 return visitor.Visit (this);
1619 public class BlockConstantDeclaration : BlockVariableDeclaration
1621 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1626 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1628 initializer = initializer.Resolve (bc);
1629 if (initializer == null)
1632 var c = initializer as Constant;
1634 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1638 c = c.ConvertImplicitly (li.Type);
1640 if (TypeSpec.IsReferenceType (li.Type))
1641 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1643 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1648 li.ConstantValue = c;
1652 public override object Accept (StructuralVisitor visitor)
1654 return visitor.Visit (this);
1659 // The information about a user-perceived local variable
1661 public class LocalVariable : INamedBlockVariable, ILocalVariable
1668 AddressTaken = 1 << 2,
1669 CompilerGenerated = 1 << 3,
1671 ForeachVariable = 1 << 5,
1672 FixedVariable = 1 << 6,
1673 UsingVariable = 1 << 7,
1674 // DefinitelyAssigned = 1 << 8,
1677 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1681 readonly string name;
1682 readonly Location loc;
1683 readonly Block block;
1685 Constant const_value;
1687 public VariableInfo VariableInfo;
1688 HoistedVariable hoisted_variant;
1690 LocalBuilder builder;
1692 public LocalVariable (Block block, string name, Location loc)
1699 public LocalVariable (Block block, string name, Flags flags, Location loc)
1700 : this (block, name, loc)
1706 // Used by variable declarators
1708 public LocalVariable (LocalVariable li, string name, Location loc)
1709 : this (li.block, name, li.flags, loc)
1715 public bool AddressTaken {
1717 return (flags & Flags.AddressTaken) != 0;
1721 public Block Block {
1727 public Constant ConstantValue {
1732 const_value = value;
1737 // Hoisted local variable variant
1739 public HoistedVariable HoistedVariant {
1741 return hoisted_variant;
1744 hoisted_variant = value;
1748 public bool IsDeclared {
1750 return type != null;
1754 public bool IsConstant {
1756 return (flags & Flags.Constant) != 0;
1760 public bool IsLocked {
1762 return (flags & Flags.IsLocked) != 0;
1765 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1769 public bool IsThis {
1771 return (flags & Flags.IsThis) != 0;
1775 public bool IsFixed {
1777 return (flags & Flags.FixedVariable) != 0;
1781 bool INamedBlockVariable.IsParameter {
1787 public bool IsReadonly {
1789 return (flags & Flags.ReadonlyMask) != 0;
1793 public Location Location {
1799 public string Name {
1805 public TypeSpec Type {
1816 public void CreateBuilder (EmitContext ec)
1818 if ((flags & Flags.Used) == 0) {
1819 if (VariableInfo == null) {
1820 // Missing flow analysis or wrong variable flags
1821 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1824 if (VariableInfo.IsEverAssigned)
1825 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1827 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1830 if (HoistedVariant != null)
1833 if (builder != null) {
1834 if ((flags & Flags.CompilerGenerated) != 0)
1837 // To avoid Used warning duplicates
1838 throw new InternalErrorException ("Already created variable `{0}'", name);
1842 // All fixed variabled are pinned, a slot has to be alocated
1844 builder = ec.DeclareLocal (Type, IsFixed);
1845 if (SymbolWriter.HasSymbolWriter)
1846 ec.DefineLocalVariable (name, builder);
1849 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1851 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1856 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1858 if (IsConstant && const_value != null)
1859 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1861 return new LocalVariableReference (this, loc);
1864 public void Emit (EmitContext ec)
1866 // TODO: Need something better for temporary variables
1867 if ((flags & Flags.CompilerGenerated) != 0)
1870 ec.Emit (OpCodes.Ldloc, builder);
1873 public void EmitAssign (EmitContext ec)
1875 // TODO: Need something better for temporary variables
1876 if ((flags & Flags.CompilerGenerated) != 0)
1879 ec.Emit (OpCodes.Stloc, builder);
1882 public void EmitAddressOf (EmitContext ec)
1884 ec.Emit (OpCodes.Ldloca, builder);
1887 public static string GetCompilerGeneratedName (Block block)
1889 // HACK: Debugger depends on the name semantics
1890 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1893 public string GetReadOnlyContext ()
1895 switch (flags & Flags.ReadonlyMask) {
1896 case Flags.FixedVariable:
1897 return "fixed variable";
1898 case Flags.ForeachVariable:
1899 return "foreach iteration variable";
1900 case Flags.UsingVariable:
1901 return "using variable";
1904 throw new InternalErrorException ("Variable is not readonly");
1907 public bool IsThisAssigned (BlockContext ec, Block block)
1909 if (VariableInfo == null)
1910 throw new Exception ();
1912 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1915 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1918 public bool IsAssigned (BlockContext ec)
1920 if (VariableInfo == null)
1921 throw new Exception ();
1923 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1926 public void PrepareForFlowAnalysis (BlockContext bc)
1929 // No need for definitely assigned check for these guys
1931 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1934 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1935 bc.FlowOffset += VariableInfo.Length;
1939 // Mark the variables as referenced in the user code
1941 public void SetIsUsed ()
1943 flags |= Flags.Used;
1946 public void SetHasAddressTaken ()
1948 flags |= (Flags.AddressTaken | Flags.Used);
1951 public override string ToString ()
1953 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1958 /// Block represents a C# block.
1962 /// This class is used in a number of places: either to represent
1963 /// explicit blocks that the programmer places or implicit blocks.
1965 /// Implicit blocks are used as labels or to introduce variable
1968 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1969 /// they contain extra information that is not necessary on normal blocks.
1971 public class Block : Statement {
1978 HasCapturedVariable = 64,
1979 HasCapturedThis = 1 << 7,
1980 IsExpressionTree = 1 << 8,
1981 CompilerGenerated = 1 << 9,
1982 HasAsyncModifier = 1 << 10,
1984 YieldBlock = 1 << 12,
1985 AwaitBlock = 1 << 13
1988 public Block Parent;
1989 public Location StartLocation;
1990 public Location EndLocation;
1992 public ExplicitBlock Explicit;
1993 public ParametersBlock ParametersBlock;
1995 protected Flags flags;
1998 // The statements in this block
2000 protected List<Statement> statements;
2002 protected List<Statement> scope_initializers;
2004 int? resolving_init_idx;
2010 public int ID = id++;
2012 static int clone_id_counter;
2016 // int assignable_slots;
2017 bool unreachable_shown;
2020 public Block (Block parent, Location start, Location end)
2021 : this (parent, 0, start, end)
2025 public Block (Block parent, Flags flags, Location start, Location end)
2027 if (parent != null) {
2028 // the appropriate constructors will fixup these fields
2029 ParametersBlock = parent.ParametersBlock;
2030 Explicit = parent.Explicit;
2033 this.Parent = parent;
2035 this.StartLocation = start;
2036 this.EndLocation = end;
2038 statements = new List<Statement> (4);
2040 this.original = this;
2045 public bool HasRet {
2046 get { return (flags & Flags.HasRet) != 0; }
2049 public Block Original {
2058 public bool IsCompilerGenerated {
2059 get { return (flags & Flags.CompilerGenerated) != 0; }
2060 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2063 public bool Unchecked {
2064 get { return (flags & Flags.Unchecked) != 0; }
2065 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2068 public bool Unsafe {
2069 get { return (flags & Flags.Unsafe) != 0; }
2070 set { flags |= Flags.Unsafe; }
2073 public List<Statement> Statements {
2074 get { return statements; }
2079 public Block CreateSwitchBlock (Location start)
2081 // FIXME: Only explicit block should be created
2082 var new_block = new Block (this, start, start);
2083 new_block.IsCompilerGenerated = true;
2087 public void SetEndLocation (Location loc)
2092 public void AddLabel (LabeledStatement target)
2094 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2097 public void AddLocalName (LocalVariable li)
2099 AddLocalName (li.Name, li);
2102 public void AddLocalName (string name, INamedBlockVariable li)
2104 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2107 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2109 if (reason == null) {
2110 Error_AlreadyDeclared (name, variable);
2114 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2115 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2116 "to `{0}', which is already used in a `{1}' scope to denote something else",
2120 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2122 var pi = variable as ParametersBlock.ParameterInfo;
2124 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2126 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2127 "A local variable named `{0}' is already defined in this scope", name);
2131 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2133 ParametersBlock.TopBlock.Report.Error (412, loc,
2134 "The type parameter name `{0}' is the same as local variable or parameter name",
2139 // It should be used by expressions which require to
2140 // register a statement during resolve process.
2142 public void AddScopeStatement (Statement s)
2144 if (scope_initializers == null)
2145 scope_initializers = new List<Statement> ();
2148 // Simple recursive helper, when resolve scope initializer another
2149 // new scope initializer can be added, this ensures it's initialized
2150 // before existing one. For now this can happen with expression trees
2151 // in base ctor initializer only
2153 if (resolving_init_idx.HasValue) {
2154 scope_initializers.Insert (resolving_init_idx.Value, s);
2155 ++resolving_init_idx;
2157 scope_initializers.Add (s);
2161 public void AddStatement (Statement s)
2166 public int AssignableSlots {
2168 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2170 // return assignable_slots;
2174 public LabeledStatement LookupLabel (string name)
2176 return ParametersBlock.TopBlock.GetLabel (name, this);
2179 public override bool Resolve (BlockContext ec)
2181 if ((flags & Flags.Resolved) != 0)
2184 Block prev_block = ec.CurrentBlock;
2187 ec.CurrentBlock = this;
2188 ec.StartFlowBranching (this);
2191 // Compiler generated scope statements
2193 if (scope_initializers != null) {
2194 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2195 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2198 resolving_init_idx = null;
2202 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2203 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2204 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2205 // responsible for handling the situation.
2207 int statement_count = statements.Count;
2208 for (int ix = 0; ix < statement_count; ix++){
2209 Statement s = statements [ix];
2212 // Warn if we detect unreachable code.
2215 if (s is EmptyStatement)
2218 if (!unreachable_shown && !(s is LabeledStatement)) {
2219 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2220 unreachable_shown = true;
2223 Block c_block = s as Block;
2224 if (c_block != null)
2225 c_block.unreachable = c_block.unreachable_shown = true;
2229 // Note that we're not using ResolveUnreachable() for unreachable
2230 // statements here. ResolveUnreachable() creates a temporary
2231 // flow branching and kills it afterwards. This leads to problems
2232 // if you have two unreachable statements where the first one
2233 // assigns a variable and the second one tries to access it.
2236 if (!s.Resolve (ec)) {
2238 if (ec.IsInProbingMode)
2241 statements [ix] = new EmptyStatement (s.loc);
2245 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2246 statements [ix] = new EmptyStatement (s.loc);
2248 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2249 if (unreachable && s is LabeledStatement)
2250 throw new InternalErrorException ("should not happen");
2253 while (ec.CurrentBranching is FlowBranchingLabeled)
2254 ec.EndFlowBranching ();
2256 bool flow_unreachable = ec.EndFlowBranching ();
2258 ec.CurrentBlock = prev_block;
2260 if (flow_unreachable)
2261 flags |= Flags.HasRet;
2263 // If we're a non-static `struct' constructor which doesn't have an
2264 // initializer, then we must initialize all of the struct's fields.
2265 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2268 flags |= Flags.Resolved;
2272 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2274 unreachable_shown = true;
2278 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2280 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2281 bool ok = Resolve (ec);
2282 ec.KillFlowBranching ();
2287 protected override void DoEmit (EmitContext ec)
2289 for (int ix = 0; ix < statements.Count; ix++){
2290 statements [ix].Emit (ec);
2294 public override void Emit (EmitContext ec)
2296 if (scope_initializers != null)
2297 EmitScopeInitializers (ec);
2299 ec.Mark (StartLocation);
2302 if (SymbolWriter.HasSymbolWriter)
2303 EmitSymbolInfo (ec);
2306 protected void EmitScopeInitializers (EmitContext ec)
2308 SymbolWriter.OpenCompilerGeneratedBlock (ec);
2310 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2311 foreach (Statement s in scope_initializers)
2315 SymbolWriter.CloseCompilerGeneratedBlock (ec);
2318 protected virtual void EmitSymbolInfo (EmitContext ec)
2323 public override string ToString ()
2325 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2329 protected override void CloneTo (CloneContext clonectx, Statement t)
2331 Block target = (Block) t;
2333 target.clone_id = clone_id_counter++;
2336 clonectx.AddBlockMap (this, target);
2337 if (original != this)
2338 clonectx.AddBlockMap (original, target);
2340 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2341 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2344 target.Parent = clonectx.RemapBlockCopy (Parent);
2346 target.statements = new List<Statement> (statements.Count);
2347 foreach (Statement s in statements)
2348 target.statements.Add (s.Clone (clonectx));
2351 public override object Accept (StructuralVisitor visitor)
2353 return visitor.Visit (this);
2357 public class ExplicitBlock : Block
2359 protected AnonymousMethodStorey am_storey;
2361 public ExplicitBlock (Block parent, Location start, Location end)
2362 : this (parent, (Flags) 0, start, end)
2366 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2367 : base (parent, flags, start, end)
2369 this.Explicit = this;
2374 public AnonymousMethodStorey AnonymousMethodStorey {
2380 public bool HasAwait {
2382 return (flags & Flags.AwaitBlock) != 0;
2386 public bool HasCapturedThis {
2387 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2389 return (flags & Flags.HasCapturedThis) != 0;
2393 public bool HasCapturedVariable {
2394 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2396 return (flags & Flags.HasCapturedVariable) != 0;
2400 public bool HasYield {
2402 return (flags & Flags.YieldBlock) != 0;
2409 // Creates anonymous method storey in current block
2411 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2414 // Return same story for iterator and async blocks unless we are
2415 // in nested anonymous method
2417 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2418 return ec.CurrentAnonymousMethod.Storey;
2421 // When referencing a variable in parent iterator/async storey
2422 // from nested anonymous method
2424 if (ParametersBlock.am_storey is StateMachine) {
2425 return ParametersBlock.am_storey;
2428 if (am_storey == null) {
2429 MemberBase mc = ec.MemberContext as MemberBase;
2432 // Creates anonymous method storey for this block
2434 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2440 public override void Emit (EmitContext ec)
2442 if (am_storey != null) {
2443 DefineAnonymousStorey (ec);
2444 am_storey.EmitStoreyInstantiation (ec, this);
2447 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2448 if (emit_debug_info)
2453 if (emit_debug_info)
2457 void DefineAnonymousStorey (EmitContext ec)
2460 // Creates anonymous method storey
2462 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2464 // Creates parent storey reference when hoisted this is accessible
2466 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2467 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2470 // Hoisted this exists in top-level parent storey only
2472 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2473 parent = parent.Parent.Explicit;
2475 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2478 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2480 // TODO MemberCache: Review
2481 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2484 am_storey.CreateContainer ();
2485 am_storey.DefineContainer ();
2487 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2488 if (ref_blocks != null) {
2489 foreach (ExplicitBlock ref_block in ref_blocks) {
2490 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2491 if (b.am_storey != null) {
2492 b.am_storey.AddParentStoreyReference (ec, am_storey);
2494 // Stop propagation inside same top block
2495 if (b.ParametersBlock.Original == ParametersBlock.Original)
2498 b = b.ParametersBlock;
2501 b.HasCapturedVariable = true;
2506 am_storey.Define ();
2507 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2510 public void RegisterAsyncAwait ()
2513 while ((block.flags & Flags.AwaitBlock) == 0) {
2514 block.flags |= Flags.AwaitBlock;
2516 if (block.Parent == null)
2519 block = block.Parent.Explicit;
2523 public void RegisterIteratorYield ()
2526 while ((block.flags & Flags.YieldBlock) == 0) {
2527 block.flags |= Flags.YieldBlock;
2529 if (block.Parent == null)
2532 block = block.Parent.Explicit;
2536 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2538 tryBlock.statements = statements;
2539 statements = new List<Statement> (1);
2540 statements.Add (tf);
2545 // ParametersBlock was introduced to support anonymous methods
2546 // and lambda expressions
2548 public class ParametersBlock : ExplicitBlock
2550 public class ParameterInfo : INamedBlockVariable
2552 readonly ParametersBlock block;
2554 public VariableInfo VariableInfo;
2557 public ParameterInfo (ParametersBlock block, int index)
2565 public Block Block {
2571 public bool IsDeclared {
2577 public bool IsParameter {
2583 public bool IsLocked {
2592 public Location Location {
2594 return Parameter.Location;
2598 public Parameter Parameter {
2600 return block.Parameters [index];
2604 public TypeSpec ParameterType {
2606 return Parameter.Type;
2612 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2614 return new ParameterReference (this, loc);
2619 // Block is converted into an expression
2621 sealed class BlockScopeExpression : Expression
2624 readonly ParametersBlock block;
2626 public BlockScopeExpression (Expression child, ParametersBlock block)
2632 public override bool ContainsEmitWithAwait ()
2634 return child.ContainsEmitWithAwait ();
2637 public override Expression CreateExpressionTree (ResolveContext ec)
2639 throw new NotSupportedException ();
2642 protected override Expression DoResolve (ResolveContext ec)
2647 child = child.Resolve (ec);
2651 eclass = child.eclass;
2656 public override void Emit (EmitContext ec)
2658 block.EmitScopeInitializers (ec);
2663 protected ParametersCompiled parameters;
2664 protected ParameterInfo[] parameter_info;
2666 protected bool unreachable;
2667 protected ToplevelBlock top_block;
2669 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2670 : base (parent, 0, start, start)
2672 if (parameters == null)
2673 throw new ArgumentNullException ("parameters");
2675 this.parameters = parameters;
2676 ParametersBlock = this;
2678 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2680 this.top_block = parent.ParametersBlock.top_block;
2681 ProcessParameters ();
2684 protected ParametersBlock (ParametersCompiled parameters, Location start)
2685 : base (null, 0, start, start)
2687 if (parameters == null)
2688 throw new ArgumentNullException ("parameters");
2690 this.parameters = parameters;
2691 ParametersBlock = this;
2695 // It's supposed to be used by method body implementation of anonymous methods
2697 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2698 : base (null, 0, source.StartLocation, source.EndLocation)
2700 this.parameters = parameters;
2701 this.statements = source.statements;
2702 this.scope_initializers = source.scope_initializers;
2704 this.resolved = true;
2705 this.unreachable = source.unreachable;
2706 this.am_storey = source.am_storey;
2708 ParametersBlock = this;
2711 // Overwrite original for comparison purposes when linking cross references
2712 // between anonymous methods
2719 public bool IsAsync {
2721 return (flags & Flags.HasAsyncModifier) != 0;
2724 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2729 // Block has been converted to expression tree
2731 public bool IsExpressionTree {
2733 return (flags & Flags.IsExpressionTree) != 0;
2738 // The parameters for the block.
2740 public ParametersCompiled Parameters {
2746 public ToplevelBlock TopBlock {
2752 public bool Resolved {
2754 return (flags & Flags.Resolved) != 0;
2758 public int TemporaryLocalsCount { get; set; }
2763 // Check whether all `out' parameters have been assigned.
2765 public void CheckOutParameters (FlowBranching.UsageVector vector)
2767 if (vector.IsUnreachable)
2770 int n = parameter_info == null ? 0 : parameter_info.Length;
2772 for (int i = 0; i < n; i++) {
2773 VariableInfo var = parameter_info[i].VariableInfo;
2778 if (vector.IsAssigned (var, false))
2781 var p = parameter_info[i].Parameter;
2782 TopBlock.Report.Error (177, p.Location,
2783 "The out parameter `{0}' must be assigned to before control leaves the current method",
2788 public override Expression CreateExpressionTree (ResolveContext ec)
2790 if (statements.Count == 1) {
2791 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2792 if (scope_initializers != null)
2793 expr = new BlockScopeExpression (expr, this);
2798 return base.CreateExpressionTree (ec);
2801 public ParameterInfo GetParameterInfo (Parameter p)
2803 for (int i = 0; i < parameters.Count; ++i) {
2804 if (parameters[i] == p)
2805 return parameter_info[i];
2808 throw new ArgumentException ("Invalid parameter");
2811 public Expression GetParameterReference (int index, Location loc)
2813 return new ParameterReference (parameter_info[index], loc);
2816 public Statement PerformClone ()
2818 CloneContext clonectx = new CloneContext ();
2819 return Clone (clonectx);
2822 protected void ProcessParameters ()
2824 if (parameters.Count == 0)
2827 parameter_info = new ParameterInfo[parameters.Count];
2828 for (int i = 0; i < parameter_info.Length; ++i) {
2829 var p = parameters.FixedParameters[i];
2833 // TODO: Should use Parameter only and more block there
2834 parameter_info[i] = new ParameterInfo (this, i);
2836 AddLocalName (p.Name, parameter_info[i]);
2840 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2847 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2848 flags |= Flags.IsExpressionTree;
2853 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2854 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2859 unreachable = top_level.End ();
2861 } catch (Exception e) {
2862 if (e is CompletionResult || rc.Report.IsDisabled)
2865 if (rc.CurrentBlock != null) {
2866 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2868 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2871 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2875 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2876 if (rc.CurrentAnonymousMethod == null) {
2877 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2878 if (md is StateMachineMethod) {
2881 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2886 // If an asynchronous body of F is either an expression classified as nothing, or a
2887 // statement block where no return statements have expressions, the inferred return type is Task
2890 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2891 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2892 am.ReturnTypeInference = null;
2893 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2898 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2899 rc.CurrentAnonymousMethod.GetSignatureForError ());
2907 void ResolveMeta (BlockContext ec)
2909 int orig_count = parameters.Count;
2911 for (int i = 0; i < orig_count; ++i) {
2912 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2914 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2917 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2918 parameter_info[i].VariableInfo = vi;
2919 ec.FlowOffset += vi.Length;
2923 public void WrapIntoIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
2925 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2926 pb.EndLocation = EndLocation;
2927 pb.statements = statements;
2930 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2931 am_storey = new IteratorStorey (iterator);
2933 statements = new List<Statement> (1);
2934 AddStatement (new Return (iterator, iterator.Location));
2935 flags &= ~Flags.YieldBlock;
2938 public void WrapIntoAsyncTask (IMemberContext context, TypeDefinition host, TypeSpec returnType)
2940 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, StartLocation);
2941 pb.EndLocation = EndLocation;
2942 pb.statements = statements;
2945 var block_type = host.Module.Compiler.BuiltinTypes.Void;
2946 var initializer = new AsyncInitializer (pb, host, block_type);
2947 initializer.Type = block_type;
2949 am_storey = new AsyncTaskStorey (context, initializer, returnType);
2951 statements = new List<Statement> (1);
2952 AddStatement (new StatementExpression (initializer));
2953 flags &= ~Flags.AwaitBlock;
2960 public class ToplevelBlock : ParametersBlock
2962 LocalVariable this_variable;
2963 CompilerContext compiler;
2964 Dictionary<string, object> names;
2965 Dictionary<string, object> labels;
2967 public HoistedVariable HoistedThisVariable;
2969 public Report Report {
2970 get { return compiler.Report; }
2973 public ToplevelBlock (CompilerContext ctx, Location loc)
2974 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2978 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2979 : base (parameters, start)
2981 this.compiler = ctx;
2984 ProcessParameters ();
2988 // Recreates a top level block from parameters block. Used for
2989 // compiler generated methods where the original block comes from
2990 // explicit child block. This works for already resolved blocks
2991 // only to ensure we resolve them in the correct flow order
2993 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
2994 : base (source, parameters)
2996 this.compiler = source.TopBlock.compiler;
3000 public bool IsIterator {
3006 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3009 names = new Dictionary<string, object> ();
3012 if (!names.TryGetValue (name, out value)) {
3013 names.Add (name, li);
3017 INamedBlockVariable existing = value as INamedBlockVariable;
3018 List<INamedBlockVariable> existing_list;
3019 if (existing != null) {
3020 existing_list = new List<INamedBlockVariable> ();
3021 existing_list.Add (existing);
3022 names[name] = existing_list;
3024 existing_list = (List<INamedBlockVariable>) value;
3028 // A collision checking between local names
3030 for (int i = 0; i < existing_list.Count; ++i) {
3031 existing = existing_list[i];
3032 Block b = existing.Block;
3034 // Collision at same level
3035 if (li.Block == b) {
3036 li.Block.Error_AlreadyDeclared (name, li);
3040 // Collision with parent
3042 while ((b = b.Parent) != null) {
3043 if (existing.Block == b) {
3044 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3045 i = existing_list.Count;
3050 if (!ignoreChildrenBlocks) {
3051 // Collision with children
3053 while ((b = b.Parent) != null) {
3054 if (li.Block == b) {
3055 li.Block.Error_AlreadyDeclared (name, li, "child");
3056 i = existing_list.Count;
3063 existing_list.Add (li);
3066 public void AddLabel (string name, LabeledStatement label)
3069 labels = new Dictionary<string, object> ();
3072 if (!labels.TryGetValue (name, out value)) {
3073 labels.Add (name, label);
3077 LabeledStatement existing = value as LabeledStatement;
3078 List<LabeledStatement> existing_list;
3079 if (existing != null) {
3080 existing_list = new List<LabeledStatement> ();
3081 existing_list.Add (existing);
3082 labels[name] = existing_list;
3084 existing_list = (List<LabeledStatement>) value;
3088 // A collision checking between labels
3090 for (int i = 0; i < existing_list.Count; ++i) {
3091 existing = existing_list[i];
3092 Block b = existing.Block;
3094 // Collision at same level
3095 if (label.Block == b) {
3096 Report.SymbolRelatedToPreviousError (existing.loc, name);
3097 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3101 // Collision with parent
3103 while ((b = b.Parent) != null) {
3104 if (existing.Block == b) {
3105 Report.Error (158, label.loc,
3106 "The label `{0}' shadows another label by the same name in a contained scope", name);
3107 i = existing_list.Count;
3112 // Collision with with children
3114 while ((b = b.Parent) != null) {
3115 if (label.Block == b) {
3116 Report.Error (158, label.loc,
3117 "The label `{0}' shadows another label by the same name in a contained scope", name);
3118 i = existing_list.Count;
3124 existing_list.Add (label);
3128 // Creates an arguments set from all parameters, useful for method proxy calls
3130 public Arguments GetAllParametersArguments ()
3132 int count = parameters.Count;
3133 Arguments args = new Arguments (count);
3134 for (int i = 0; i < count; ++i) {
3135 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3136 args.Add (new Argument (arg_expr));
3143 // Lookup inside a block, the returned value can represent 3 states
3145 // true+variable: A local name was found and it's valid
3146 // false+variable: A local name was found in a child block only
3147 // false+null: No local name was found
3149 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3155 if (!names.TryGetValue (name, out value))
3158 variable = value as INamedBlockVariable;
3160 if (variable != null) {
3162 if (variable.Block == b.Original)
3166 } while (b != null);
3174 } while (b != null);
3176 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3177 for (int i = 0; i < list.Count; ++i) {
3180 if (variable.Block == b.Original)
3184 } while (b != null);
3192 } while (b != null);
3202 public LabeledStatement GetLabel (string name, Block block)
3208 if (!labels.TryGetValue (name, out value)) {
3212 var label = value as LabeledStatement;
3214 if (label != null) {
3215 if (label.Block == b.Original)
3218 // TODO: Temporary workaround for the switch block implicit label block
3219 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3222 List<LabeledStatement> list = (List<LabeledStatement>) value;
3223 for (int i = 0; i < list.Count; ++i) {
3225 if (label.Block == b.Original)
3228 // TODO: Temporary workaround for the switch block implicit label block
3229 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3238 // Returns the "this" instance variable of this block.
3239 // See AddThisVariable() for more information.
3241 public LocalVariable ThisVariable {
3242 get { return this_variable; }
3246 // This is used by non-static `struct' constructors which do not have an
3247 // initializer - in this case, the constructor must initialize all of the
3248 // struct's fields. To do this, we add a "this" variable and use the flow
3249 // analysis code to ensure that it's been fully initialized before control
3250 // leaves the constructor.
3252 public void AddThisVariable (BlockContext bc)
3254 if (this_variable != null)
3255 throw new InternalErrorException (StartLocation.ToString ());
3257 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3258 this_variable.Type = bc.CurrentType;
3259 this_variable.PrepareForFlowAnalysis (bc);
3262 public bool IsThisAssigned (BlockContext ec)
3264 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3267 public override void Emit (EmitContext ec)
3269 if (Report.Errors > 0)
3278 ec.Mark (EndLocation);
3280 if (ec.HasReturnLabel)
3281 ec.MarkLabel (ec.ReturnLabel);
3283 if (ec.return_value != null) {
3284 ec.Emit (OpCodes.Ldloc, ec.return_value);
3285 ec.Emit (OpCodes.Ret);
3288 // If `HasReturnLabel' is set, then we already emitted a
3289 // jump to the end of the method, so we must emit a `ret'
3292 // Unfortunately, System.Reflection.Emit automatically emits
3293 // a leave to the end of a finally block. This is a problem
3294 // if no code is following the try/finally block since we may
3295 // jump to a point after the end of the method.
3296 // As a workaround, we're always creating a return label in
3300 if (ec.HasReturnLabel || !unreachable) {
3301 if (ec.ReturnType.Kind != MemberKind.Void)
3302 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3303 ec.Emit (OpCodes.Ret);
3308 } catch (Exception e){
3309 Console.WriteLine ("Exception caught by the compiler while emitting:");
3310 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3312 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3318 protected override void EmitSymbolInfo (EmitContext ec)
3320 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3321 if ((ae != null) && (ae.Storey != null))
3322 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3324 base.EmitSymbolInfo (ec);
3328 public class SwitchLabel {
3331 readonly Location loc;
3336 // if expr == null, then it is the default case.
3338 public SwitchLabel (Expression expr, Location l)
3344 public bool IsDefault {
3346 return label == null;
3350 public Expression Label {
3356 public Location Location {
3362 public Constant Converted {
3371 public Label GetILLabel (EmitContext ec)
3373 if (il_label == null){
3374 il_label = ec.DefineLabel ();
3377 return il_label.Value;
3381 // Resolves the expression, reduces it to a literal if possible
3382 // and then converts it to the requested type.
3384 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3386 Expression e = label.Resolve (ec);
3391 Constant c = e as Constant;
3393 ec.Report.Error (150, loc, "A constant value is expected");
3397 if (allow_nullable && c is NullLiteral) {
3402 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3403 return converted != null;
3406 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3409 if (converted == null)
3412 label = converted.GetValueAsLiteral ();
3414 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3415 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3418 public SwitchLabel Clone (CloneContext clonectx)
3423 return new SwitchLabel (label.Clone (clonectx), loc);
3427 public class SwitchSection {
3428 public readonly List<SwitchLabel> Labels;
3429 public readonly Block Block;
3431 public SwitchSection (List<SwitchLabel> labels, Block block)
3437 public SwitchSection Clone (CloneContext clonectx)
3439 var cloned_labels = new List<SwitchLabel> ();
3441 foreach (SwitchLabel sl in Labels)
3442 cloned_labels.Add (sl.Clone (clonectx));
3444 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3448 public class Switch : Statement
3450 // structure used to hold blocks of keys while calculating table switch
3451 sealed class LabelsRange : IComparable<LabelsRange>
3453 public readonly long min;
3455 public readonly List<long> label_values;
3457 public LabelsRange (long value)
3460 label_values = new List<long> ();
3461 label_values.Add (value);
3464 public LabelsRange (long min, long max, ICollection<long> values)
3468 this.label_values = new List<long> (values);
3473 return max - min + 1;
3477 public bool AddValue (long value)
3479 var gap = value - min + 1;
3480 // Ensure the range has > 50% occupancy
3481 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3485 label_values.Add (value);
3489 public int CompareTo (LabelsRange other)
3491 int nLength = label_values.Count;
3492 int nLengthOther = other.label_values.Count;
3493 if (nLengthOther == nLength)
3494 return (int) (other.min - min);
3496 return nLength - nLengthOther;
3500 sealed class LabelMarker : Statement
3503 readonly List<SwitchLabel> labels;
3505 public LabelMarker (Switch s, List<SwitchLabel> labels)
3508 this.labels = labels;
3511 protected override void CloneTo (CloneContext clonectx, Statement target)
3515 protected override void DoEmit (EmitContext ec)
3517 foreach (var l in labels) {
3519 ec.MarkLabel (s.DefaultLabel);
3521 ec.MarkLabel (l.GetILLabel (ec));
3526 public List<SwitchSection> Sections;
3527 public Expression Expr;
3530 // Mapping of all labels to their SwitchLabels
3532 Dictionary<long, SwitchLabel> labels;
3533 Dictionary<string, SwitchLabel> string_labels;
3536 /// The governing switch type
3538 public TypeSpec SwitchType;
3543 Label default_target;
3545 Expression new_expr;
3548 SwitchSection constant_section;
3549 SwitchSection default_section;
3550 SwitchLabel null_section;
3552 Statement simple_stmt;
3553 VariableReference value;
3554 ExpressionStatement string_dictionary;
3555 FieldExpr switch_cache_field;
3556 ExplicitBlock block;
3559 // Nullable Types support
3561 Nullable.Unwrap unwrap;
3563 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3571 public ExplicitBlock Block {
3577 public Label DefaultLabel {
3579 return default_target;
3583 public bool GotDefault {
3585 return default_section != null;
3589 public bool IsNullable {
3591 return unwrap != null;
3596 // Determines the governing type for a switch. The returned
3597 // expression might be the expression from the switch, or an
3598 // expression that includes any potential conversions to
3600 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3602 switch (expr.Type.BuiltinType) {
3603 case BuiltinTypeSpec.Type.Byte:
3604 case BuiltinTypeSpec.Type.SByte:
3605 case BuiltinTypeSpec.Type.UShort:
3606 case BuiltinTypeSpec.Type.Short:
3607 case BuiltinTypeSpec.Type.UInt:
3608 case BuiltinTypeSpec.Type.Int:
3609 case BuiltinTypeSpec.Type.ULong:
3610 case BuiltinTypeSpec.Type.Long:
3611 case BuiltinTypeSpec.Type.Char:
3612 case BuiltinTypeSpec.Type.String:
3613 case BuiltinTypeSpec.Type.Bool:
3617 if (expr.Type.IsEnum)
3621 // Try to find a *user* defined implicit conversion.
3623 // If there is no implicit conversion, or if there are multiple
3624 // conversions, we have to report an error
3626 Expression converted = null;
3627 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3630 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3635 // Ignore over-worked ImplicitUserConversions that do
3636 // an implicit conversion in addition to the user conversion.
3638 if (!(e is UserCast))
3641 if (converted != null){
3642 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3651 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3653 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3669 // Performs the basic sanity checks on the switch statement
3670 // (looks for duplicate keys and non-constant expressions).
3672 // It also returns a hashtable with the keys that we will later
3673 // use to compute the switch tables
3675 bool CheckSwitch (ResolveContext ec)
3678 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3679 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3681 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3683 foreach (SwitchSection ss in Sections){
3684 foreach (SwitchLabel sl in ss.Labels){
3686 if (default_section != null){
3687 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3690 default_section = ss;
3694 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3700 if (string_labels != null) {
3701 string s = sl.Converted.GetValue () as string;
3705 string_labels.Add (s, sl);
3707 if (sl.Converted is NullLiteral) {
3710 labels.Add (sl.Converted.GetValueAsLong (), sl);
3713 } catch (ArgumentException) {
3714 if (string_labels != null)
3715 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3717 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3727 // This method emits code for a lookup-based switch statement (non-string)
3728 // Basically it groups the cases into blocks that are at least half full,
3729 // and then spits out individual lookup opcodes for each block.
3730 // It emits the longest blocks first, and short blocks are just
3731 // handled with direct compares.
3733 void EmitTableSwitch (EmitContext ec, Expression val)
3735 Label lbl_default = default_target;
3737 if (labels != null && labels.Count > 0) {
3738 List<LabelsRange> ranges;
3739 if (string_labels != null) {
3740 // We have done all hard work for string already
3741 // setup single range only
3742 ranges = new List<LabelsRange> (1);
3743 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3745 var element_keys = new long[labels.Count];
3746 labels.Keys.CopyTo (element_keys, 0);
3747 Array.Sort (element_keys);
3750 // Build possible ranges of switch labes to reduce number
3753 ranges = new List<LabelsRange> (element_keys.Length);
3754 var range = new LabelsRange (element_keys[0]);
3756 for (int i = 1; i < element_keys.Length; ++i) {
3757 var l = element_keys[i];
3758 if (range.AddValue (l))
3761 range = new LabelsRange (l);
3765 // sort the blocks so we can tackle the largest ones first
3769 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3771 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3772 LabelsRange kb = ranges[range_index];
3773 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3775 // Optimize small ranges using simple equality check
3776 if (kb.Range <= 2) {
3777 foreach (var key in kb.label_values) {
3778 SwitchLabel sl = labels[key];
3779 if (sl.Converted.IsDefaultValue) {
3780 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3783 sl.Converted.Emit (ec);
3784 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3788 // TODO: if all the keys in the block are the same and there are
3789 // no gaps/defaults then just use a range-check.
3790 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3791 // TODO: optimize constant/I4 cases
3793 // check block range (could be > 2^31)
3795 ec.EmitLong (kb.min);
3796 ec.Emit (OpCodes.Blt, lbl_default);
3799 ec.EmitLong (kb.max);
3800 ec.Emit (OpCodes.Bgt, lbl_default);
3805 ec.EmitLong (kb.min);
3806 ec.Emit (OpCodes.Sub);
3809 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3813 int first = (int) kb.min;
3816 ec.Emit (OpCodes.Sub);
3817 } else if (first < 0) {
3818 ec.EmitInt (-first);
3819 ec.Emit (OpCodes.Add);
3823 // first, build the list of labels for the switch
3825 long cJumps = kb.Range;
3826 Label[] switch_labels = new Label[cJumps];
3827 for (int iJump = 0; iJump < cJumps; iJump++) {
3828 var key = kb.label_values[iKey];
3829 if (key == kb.min + iJump) {
3830 switch_labels[iJump] = labels[key].GetILLabel (ec);
3833 switch_labels[iJump] = lbl_default;
3837 // emit the switch opcode
3838 ec.Emit (OpCodes.Switch, switch_labels);
3841 // mark the default for this block
3842 if (range_index != 0)
3843 ec.MarkLabel (lbl_default);
3846 // the last default just goes to the end
3847 if (ranges.Count > 0)
3848 ec.Emit (OpCodes.Br, lbl_default);
3851 // now emit the code for the sections
3852 bool found_default = false;
3854 foreach (SwitchSection ss in Sections) {
3855 foreach (SwitchLabel sl in ss.Labels) {
3857 ec.MarkLabel (lbl_default);
3858 found_default = true;
3859 if (null_section == null)
3860 ec.MarkLabel (null_target);
3861 } else if (sl.Converted.IsNull) {
3862 ec.MarkLabel (null_target);
3865 ec.MarkLabel (sl.GetILLabel (ec));
3871 if (!found_default) {
3872 ec.MarkLabel (lbl_default);
3873 if (null_section == null) {
3874 ec.MarkLabel (null_target);
3879 SwitchLabel FindLabel (Constant value)
3881 SwitchLabel sl = null;
3883 if (string_labels != null) {
3884 string s = value.GetValue () as string;
3886 if (null_section != null)
3888 else if (default_section != null)
3889 sl = default_section.Labels[0];
3891 string_labels.TryGetValue (s, out sl);
3894 if (value is NullLiteral) {
3897 labels.TryGetValue (value.GetValueAsLong (), out sl);
3904 SwitchSection FindSection (SwitchLabel label)
3906 foreach (SwitchSection ss in Sections){
3907 foreach (SwitchLabel sl in ss.Labels){
3916 public override bool Resolve (BlockContext ec)
3918 Expr = Expr.Resolve (ec);
3922 new_expr = SwitchGoverningType (ec, Expr);
3924 if (new_expr == null && Expr.Type.IsNullableType) {
3925 unwrap = Nullable.Unwrap.Create (Expr, false);
3929 new_expr = SwitchGoverningType (ec, unwrap);
3932 if (new_expr == null){
3933 ec.Report.Error (151, loc,
3934 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3935 TypeManager.CSharpName (Expr.Type));
3940 SwitchType = new_expr.Type;
3942 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3943 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3947 if (!CheckSwitch (ec))
3950 Switch old_switch = ec.Switch;
3952 ec.Switch.SwitchType = SwitchType;
3954 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3956 var constant = new_expr as Constant;
3957 if (constant != null) {
3959 SwitchLabel label = FindLabel (constant);
3961 constant_section = FindSection (label);
3963 if (constant_section == null)
3964 constant_section = default_section;
3967 // Store switch expression for comparission purposes
3969 value = new_expr as VariableReference;
3971 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
3976 foreach (SwitchSection ss in Sections){
3978 ec.CurrentBranching.CreateSibling (
3979 null, FlowBranching.SiblingType.SwitchSection);
3983 if (is_constant && (ss != constant_section)) {
3984 // If we're a constant switch, we're only emitting
3985 // one single section - mark all the others as
3987 ec.CurrentBranching.CurrentUsageVector.Goto ();
3988 if (!ss.Block.ResolveUnreachable (ec, true)) {
3992 if (!ss.Block.Resolve (ec))
3997 if (default_section == null)
3998 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4000 ec.EndFlowBranching ();
4001 ec.Switch = old_switch;
4007 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4008 if (string_labels.Count < 7)
4009 ResolveSimpleSwitch (ec);
4011 ResolveStringSwitchMap (ec);
4012 } else if (labels.Count < 3 && !IsNullable) {
4013 ResolveSimpleSwitch (ec);
4020 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4022 var sl = FindLabel (value);
4025 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4032 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4034 void ResolveSimpleSwitch (BlockContext bc)
4036 simple_stmt = default_section != null ? default_section.Block : null;
4038 for (int i = Sections.Count - 1; i >= 0; --i) {
4039 var s = Sections[i];
4041 if (s == default_section) {
4042 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4046 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4048 Expression cond = null;
4049 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4050 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4053 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4059 simple_stmt = new If (cond, s.Block, simple_stmt, loc);
4062 // It's null for empty switch
4063 if (simple_stmt != null)
4064 simple_stmt.Resolve (bc);
4068 // Converts string switch into string hashtable
4070 void ResolveStringSwitchMap (ResolveContext ec)
4072 FullNamedExpression string_dictionary_type;
4073 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4074 string_dictionary_type = new TypeExpression (
4075 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4076 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4078 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4079 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4081 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4085 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4086 Field field = new Field (ctype, string_dictionary_type,
4087 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4088 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4089 if (!field.Define ())
4091 ctype.AddField (field);
4093 var init = new List<Expression> ();
4095 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4096 string value = null;
4097 foreach (SwitchSection section in Sections) {
4098 bool contains_label = false;
4099 foreach (SwitchLabel sl in section.Labels) {
4100 if (sl.IsDefault || sl.Converted.IsNull)
4103 if (!contains_label) {
4104 labels.Add (counter, sl);
4105 contains_label = true;
4108 value = (string) sl.Converted.GetValue ();
4109 var init_args = new List<Expression> (2);
4110 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4112 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4113 init_args.Add (sl.Converted);
4115 init.Add (new CollectionElementInitializer (init_args, loc));
4119 // Don't add empty sections
4125 Arguments args = new Arguments (1);
4126 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4127 Expression initializer = new NewInitialize (string_dictionary_type, args,
4128 new CollectionOrObjectInitializers (init, loc), loc);
4130 switch_cache_field = new FieldExpr (field, loc);
4131 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4134 void DoEmitStringSwitch (EmitContext ec)
4136 Label l_initialized = ec.DefineLabel ();
4139 // Skip initialization when value is null
4141 value.EmitBranchable (ec, null_target, false);
4144 // Check if string dictionary is initialized and initialize
4146 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4147 string_dictionary.EmitStatement (ec);
4148 ec.MarkLabel (l_initialized);
4150 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4152 ResolveContext rc = new ResolveContext (ec.MemberContext);
4154 if (switch_cache_field.Type.IsGeneric) {
4155 Arguments get_value_args = new Arguments (2);
4156 get_value_args.Add (new Argument (value));
4157 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4158 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4159 if (get_item == null)
4163 // A value was not found, go to default case
4165 get_item.EmitBranchable (ec, default_target, false);
4167 Arguments get_value_args = new Arguments (1);
4168 get_value_args.Add (new Argument (value));
4170 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4171 if (get_item == null)
4174 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4175 get_item_object.EmitAssign (ec, get_item, true, false);
4176 ec.Emit (OpCodes.Brfalse, default_target);
4178 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4179 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4181 get_item_int.EmitStatement (ec);
4182 get_item_object.Release (ec);
4185 EmitTableSwitch (ec, string_switch_variable);
4186 string_switch_variable.Release (ec);
4189 protected override void DoEmit (EmitContext ec)
4192 // Needed to emit anonymous storey initialization
4193 // Otherwise it does not contain any statements for now
4197 default_target = ec.DefineLabel ();
4198 null_target = ec.DefineLabel ();
4201 unwrap.EmitCheck (ec);
4202 ec.Emit (OpCodes.Brfalse, null_target);
4203 value.EmitAssign (ec, new_expr, false, false);
4204 } else if (new_expr != value && !is_constant) {
4205 value.EmitAssign (ec, new_expr, false, false);
4209 // Setup the codegen context
4211 Label old_end = ec.LoopEnd;
4212 Switch old_switch = ec.Switch;
4214 ec.LoopEnd = ec.DefineLabel ();
4219 if (constant_section != null)
4220 constant_section.Block.Emit (ec);
4221 } else if (string_dictionary != null) {
4222 DoEmitStringSwitch (ec);
4223 } else if (simple_stmt != null) {
4224 simple_stmt.Emit (ec);
4226 EmitTableSwitch (ec, value);
4229 // Restore context state.
4230 ec.MarkLabel (ec.LoopEnd);
4233 // Restore the previous context
4235 ec.LoopEnd = old_end;
4236 ec.Switch = old_switch;
4239 protected override void CloneTo (CloneContext clonectx, Statement t)
4241 Switch target = (Switch) t;
4243 target.Expr = Expr.Clone (clonectx);
4244 target.Sections = new List<SwitchSection> ();
4245 foreach (SwitchSection ss in Sections){
4246 target.Sections.Add (ss.Clone (clonectx));
4250 public override object Accept (StructuralVisitor visitor)
4252 return visitor.Visit (this);
4256 // A place where execution can restart in an iterator
4257 public abstract class ResumableStatement : Statement
4260 protected Label resume_point;
4262 public Label PrepareForEmit (EmitContext ec)
4266 resume_point = ec.DefineLabel ();
4268 return resume_point;
4271 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4276 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4281 public abstract class TryFinallyBlock : ExceptionStatement
4283 protected Statement stmt;
4284 Label dispose_try_block;
4285 bool prepared_for_dispose, emitted_dispose;
4287 protected TryFinallyBlock (Statement stmt, Location loc)
4295 public Statement Statement {
4303 protected abstract void EmitTryBody (EmitContext ec);
4304 protected abstract void EmitFinallyBody (EmitContext ec);
4306 public override Label PrepareForDispose (EmitContext ec, Label end)
4308 if (!prepared_for_dispose) {
4309 prepared_for_dispose = true;
4310 dispose_try_block = ec.DefineLabel ();
4312 return dispose_try_block;
4315 protected sealed override void DoEmit (EmitContext ec)
4317 EmitTryBodyPrepare (ec);
4320 ec.BeginFinallyBlock ();
4322 Label start_finally = ec.DefineLabel ();
4323 if (resume_points != null) {
4324 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4326 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4327 ec.Emit (OpCodes.Brfalse_S, start_finally);
4328 ec.Emit (OpCodes.Endfinally);
4331 ec.MarkLabel (start_finally);
4332 EmitFinallyBody (ec);
4334 ec.EndExceptionBlock ();
4337 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4339 if (emitted_dispose)
4342 emitted_dispose = true;
4344 Label end_of_try = ec.DefineLabel ();
4346 // Ensure that the only way we can get into this code is through a dispatcher
4347 if (have_dispatcher)
4348 ec.Emit (OpCodes.Br, end);
4350 ec.BeginExceptionBlock ();
4352 ec.MarkLabel (dispose_try_block);
4354 Label[] labels = null;
4355 for (int i = 0; i < resume_points.Count; ++i) {
4356 ResumableStatement s = resume_points[i];
4357 Label ret = s.PrepareForDispose (ec, end_of_try);
4358 if (ret.Equals (end_of_try) && labels == null)
4360 if (labels == null) {
4361 labels = new Label[resume_points.Count];
4362 for (int j = 0; j < i; ++j)
4363 labels[j] = end_of_try;
4368 if (labels != null) {
4370 for (j = 1; j < labels.Length; ++j)
4371 if (!labels[0].Equals (labels[j]))
4373 bool emit_dispatcher = j < labels.Length;
4375 if (emit_dispatcher) {
4376 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4377 ec.Emit (OpCodes.Ldloc, pc);
4378 ec.EmitInt (first_resume_pc);
4379 ec.Emit (OpCodes.Sub);
4380 ec.Emit (OpCodes.Switch, labels);
4381 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4384 foreach (ResumableStatement s in resume_points)
4385 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4388 ec.MarkLabel (end_of_try);
4390 ec.BeginFinallyBlock ();
4392 EmitFinallyBody (ec);
4394 ec.EndExceptionBlock ();
4399 // Base class for blocks using exception handling
4401 public abstract class ExceptionStatement : ResumableStatement
4406 protected List<ResumableStatement> resume_points;
4407 protected int first_resume_pc;
4409 protected ExceptionStatement (Location loc)
4414 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4416 StateMachineInitializer state_machine = null;
4417 if (resume_points != null) {
4418 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4420 ec.EmitInt ((int) IteratorStorey.State.Running);
4421 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4424 ec.BeginExceptionBlock ();
4426 if (resume_points != null) {
4427 ec.MarkLabel (resume_point);
4429 // For normal control flow, we want to fall-through the Switch
4430 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4431 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4432 ec.EmitInt (first_resume_pc);
4433 ec.Emit (OpCodes.Sub);
4435 Label[] labels = new Label[resume_points.Count];
4436 for (int i = 0; i < resume_points.Count; ++i)
4437 labels[i] = resume_points[i].PrepareForEmit (ec);
4438 ec.Emit (OpCodes.Switch, labels);
4442 public void SomeCodeFollows ()
4445 code_follows = true;
4449 public override bool Resolve (BlockContext ec)
4452 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4453 // So, ensure there's some IL code after this statement.
4454 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4455 ec.NeedReturnLabel ();
4460 public void AddResumePoint (ResumableStatement stmt, int pc)
4462 if (resume_points == null) {
4463 resume_points = new List<ResumableStatement> ();
4464 first_resume_pc = pc;
4467 if (pc != first_resume_pc + resume_points.Count)
4468 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4470 resume_points.Add (stmt);
4475 public class Lock : TryFinallyBlock
4478 TemporaryVariableReference expr_copy;
4479 TemporaryVariableReference lock_taken;
4481 public Lock (Expression expr, Statement stmt, Location loc)
4487 public Expression Expr {
4493 public override bool Resolve (BlockContext ec)
4495 expr = expr.Resolve (ec);
4499 if (!TypeSpec.IsReferenceType (expr.Type)) {
4500 ec.Report.Error (185, loc,
4501 "`{0}' is not a reference type as required by the lock statement",
4502 expr.Type.GetSignatureForError ());
4505 if (expr.Type.IsGenericParameter) {
4506 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4509 VariableReference lv = expr as VariableReference;
4512 locked = lv.IsLockedByStatement;
4513 lv.IsLockedByStatement = true;
4519 using (ec.Set (ResolveContext.Options.LockScope)) {
4520 ec.StartFlowBranching (this);
4521 Statement.Resolve (ec);
4522 ec.EndFlowBranching ();
4526 lv.IsLockedByStatement = locked;
4532 // Have to keep original lock value around to unlock same location
4533 // in the case the original has changed or is null
4535 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4536 expr_copy.Resolve (ec);
4539 // Ensure Monitor methods are available
4541 if (ResolvePredefinedMethods (ec) > 1) {
4542 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4543 lock_taken.Resolve (ec);
4549 protected override void EmitTryBodyPrepare (EmitContext ec)
4551 expr_copy.EmitAssign (ec, expr);
4553 if (lock_taken != null) {
4555 // Initialize ref variable
4557 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4560 // Monitor.Enter (expr_copy)
4562 expr_copy.Emit (ec);
4563 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4566 base.EmitTryBodyPrepare (ec);
4569 protected override void EmitTryBody (EmitContext ec)
4572 // Monitor.Enter (expr_copy, ref lock_taken)
4574 if (lock_taken != null) {
4575 expr_copy.Emit (ec);
4576 lock_taken.LocalInfo.CreateBuilder (ec);
4577 lock_taken.AddressOf (ec, AddressOp.Load);
4578 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4581 Statement.Emit (ec);
4584 protected override void EmitFinallyBody (EmitContext ec)
4587 // if (lock_taken) Monitor.Exit (expr_copy)
4589 Label skip = ec.DefineLabel ();
4591 if (lock_taken != null) {
4592 lock_taken.Emit (ec);
4593 ec.Emit (OpCodes.Brfalse_S, skip);
4596 expr_copy.Emit (ec);
4597 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4599 ec.Emit (OpCodes.Call, m);
4601 ec.MarkLabel (skip);
4604 int ResolvePredefinedMethods (ResolveContext rc)
4606 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4607 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4611 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4615 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4619 protected override void CloneTo (CloneContext clonectx, Statement t)
4621 Lock target = (Lock) t;
4623 target.expr = expr.Clone (clonectx);
4624 target.stmt = Statement.Clone (clonectx);
4627 public override object Accept (StructuralVisitor visitor)
4629 return visitor.Visit (this);
4634 public class Unchecked : Statement {
4637 public Unchecked (Block b, Location loc)
4644 public override bool Resolve (BlockContext ec)
4646 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4647 return Block.Resolve (ec);
4650 protected override void DoEmit (EmitContext ec)
4652 using (ec.With (EmitContext.Options.CheckedScope, false))
4656 protected override void CloneTo (CloneContext clonectx, Statement t)
4658 Unchecked target = (Unchecked) t;
4660 target.Block = clonectx.LookupBlock (Block);
4663 public override object Accept (StructuralVisitor visitor)
4665 return visitor.Visit (this);
4669 public class Checked : Statement {
4672 public Checked (Block b, Location loc)
4675 b.Unchecked = false;
4679 public override bool Resolve (BlockContext ec)
4681 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4682 return Block.Resolve (ec);
4685 protected override void DoEmit (EmitContext ec)
4687 using (ec.With (EmitContext.Options.CheckedScope, true))
4691 protected override void CloneTo (CloneContext clonectx, Statement t)
4693 Checked target = (Checked) t;
4695 target.Block = clonectx.LookupBlock (Block);
4698 public override object Accept (StructuralVisitor visitor)
4700 return visitor.Visit (this);
4704 public class Unsafe : Statement {
4707 public Unsafe (Block b, Location loc)
4710 Block.Unsafe = true;
4714 public override bool Resolve (BlockContext ec)
4716 if (ec.CurrentIterator != null)
4717 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4719 using (ec.Set (ResolveContext.Options.UnsafeScope))
4720 return Block.Resolve (ec);
4723 protected override void DoEmit (EmitContext ec)
4728 protected override void CloneTo (CloneContext clonectx, Statement t)
4730 Unsafe target = (Unsafe) t;
4732 target.Block = clonectx.LookupBlock (Block);
4735 public override object Accept (StructuralVisitor visitor)
4737 return visitor.Visit (this);
4744 public class Fixed : Statement
4746 abstract class Emitter : ShimExpression
4748 protected LocalVariable vi;
4750 protected Emitter (Expression expr, LocalVariable li)
4756 public abstract void EmitExit (EmitContext ec);
4759 class ExpressionEmitter : Emitter {
4760 public ExpressionEmitter (Expression converted, LocalVariable li) :
4761 base (converted, li)
4765 protected override Expression DoResolve (ResolveContext rc)
4767 throw new NotImplementedException ();
4770 public override void Emit (EmitContext ec) {
4772 // Store pointer in pinned location
4778 public override void EmitExit (EmitContext ec)
4781 ec.Emit (OpCodes.Conv_U);
4786 class StringEmitter : Emitter
4788 LocalVariable pinned_string;
4790 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4795 protected override Expression DoResolve (ResolveContext rc)
4797 pinned_string = new LocalVariable (vi.Block, "$pinned",
4798 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4800 pinned_string.Type = rc.BuiltinTypes.String;
4802 eclass = ExprClass.Variable;
4803 type = rc.BuiltinTypes.Int;
4807 public override void Emit (EmitContext ec)
4809 pinned_string.CreateBuilder (ec);
4812 pinned_string.EmitAssign (ec);
4814 // TODO: Should use Binary::Add
4815 pinned_string.Emit (ec);
4816 ec.Emit (OpCodes.Conv_I);
4818 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4822 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4823 //pe.InstanceExpression = pinned_string;
4824 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4826 ec.Emit (OpCodes.Add);
4830 public override void EmitExit (EmitContext ec)
4833 pinned_string.EmitAssign (ec);
4837 public class VariableDeclaration : BlockVariableDeclaration
4839 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4844 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4846 if (!Variable.Type.IsPointer && li == Variable) {
4847 bc.Report.Error (209, TypeExpression.Location,
4848 "The type of locals declared in a fixed statement must be a pointer type");
4853 // The rules for the possible declarators are pretty wise,
4854 // but the production on the grammar is more concise.
4856 // So we have to enforce these rules here.
4858 // We do not resolve before doing the case 1 test,
4859 // because the grammar is explicit in that the token &
4860 // is present, so we need to test for this particular case.
4863 if (initializer is Cast) {
4864 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4868 initializer = initializer.Resolve (bc);
4870 if (initializer == null)
4876 if (initializer.Type.IsArray) {
4877 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4880 // Provided that array_type is unmanaged,
4882 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4886 // and T* is implicitly convertible to the
4887 // pointer type given in the fixed statement.
4889 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4891 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4892 if (converted == null)
4896 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4898 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4899 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4900 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4901 new NullLiteral (loc),
4904 converted = converted.Resolve (bc);
4906 return new ExpressionEmitter (converted, li);
4912 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4913 return new StringEmitter (initializer, li, loc).Resolve (bc);
4916 // Case 3: fixed buffer
4917 if (initializer is FixedBufferPtr) {
4918 return new ExpressionEmitter (initializer, li);
4922 // Case 4: & object.
4924 bool already_fixed = true;
4925 Unary u = initializer as Unary;
4926 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4927 IVariableReference vr = u.Expr as IVariableReference;
4928 if (vr == null || !vr.IsFixed) {
4929 already_fixed = false;
4933 if (already_fixed) {
4934 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4937 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4938 return new ExpressionEmitter (initializer, li);
4943 VariableDeclaration decl;
4944 Statement statement;
4947 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4956 public Statement Statement {
4962 public BlockVariableDeclaration Variables {
4970 public override bool Resolve (BlockContext ec)
4972 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4973 if (!decl.Resolve (ec))
4977 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4978 bool ok = statement.Resolve (ec);
4979 bool flow_unreachable = ec.EndFlowBranching ();
4980 has_ret = flow_unreachable;
4985 protected override void DoEmit (EmitContext ec)
4987 decl.Variable.CreateBuilder (ec);
4988 decl.Initializer.Emit (ec);
4989 if (decl.Declarators != null) {
4990 foreach (var d in decl.Declarators) {
4991 d.Variable.CreateBuilder (ec);
4992 d.Initializer.Emit (ec);
4996 statement.Emit (ec);
5002 // Clear the pinned variable
5004 ((Emitter) decl.Initializer).EmitExit (ec);
5005 if (decl.Declarators != null) {
5006 foreach (var d in decl.Declarators) {
5007 ((Emitter)d.Initializer).EmitExit (ec);
5012 protected override void CloneTo (CloneContext clonectx, Statement t)
5014 Fixed target = (Fixed) t;
5016 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5017 target.statement = statement.Clone (clonectx);
5020 public override object Accept (StructuralVisitor visitor)
5022 return visitor.Visit (this);
5026 public class Catch : Statement
5030 FullNamedExpression type_expr;
5031 CompilerAssign assign;
5034 public Catch (Block block, Location loc)
5042 public Block Block {
5048 public TypeSpec CatchType {
5054 public bool IsGeneral {
5056 return type_expr == null;
5060 public FullNamedExpression TypeExpression {
5069 public LocalVariable Variable {
5080 protected override void DoEmit (EmitContext ec)
5083 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5085 ec.BeginCatchBlock (CatchType);
5088 li.CreateBuilder (ec);
5091 // Special case hoisted catch variable, we have to use a temporary variable
5092 // to pass via anonymous storey initialization with the value still on top
5095 if (li.HoistedVariant != null) {
5096 LocalTemporary lt = new LocalTemporary (li.Type);
5097 SymbolWriter.OpenCompilerGeneratedBlock (ec);
5099 SymbolWriter.CloseCompilerGeneratedBlock (ec);
5101 // switch to assigning from the temporary variable and not from top of the stack
5102 assign.UpdateSource (lt);
5105 SymbolWriter.OpenCompilerGeneratedBlock (ec);
5106 ec.Emit (OpCodes.Pop);
5107 SymbolWriter.CloseCompilerGeneratedBlock (ec);
5113 public override bool Resolve (BlockContext ec)
5115 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5116 if (type_expr != null) {
5117 type = type_expr.ResolveAsType (ec);
5121 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5122 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5123 } else if (li != null) {
5125 li.PrepareForFlowAnalysis (ec);
5127 // source variable is at the top of the stack
5128 Expression source = new EmptyExpression (li.Type);
5129 if (li.Type.IsGenericParameter)
5130 source = new UnboxCast (source, li.Type);
5132 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
5133 Block.AddScopeStatement (new StatementExpression (assign));
5137 return Block.Resolve (ec);
5141 protected override void CloneTo (CloneContext clonectx, Statement t)
5143 Catch target = (Catch) t;
5145 if (type_expr != null)
5146 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5148 target.block = clonectx.LookupBlock (block);
5152 public class TryFinally : TryFinallyBlock
5156 public TryFinally (Statement stmt, Block fini, Location loc)
5162 public Block Finallyblock {
5168 public override bool Resolve (BlockContext ec)
5172 ec.StartFlowBranching (this);
5174 if (!stmt.Resolve (ec))
5178 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5179 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5180 if (!fini.Resolve (ec))
5184 ec.EndFlowBranching ();
5186 ok &= base.Resolve (ec);
5191 protected override void EmitTryBody (EmitContext ec)
5196 protected override void EmitFinallyBody (EmitContext ec)
5201 protected override void CloneTo (CloneContext clonectx, Statement t)
5203 TryFinally target = (TryFinally) t;
5205 target.stmt = (Statement) stmt.Clone (clonectx);
5207 target.fini = clonectx.LookupBlock (fini);
5210 public override object Accept (StructuralVisitor visitor)
5212 return visitor.Visit (this);
5216 public class TryCatch : ExceptionStatement
5219 List<Catch> clauses;
5220 readonly bool inside_try_finally;
5222 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5226 this.clauses = catch_clauses;
5227 this.inside_try_finally = inside_try_finally;
5230 public List<Catch> Clauses {
5236 public bool IsTryCatchFinally {
5238 return inside_try_finally;
5242 public override bool Resolve (BlockContext ec)
5246 ec.StartFlowBranching (this);
5248 if (!Block.Resolve (ec))
5251 for (int i = 0; i < clauses.Count; ++i) {
5253 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5255 if (!c.Resolve (ec)) {
5260 TypeSpec resolved_type = c.CatchType;
5261 for (int ii = 0; ii < clauses.Count; ++ii) {
5265 if (clauses[ii].IsGeneral) {
5266 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5269 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5272 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5275 ec.Report.Warning (1058, 1, c.loc,
5276 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5284 var ct = clauses[ii].CatchType;
5288 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5289 ec.Report.Error (160, c.loc,
5290 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5291 ct.GetSignatureForError ());
5297 ec.EndFlowBranching ();
5299 return base.Resolve (ec) && ok;
5302 protected sealed override void DoEmit (EmitContext ec)
5304 if (!inside_try_finally)
5305 EmitTryBodyPrepare (ec);
5309 foreach (Catch c in clauses)
5312 if (!inside_try_finally)
5313 ec.EndExceptionBlock ();
5316 protected override void CloneTo (CloneContext clonectx, Statement t)
5318 TryCatch target = (TryCatch) t;
5320 target.Block = clonectx.LookupBlock (Block);
5321 if (clauses != null){
5322 target.clauses = new List<Catch> ();
5323 foreach (Catch c in clauses)
5324 target.clauses.Add ((Catch) c.Clone (clonectx));
5328 public override object Accept (StructuralVisitor visitor)
5330 return visitor.Visit (this);
5334 public class Using : TryFinallyBlock
5336 public class VariableDeclaration : BlockVariableDeclaration
5338 Statement dispose_call;
5340 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5345 public VariableDeclaration (LocalVariable li, Location loc)
5351 public VariableDeclaration (Expression expr)
5354 loc = expr.Location;
5360 public bool IsNested { get; private set; }
5364 public void EmitDispose (EmitContext ec)
5366 dispose_call.Emit (ec);
5369 public override bool Resolve (BlockContext bc)
5374 return base.Resolve (bc, false);
5377 public Expression ResolveExpression (BlockContext bc)
5379 var e = Initializer.Resolve (bc);
5383 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5384 Initializer = ResolveInitializer (bc, Variable, e);
5388 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5390 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5391 initializer = initializer.Resolve (bc);
5392 if (initializer == null)
5395 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5396 Arguments args = new Arguments (1);
5397 args.Add (new Argument (initializer));
5398 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5399 if (initializer == null)
5402 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5403 dispose_call = CreateDisposeCall (bc, var);
5404 dispose_call.Resolve (bc);
5406 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5409 if (li == Variable) {
5410 CheckIDiposableConversion (bc, li, initializer);
5411 dispose_call = CreateDisposeCall (bc, li);
5412 dispose_call.Resolve (bc);
5415 return base.ResolveInitializer (bc, li, initializer);
5418 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5422 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5423 if (type.IsNullableType) {
5424 // it's handled in CreateDisposeCall
5428 bc.Report.SymbolRelatedToPreviousError (type);
5429 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5430 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5431 type.GetSignatureForError ());
5437 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5439 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5441 var loc = lv.Location;
5443 var idt = bc.BuiltinTypes.IDisposable;
5444 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5446 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5447 dispose_mg.InstanceExpression = type.IsNullableType ?
5448 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5451 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5453 // Add conditional call when disposing possible null variable
5454 if (!type.IsStruct || type.IsNullableType)
5455 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5460 public void ResolveDeclaratorInitializer (BlockContext bc)
5462 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5465 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5467 for (int i = declarators.Count - 1; i >= 0; --i) {
5468 var d = declarators [i];
5469 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5470 vd.Initializer = d.Initializer;
5472 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5473 vd.dispose_call.Resolve (bc);
5475 stmt = new Using (vd, stmt, d.Variable.Location);
5482 public override object Accept (StructuralVisitor visitor)
5484 return visitor.Visit (this);
5488 VariableDeclaration decl;
5490 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5496 public Using (Expression expr, Statement stmt, Location loc)
5499 this.decl = new VariableDeclaration (expr);
5504 public Expression Expr {
5506 return decl.Variable == null ? decl.Initializer : null;
5510 public BlockVariableDeclaration Variables {
5518 protected override void EmitTryBodyPrepare (EmitContext ec)
5521 base.EmitTryBodyPrepare (ec);
5524 protected override void EmitTryBody (EmitContext ec)
5529 protected override void EmitFinallyBody (EmitContext ec)
5531 decl.EmitDispose (ec);
5534 public override bool Resolve (BlockContext ec)
5536 VariableReference vr;
5537 bool vr_locked = false;
5539 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5540 if (decl.Variable == null) {
5541 vr = decl.ResolveExpression (ec) as VariableReference;
5543 vr_locked = vr.IsLockedByStatement;
5544 vr.IsLockedByStatement = true;
5547 if (decl.IsNested) {
5548 decl.ResolveDeclaratorInitializer (ec);
5550 if (!decl.Resolve (ec))
5553 if (decl.Declarators != null) {
5554 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5562 ec.StartFlowBranching (this);
5566 ec.EndFlowBranching ();
5569 vr.IsLockedByStatement = vr_locked;
5576 protected override void CloneTo (CloneContext clonectx, Statement t)
5578 Using target = (Using) t;
5580 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5581 target.stmt = stmt.Clone (clonectx);
5584 public override object Accept (StructuralVisitor visitor)
5586 return visitor.Visit (this);
5591 /// Implementation of the foreach C# statement
5593 public class Foreach : Statement
5595 sealed class ArrayForeach : Statement
5597 readonly Foreach for_each;
5598 readonly Statement statement;
5601 TemporaryVariableReference[] lengths;
5602 Expression [] length_exprs;
5603 StatementExpression[] counter;
5604 TemporaryVariableReference[] variables;
5606 TemporaryVariableReference copy;
5608 LocalVariableReference variable;
5610 public ArrayForeach (Foreach @foreach, int rank)
5612 for_each = @foreach;
5613 statement = for_each.statement;
5616 counter = new StatementExpression[rank];
5617 variables = new TemporaryVariableReference[rank];
5618 length_exprs = new Expression [rank];
5621 // Only use temporary length variables when dealing with
5622 // multi-dimensional arrays
5625 lengths = new TemporaryVariableReference [rank];
5628 protected override void CloneTo (CloneContext clonectx, Statement target)
5630 throw new NotImplementedException ();
5633 public override bool Resolve (BlockContext ec)
5635 Block variables_block = for_each.variable.Block;
5636 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5639 int rank = length_exprs.Length;
5640 Arguments list = new Arguments (rank);
5641 for (int i = 0; i < rank; i++) {
5642 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5644 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5645 counter[i].Resolve (ec);
5648 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5650 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5651 lengths[i].Resolve (ec);
5653 Arguments args = new Arguments (1);
5654 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5655 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5658 list.Add (new Argument (v));
5661 access = new ElementAccess (copy, list, loc).Resolve (ec);
5666 if (for_each.type is VarExpr) {
5667 // Infer implicitly typed local variable from foreach array type
5668 var_type = access.Type;
5670 var_type = for_each.type.ResolveAsType (ec);
5673 if (var_type == null)
5676 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5682 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5683 ec.CurrentBranching.CreateSibling ();
5685 for_each.variable.Type = conv.Type;
5686 variable = new LocalVariableReference (for_each.variable, loc);
5687 variable.Resolve (ec);
5689 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5690 if (!statement.Resolve (ec))
5692 ec.EndFlowBranching ();
5694 // There's no direct control flow from the end of the embedded statement to the end of the loop
5695 ec.CurrentBranching.CurrentUsageVector.Goto ();
5697 ec.EndFlowBranching ();
5702 protected override void DoEmit (EmitContext ec)
5704 copy.EmitAssign (ec, for_each.expr);
5706 int rank = length_exprs.Length;
5707 Label[] test = new Label [rank];
5708 Label[] loop = new Label [rank];
5710 for (int i = 0; i < rank; i++) {
5711 test [i] = ec.DefineLabel ();
5712 loop [i] = ec.DefineLabel ();
5714 if (lengths != null)
5715 lengths [i].EmitAssign (ec, length_exprs [i]);
5718 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5719 for (int i = 0; i < rank; i++) {
5720 variables [i].EmitAssign (ec, zero);
5722 ec.Emit (OpCodes.Br, test [i]);
5723 ec.MarkLabel (loop [i]);
5726 variable.local_info.CreateBuilder (ec);
5727 variable.EmitAssign (ec, conv, false, false);
5729 statement.Emit (ec);
5731 ec.MarkLabel (ec.LoopBegin);
5733 for (int i = rank - 1; i >= 0; i--){
5734 counter [i].Emit (ec);
5736 ec.MarkLabel (test [i]);
5737 variables [i].Emit (ec);
5739 if (lengths != null)
5740 lengths [i].Emit (ec);
5742 length_exprs [i].Emit (ec);
5744 ec.Emit (OpCodes.Blt, loop [i]);
5747 ec.MarkLabel (ec.LoopEnd);
5751 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5753 class Body : Statement
5756 LocalVariableReference variable;
5757 Expression current, conv;
5758 Statement statement;
5760 public Body (TypeSpec type, LocalVariable variable,
5761 Expression current, Statement statement,
5765 this.variable = new LocalVariableReference (variable, loc);
5766 this.current = current;
5767 this.statement = statement;
5771 protected override void CloneTo (CloneContext clonectx, Statement target)
5773 throw new NotImplementedException ();
5776 public override bool Resolve (BlockContext ec)
5778 current = current.Resolve (ec);
5779 if (current == null)
5782 conv = Convert.ExplicitConversion (ec, current, type, loc);
5786 variable.local_info.Type = conv.Type;
5787 variable.Resolve (ec);
5789 if (!statement.Resolve (ec))
5795 protected override void DoEmit (EmitContext ec)
5797 variable.local_info.CreateBuilder (ec);
5798 variable.EmitAssign (ec, conv, false, false);
5800 statement.Emit (ec);
5804 class RuntimeDispose : Using.VariableDeclaration
5806 public RuntimeDispose (LocalVariable lv, Location loc)
5811 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5813 // Defered to runtime check
5816 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5818 var idt = bc.BuiltinTypes.IDisposable;
5821 // Fabricates code like
5823 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5826 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5828 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5829 dispose_variable.CreateReferenceExpression (bc, loc),
5830 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5831 loc), new NullLiteral (loc), loc);
5833 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5835 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5836 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5838 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5839 return new If (idisaposable_test, dispose, loc);
5843 LocalVariable variable;
5845 Statement statement;
5846 Expression var_type;
5847 ExpressionStatement init;
5848 TemporaryVariableReference enumerator_variable;
5849 bool ambiguous_getenumerator_name;
5851 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5853 this.var_type = var_type;
5854 this.variable = var;
5860 protected override void CloneTo (CloneContext clonectx, Statement target)
5862 throw new NotImplementedException ();
5865 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5867 rc.Report.SymbolRelatedToPreviousError (enumerator);
5868 rc.Report.Error (202, loc,
5869 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5870 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5873 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5876 // Option 1: Try to match by name GetEnumerator first
5878 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5879 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5881 var mg = mexpr as MethodGroupExpr;
5883 mg.InstanceExpression = expr;
5884 Arguments args = new Arguments (0);
5885 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5887 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5888 if (ambiguous_getenumerator_name)
5891 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5897 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5900 PredefinedMember<MethodSpec> iface_candidate = null;
5901 var ptypes = rc.Module.PredefinedTypes;
5902 var gen_ienumerable = ptypes.IEnumerableGeneric;
5903 if (!gen_ienumerable.Define ())
5904 gen_ienumerable = null;
5907 var ifaces = t.Interfaces;
5908 if (ifaces != null) {
5909 foreach (var iface in ifaces) {
5910 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5911 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5912 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5913 rc.Report.Error (1640, loc,
5914 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5915 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5920 // TODO: Cache this somehow
5921 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5922 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5927 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5928 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5933 if (t.IsGenericParameter)
5938 } while (t != null);
5940 if (iface_candidate == null) {
5941 if (expr.Type != InternalType.ErrorType) {
5942 rc.Report.Error (1579, loc,
5943 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5944 expr.Type.GetSignatureForError (), "GetEnumerator");
5950 var method = iface_candidate.Resolve (loc);
5954 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5955 mg.InstanceExpression = expr;
5959 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5961 var ms = MemberCache.FindMember (enumerator.ReturnType,
5962 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5963 BindingRestriction.InstanceOnly) as MethodSpec;
5965 if (ms == null || !ms.IsPublic) {
5966 Error_WrongEnumerator (rc, enumerator);
5970 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5973 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5975 var ps = MemberCache.FindMember (enumerator.ReturnType,
5976 MemberFilter.Property ("Current", null),
5977 BindingRestriction.InstanceOnly) as PropertySpec;
5979 if (ps == null || !ps.IsPublic) {
5980 Error_WrongEnumerator (rc, enumerator);
5987 public override bool Resolve (BlockContext ec)
5989 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5992 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5993 } else if (expr.Type.IsNullableType) {
5994 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
5997 var get_enumerator_mg = ResolveGetEnumerator (ec);
5998 if (get_enumerator_mg == null) {
6002 var get_enumerator = get_enumerator_mg.BestCandidate;
6003 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6004 enumerator_variable.Resolve (ec);
6006 // Prepare bool MoveNext ()
6007 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6008 if (move_next_mg == null) {
6012 move_next_mg.InstanceExpression = enumerator_variable;
6014 // Prepare ~T~ Current { get; }
6015 var current_prop = ResolveCurrent (ec, get_enumerator);
6016 if (current_prop == null) {
6020 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6021 if (current_pe == null)
6024 VarExpr ve = var_type as VarExpr;
6028 // Source type is dynamic, set element type to dynamic too
6029 variable.Type = ec.BuiltinTypes.Dynamic;
6031 // Infer implicitly typed local variable from foreach enumerable type
6032 variable.Type = current_pe.Type;
6036 // Explicit cast of dynamic collection elements has to be done at runtime
6037 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6040 variable.Type = var_type.ResolveAsType (ec);
6043 if (variable.Type == null)
6046 var init = new Invocation (get_enumerator_mg, null);
6048 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6049 new Body (variable.Type, variable, current_pe, statement, loc), loc);
6051 var enum_type = enumerator_variable.Type;
6054 // Add Dispose method call when enumerator can be IDisposable
6056 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6057 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6059 // Runtime Dispose check
6061 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
6062 vd.Initializer = init;
6063 statement = new Using (vd, statement, loc);
6066 // No Dispose call needed
6068 this.init = new SimpleAssign (enumerator_variable, init);
6069 this.init.Resolve (ec);
6073 // Static Dispose check
6075 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
6076 vd.Initializer = init;
6077 statement = new Using (vd, statement, loc);
6080 return statement.Resolve (ec);
6083 protected override void DoEmit (EmitContext ec)
6085 enumerator_variable.LocalInfo.CreateBuilder (ec);
6088 init.EmitStatement (ec);
6090 statement.Emit (ec);
6093 #region IErrorHandler Members
6095 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6097 ec.Report.SymbolRelatedToPreviousError (best);
6098 ec.Report.Warning (278, 2, loc,
6099 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6100 expr.Type.GetSignatureForError (), "enumerable",
6101 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6103 ambiguous_getenumerator_name = true;
6107 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6112 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6117 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6126 LocalVariable variable;
6128 Statement statement;
6130 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
6133 this.variable = var;
6139 public Expression Expr {
6140 get { return expr; }
6143 public Statement Statement {
6144 get { return statement; }
6147 public Expression TypeExpression {
6148 get { return type; }
6151 public LocalVariable Variable {
6152 get { return variable; }
6156 public override bool Resolve (BlockContext ec)
6158 expr = expr.Resolve (ec);
6163 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6167 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6168 statement = new ArrayForeach (this, 1);
6169 } else if (expr.Type is ArrayContainer) {
6170 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6172 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6173 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6174 expr.ExprClassName);
6178 statement = new CollectionForeach (type, variable, expr, statement, loc);
6181 return statement.Resolve (ec);
6184 protected override void DoEmit (EmitContext ec)
6186 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6187 ec.LoopBegin = ec.DefineLabel ();
6188 ec.LoopEnd = ec.DefineLabel ();
6190 statement.Emit (ec);
6192 ec.LoopBegin = old_begin;
6193 ec.LoopEnd = old_end;
6196 protected override void CloneTo (CloneContext clonectx, Statement t)
6198 Foreach target = (Foreach) t;
6200 target.type = type.Clone (clonectx);
6201 target.expr = expr.Clone (clonectx);
6202 target.statement = statement.Clone (clonectx);
6205 public override object Accept (StructuralVisitor visitor)
6207 return visitor.Visit (this);