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.TypeInfo.IsFullyInitialized (ec, VariableInfo, 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 virtual void AddLocalName (string name, INamedBlockVariable li)
2104 ParametersBlock.TopBlock.AddLocalName (name, li);
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 override void AddLocalName (string name, INamedBlockVariable li)
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 // Collision with with children
3052 while ((b = b.Parent) != null) {
3053 if (li.Block == b) {
3054 li.Block.Error_AlreadyDeclared (name, li, "child");
3055 i = existing_list.Count;
3061 existing_list.Add (li);
3064 public void AddLabel (string name, LabeledStatement label)
3067 labels = new Dictionary<string, object> ();
3070 if (!labels.TryGetValue (name, out value)) {
3071 labels.Add (name, label);
3075 LabeledStatement existing = value as LabeledStatement;
3076 List<LabeledStatement> existing_list;
3077 if (existing != null) {
3078 existing_list = new List<LabeledStatement> ();
3079 existing_list.Add (existing);
3080 labels[name] = existing_list;
3082 existing_list = (List<LabeledStatement>) value;
3086 // A collision checking between labels
3088 for (int i = 0; i < existing_list.Count; ++i) {
3089 existing = existing_list[i];
3090 Block b = existing.Block;
3092 // Collision at same level
3093 if (label.Block == b) {
3094 Report.SymbolRelatedToPreviousError (existing.loc, name);
3095 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3099 // Collision with parent
3101 while ((b = b.Parent) != null) {
3102 if (existing.Block == b) {
3103 Report.Error (158, label.loc,
3104 "The label `{0}' shadows another label by the same name in a contained scope", name);
3105 i = existing_list.Count;
3110 // Collision with with children
3112 while ((b = b.Parent) != null) {
3113 if (label.Block == b) {
3114 Report.Error (158, label.loc,
3115 "The label `{0}' shadows another label by the same name in a contained scope", name);
3116 i = existing_list.Count;
3122 existing_list.Add (label);
3126 // Creates an arguments set from all parameters, useful for method proxy calls
3128 public Arguments GetAllParametersArguments ()
3130 int count = parameters.Count;
3131 Arguments args = new Arguments (count);
3132 for (int i = 0; i < count; ++i) {
3133 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3134 args.Add (new Argument (arg_expr));
3141 // Lookup inside a block, the returned value can represent 3 states
3143 // true+variable: A local name was found and it's valid
3144 // false+variable: A local name was found in a child block only
3145 // false+null: No local name was found
3147 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3153 if (!names.TryGetValue (name, out value))
3156 variable = value as INamedBlockVariable;
3158 if (variable != null) {
3160 if (variable.Block == b.Original)
3164 } while (b != null);
3172 } while (b != null);
3174 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3175 for (int i = 0; i < list.Count; ++i) {
3178 if (variable.Block == b.Original)
3182 } while (b != null);
3190 } while (b != null);
3200 public LabeledStatement GetLabel (string name, Block block)
3206 if (!labels.TryGetValue (name, out value)) {
3210 var label = value as LabeledStatement;
3212 if (label != null) {
3213 if (label.Block == b.Original)
3216 // TODO: Temporary workaround for the switch block implicit label block
3217 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3220 List<LabeledStatement> list = (List<LabeledStatement>) value;
3221 for (int i = 0; i < list.Count; ++i) {
3223 if (label.Block == b.Original)
3226 // TODO: Temporary workaround for the switch block implicit label block
3227 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3236 // Returns the "this" instance variable of this block.
3237 // See AddThisVariable() for more information.
3239 public LocalVariable ThisVariable {
3240 get { return this_variable; }
3244 // This is used by non-static `struct' constructors which do not have an
3245 // initializer - in this case, the constructor must initialize all of the
3246 // struct's fields. To do this, we add a "this" variable and use the flow
3247 // analysis code to ensure that it's been fully initialized before control
3248 // leaves the constructor.
3250 public void AddThisVariable (BlockContext bc)
3252 if (this_variable != null)
3253 throw new InternalErrorException (StartLocation.ToString ());
3255 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3256 this_variable.Type = bc.CurrentType;
3257 this_variable.PrepareForFlowAnalysis (bc);
3260 public bool IsThisAssigned (BlockContext ec)
3262 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3265 public override void Emit (EmitContext ec)
3267 if (Report.Errors > 0)
3276 ec.Mark (EndLocation);
3278 if (ec.HasReturnLabel)
3279 ec.MarkLabel (ec.ReturnLabel);
3281 if (ec.return_value != null) {
3282 ec.Emit (OpCodes.Ldloc, ec.return_value);
3283 ec.Emit (OpCodes.Ret);
3286 // If `HasReturnLabel' is set, then we already emitted a
3287 // jump to the end of the method, so we must emit a `ret'
3290 // Unfortunately, System.Reflection.Emit automatically emits
3291 // a leave to the end of a finally block. This is a problem
3292 // if no code is following the try/finally block since we may
3293 // jump to a point after the end of the method.
3294 // As a workaround, we're always creating a return label in
3298 if (ec.HasReturnLabel || !unreachable) {
3299 if (ec.ReturnType.Kind != MemberKind.Void)
3300 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3301 ec.Emit (OpCodes.Ret);
3306 } catch (Exception e){
3307 Console.WriteLine ("Exception caught by the compiler while emitting:");
3308 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3310 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3316 protected override void EmitSymbolInfo (EmitContext ec)
3318 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3319 if ((ae != null) && (ae.Storey != null))
3320 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3322 base.EmitSymbolInfo (ec);
3326 public class SwitchLabel {
3329 readonly Location loc;
3334 // if expr == null, then it is the default case.
3336 public SwitchLabel (Expression expr, Location l)
3342 public bool IsDefault {
3344 return label == null;
3348 public Expression Label {
3354 public Location Location {
3360 public Constant Converted {
3369 public Label GetILLabel (EmitContext ec)
3371 if (il_label == null){
3372 il_label = ec.DefineLabel ();
3375 return il_label.Value;
3379 // Resolves the expression, reduces it to a literal if possible
3380 // and then converts it to the requested type.
3382 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3384 Expression e = label.Resolve (ec);
3389 Constant c = e as Constant;
3391 ec.Report.Error (150, loc, "A constant value is expected");
3395 if (allow_nullable && c is NullLiteral) {
3400 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3401 return converted != null;
3404 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3407 if (converted == null)
3410 label = converted.GetValueAsLiteral ();
3412 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3413 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3416 public SwitchLabel Clone (CloneContext clonectx)
3421 return new SwitchLabel (label.Clone (clonectx), loc);
3425 public class SwitchSection {
3426 public readonly List<SwitchLabel> Labels;
3427 public readonly Block Block;
3429 public SwitchSection (List<SwitchLabel> labels, Block block)
3435 public SwitchSection Clone (CloneContext clonectx)
3437 var cloned_labels = new List<SwitchLabel> ();
3439 foreach (SwitchLabel sl in Labels)
3440 cloned_labels.Add (sl.Clone (clonectx));
3442 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3446 public class Switch : Statement
3448 // structure used to hold blocks of keys while calculating table switch
3449 sealed class LabelsRange : IComparable<LabelsRange>
3451 public readonly long min;
3453 public readonly List<long> label_values;
3455 public LabelsRange (long value)
3458 label_values = new List<long> ();
3459 label_values.Add (value);
3462 public LabelsRange (long min, long max, ICollection<long> values)
3466 this.label_values = new List<long> (values);
3471 return max - min + 1;
3475 public bool AddValue (long value)
3477 var gap = value - min + 1;
3478 // Ensure the range has > 50% occupancy
3479 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3483 label_values.Add (value);
3487 public int CompareTo (LabelsRange other)
3489 int nLength = label_values.Count;
3490 int nLengthOther = other.label_values.Count;
3491 if (nLengthOther == nLength)
3492 return (int) (other.min - min);
3494 return nLength - nLengthOther;
3498 sealed class LabelMarker : Statement
3501 readonly List<SwitchLabel> labels;
3503 public LabelMarker (Switch s, List<SwitchLabel> labels)
3506 this.labels = labels;
3509 protected override void CloneTo (CloneContext clonectx, Statement target)
3513 protected override void DoEmit (EmitContext ec)
3515 foreach (var l in labels) {
3517 ec.MarkLabel (s.DefaultLabel);
3519 ec.MarkLabel (l.GetILLabel (ec));
3524 public List<SwitchSection> Sections;
3525 public Expression Expr;
3528 // Mapping of all labels to their SwitchLabels
3530 Dictionary<long, SwitchLabel> labels;
3531 Dictionary<string, SwitchLabel> string_labels;
3534 /// The governing switch type
3536 public TypeSpec SwitchType;
3541 Label default_target;
3543 Expression new_expr;
3546 SwitchSection constant_section;
3547 SwitchSection default_section;
3548 SwitchLabel null_section;
3550 Statement simple_stmt;
3551 VariableReference value;
3552 ExpressionStatement string_dictionary;
3553 FieldExpr switch_cache_field;
3554 static int unique_counter;
3555 ExplicitBlock block;
3558 // Nullable Types support
3560 Nullable.Unwrap unwrap;
3562 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3570 public ExplicitBlock Block {
3576 public Label DefaultLabel {
3578 return default_target;
3582 public bool GotDefault {
3584 return default_section != null;
3588 public bool IsNullable {
3590 return unwrap != null;
3595 // Determines the governing type for a switch. The returned
3596 // expression might be the expression from the switch, or an
3597 // expression that includes any potential conversions to
3599 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3601 switch (expr.Type.BuiltinType) {
3602 case BuiltinTypeSpec.Type.Byte:
3603 case BuiltinTypeSpec.Type.SByte:
3604 case BuiltinTypeSpec.Type.UShort:
3605 case BuiltinTypeSpec.Type.Short:
3606 case BuiltinTypeSpec.Type.UInt:
3607 case BuiltinTypeSpec.Type.Int:
3608 case BuiltinTypeSpec.Type.ULong:
3609 case BuiltinTypeSpec.Type.Long:
3610 case BuiltinTypeSpec.Type.Char:
3611 case BuiltinTypeSpec.Type.String:
3612 case BuiltinTypeSpec.Type.Bool:
3616 if (expr.Type.IsEnum)
3620 // Try to find a *user* defined implicit conversion.
3622 // If there is no implicit conversion, or if there are multiple
3623 // conversions, we have to report an error
3625 Expression converted = null;
3626 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3629 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3634 // Ignore over-worked ImplicitUserConversions that do
3635 // an implicit conversion in addition to the user conversion.
3637 if (!(e is UserCast))
3640 if (converted != null){
3641 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3650 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3652 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3668 // Performs the basic sanity checks on the switch statement
3669 // (looks for duplicate keys and non-constant expressions).
3671 // It also returns a hashtable with the keys that we will later
3672 // use to compute the switch tables
3674 bool CheckSwitch (ResolveContext ec)
3677 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3678 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3680 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3682 foreach (SwitchSection ss in Sections){
3683 foreach (SwitchLabel sl in ss.Labels){
3685 if (default_section != null){
3686 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3689 default_section = ss;
3693 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3699 if (string_labels != null) {
3700 string s = sl.Converted.GetValue () as string;
3704 string_labels.Add (s, sl);
3706 if (sl.Converted is NullLiteral) {
3709 labels.Add (sl.Converted.GetValueAsLong (), sl);
3712 } catch (ArgumentException) {
3713 if (string_labels != null)
3714 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3716 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3726 // This method emits code for a lookup-based switch statement (non-string)
3727 // Basically it groups the cases into blocks that are at least half full,
3728 // and then spits out individual lookup opcodes for each block.
3729 // It emits the longest blocks first, and short blocks are just
3730 // handled with direct compares.
3732 void EmitTableSwitch (EmitContext ec, Expression val)
3734 Label lbl_default = default_target;
3736 if (labels != null && labels.Count > 0) {
3737 List<LabelsRange> ranges;
3738 if (string_labels != null) {
3739 // We have done all hard work for string already
3740 // setup single range only
3741 ranges = new List<LabelsRange> (1);
3742 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3744 var element_keys = new long[labels.Count];
3745 labels.Keys.CopyTo (element_keys, 0);
3746 Array.Sort (element_keys);
3749 // Build possible ranges of switch labes to reduce number
3752 ranges = new List<LabelsRange> (element_keys.Length);
3753 var range = new LabelsRange (element_keys[0]);
3755 for (int i = 1; i < element_keys.Length; ++i) {
3756 var l = element_keys[i];
3757 if (range.AddValue (l))
3760 range = new LabelsRange (l);
3764 // sort the blocks so we can tackle the largest ones first
3768 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3770 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3771 LabelsRange kb = ranges[range_index];
3772 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3774 // Optimize small ranges using simple equality check
3775 if (kb.Range <= 2) {
3776 foreach (var key in kb.label_values) {
3777 SwitchLabel sl = labels[key];
3778 if (sl.Converted.IsDefaultValue) {
3779 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3782 sl.Converted.Emit (ec);
3783 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3787 // TODO: if all the keys in the block are the same and there are
3788 // no gaps/defaults then just use a range-check.
3789 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3790 // TODO: optimize constant/I4 cases
3792 // check block range (could be > 2^31)
3794 ec.EmitLong (kb.min);
3795 ec.Emit (OpCodes.Blt, lbl_default);
3798 ec.EmitLong (kb.max);
3799 ec.Emit (OpCodes.Bgt, lbl_default);
3804 ec.EmitLong (kb.min);
3805 ec.Emit (OpCodes.Sub);
3808 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3812 int first = (int) kb.min;
3815 ec.Emit (OpCodes.Sub);
3816 } else if (first < 0) {
3817 ec.EmitInt (-first);
3818 ec.Emit (OpCodes.Add);
3822 // first, build the list of labels for the switch
3824 long cJumps = kb.Range;
3825 Label[] switch_labels = new Label[cJumps];
3826 for (int iJump = 0; iJump < cJumps; iJump++) {
3827 var key = kb.label_values[iKey];
3828 if (key == kb.min + iJump) {
3829 switch_labels[iJump] = labels[key].GetILLabel (ec);
3832 switch_labels[iJump] = lbl_default;
3836 // emit the switch opcode
3837 ec.Emit (OpCodes.Switch, switch_labels);
3840 // mark the default for this block
3841 if (range_index != 0)
3842 ec.MarkLabel (lbl_default);
3845 // the last default just goes to the end
3846 if (ranges.Count > 0)
3847 ec.Emit (OpCodes.Br, lbl_default);
3850 // now emit the code for the sections
3851 bool found_default = false;
3853 foreach (SwitchSection ss in Sections) {
3854 foreach (SwitchLabel sl in ss.Labels) {
3856 ec.MarkLabel (lbl_default);
3857 found_default = true;
3858 if (null_section == null)
3859 ec.MarkLabel (null_target);
3860 } else if (sl.Converted.IsNull) {
3861 ec.MarkLabel (null_target);
3864 ec.MarkLabel (sl.GetILLabel (ec));
3870 if (!found_default) {
3871 ec.MarkLabel (lbl_default);
3872 if (null_section == null) {
3873 ec.MarkLabel (null_target);
3878 SwitchLabel FindLabel (Constant value)
3880 SwitchLabel sl = null;
3882 if (string_labels != null) {
3883 string s = value.GetValue () as string;
3885 if (null_section != null)
3887 else if (default_section != null)
3888 sl = default_section.Labels[0];
3890 string_labels.TryGetValue (s, out sl);
3893 if (value is NullLiteral) {
3896 labels.TryGetValue (value.GetValueAsLong (), out sl);
3903 SwitchSection FindSection (SwitchLabel label)
3905 foreach (SwitchSection ss in Sections){
3906 foreach (SwitchLabel sl in ss.Labels){
3915 public static void Reset ()
3920 public override bool Resolve (BlockContext ec)
3922 Expr = Expr.Resolve (ec);
3926 new_expr = SwitchGoverningType (ec, Expr);
3928 if (new_expr == null && Expr.Type.IsNullableType) {
3929 unwrap = Nullable.Unwrap.Create (Expr, false);
3933 new_expr = SwitchGoverningType (ec, unwrap);
3936 if (new_expr == null){
3937 ec.Report.Error (151, loc,
3938 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3939 TypeManager.CSharpName (Expr.Type));
3944 SwitchType = new_expr.Type;
3946 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3947 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3951 if (!CheckSwitch (ec))
3954 Switch old_switch = ec.Switch;
3956 ec.Switch.SwitchType = SwitchType;
3958 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3960 var constant = new_expr as Constant;
3961 if (constant != null) {
3963 SwitchLabel label = FindLabel (constant);
3965 constant_section = FindSection (label);
3967 if (constant_section == null)
3968 constant_section = default_section;
3971 // Store switch expression for comparission purposes
3973 value = new_expr as VariableReference;
3975 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
3980 foreach (SwitchSection ss in Sections){
3982 ec.CurrentBranching.CreateSibling (
3983 null, FlowBranching.SiblingType.SwitchSection);
3987 if (is_constant && (ss != constant_section)) {
3988 // If we're a constant switch, we're only emitting
3989 // one single section - mark all the others as
3991 ec.CurrentBranching.CurrentUsageVector.Goto ();
3992 if (!ss.Block.ResolveUnreachable (ec, true)) {
3996 if (!ss.Block.Resolve (ec))
4001 if (default_section == null)
4002 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4004 ec.EndFlowBranching ();
4005 ec.Switch = old_switch;
4011 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4012 if (string_labels.Count < 7)
4013 ResolveSimpleSwitch (ec);
4015 ResolveStringSwitchMap (ec);
4016 } else if (labels.Count < 3 && !IsNullable) {
4017 ResolveSimpleSwitch (ec);
4024 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4026 var sl = FindLabel (value);
4029 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4036 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4038 void ResolveSimpleSwitch (BlockContext bc)
4040 simple_stmt = default_section != null ? default_section.Block : null;
4042 for (int i = Sections.Count - 1; i >= 0; --i) {
4043 var s = Sections[i];
4045 if (s == default_section) {
4046 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4050 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4052 Expression cond = null;
4053 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4054 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4057 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4063 simple_stmt = new If (cond, s.Block, simple_stmt, loc);
4066 // It's null for empty switch
4067 if (simple_stmt != null)
4068 simple_stmt.Resolve (bc);
4072 // Converts string switch into string hashtable
4074 void ResolveStringSwitchMap (ResolveContext ec)
4076 FullNamedExpression string_dictionary_type;
4077 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4078 string_dictionary_type = new TypeExpression (
4079 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4080 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4082 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4083 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4085 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4089 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4090 Field field = new Field (ctype, string_dictionary_type,
4091 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4092 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
4093 if (!field.Define ())
4095 ctype.AddField (field);
4097 var init = new List<Expression> ();
4099 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4100 string value = null;
4101 foreach (SwitchSection section in Sections) {
4102 bool contains_label = false;
4103 foreach (SwitchLabel sl in section.Labels) {
4104 if (sl.IsDefault || sl.Converted.IsNull)
4107 if (!contains_label) {
4108 labels.Add (counter, sl);
4109 contains_label = true;
4112 value = (string) sl.Converted.GetValue ();
4113 var init_args = new List<Expression> (2);
4114 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4116 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4117 init_args.Add (sl.Converted);
4119 init.Add (new CollectionElementInitializer (init_args, loc));
4123 // Don't add empty sections
4129 Arguments args = new Arguments (1);
4130 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4131 Expression initializer = new NewInitialize (string_dictionary_type, args,
4132 new CollectionOrObjectInitializers (init, loc), loc);
4134 switch_cache_field = new FieldExpr (field, loc);
4135 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4138 void DoEmitStringSwitch (EmitContext ec)
4140 Label l_initialized = ec.DefineLabel ();
4143 // Skip initialization when value is null
4145 value.EmitBranchable (ec, null_target, false);
4148 // Check if string dictionary is initialized and initialize
4150 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4151 string_dictionary.EmitStatement (ec);
4152 ec.MarkLabel (l_initialized);
4154 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4156 ResolveContext rc = new ResolveContext (ec.MemberContext);
4158 if (switch_cache_field.Type.IsGeneric) {
4159 Arguments get_value_args = new Arguments (2);
4160 get_value_args.Add (new Argument (value));
4161 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4162 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4163 if (get_item == null)
4167 // A value was not found, go to default case
4169 get_item.EmitBranchable (ec, default_target, false);
4171 Arguments get_value_args = new Arguments (1);
4172 get_value_args.Add (new Argument (value));
4174 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4175 if (get_item == null)
4178 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4179 get_item_object.EmitAssign (ec, get_item, true, false);
4180 ec.Emit (OpCodes.Brfalse, default_target);
4182 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4183 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4185 get_item_int.EmitStatement (ec);
4186 get_item_object.Release (ec);
4189 EmitTableSwitch (ec, string_switch_variable);
4190 string_switch_variable.Release (ec);
4193 protected override void DoEmit (EmitContext ec)
4196 // Needed to emit anonymous storey initialization
4197 // Otherwise it does not contain any statements for now
4201 default_target = ec.DefineLabel ();
4202 null_target = ec.DefineLabel ();
4205 unwrap.EmitCheck (ec);
4206 ec.Emit (OpCodes.Brfalse, null_target);
4207 value.EmitAssign (ec, new_expr, false, false);
4208 } else if (new_expr != value && !is_constant) {
4209 value.EmitAssign (ec, new_expr, false, false);
4213 // Setup the codegen context
4215 Label old_end = ec.LoopEnd;
4216 Switch old_switch = ec.Switch;
4218 ec.LoopEnd = ec.DefineLabel ();
4223 if (constant_section != null)
4224 constant_section.Block.Emit (ec);
4225 } else if (string_dictionary != null) {
4226 DoEmitStringSwitch (ec);
4227 } else if (simple_stmt != null) {
4228 simple_stmt.Emit (ec);
4230 EmitTableSwitch (ec, value);
4233 // Restore context state.
4234 ec.MarkLabel (ec.LoopEnd);
4237 // Restore the previous context
4239 ec.LoopEnd = old_end;
4240 ec.Switch = old_switch;
4243 protected override void CloneTo (CloneContext clonectx, Statement t)
4245 Switch target = (Switch) t;
4247 target.Expr = Expr.Clone (clonectx);
4248 target.Sections = new List<SwitchSection> ();
4249 foreach (SwitchSection ss in Sections){
4250 target.Sections.Add (ss.Clone (clonectx));
4254 public override object Accept (StructuralVisitor visitor)
4256 return visitor.Visit (this);
4260 // A place where execution can restart in an iterator
4261 public abstract class ResumableStatement : Statement
4264 protected Label resume_point;
4266 public Label PrepareForEmit (EmitContext ec)
4270 resume_point = ec.DefineLabel ();
4272 return resume_point;
4275 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4280 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4285 public abstract class TryFinallyBlock : ExceptionStatement
4287 protected Statement stmt;
4288 Label dispose_try_block;
4289 bool prepared_for_dispose, emitted_dispose;
4291 protected TryFinallyBlock (Statement stmt, Location loc)
4299 public Statement Statement {
4307 protected abstract void EmitTryBody (EmitContext ec);
4308 protected abstract void EmitFinallyBody (EmitContext ec);
4310 public override Label PrepareForDispose (EmitContext ec, Label end)
4312 if (!prepared_for_dispose) {
4313 prepared_for_dispose = true;
4314 dispose_try_block = ec.DefineLabel ();
4316 return dispose_try_block;
4319 protected sealed override void DoEmit (EmitContext ec)
4321 EmitTryBodyPrepare (ec);
4324 ec.BeginFinallyBlock ();
4326 Label start_finally = ec.DefineLabel ();
4327 if (resume_points != null) {
4328 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4330 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4331 ec.Emit (OpCodes.Brfalse_S, start_finally);
4332 ec.Emit (OpCodes.Endfinally);
4335 ec.MarkLabel (start_finally);
4336 EmitFinallyBody (ec);
4338 ec.EndExceptionBlock ();
4341 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4343 if (emitted_dispose)
4346 emitted_dispose = true;
4348 Label end_of_try = ec.DefineLabel ();
4350 // Ensure that the only way we can get into this code is through a dispatcher
4351 if (have_dispatcher)
4352 ec.Emit (OpCodes.Br, end);
4354 ec.BeginExceptionBlock ();
4356 ec.MarkLabel (dispose_try_block);
4358 Label[] labels = null;
4359 for (int i = 0; i < resume_points.Count; ++i) {
4360 ResumableStatement s = resume_points[i];
4361 Label ret = s.PrepareForDispose (ec, end_of_try);
4362 if (ret.Equals (end_of_try) && labels == null)
4364 if (labels == null) {
4365 labels = new Label[resume_points.Count];
4366 for (int j = 0; j < i; ++j)
4367 labels[j] = end_of_try;
4372 if (labels != null) {
4374 for (j = 1; j < labels.Length; ++j)
4375 if (!labels[0].Equals (labels[j]))
4377 bool emit_dispatcher = j < labels.Length;
4379 if (emit_dispatcher) {
4380 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4381 ec.Emit (OpCodes.Ldloc, pc);
4382 ec.EmitInt (first_resume_pc);
4383 ec.Emit (OpCodes.Sub);
4384 ec.Emit (OpCodes.Switch, labels);
4385 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4388 foreach (ResumableStatement s in resume_points)
4389 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4392 ec.MarkLabel (end_of_try);
4394 ec.BeginFinallyBlock ();
4396 EmitFinallyBody (ec);
4398 ec.EndExceptionBlock ();
4403 // Base class for blocks using exception handling
4405 public abstract class ExceptionStatement : ResumableStatement
4410 protected List<ResumableStatement> resume_points;
4411 protected int first_resume_pc;
4413 protected ExceptionStatement (Location loc)
4418 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4420 StateMachineInitializer state_machine = null;
4421 if (resume_points != null) {
4422 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4424 ec.EmitInt ((int) IteratorStorey.State.Running);
4425 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4428 ec.BeginExceptionBlock ();
4430 if (resume_points != null) {
4431 ec.MarkLabel (resume_point);
4433 // For normal control flow, we want to fall-through the Switch
4434 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4435 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4436 ec.EmitInt (first_resume_pc);
4437 ec.Emit (OpCodes.Sub);
4439 Label[] labels = new Label[resume_points.Count];
4440 for (int i = 0; i < resume_points.Count; ++i)
4441 labels[i] = resume_points[i].PrepareForEmit (ec);
4442 ec.Emit (OpCodes.Switch, labels);
4446 public void SomeCodeFollows ()
4449 code_follows = true;
4453 public override bool Resolve (BlockContext ec)
4456 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4457 // So, ensure there's some IL code after this statement.
4458 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4459 ec.NeedReturnLabel ();
4464 public void AddResumePoint (ResumableStatement stmt, int pc)
4466 if (resume_points == null) {
4467 resume_points = new List<ResumableStatement> ();
4468 first_resume_pc = pc;
4471 if (pc != first_resume_pc + resume_points.Count)
4472 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4474 resume_points.Add (stmt);
4479 public class Lock : TryFinallyBlock
4482 TemporaryVariableReference expr_copy;
4483 TemporaryVariableReference lock_taken;
4485 public Lock (Expression expr, Statement stmt, Location loc)
4491 public Expression Expr {
4497 public override bool Resolve (BlockContext ec)
4499 expr = expr.Resolve (ec);
4503 if (!TypeSpec.IsReferenceType (expr.Type)) {
4504 ec.Report.Error (185, loc,
4505 "`{0}' is not a reference type as required by the lock statement",
4506 expr.Type.GetSignatureForError ());
4509 if (expr.Type.IsGenericParameter) {
4510 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4513 VariableReference lv = expr as VariableReference;
4516 locked = lv.IsLockedByStatement;
4517 lv.IsLockedByStatement = true;
4523 using (ec.Set (ResolveContext.Options.LockScope)) {
4524 ec.StartFlowBranching (this);
4525 Statement.Resolve (ec);
4526 ec.EndFlowBranching ();
4530 lv.IsLockedByStatement = locked;
4536 // Have to keep original lock value around to unlock same location
4537 // in the case the original has changed or is null
4539 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4540 expr_copy.Resolve (ec);
4543 // Ensure Monitor methods are available
4545 if (ResolvePredefinedMethods (ec) > 1) {
4546 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4547 lock_taken.Resolve (ec);
4553 protected override void EmitTryBodyPrepare (EmitContext ec)
4555 expr_copy.EmitAssign (ec, expr);
4557 if (lock_taken != null) {
4559 // Initialize ref variable
4561 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4564 // Monitor.Enter (expr_copy)
4566 expr_copy.Emit (ec);
4567 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4570 base.EmitTryBodyPrepare (ec);
4573 protected override void EmitTryBody (EmitContext ec)
4576 // Monitor.Enter (expr_copy, ref lock_taken)
4578 if (lock_taken != null) {
4579 expr_copy.Emit (ec);
4580 lock_taken.LocalInfo.CreateBuilder (ec);
4581 lock_taken.AddressOf (ec, AddressOp.Load);
4582 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4585 Statement.Emit (ec);
4588 protected override void EmitFinallyBody (EmitContext ec)
4591 // if (lock_taken) Monitor.Exit (expr_copy)
4593 Label skip = ec.DefineLabel ();
4595 if (lock_taken != null) {
4596 lock_taken.Emit (ec);
4597 ec.Emit (OpCodes.Brfalse_S, skip);
4600 expr_copy.Emit (ec);
4601 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4603 ec.Emit (OpCodes.Call, m);
4605 ec.MarkLabel (skip);
4608 int ResolvePredefinedMethods (ResolveContext rc)
4610 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4611 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4615 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4619 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4623 protected override void CloneTo (CloneContext clonectx, Statement t)
4625 Lock target = (Lock) t;
4627 target.expr = expr.Clone (clonectx);
4628 target.stmt = Statement.Clone (clonectx);
4631 public override object Accept (StructuralVisitor visitor)
4633 return visitor.Visit (this);
4638 public class Unchecked : Statement {
4641 public Unchecked (Block b, Location loc)
4648 public override bool Resolve (BlockContext ec)
4650 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4651 return Block.Resolve (ec);
4654 protected override void DoEmit (EmitContext ec)
4656 using (ec.With (EmitContext.Options.CheckedScope, false))
4660 protected override void CloneTo (CloneContext clonectx, Statement t)
4662 Unchecked target = (Unchecked) t;
4664 target.Block = clonectx.LookupBlock (Block);
4667 public override object Accept (StructuralVisitor visitor)
4669 return visitor.Visit (this);
4673 public class Checked : Statement {
4676 public Checked (Block b, Location loc)
4679 b.Unchecked = false;
4683 public override bool Resolve (BlockContext ec)
4685 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4686 return Block.Resolve (ec);
4689 protected override void DoEmit (EmitContext ec)
4691 using (ec.With (EmitContext.Options.CheckedScope, true))
4695 protected override void CloneTo (CloneContext clonectx, Statement t)
4697 Checked target = (Checked) t;
4699 target.Block = clonectx.LookupBlock (Block);
4702 public override object Accept (StructuralVisitor visitor)
4704 return visitor.Visit (this);
4708 public class Unsafe : Statement {
4711 public Unsafe (Block b, Location loc)
4714 Block.Unsafe = true;
4718 public override bool Resolve (BlockContext ec)
4720 if (ec.CurrentIterator != null)
4721 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4723 using (ec.Set (ResolveContext.Options.UnsafeScope))
4724 return Block.Resolve (ec);
4727 protected override void DoEmit (EmitContext ec)
4732 protected override void CloneTo (CloneContext clonectx, Statement t)
4734 Unsafe target = (Unsafe) t;
4736 target.Block = clonectx.LookupBlock (Block);
4739 public override object Accept (StructuralVisitor visitor)
4741 return visitor.Visit (this);
4748 public class Fixed : Statement
4750 abstract class Emitter : ShimExpression
4752 protected LocalVariable vi;
4754 protected Emitter (Expression expr, LocalVariable li)
4760 public abstract void EmitExit (EmitContext ec);
4763 class ExpressionEmitter : Emitter {
4764 public ExpressionEmitter (Expression converted, LocalVariable li) :
4765 base (converted, li)
4769 protected override Expression DoResolve (ResolveContext rc)
4771 throw new NotImplementedException ();
4774 public override void Emit (EmitContext ec) {
4776 // Store pointer in pinned location
4782 public override void EmitExit (EmitContext ec)
4785 ec.Emit (OpCodes.Conv_U);
4790 class StringEmitter : Emitter
4792 LocalVariable pinned_string;
4794 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4799 protected override Expression DoResolve (ResolveContext rc)
4801 pinned_string = new LocalVariable (vi.Block, "$pinned",
4802 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4804 pinned_string.Type = rc.BuiltinTypes.String;
4806 eclass = ExprClass.Variable;
4807 type = rc.BuiltinTypes.Int;
4811 public override void Emit (EmitContext ec)
4813 pinned_string.CreateBuilder (ec);
4816 pinned_string.EmitAssign (ec);
4818 // TODO: Should use Binary::Add
4819 pinned_string.Emit (ec);
4820 ec.Emit (OpCodes.Conv_I);
4822 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4826 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4827 //pe.InstanceExpression = pinned_string;
4828 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4830 ec.Emit (OpCodes.Add);
4834 public override void EmitExit (EmitContext ec)
4837 pinned_string.EmitAssign (ec);
4841 public class VariableDeclaration : BlockVariableDeclaration
4843 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4848 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4850 if (!Variable.Type.IsPointer && li == Variable) {
4851 bc.Report.Error (209, TypeExpression.Location,
4852 "The type of locals declared in a fixed statement must be a pointer type");
4857 // The rules for the possible declarators are pretty wise,
4858 // but the production on the grammar is more concise.
4860 // So we have to enforce these rules here.
4862 // We do not resolve before doing the case 1 test,
4863 // because the grammar is explicit in that the token &
4864 // is present, so we need to test for this particular case.
4867 if (initializer is Cast) {
4868 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4872 initializer = initializer.Resolve (bc);
4874 if (initializer == null)
4880 if (initializer.Type.IsArray) {
4881 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4884 // Provided that array_type is unmanaged,
4886 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4890 // and T* is implicitly convertible to the
4891 // pointer type given in the fixed statement.
4893 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4895 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4896 if (converted == null)
4900 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4902 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4903 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4904 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4905 new NullLiteral (loc),
4908 converted = converted.Resolve (bc);
4910 return new ExpressionEmitter (converted, li);
4916 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4917 return new StringEmitter (initializer, li, loc).Resolve (bc);
4920 // Case 3: fixed buffer
4921 if (initializer is FixedBufferPtr) {
4922 return new ExpressionEmitter (initializer, li);
4926 // Case 4: & object.
4928 bool already_fixed = true;
4929 Unary u = initializer as Unary;
4930 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4931 IVariableReference vr = u.Expr as IVariableReference;
4932 if (vr == null || !vr.IsFixed) {
4933 already_fixed = false;
4937 if (already_fixed) {
4938 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4941 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4942 return new ExpressionEmitter (initializer, li);
4947 VariableDeclaration decl;
4948 Statement statement;
4951 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4960 public Statement Statement {
4966 public BlockVariableDeclaration Variables {
4974 public override bool Resolve (BlockContext ec)
4976 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4977 if (!decl.Resolve (ec))
4981 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4982 bool ok = statement.Resolve (ec);
4983 bool flow_unreachable = ec.EndFlowBranching ();
4984 has_ret = flow_unreachable;
4989 protected override void DoEmit (EmitContext ec)
4991 decl.Variable.CreateBuilder (ec);
4992 decl.Initializer.Emit (ec);
4993 if (decl.Declarators != null) {
4994 foreach (var d in decl.Declarators) {
4995 d.Variable.CreateBuilder (ec);
4996 d.Initializer.Emit (ec);
5000 statement.Emit (ec);
5006 // Clear the pinned variable
5008 ((Emitter) decl.Initializer).EmitExit (ec);
5009 if (decl.Declarators != null) {
5010 foreach (var d in decl.Declarators) {
5011 ((Emitter)d.Initializer).EmitExit (ec);
5016 protected override void CloneTo (CloneContext clonectx, Statement t)
5018 Fixed target = (Fixed) t;
5020 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5021 target.statement = statement.Clone (clonectx);
5024 public override object Accept (StructuralVisitor visitor)
5026 return visitor.Visit (this);
5030 public class Catch : Statement
5034 FullNamedExpression type_expr;
5035 CompilerAssign assign;
5038 public Catch (Block block, Location loc)
5046 public Block Block {
5052 public TypeSpec CatchType {
5058 public bool IsGeneral {
5060 return type_expr == null;
5064 public FullNamedExpression TypeExpression {
5073 public LocalVariable Variable {
5084 protected override void DoEmit (EmitContext ec)
5087 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5089 ec.BeginCatchBlock (CatchType);
5092 li.CreateBuilder (ec);
5095 // Special case hoisted catch variable, we have to use a temporary variable
5096 // to pass via anonymous storey initialization with the value still on top
5099 if (li.HoistedVariant != null) {
5100 LocalTemporary lt = new LocalTemporary (li.Type);
5101 SymbolWriter.OpenCompilerGeneratedBlock (ec);
5103 SymbolWriter.CloseCompilerGeneratedBlock (ec);
5105 // switch to assigning from the temporary variable and not from top of the stack
5106 assign.UpdateSource (lt);
5109 SymbolWriter.OpenCompilerGeneratedBlock (ec);
5110 ec.Emit (OpCodes.Pop);
5111 SymbolWriter.CloseCompilerGeneratedBlock (ec);
5117 public override bool Resolve (BlockContext ec)
5119 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5120 if (type_expr != null) {
5121 type = type_expr.ResolveAsType (ec);
5125 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5126 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5127 } else if (li != null) {
5129 li.PrepareForFlowAnalysis (ec);
5131 // source variable is at the top of the stack
5132 Expression source = new EmptyExpression (li.Type);
5133 if (li.Type.IsGenericParameter)
5134 source = new UnboxCast (source, li.Type);
5136 assign = new CompilerAssign (new LocalVariableReference (li, loc), source, loc);
5137 Block.AddScopeStatement (new StatementExpression (assign));
5141 return Block.Resolve (ec);
5145 protected override void CloneTo (CloneContext clonectx, Statement t)
5147 Catch target = (Catch) t;
5149 if (type_expr != null)
5150 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5152 target.block = clonectx.LookupBlock (block);
5156 public class TryFinally : TryFinallyBlock
5160 public TryFinally (Statement stmt, Block fini, Location loc)
5166 public Block Finallyblock {
5172 public override bool Resolve (BlockContext ec)
5176 ec.StartFlowBranching (this);
5178 if (!stmt.Resolve (ec))
5182 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5183 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5184 if (!fini.Resolve (ec))
5188 ec.EndFlowBranching ();
5190 ok &= base.Resolve (ec);
5195 protected override void EmitTryBody (EmitContext ec)
5200 protected override void EmitFinallyBody (EmitContext ec)
5205 protected override void CloneTo (CloneContext clonectx, Statement t)
5207 TryFinally target = (TryFinally) t;
5209 target.stmt = (Statement) stmt.Clone (clonectx);
5211 target.fini = clonectx.LookupBlock (fini);
5214 public override object Accept (StructuralVisitor visitor)
5216 return visitor.Visit (this);
5220 public class TryCatch : ExceptionStatement
5223 List<Catch> clauses;
5224 readonly bool inside_try_finally;
5226 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5230 this.clauses = catch_clauses;
5231 this.inside_try_finally = inside_try_finally;
5234 public List<Catch> Clauses {
5240 public bool IsTryCatchFinally {
5242 return inside_try_finally;
5246 public override bool Resolve (BlockContext ec)
5250 ec.StartFlowBranching (this);
5252 if (!Block.Resolve (ec))
5255 for (int i = 0; i < clauses.Count; ++i) {
5257 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5259 if (!c.Resolve (ec)) {
5264 TypeSpec resolved_type = c.CatchType;
5265 for (int ii = 0; ii < clauses.Count; ++ii) {
5269 if (clauses[ii].IsGeneral) {
5270 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5273 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5276 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5279 ec.Report.Warning (1058, 1, c.loc,
5280 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5288 var ct = clauses[ii].CatchType;
5292 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5293 ec.Report.Error (160, c.loc,
5294 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5295 ct.GetSignatureForError ());
5301 ec.EndFlowBranching ();
5303 return base.Resolve (ec) && ok;
5306 protected sealed override void DoEmit (EmitContext ec)
5308 if (!inside_try_finally)
5309 EmitTryBodyPrepare (ec);
5313 foreach (Catch c in clauses)
5316 if (!inside_try_finally)
5317 ec.EndExceptionBlock ();
5320 protected override void CloneTo (CloneContext clonectx, Statement t)
5322 TryCatch target = (TryCatch) t;
5324 target.Block = clonectx.LookupBlock (Block);
5325 if (clauses != null){
5326 target.clauses = new List<Catch> ();
5327 foreach (Catch c in clauses)
5328 target.clauses.Add ((Catch) c.Clone (clonectx));
5332 public override object Accept (StructuralVisitor visitor)
5334 return visitor.Visit (this);
5338 public class Using : TryFinallyBlock
5340 public class VariableDeclaration : BlockVariableDeclaration
5342 Statement dispose_call;
5344 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5349 public VariableDeclaration (LocalVariable li, Location loc)
5355 public VariableDeclaration (Expression expr)
5358 loc = expr.Location;
5364 public bool IsNested { get; private set; }
5368 public void EmitDispose (EmitContext ec)
5370 dispose_call.Emit (ec);
5373 public override bool Resolve (BlockContext bc)
5378 return base.Resolve (bc, false);
5381 public Expression ResolveExpression (BlockContext bc)
5383 var e = Initializer.Resolve (bc);
5387 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5388 Initializer = ResolveInitializer (bc, Variable, e);
5392 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5394 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5395 initializer = initializer.Resolve (bc);
5396 if (initializer == null)
5399 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5400 Arguments args = new Arguments (1);
5401 args.Add (new Argument (initializer));
5402 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5403 if (initializer == null)
5406 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5407 dispose_call = CreateDisposeCall (bc, var);
5408 dispose_call.Resolve (bc);
5410 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5413 if (li == Variable) {
5414 CheckIDiposableConversion (bc, li, initializer);
5415 dispose_call = CreateDisposeCall (bc, li);
5416 dispose_call.Resolve (bc);
5419 return base.ResolveInitializer (bc, li, initializer);
5422 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5426 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5427 if (type.IsNullableType) {
5428 // it's handled in CreateDisposeCall
5432 bc.Report.SymbolRelatedToPreviousError (type);
5433 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5434 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5435 type.GetSignatureForError ());
5441 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5443 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5445 var loc = lv.Location;
5447 var idt = bc.BuiltinTypes.IDisposable;
5448 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5450 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5451 dispose_mg.InstanceExpression = type.IsNullableType ?
5452 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5455 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5457 // Add conditional call when disposing possible null variable
5458 if (!type.IsStruct || type.IsNullableType)
5459 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5464 public void ResolveDeclaratorInitializer (BlockContext bc)
5466 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5469 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5471 for (int i = declarators.Count - 1; i >= 0; --i) {
5472 var d = declarators [i];
5473 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5474 vd.Initializer = d.Initializer;
5476 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5477 vd.dispose_call.Resolve (bc);
5479 stmt = new Using (vd, stmt, d.Variable.Location);
5486 public override object Accept (StructuralVisitor visitor)
5488 return visitor.Visit (this);
5492 VariableDeclaration decl;
5494 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5500 public Using (Expression expr, Statement stmt, Location loc)
5503 this.decl = new VariableDeclaration (expr);
5508 public Expression Expr {
5510 return decl.Variable == null ? decl.Initializer : null;
5514 public BlockVariableDeclaration Variables {
5522 protected override void EmitTryBodyPrepare (EmitContext ec)
5525 base.EmitTryBodyPrepare (ec);
5528 protected override void EmitTryBody (EmitContext ec)
5533 protected override void EmitFinallyBody (EmitContext ec)
5535 decl.EmitDispose (ec);
5538 public override bool Resolve (BlockContext ec)
5540 VariableReference vr;
5541 bool vr_locked = false;
5543 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5544 if (decl.Variable == null) {
5545 vr = decl.ResolveExpression (ec) as VariableReference;
5547 vr_locked = vr.IsLockedByStatement;
5548 vr.IsLockedByStatement = true;
5551 if (decl.IsNested) {
5552 decl.ResolveDeclaratorInitializer (ec);
5554 if (!decl.Resolve (ec))
5557 if (decl.Declarators != null) {
5558 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5566 ec.StartFlowBranching (this);
5570 ec.EndFlowBranching ();
5573 vr.IsLockedByStatement = vr_locked;
5580 protected override void CloneTo (CloneContext clonectx, Statement t)
5582 Using target = (Using) t;
5584 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5585 target.stmt = stmt.Clone (clonectx);
5588 public override object Accept (StructuralVisitor visitor)
5590 return visitor.Visit (this);
5595 /// Implementation of the foreach C# statement
5597 public class Foreach : Statement
5599 sealed class ArrayForeach : Statement
5601 readonly Foreach for_each;
5602 readonly Statement statement;
5605 TemporaryVariableReference[] lengths;
5606 Expression [] length_exprs;
5607 StatementExpression[] counter;
5608 TemporaryVariableReference[] variables;
5610 TemporaryVariableReference copy;
5612 LocalVariableReference variable;
5614 public ArrayForeach (Foreach @foreach, int rank)
5616 for_each = @foreach;
5617 statement = for_each.statement;
5620 counter = new StatementExpression[rank];
5621 variables = new TemporaryVariableReference[rank];
5622 length_exprs = new Expression [rank];
5625 // Only use temporary length variables when dealing with
5626 // multi-dimensional arrays
5629 lengths = new TemporaryVariableReference [rank];
5632 protected override void CloneTo (CloneContext clonectx, Statement target)
5634 throw new NotImplementedException ();
5637 public override bool Resolve (BlockContext ec)
5639 Block variables_block = for_each.variable.Block;
5640 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5643 int rank = length_exprs.Length;
5644 Arguments list = new Arguments (rank);
5645 for (int i = 0; i < rank; i++) {
5646 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5648 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5649 counter[i].Resolve (ec);
5652 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5654 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5655 lengths[i].Resolve (ec);
5657 Arguments args = new Arguments (1);
5658 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5659 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5662 list.Add (new Argument (v));
5665 access = new ElementAccess (copy, list, loc).Resolve (ec);
5670 if (for_each.type is VarExpr) {
5671 // Infer implicitly typed local variable from foreach array type
5672 var_type = access.Type;
5674 var_type = for_each.type.ResolveAsType (ec);
5677 if (var_type == null)
5680 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5686 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5687 ec.CurrentBranching.CreateSibling ();
5689 for_each.variable.Type = conv.Type;
5690 variable = new LocalVariableReference (for_each.variable, loc);
5691 variable.Resolve (ec);
5693 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5694 if (!statement.Resolve (ec))
5696 ec.EndFlowBranching ();
5698 // There's no direct control flow from the end of the embedded statement to the end of the loop
5699 ec.CurrentBranching.CurrentUsageVector.Goto ();
5701 ec.EndFlowBranching ();
5706 protected override void DoEmit (EmitContext ec)
5708 copy.EmitAssign (ec, for_each.expr);
5710 int rank = length_exprs.Length;
5711 Label[] test = new Label [rank];
5712 Label[] loop = new Label [rank];
5714 for (int i = 0; i < rank; i++) {
5715 test [i] = ec.DefineLabel ();
5716 loop [i] = ec.DefineLabel ();
5718 if (lengths != null)
5719 lengths [i].EmitAssign (ec, length_exprs [i]);
5722 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5723 for (int i = 0; i < rank; i++) {
5724 variables [i].EmitAssign (ec, zero);
5726 ec.Emit (OpCodes.Br, test [i]);
5727 ec.MarkLabel (loop [i]);
5730 variable.local_info.CreateBuilder (ec);
5731 variable.EmitAssign (ec, conv, false, false);
5733 statement.Emit (ec);
5735 ec.MarkLabel (ec.LoopBegin);
5737 for (int i = rank - 1; i >= 0; i--){
5738 counter [i].Emit (ec);
5740 ec.MarkLabel (test [i]);
5741 variables [i].Emit (ec);
5743 if (lengths != null)
5744 lengths [i].Emit (ec);
5746 length_exprs [i].Emit (ec);
5748 ec.Emit (OpCodes.Blt, loop [i]);
5751 ec.MarkLabel (ec.LoopEnd);
5755 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5757 class Body : Statement
5760 LocalVariableReference variable;
5761 Expression current, conv;
5762 Statement statement;
5764 public Body (TypeSpec type, LocalVariable variable,
5765 Expression current, Statement statement,
5769 this.variable = new LocalVariableReference (variable, loc);
5770 this.current = current;
5771 this.statement = statement;
5775 protected override void CloneTo (CloneContext clonectx, Statement target)
5777 throw new NotImplementedException ();
5780 public override bool Resolve (BlockContext ec)
5782 current = current.Resolve (ec);
5783 if (current == null)
5786 conv = Convert.ExplicitConversion (ec, current, type, loc);
5790 variable.local_info.Type = conv.Type;
5791 variable.Resolve (ec);
5793 if (!statement.Resolve (ec))
5799 protected override void DoEmit (EmitContext ec)
5801 variable.local_info.CreateBuilder (ec);
5802 variable.EmitAssign (ec, conv, false, false);
5804 statement.Emit (ec);
5808 class RuntimeDispose : Using.VariableDeclaration
5810 public RuntimeDispose (LocalVariable lv, Location loc)
5815 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5817 // Defered to runtime check
5820 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5822 var idt = bc.BuiltinTypes.IDisposable;
5825 // Fabricates code like
5827 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5830 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5832 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5833 dispose_variable.CreateReferenceExpression (bc, loc),
5834 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5835 loc), new NullLiteral (loc), loc);
5837 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5839 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5840 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5842 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5843 return new If (idisaposable_test, dispose, loc);
5847 LocalVariable variable;
5849 Statement statement;
5850 Expression var_type;
5851 ExpressionStatement init;
5852 TemporaryVariableReference enumerator_variable;
5853 bool ambiguous_getenumerator_name;
5855 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5857 this.var_type = var_type;
5858 this.variable = var;
5864 protected override void CloneTo (CloneContext clonectx, Statement target)
5866 throw new NotImplementedException ();
5869 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5871 rc.Report.SymbolRelatedToPreviousError (enumerator);
5872 rc.Report.Error (202, loc,
5873 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5874 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5877 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5880 // Option 1: Try to match by name GetEnumerator first
5882 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5883 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5885 var mg = mexpr as MethodGroupExpr;
5887 mg.InstanceExpression = expr;
5888 Arguments args = new Arguments (0);
5889 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5891 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5892 if (ambiguous_getenumerator_name)
5895 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5901 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5904 PredefinedMember<MethodSpec> iface_candidate = null;
5905 var ptypes = rc.Module.PredefinedTypes;
5906 var gen_ienumerable = ptypes.IEnumerableGeneric;
5907 if (!gen_ienumerable.Define ())
5908 gen_ienumerable = null;
5911 var ifaces = t.Interfaces;
5912 if (ifaces != null) {
5913 foreach (var iface in ifaces) {
5914 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5915 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5916 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5917 rc.Report.Error (1640, loc,
5918 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5919 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5924 // TODO: Cache this somehow
5925 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5926 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5931 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5932 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5937 if (t.IsGenericParameter)
5942 } while (t != null);
5944 if (iface_candidate == null) {
5945 if (expr.Type != InternalType.ErrorType) {
5946 rc.Report.Error (1579, loc,
5947 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5948 expr.Type.GetSignatureForError (), "GetEnumerator");
5954 var method = iface_candidate.Resolve (loc);
5958 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5959 mg.InstanceExpression = expr;
5963 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5965 var ms = MemberCache.FindMember (enumerator.ReturnType,
5966 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5967 BindingRestriction.InstanceOnly) as MethodSpec;
5969 if (ms == null || !ms.IsPublic) {
5970 Error_WrongEnumerator (rc, enumerator);
5974 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, loc);
5977 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5979 var ps = MemberCache.FindMember (enumerator.ReturnType,
5980 MemberFilter.Property ("Current", null),
5981 BindingRestriction.InstanceOnly) as PropertySpec;
5983 if (ps == null || !ps.IsPublic) {
5984 Error_WrongEnumerator (rc, enumerator);
5991 public override bool Resolve (BlockContext ec)
5993 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
5996 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
5997 } else if (expr.Type.IsNullableType) {
5998 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6001 var get_enumerator_mg = ResolveGetEnumerator (ec);
6002 if (get_enumerator_mg == null) {
6006 var get_enumerator = get_enumerator_mg.BestCandidate;
6007 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6008 enumerator_variable.Resolve (ec);
6010 // Prepare bool MoveNext ()
6011 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6012 if (move_next_mg == null) {
6016 move_next_mg.InstanceExpression = enumerator_variable;
6018 // Prepare ~T~ Current { get; }
6019 var current_prop = ResolveCurrent (ec, get_enumerator);
6020 if (current_prop == null) {
6024 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6025 if (current_pe == null)
6028 VarExpr ve = var_type as VarExpr;
6032 // Source type is dynamic, set element type to dynamic too
6033 variable.Type = ec.BuiltinTypes.Dynamic;
6035 // Infer implicitly typed local variable from foreach enumerable type
6036 variable.Type = current_pe.Type;
6040 // Explicit cast of dynamic collection elements has to be done at runtime
6041 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6044 variable.Type = var_type.ResolveAsType (ec);
6047 if (variable.Type == null)
6050 var init = new Invocation (get_enumerator_mg, null);
6052 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6053 new Body (variable.Type, variable, current_pe, statement, loc), loc);
6055 var enum_type = enumerator_variable.Type;
6058 // Add Dispose method call when enumerator can be IDisposable
6060 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6061 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6063 // Runtime Dispose check
6065 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, loc);
6066 vd.Initializer = init;
6067 statement = new Using (vd, statement, loc);
6070 // No Dispose call needed
6072 this.init = new SimpleAssign (enumerator_variable, init);
6073 this.init.Resolve (ec);
6077 // Static Dispose check
6079 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, loc);
6080 vd.Initializer = init;
6081 statement = new Using (vd, statement, loc);
6084 return statement.Resolve (ec);
6087 protected override void DoEmit (EmitContext ec)
6089 enumerator_variable.LocalInfo.CreateBuilder (ec);
6092 init.EmitStatement (ec);
6094 statement.Emit (ec);
6097 #region IErrorHandler Members
6099 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6101 ec.Report.SymbolRelatedToPreviousError (best);
6102 ec.Report.Warning (278, 2, loc,
6103 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6104 expr.Type.GetSignatureForError (), "enumerable",
6105 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6107 ambiguous_getenumerator_name = true;
6111 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6116 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6121 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6130 LocalVariable variable;
6132 Statement statement;
6134 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
6137 this.variable = var;
6143 public Expression Expr {
6144 get { return expr; }
6147 public Statement Statement {
6148 get { return statement; }
6151 public Expression TypeExpression {
6152 get { return type; }
6155 public LocalVariable Variable {
6156 get { return variable; }
6160 public override bool Resolve (BlockContext ec)
6162 expr = expr.Resolve (ec);
6167 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6171 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6172 statement = new ArrayForeach (this, 1);
6173 } else if (expr.Type is ArrayContainer) {
6174 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6176 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6177 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6178 expr.ExprClassName);
6182 statement = new CollectionForeach (type, variable, expr, statement, loc);
6185 return statement.Resolve (ec);
6188 protected override void DoEmit (EmitContext ec)
6190 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6191 ec.LoopBegin = ec.DefineLabel ();
6192 ec.LoopEnd = ec.DefineLabel ();
6194 statement.Emit (ec);
6196 ec.LoopBegin = old_begin;
6197 ec.LoopEnd = old_end;
6200 protected override void CloneTo (CloneContext clonectx, Statement t)
6202 Foreach target = (Foreach) t;
6204 target.type = type.Clone (clonectx);
6205 target.expr = expr.Clone (clonectx);
6206 target.statement = statement.Clone (clonectx);
6209 public override object Accept (StructuralVisitor visitor)
6211 return visitor.Visit (this);