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);
330 // Mark start of while condition
331 ec.Mark (expr.Location);
334 // Dead code elimination
336 if (expr is Constant) {
337 bool res = !((Constant) expr).IsDefaultValue;
339 expr.EmitSideEffect (ec);
341 ec.Emit (OpCodes.Br, loop);
343 expr.EmitBranchable (ec, loop, true);
346 ec.MarkLabel (ec.LoopEnd);
348 ec.LoopBegin = old_begin;
349 ec.LoopEnd = old_end;
352 protected override void CloneTo (CloneContext clonectx, Statement t)
356 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
357 target.expr = expr.Clone (clonectx);
360 public override object Accept (StructuralVisitor visitor)
362 return visitor.Visit (this);
366 public class While : Statement {
367 public Expression expr;
368 public Statement Statement;
369 bool infinite, empty;
371 public While (BooleanExpression bool_expr, Statement statement, Location l)
373 this.expr = bool_expr;
374 Statement = statement;
378 public override bool Resolve (BlockContext ec)
382 expr = expr.Resolve (ec);
387 // Inform whether we are infinite or not
389 if (expr is Constant){
390 bool value = !((Constant) expr).IsDefaultValue;
393 if (!Statement.ResolveUnreachable (ec, true))
401 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
403 ec.CurrentBranching.CreateSibling ();
405 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
406 if (!Statement.Resolve (ec))
408 ec.EndFlowBranching ();
410 // There's no direct control flow from the end of the embedded statement to the end of the loop
411 ec.CurrentBranching.CurrentUsageVector.Goto ();
413 ec.EndFlowBranching ();
418 protected override void DoEmit (EmitContext ec)
421 expr.EmitSideEffect (ec);
425 Label old_begin = ec.LoopBegin;
426 Label old_end = ec.LoopEnd;
428 ec.LoopBegin = ec.DefineLabel ();
429 ec.LoopEnd = ec.DefineLabel ();
432 // Inform whether we are infinite or not
434 if (expr is Constant) {
435 // expr is 'true', since the 'empty' case above handles the 'false' case
436 ec.MarkLabel (ec.LoopBegin);
438 if (ec.EmitAccurateDebugInfo)
439 ec.Emit (OpCodes.Nop);
441 expr.EmitSideEffect (ec);
443 ec.Emit (OpCodes.Br, ec.LoopBegin);
446 // Inform that we are infinite (ie, `we return'), only
447 // if we do not `break' inside the code.
449 ec.MarkLabel (ec.LoopEnd);
451 Label while_loop = ec.DefineLabel ();
453 ec.Emit (OpCodes.Br, ec.LoopBegin);
454 ec.MarkLabel (while_loop);
458 ec.MarkLabel (ec.LoopBegin);
460 ec.Mark (expr.Location);
461 expr.EmitBranchable (ec, while_loop, true);
463 ec.MarkLabel (ec.LoopEnd);
466 ec.LoopBegin = old_begin;
467 ec.LoopEnd = old_end;
470 protected override void CloneTo (CloneContext clonectx, Statement t)
472 While target = (While) t;
474 target.expr = expr.Clone (clonectx);
475 target.Statement = Statement.Clone (clonectx);
478 public override object Accept (StructuralVisitor visitor)
480 return visitor.Visit (this);
484 public class For : Statement
486 bool infinite, empty;
488 public For (Location l)
493 public Statement Initializer {
497 public Expression Condition {
501 public Statement Iterator {
505 public Statement Statement {
509 public override bool Resolve (BlockContext ec)
513 if (Initializer != null) {
514 if (!Initializer.Resolve (ec))
518 if (Condition != null) {
519 Condition = Condition.Resolve (ec);
520 if (Condition == null)
522 else if (Condition is Constant) {
523 bool value = !((Constant) Condition).IsDefaultValue;
526 if (!Statement.ResolveUnreachable (ec, true))
528 if ((Iterator != null) &&
529 !Iterator.ResolveUnreachable (ec, false))
539 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
541 ec.CurrentBranching.CreateSibling ();
543 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
545 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
546 if (!Statement.Resolve (ec))
548 ec.EndFlowBranching ();
550 if (Iterator != null){
551 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
552 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
555 if (!Iterator.Resolve (ec))
560 // There's no direct control flow from the end of the embedded statement to the end of the loop
561 ec.CurrentBranching.CurrentUsageVector.Goto ();
563 ec.EndFlowBranching ();
568 protected override void DoEmit (EmitContext ec)
570 if (Initializer != null)
571 Initializer.Emit (ec);
574 Condition.EmitSideEffect (ec);
578 Label old_begin = ec.LoopBegin;
579 Label old_end = ec.LoopEnd;
580 Label loop = ec.DefineLabel ();
581 Label test = ec.DefineLabel ();
583 ec.LoopBegin = ec.DefineLabel ();
584 ec.LoopEnd = ec.DefineLabel ();
586 ec.Emit (OpCodes.Br, test);
590 ec.MarkLabel (ec.LoopBegin);
595 // If test is null, there is no test, and we are just
598 if (Condition != null) {
599 ec.Mark (Condition.Location);
602 // The Resolve code already catches the case for
603 // Test == Constant (false) so we know that
606 if (Condition is Constant) {
607 Condition.EmitSideEffect (ec);
608 ec.Emit (OpCodes.Br, loop);
610 Condition.EmitBranchable (ec, loop, true);
614 ec.Emit (OpCodes.Br, loop);
615 ec.MarkLabel (ec.LoopEnd);
617 ec.LoopBegin = old_begin;
618 ec.LoopEnd = old_end;
621 protected override void CloneTo (CloneContext clonectx, Statement t)
623 For target = (For) t;
625 if (Initializer != null)
626 target.Initializer = Initializer.Clone (clonectx);
627 if (Condition != null)
628 target.Condition = Condition.Clone (clonectx);
629 if (Iterator != null)
630 target.Iterator = Iterator.Clone (clonectx);
631 target.Statement = Statement.Clone (clonectx);
634 public override object Accept (StructuralVisitor visitor)
636 return visitor.Visit (this);
640 public class StatementExpression : Statement
642 ExpressionStatement expr;
644 public StatementExpression (ExpressionStatement expr)
650 public StatementExpression (ExpressionStatement expr, Location loc)
656 public ExpressionStatement Expr {
662 protected override void CloneTo (CloneContext clonectx, Statement t)
664 StatementExpression target = (StatementExpression) t;
665 target.expr = (ExpressionStatement) expr.Clone (clonectx);
668 protected override void DoEmit (EmitContext ec)
670 expr.EmitStatement (ec);
673 public override bool Resolve (BlockContext ec)
675 expr = expr.ResolveStatement (ec);
679 public override object Accept (StructuralVisitor visitor)
681 return visitor.Visit (this);
685 public class StatementErrorExpression : Statement
687 readonly Expression expr;
689 public StatementErrorExpression (Expression expr)
694 public Expression Expr {
700 protected override void DoEmit (EmitContext ec)
702 throw new NotSupportedException ();
705 protected override void CloneTo (CloneContext clonectx, Statement target)
707 throw new NotImplementedException ();
710 public override object Accept (StructuralVisitor visitor)
712 return visitor.Visit (this);
717 // Simple version of statement list not requiring a block
719 public class StatementList : Statement
721 List<Statement> statements;
723 public StatementList (Statement first, Statement second)
725 statements = new List<Statement> () { first, second };
729 public IList<Statement> Statements {
736 public void Add (Statement statement)
738 statements.Add (statement);
741 public override bool Resolve (BlockContext ec)
743 foreach (var s in statements)
749 protected override void DoEmit (EmitContext ec)
751 foreach (var s in statements)
755 protected override void CloneTo (CloneContext clonectx, Statement target)
757 StatementList t = (StatementList) target;
759 t.statements = new List<Statement> (statements.Count);
760 foreach (Statement s in statements)
761 t.statements.Add (s.Clone (clonectx));
764 public override object Accept (StructuralVisitor visitor)
766 return visitor.Visit (this);
770 // A 'return' or a 'yield break'
771 public abstract class ExitStatement : Statement
773 protected bool unwind_protect;
774 protected abstract bool DoResolve (BlockContext ec);
776 public virtual void Error_FinallyClause (Report Report)
778 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
781 public sealed override bool Resolve (BlockContext ec)
786 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
787 ec.CurrentBranching.CurrentUsageVector.Goto ();
793 /// Implements the return statement
795 public class Return : ExitStatement
799 public Return (Expression expr, Location l)
807 public Expression Expr {
818 protected override bool DoResolve (BlockContext ec)
821 if (ec.ReturnType.Kind == MemberKind.Void)
825 // Return must not be followed by an expression when
826 // the method return type is Task
828 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
829 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
830 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
832 // Extra trick not to emit ret/leave inside awaiter body
834 expr = EmptyExpression.Null;
839 if (ec.CurrentIterator != null) {
840 Error_ReturnFromIterator (ec);
842 ec.Report.Error (126, loc,
843 "An object of a type convertible to `{0}' is required for the return statement",
844 ec.ReturnType.GetSignatureForError ());
850 expr = expr.Resolve (ec);
851 TypeSpec block_return_type = ec.ReturnType;
853 AnonymousExpression am = ec.CurrentAnonymousMethod;
855 if (block_return_type.Kind == MemberKind.Void) {
856 ec.Report.Error (127, loc,
857 "`{0}': A return keyword must not be followed by any expression when method returns void",
858 ec.GetSignatureForError ());
862 Error_ReturnFromIterator (ec);
866 var async_block = am as AsyncInitializer;
867 if (async_block != null) {
869 var storey = (AsyncTaskStorey) am.Storey;
870 var async_type = storey.ReturnType;
872 if (async_type == null && async_block.ReturnTypeInference != null) {
873 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
877 if (!async_type.IsGenericTask) {
878 if (this is ContextualReturn)
881 ec.Report.Error (1997, loc,
882 "`{0}': A return keyword must not be followed by an expression when async method returns Task. Consider using Task<T>",
883 ec.GetSignatureForError ());
888 // The return type is actually Task<T> type argument
890 block_return_type = async_type.TypeArguments[0];
893 var l = am as AnonymousMethodBody;
894 if (l != null && l.ReturnTypeInference != null && expr != null) {
895 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
904 if (expr.Type != block_return_type) {
905 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
908 if (am != null && block_return_type == ec.ReturnType) {
909 ec.Report.Error (1662, loc,
910 "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",
911 am.ContainerType, am.GetSignatureForError ());
920 protected override void DoEmit (EmitContext ec)
925 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
926 if (async_body != null) {
927 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
929 // It's null for await without async
930 if (async_return != null) {
931 async_return.EmitAssign (ec);
933 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
939 if (unwind_protect || ec.EmitAccurateDebugInfo)
940 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
943 if (unwind_protect) {
944 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
945 } else if (ec.EmitAccurateDebugInfo) {
946 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
948 ec.Emit (OpCodes.Ret);
952 void Error_ReturnFromIterator (ResolveContext rc)
954 rc.Report.Error (1622, loc,
955 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
958 protected override void CloneTo (CloneContext clonectx, Statement t)
960 Return target = (Return) t;
961 // It's null for simple return;
963 target.expr = expr.Clone (clonectx);
966 public override object Accept (StructuralVisitor visitor)
968 return visitor.Visit (this);
972 public class Goto : Statement {
974 LabeledStatement label;
977 public override bool Resolve (BlockContext ec)
979 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
980 ec.CurrentBranching.CurrentUsageVector.Goto ();
984 public Goto (string label, Location l)
990 public string Target {
991 get { return target; }
994 public void SetResolvedTarget (LabeledStatement label)
997 label.AddReference ();
1000 protected override void CloneTo (CloneContext clonectx, Statement target)
1005 protected override void DoEmit (EmitContext ec)
1008 throw new InternalErrorException ("goto emitted before target resolved");
1009 Label l = label.LabelTarget (ec);
1010 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1013 public override object Accept (StructuralVisitor visitor)
1015 return visitor.Visit (this);
1019 public class LabeledStatement : Statement {
1026 FlowBranching.UsageVector vectors;
1028 public LabeledStatement (string name, Block block, Location l)
1035 public Label LabelTarget (EmitContext ec)
1040 label = ec.DefineLabel ();
1045 public Block Block {
1051 public string Name {
1052 get { return name; }
1055 public bool IsDefined {
1056 get { return defined; }
1059 public bool HasBeenReferenced {
1060 get { return referenced; }
1063 public FlowBranching.UsageVector JumpOrigins {
1064 get { return vectors; }
1067 public void AddUsageVector (FlowBranching.UsageVector vector)
1069 vector = vector.Clone ();
1070 vector.Next = vectors;
1074 protected override void CloneTo (CloneContext clonectx, Statement target)
1079 public override bool Resolve (BlockContext ec)
1081 // this flow-branching will be terminated when the surrounding block ends
1082 ec.StartFlowBranching (this);
1086 protected override void DoEmit (EmitContext ec)
1088 if (!HasBeenReferenced)
1089 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1092 ec.MarkLabel (label);
1095 public void AddReference ()
1100 public override object Accept (StructuralVisitor visitor)
1102 return visitor.Visit (this);
1108 /// `goto default' statement
1110 public class GotoDefault : Statement {
1112 public GotoDefault (Location l)
1117 protected override void CloneTo (CloneContext clonectx, Statement target)
1122 public override bool Resolve (BlockContext ec)
1124 ec.CurrentBranching.CurrentUsageVector.Goto ();
1126 if (ec.Switch == null) {
1127 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1131 if (!ec.Switch.GotDefault) {
1132 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1139 protected override void DoEmit (EmitContext ec)
1141 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1144 public override object Accept (StructuralVisitor visitor)
1146 return visitor.Visit (this);
1151 /// `goto case' statement
1153 public class GotoCase : Statement {
1157 public GotoCase (Expression e, Location l)
1163 public Expression Expr {
1169 public override bool Resolve (BlockContext ec)
1171 if (ec.Switch == null){
1172 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1176 ec.CurrentBranching.CurrentUsageVector.Goto ();
1178 expr = expr.Resolve (ec);
1182 Constant c = expr as Constant;
1184 ec.Report.Error (150, expr.Location, "A constant value is expected");
1189 if (ec.Switch.IsNullable && c is NullLiteral) {
1192 TypeSpec type = ec.Switch.SwitchType;
1193 res = c.TryReduce (ec, type, c.Location);
1195 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1199 if (!Convert.ImplicitStandardConversionExists (c, type))
1200 ec.Report.Warning (469, 2, loc,
1201 "The `goto case' value is not implicitly convertible to type `{0}'",
1202 TypeManager.CSharpName (type));
1206 sl = ec.Switch.ResolveGotoCase (ec, res);
1210 protected override void DoEmit (EmitContext ec)
1212 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1215 protected override void CloneTo (CloneContext clonectx, Statement t)
1217 GotoCase target = (GotoCase) t;
1219 target.expr = expr.Clone (clonectx);
1222 public override object Accept (StructuralVisitor visitor)
1224 return visitor.Visit (this);
1228 public class Throw : Statement {
1231 public Throw (Expression expr, Location l)
1237 public Expression Expr {
1243 public override bool Resolve (BlockContext ec)
1246 ec.CurrentBranching.CurrentUsageVector.Goto ();
1247 return ec.CurrentBranching.CheckRethrow (loc);
1250 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1251 ec.CurrentBranching.CurrentUsageVector.Goto ();
1256 var et = ec.BuiltinTypes.Exception;
1257 if (Convert.ImplicitConversionExists (ec, expr, et))
1258 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1260 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1265 protected override void DoEmit (EmitContext ec)
1268 ec.Emit (OpCodes.Rethrow);
1272 ec.Emit (OpCodes.Throw);
1276 protected override void CloneTo (CloneContext clonectx, Statement t)
1278 Throw target = (Throw) t;
1281 target.expr = expr.Clone (clonectx);
1284 public override object Accept (StructuralVisitor visitor)
1286 return visitor.Visit (this);
1290 public class Break : Statement {
1292 public Break (Location l)
1297 bool unwind_protect;
1299 public override bool Resolve (BlockContext ec)
1301 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1302 ec.CurrentBranching.CurrentUsageVector.Goto ();
1306 protected override void DoEmit (EmitContext ec)
1308 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1311 protected override void CloneTo (CloneContext clonectx, Statement t)
1316 public override object Accept (StructuralVisitor visitor)
1318 return visitor.Visit (this);
1322 public class Continue : Statement {
1324 public Continue (Location l)
1329 bool unwind_protect;
1331 public override bool Resolve (BlockContext ec)
1333 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1334 ec.CurrentBranching.CurrentUsageVector.Goto ();
1338 protected override void DoEmit (EmitContext ec)
1340 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1343 protected override void CloneTo (CloneContext clonectx, Statement t)
1348 public override object Accept (StructuralVisitor visitor)
1350 return visitor.Visit (this);
1354 public interface ILocalVariable
1356 void Emit (EmitContext ec);
1357 void EmitAssign (EmitContext ec);
1358 void EmitAddressOf (EmitContext ec);
1361 public interface INamedBlockVariable
1363 Block Block { get; }
1364 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1365 bool IsDeclared { get; }
1366 bool IsParameter { get; }
1367 Location Location { get; }
1370 public class BlockVariableDeclaration : Statement
1372 public class Declarator
1375 Expression initializer;
1377 public Declarator (LocalVariable li, Expression initializer)
1379 if (li.Type != null)
1380 throw new ArgumentException ("Expected null variable type");
1383 this.initializer = initializer;
1386 public Declarator (Declarator clone, Expression initializer)
1389 this.initializer = initializer;
1394 public LocalVariable Variable {
1400 public Expression Initializer {
1405 initializer = value;
1412 Expression initializer;
1413 protected FullNamedExpression type_expr;
1414 protected LocalVariable li;
1415 protected List<Declarator> declarators;
1417 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1419 this.type_expr = type;
1421 this.loc = type_expr.Location;
1424 protected BlockVariableDeclaration (LocalVariable li)
1431 public List<Declarator> Declarators {
1437 public Expression Initializer {
1442 initializer = value;
1446 public FullNamedExpression TypeExpression {
1452 public LocalVariable Variable {
1460 public void AddDeclarator (Declarator decl)
1462 if (declarators == null)
1463 declarators = new List<Declarator> ();
1465 declarators.Add (decl);
1468 void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1470 if (bc.Report.Errors != 0)
1473 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1475 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1476 new MemberName (li.Name, li.Location), null);
1478 container.AddField (f);
1481 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1485 public override bool Resolve (BlockContext bc)
1487 return Resolve (bc, true);
1490 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1492 if (li.Type == null) {
1493 TypeSpec type = null;
1494 var vexpr = type_expr as VarExpr;
1497 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1498 // same name exists or as a keyword when no type was found
1500 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1501 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1502 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1505 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1509 if (li.IsConstant) {
1510 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1514 if (Initializer == null) {
1515 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1519 if (declarators != null) {
1520 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1524 Initializer = Initializer.Resolve (bc);
1525 if (Initializer != null) {
1526 ((VarExpr) type_expr).InferType (bc, Initializer);
1527 type = type_expr.Type;
1529 // Set error type to indicate the var was placed correctly but could
1532 // var a = missing ();
1534 type = InternalType.ErrorType;
1539 type = type_expr.ResolveAsType (bc);
1543 if (li.IsConstant && !type.IsConstantCompatible) {
1544 Const.Error_InvalidConstantType (type, loc, bc.Report);
1549 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1554 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1556 CreateEvaluatorVariable (bc, li);
1558 li.PrepareForFlowAnalysis (bc);
1561 if (initializer != null) {
1562 initializer = ResolveInitializer (bc, li, initializer);
1563 // li.Variable.DefinitelyAssigned
1566 if (declarators != null) {
1567 foreach (var d in declarators) {
1568 d.Variable.Type = li.Type;
1570 CreateEvaluatorVariable (bc, d.Variable);
1572 d.Variable.PrepareForFlowAnalysis (bc);
1575 if (d.Initializer != null && resolveDeclaratorInitializers) {
1576 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1577 // d.Variable.DefinitelyAssigned
1585 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1587 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1588 return a.ResolveStatement (bc);
1591 protected override void DoEmit (EmitContext ec)
1593 li.CreateBuilder (ec);
1595 if (Initializer != null)
1596 ((ExpressionStatement) Initializer).EmitStatement (ec);
1598 if (declarators != null) {
1599 foreach (var d in declarators) {
1600 d.Variable.CreateBuilder (ec);
1601 if (d.Initializer != null)
1602 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1607 protected override void CloneTo (CloneContext clonectx, Statement target)
1609 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1611 if (type_expr != null)
1612 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1614 if (initializer != null)
1615 t.initializer = initializer.Clone (clonectx);
1617 if (declarators != null) {
1618 t.declarators = null;
1619 foreach (var d in declarators)
1620 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1624 public override object Accept (StructuralVisitor visitor)
1626 return visitor.Visit (this);
1630 public class BlockConstantDeclaration : BlockVariableDeclaration
1632 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1637 public override void Emit (EmitContext ec)
1639 // Nothing to emit, not even sequence point
1642 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1644 initializer = initializer.Resolve (bc);
1645 if (initializer == null)
1648 var c = initializer as Constant;
1650 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1654 c = c.ConvertImplicitly (li.Type);
1656 if (TypeSpec.IsReferenceType (li.Type))
1657 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1659 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1664 li.ConstantValue = c;
1668 public override object Accept (StructuralVisitor visitor)
1670 return visitor.Visit (this);
1675 // The information about a user-perceived local variable
1677 public class LocalVariable : INamedBlockVariable, ILocalVariable
1684 AddressTaken = 1 << 2,
1685 CompilerGenerated = 1 << 3,
1687 ForeachVariable = 1 << 5,
1688 FixedVariable = 1 << 6,
1689 UsingVariable = 1 << 7,
1690 // DefinitelyAssigned = 1 << 8,
1693 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1697 readonly string name;
1698 readonly Location loc;
1699 readonly Block block;
1701 Constant const_value;
1703 public VariableInfo VariableInfo;
1704 HoistedVariable hoisted_variant;
1706 LocalBuilder builder;
1708 public LocalVariable (Block block, string name, Location loc)
1715 public LocalVariable (Block block, string name, Flags flags, Location loc)
1716 : this (block, name, loc)
1722 // Used by variable declarators
1724 public LocalVariable (LocalVariable li, string name, Location loc)
1725 : this (li.block, name, li.flags, loc)
1731 public bool AddressTaken {
1733 return (flags & Flags.AddressTaken) != 0;
1737 public Block Block {
1743 public Constant ConstantValue {
1748 const_value = value;
1753 // Hoisted local variable variant
1755 public HoistedVariable HoistedVariant {
1757 return hoisted_variant;
1760 hoisted_variant = value;
1764 public bool IsDeclared {
1766 return type != null;
1770 public bool IsConstant {
1772 return (flags & Flags.Constant) != 0;
1776 public bool IsLocked {
1778 return (flags & Flags.IsLocked) != 0;
1781 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1785 public bool IsThis {
1787 return (flags & Flags.IsThis) != 0;
1791 public bool IsFixed {
1793 return (flags & Flags.FixedVariable) != 0;
1797 bool INamedBlockVariable.IsParameter {
1803 public bool IsReadonly {
1805 return (flags & Flags.ReadonlyMask) != 0;
1809 public Location Location {
1815 public string Name {
1821 public TypeSpec Type {
1832 public void CreateBuilder (EmitContext ec)
1834 if ((flags & Flags.Used) == 0) {
1835 if (VariableInfo == null) {
1836 // Missing flow analysis or wrong variable flags
1837 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1840 if (VariableInfo.IsEverAssigned)
1841 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1843 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1846 if (HoistedVariant != null)
1849 if (builder != null) {
1850 if ((flags & Flags.CompilerGenerated) != 0)
1853 // To avoid Used warning duplicates
1854 throw new InternalErrorException ("Already created variable `{0}'", name);
1858 // All fixed variabled are pinned, a slot has to be alocated
1860 builder = ec.DeclareLocal (Type, IsFixed);
1861 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1862 ec.DefineLocalVariable (name, builder);
1865 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1867 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1872 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1874 if (IsConstant && const_value != null)
1875 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1877 return new LocalVariableReference (this, loc);
1880 public void Emit (EmitContext ec)
1882 // TODO: Need something better for temporary variables
1883 if ((flags & Flags.CompilerGenerated) != 0)
1886 ec.Emit (OpCodes.Ldloc, builder);
1889 public void EmitAssign (EmitContext ec)
1891 // TODO: Need something better for temporary variables
1892 if ((flags & Flags.CompilerGenerated) != 0)
1895 ec.Emit (OpCodes.Stloc, builder);
1898 public void EmitAddressOf (EmitContext ec)
1900 ec.Emit (OpCodes.Ldloca, builder);
1903 public static string GetCompilerGeneratedName (Block block)
1905 // HACK: Debugger depends on the name semantics
1906 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1909 public string GetReadOnlyContext ()
1911 switch (flags & Flags.ReadonlyMask) {
1912 case Flags.FixedVariable:
1913 return "fixed variable";
1914 case Flags.ForeachVariable:
1915 return "foreach iteration variable";
1916 case Flags.UsingVariable:
1917 return "using variable";
1920 throw new InternalErrorException ("Variable is not readonly");
1923 public bool IsThisAssigned (BlockContext ec, Block block)
1925 if (VariableInfo == null)
1926 throw new Exception ();
1928 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1931 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1934 public bool IsAssigned (BlockContext ec)
1936 if (VariableInfo == null)
1937 throw new Exception ();
1939 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1942 public void PrepareForFlowAnalysis (BlockContext bc)
1945 // No need for definitely assigned check for these guys
1947 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1950 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1951 bc.FlowOffset += VariableInfo.Length;
1955 // Mark the variables as referenced in the user code
1957 public void SetIsUsed ()
1959 flags |= Flags.Used;
1962 public void SetHasAddressTaken ()
1964 flags |= (Flags.AddressTaken | Flags.Used);
1967 public override string ToString ()
1969 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
1974 /// Block represents a C# block.
1978 /// This class is used in a number of places: either to represent
1979 /// explicit blocks that the programmer places or implicit blocks.
1981 /// Implicit blocks are used as labels or to introduce variable
1984 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1985 /// they contain extra information that is not necessary on normal blocks.
1987 public class Block : Statement {
1994 HasCapturedVariable = 64,
1995 HasCapturedThis = 1 << 7,
1996 IsExpressionTree = 1 << 8,
1997 CompilerGenerated = 1 << 9,
1998 HasAsyncModifier = 1 << 10,
2000 YieldBlock = 1 << 12,
2001 AwaitBlock = 1 << 13
2004 public Block Parent;
2005 public Location StartLocation;
2006 public Location EndLocation;
2008 public ExplicitBlock Explicit;
2009 public ParametersBlock ParametersBlock;
2011 protected Flags flags;
2014 // The statements in this block
2016 protected List<Statement> statements;
2018 protected List<Statement> scope_initializers;
2020 int? resolving_init_idx;
2026 public int ID = id++;
2028 static int clone_id_counter;
2032 // int assignable_slots;
2033 bool unreachable_shown;
2036 public Block (Block parent, Location start, Location end)
2037 : this (parent, 0, start, end)
2041 public Block (Block parent, Flags flags, Location start, Location end)
2043 if (parent != null) {
2044 // the appropriate constructors will fixup these fields
2045 ParametersBlock = parent.ParametersBlock;
2046 Explicit = parent.Explicit;
2049 this.Parent = parent;
2051 this.StartLocation = start;
2052 this.EndLocation = end;
2054 statements = new List<Statement> (4);
2056 this.original = this;
2061 public bool HasUnreachableClosingBrace {
2063 return (flags & Flags.HasRet) != 0;
2066 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2070 public Block Original {
2079 public bool IsCompilerGenerated {
2080 get { return (flags & Flags.CompilerGenerated) != 0; }
2081 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2084 public bool Unchecked {
2085 get { return (flags & Flags.Unchecked) != 0; }
2086 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2089 public bool Unsafe {
2090 get { return (flags & Flags.Unsafe) != 0; }
2091 set { flags |= Flags.Unsafe; }
2094 public List<Statement> Statements {
2095 get { return statements; }
2100 public Block CreateSwitchBlock (Location start)
2102 // FIXME: Only explicit block should be created
2103 var new_block = new Block (this, start, start);
2104 new_block.IsCompilerGenerated = true;
2108 public void SetEndLocation (Location loc)
2113 public void AddLabel (LabeledStatement target)
2115 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2118 public void AddLocalName (LocalVariable li)
2120 AddLocalName (li.Name, li);
2123 public void AddLocalName (string name, INamedBlockVariable li)
2125 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2128 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2130 if (reason == null) {
2131 Error_AlreadyDeclared (name, variable);
2135 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2136 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2137 "to `{0}', which is already used in a `{1}' scope to denote something else",
2141 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2143 var pi = variable as ParametersBlock.ParameterInfo;
2145 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2147 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2148 "A local variable named `{0}' is already defined in this scope", name);
2152 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2154 ParametersBlock.TopBlock.Report.Error (412, loc,
2155 "The type parameter name `{0}' is the same as local variable or parameter name",
2160 // It should be used by expressions which require to
2161 // register a statement during resolve process.
2163 public void AddScopeStatement (Statement s)
2165 if (scope_initializers == null)
2166 scope_initializers = new List<Statement> ();
2169 // Simple recursive helper, when resolve scope initializer another
2170 // new scope initializer can be added, this ensures it's initialized
2171 // before existing one. For now this can happen with expression trees
2172 // in base ctor initializer only
2174 if (resolving_init_idx.HasValue) {
2175 scope_initializers.Insert (resolving_init_idx.Value, s);
2176 ++resolving_init_idx;
2178 scope_initializers.Add (s);
2182 public void AddStatement (Statement s)
2187 public int AssignableSlots {
2189 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2191 // return assignable_slots;
2195 public LabeledStatement LookupLabel (string name)
2197 return ParametersBlock.TopBlock.GetLabel (name, this);
2200 public override bool Resolve (BlockContext ec)
2202 if ((flags & Flags.Resolved) != 0)
2205 Block prev_block = ec.CurrentBlock;
2208 ec.CurrentBlock = this;
2209 ec.StartFlowBranching (this);
2212 // Compiler generated scope statements
2214 if (scope_initializers != null) {
2215 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2216 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2219 resolving_init_idx = null;
2223 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2224 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2225 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2226 // responsible for handling the situation.
2228 int statement_count = statements.Count;
2229 for (int ix = 0; ix < statement_count; ix++){
2230 Statement s = statements [ix];
2233 // Warn if we detect unreachable code.
2236 if (s is EmptyStatement)
2239 if (!unreachable_shown && !(s is LabeledStatement)) {
2240 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2241 unreachable_shown = true;
2244 Block c_block = s as Block;
2245 if (c_block != null)
2246 c_block.unreachable = c_block.unreachable_shown = true;
2250 // Note that we're not using ResolveUnreachable() for unreachable
2251 // statements here. ResolveUnreachable() creates a temporary
2252 // flow branching and kills it afterwards. This leads to problems
2253 // if you have two unreachable statements where the first one
2254 // assigns a variable and the second one tries to access it.
2257 if (!s.Resolve (ec)) {
2259 if (ec.IsInProbingMode)
2262 statements [ix] = new EmptyStatement (s.loc);
2266 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2267 statements [ix] = new EmptyStatement (s.loc);
2269 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2270 if (unreachable && s is LabeledStatement)
2271 throw new InternalErrorException ("should not happen");
2274 while (ec.CurrentBranching is FlowBranchingLabeled)
2275 ec.EndFlowBranching ();
2277 bool flow_unreachable = ec.EndFlowBranching ();
2279 ec.CurrentBlock = prev_block;
2281 if (flow_unreachable)
2282 flags |= Flags.HasRet;
2284 // If we're a non-static `struct' constructor which doesn't have an
2285 // initializer, then we must initialize all of the struct's fields.
2286 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2289 flags |= Flags.Resolved;
2293 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2295 unreachable_shown = true;
2299 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2301 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2302 fb.CurrentUsageVector.IsUnreachable = true;
2303 bool ok = Resolve (ec);
2304 ec.KillFlowBranching ();
2309 protected override void DoEmit (EmitContext ec)
2311 for (int ix = 0; ix < statements.Count; ix++){
2312 statements [ix].Emit (ec);
2316 public override void Emit (EmitContext ec)
2318 if (scope_initializers != null)
2319 EmitScopeInitializers (ec);
2324 protected void EmitScopeInitializers (EmitContext ec)
2326 foreach (Statement s in scope_initializers)
2331 public override string ToString ()
2333 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2337 protected override void CloneTo (CloneContext clonectx, Statement t)
2339 Block target = (Block) t;
2341 target.clone_id = clone_id_counter++;
2344 clonectx.AddBlockMap (this, target);
2345 if (original != this)
2346 clonectx.AddBlockMap (original, target);
2348 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2349 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2352 target.Parent = clonectx.RemapBlockCopy (Parent);
2354 target.statements = new List<Statement> (statements.Count);
2355 foreach (Statement s in statements)
2356 target.statements.Add (s.Clone (clonectx));
2359 public override object Accept (StructuralVisitor visitor)
2361 return visitor.Visit (this);
2365 public class ExplicitBlock : Block
2367 protected AnonymousMethodStorey am_storey;
2369 public ExplicitBlock (Block parent, Location start, Location end)
2370 : this (parent, (Flags) 0, start, end)
2374 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2375 : base (parent, flags, start, end)
2377 this.Explicit = this;
2382 public AnonymousMethodStorey AnonymousMethodStorey {
2388 public bool HasAwait {
2390 return (flags & Flags.AwaitBlock) != 0;
2394 public bool HasCapturedThis {
2395 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2397 return (flags & Flags.HasCapturedThis) != 0;
2401 public bool HasCapturedVariable {
2402 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2404 return (flags & Flags.HasCapturedVariable) != 0;
2408 public bool HasYield {
2410 return (flags & Flags.YieldBlock) != 0;
2417 // Creates anonymous method storey in current block
2419 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2422 // Return same story for iterator and async blocks unless we are
2423 // in nested anonymous method
2425 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2426 return ec.CurrentAnonymousMethod.Storey;
2429 // When referencing a variable in parent iterator/async storey
2430 // from nested anonymous method
2432 if (ParametersBlock.am_storey is StateMachine) {
2433 return ParametersBlock.am_storey;
2436 if (am_storey == null) {
2437 MemberBase mc = ec.MemberContext as MemberBase;
2440 // Creates anonymous method storey for this block
2442 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey");
2448 public override void Emit (EmitContext ec)
2450 if (am_storey != null) {
2451 DefineAnonymousStorey (ec);
2452 am_storey.EmitStoreyInstantiation (ec, this);
2455 if (scope_initializers != null)
2456 EmitScopeInitializers (ec);
2458 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2459 ec.Emit (OpCodes.Nop);
2470 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2471 ec.Emit (OpCodes.Nop);
2475 void DefineAnonymousStorey (EmitContext ec)
2478 // Creates anonymous method storey
2480 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2482 // Creates parent storey reference when hoisted this is accessible
2484 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2485 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2488 // Hoisted this exists in top-level parent storey only
2490 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2491 parent = parent.Parent.Explicit;
2493 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2496 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2498 // TODO MemberCache: Review
2499 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2502 am_storey.CreateContainer ();
2503 am_storey.DefineContainer ();
2505 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2506 if (ref_blocks != null) {
2507 foreach (ExplicitBlock ref_block in ref_blocks) {
2508 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2509 if (b.am_storey != null) {
2510 b.am_storey.AddParentStoreyReference (ec, am_storey);
2512 // Stop propagation inside same top block
2513 if (b.ParametersBlock.Original == ParametersBlock.Original)
2516 b = b.ParametersBlock;
2519 b.HasCapturedVariable = true;
2524 am_storey.Define ();
2525 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2528 public void RegisterAsyncAwait ()
2531 while ((block.flags & Flags.AwaitBlock) == 0) {
2532 block.flags |= Flags.AwaitBlock;
2534 if (block.Parent == null)
2537 block = block.Parent.Explicit;
2541 public void RegisterIteratorYield ()
2544 while ((block.flags & Flags.YieldBlock) == 0) {
2545 block.flags |= Flags.YieldBlock;
2547 if (block.Parent == null)
2550 block = block.Parent.Explicit;
2554 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2556 tryBlock.statements = statements;
2557 statements = new List<Statement> (1);
2558 statements.Add (tf);
2563 // ParametersBlock was introduced to support anonymous methods
2564 // and lambda expressions
2566 public class ParametersBlock : ExplicitBlock
2568 public class ParameterInfo : INamedBlockVariable
2570 readonly ParametersBlock block;
2572 public VariableInfo VariableInfo;
2575 public ParameterInfo (ParametersBlock block, int index)
2583 public Block Block {
2589 public bool IsDeclared {
2595 public bool IsParameter {
2601 public bool IsLocked {
2610 public Location Location {
2612 return Parameter.Location;
2616 public Parameter Parameter {
2618 return block.Parameters [index];
2622 public TypeSpec ParameterType {
2624 return Parameter.Type;
2630 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2632 return new ParameterReference (this, loc);
2637 // Block is converted into an expression
2639 sealed class BlockScopeExpression : Expression
2642 readonly ParametersBlock block;
2644 public BlockScopeExpression (Expression child, ParametersBlock block)
2650 public override bool ContainsEmitWithAwait ()
2652 return child.ContainsEmitWithAwait ();
2655 public override Expression CreateExpressionTree (ResolveContext ec)
2657 throw new NotSupportedException ();
2660 protected override Expression DoResolve (ResolveContext ec)
2665 child = child.Resolve (ec);
2669 eclass = child.eclass;
2674 public override void Emit (EmitContext ec)
2676 block.EmitScopeInitializers (ec);
2681 protected ParametersCompiled parameters;
2682 protected ParameterInfo[] parameter_info;
2684 protected bool unreachable;
2685 protected ToplevelBlock top_block;
2687 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2688 : base (parent, 0, start, start)
2690 if (parameters == null)
2691 throw new ArgumentNullException ("parameters");
2693 this.parameters = parameters;
2694 ParametersBlock = this;
2696 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2698 this.top_block = parent.ParametersBlock.top_block;
2699 ProcessParameters ();
2702 protected ParametersBlock (ParametersCompiled parameters, Location start)
2703 : base (null, 0, start, start)
2705 if (parameters == null)
2706 throw new ArgumentNullException ("parameters");
2708 this.parameters = parameters;
2709 ParametersBlock = this;
2713 // It's supposed to be used by method body implementation of anonymous methods
2715 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2716 : base (null, 0, source.StartLocation, source.EndLocation)
2718 this.parameters = parameters;
2719 this.statements = source.statements;
2720 this.scope_initializers = source.scope_initializers;
2722 this.resolved = true;
2723 this.unreachable = source.unreachable;
2724 this.am_storey = source.am_storey;
2726 ParametersBlock = this;
2729 // Overwrite original for comparison purposes when linking cross references
2730 // between anonymous methods
2737 public bool IsAsync {
2739 return (flags & Flags.HasAsyncModifier) != 0;
2742 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2747 // Block has been converted to expression tree
2749 public bool IsExpressionTree {
2751 return (flags & Flags.IsExpressionTree) != 0;
2756 // The parameters for the block.
2758 public ParametersCompiled Parameters {
2764 public ToplevelBlock TopBlock {
2770 public bool Resolved {
2772 return (flags & Flags.Resolved) != 0;
2776 public int TemporaryLocalsCount { get; set; }
2781 // Check whether all `out' parameters have been assigned.
2783 public void CheckOutParameters (FlowBranching.UsageVector vector)
2785 if (vector.IsUnreachable)
2788 int n = parameter_info == null ? 0 : parameter_info.Length;
2790 for (int i = 0; i < n; i++) {
2791 VariableInfo var = parameter_info[i].VariableInfo;
2796 if (vector.IsAssigned (var, false))
2799 var p = parameter_info[i].Parameter;
2800 TopBlock.Report.Error (177, p.Location,
2801 "The out parameter `{0}' must be assigned to before control leaves the current method",
2806 public override Expression CreateExpressionTree (ResolveContext ec)
2808 if (statements.Count == 1) {
2809 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2810 if (scope_initializers != null)
2811 expr = new BlockScopeExpression (expr, this);
2816 return base.CreateExpressionTree (ec);
2819 public ParameterInfo GetParameterInfo (Parameter p)
2821 for (int i = 0; i < parameters.Count; ++i) {
2822 if (parameters[i] == p)
2823 return parameter_info[i];
2826 throw new ArgumentException ("Invalid parameter");
2829 public Expression GetParameterReference (int index, Location loc)
2831 return new ParameterReference (parameter_info[index], loc);
2834 public Statement PerformClone ()
2836 CloneContext clonectx = new CloneContext ();
2837 return Clone (clonectx);
2840 protected void ProcessParameters ()
2842 if (parameters.Count == 0)
2845 parameter_info = new ParameterInfo[parameters.Count];
2846 for (int i = 0; i < parameter_info.Length; ++i) {
2847 var p = parameters.FixedParameters[i];
2851 // TODO: Should use Parameter only and more block there
2852 parameter_info[i] = new ParameterInfo (this, i);
2854 AddLocalName (p.Name, parameter_info[i]);
2858 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2865 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2866 flags |= Flags.IsExpressionTree;
2871 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2872 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2877 unreachable = top_level.End ();
2879 } catch (Exception e) {
2880 if (e is CompletionResult || rc.Report.IsDisabled)
2883 if (rc.CurrentBlock != null) {
2884 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2886 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2889 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2893 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2894 if (rc.CurrentAnonymousMethod == null) {
2895 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2896 if (md is StateMachineMethod) {
2899 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2904 // If an asynchronous body of F is either an expression classified as nothing, or a
2905 // statement block where no return statements have expressions, the inferred return type is Task
2908 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2909 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2910 am.ReturnTypeInference = null;
2911 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2916 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2917 rc.CurrentAnonymousMethod.GetSignatureForError ());
2925 void ResolveMeta (BlockContext ec)
2927 int orig_count = parameters.Count;
2929 for (int i = 0; i < orig_count; ++i) {
2930 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2932 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2935 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2936 parameter_info[i].VariableInfo = vi;
2937 ec.FlowOffset += vi.Length;
2941 public void WrapIntoIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
2943 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, Location.Null);
2944 pb.statements = statements;
2947 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2948 am_storey = new IteratorStorey (iterator);
2950 statements = new List<Statement> (1);
2951 AddStatement (new Return (iterator, iterator.Location));
2952 flags &= ~Flags.YieldBlock;
2953 IsCompilerGenerated = true;
2956 public void WrapIntoAsyncTask (IMemberContext context, TypeDefinition host, TypeSpec returnType)
2958 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, Location.Null);
2959 pb.statements = statements;
2962 var block_type = host.Module.Compiler.BuiltinTypes.Void;
2963 var initializer = new AsyncInitializer (pb, host, block_type);
2964 initializer.Type = block_type;
2966 am_storey = new AsyncTaskStorey (context, initializer, returnType);
2968 statements = new List<Statement> (1);
2969 AddStatement (new StatementExpression (initializer));
2970 flags &= ~Flags.AwaitBlock;
2971 IsCompilerGenerated = true;
2978 public class ToplevelBlock : ParametersBlock
2980 LocalVariable this_variable;
2981 CompilerContext compiler;
2982 Dictionary<string, object> names;
2983 Dictionary<string, object> labels;
2985 public HoistedVariable HoistedThisVariable;
2987 public Report Report {
2988 get { return compiler.Report; }
2991 public ToplevelBlock (CompilerContext ctx, Location loc)
2992 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
2996 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
2997 : base (parameters, start)
2999 this.compiler = ctx;
3001 flags |= Flags.HasRet;
3003 ProcessParameters ();
3007 // Recreates a top level block from parameters block. Used for
3008 // compiler generated methods where the original block comes from
3009 // explicit child block. This works for already resolved blocks
3010 // only to ensure we resolve them in the correct flow order
3012 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3013 : base (source, parameters)
3015 this.compiler = source.TopBlock.compiler;
3017 flags |= Flags.HasRet;
3020 public bool IsIterator {
3026 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3029 names = new Dictionary<string, object> ();
3032 if (!names.TryGetValue (name, out value)) {
3033 names.Add (name, li);
3037 INamedBlockVariable existing = value as INamedBlockVariable;
3038 List<INamedBlockVariable> existing_list;
3039 if (existing != null) {
3040 existing_list = new List<INamedBlockVariable> ();
3041 existing_list.Add (existing);
3042 names[name] = existing_list;
3044 existing_list = (List<INamedBlockVariable>) value;
3048 // A collision checking between local names
3050 for (int i = 0; i < existing_list.Count; ++i) {
3051 existing = existing_list[i];
3052 Block b = existing.Block.Explicit;
3054 // Collision at same level
3055 if (li.Block.Explicit == b) {
3056 li.Block.Error_AlreadyDeclared (name, li);
3060 // Collision with parent
3061 Block parent = li.Block.Explicit;
3062 while ((parent = parent.Parent) != null) {
3064 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3065 i = existing_list.Count;
3070 if (!ignoreChildrenBlocks) {
3071 // Collision with children
3072 while ((b = b.Parent) != null) {
3073 if (li.Block.Explicit == b) {
3074 li.Block.Error_AlreadyDeclared (name, li, "child");
3075 i = existing_list.Count;
3082 existing_list.Add (li);
3085 public void AddLabel (string name, LabeledStatement label)
3088 labels = new Dictionary<string, object> ();
3091 if (!labels.TryGetValue (name, out value)) {
3092 labels.Add (name, label);
3096 LabeledStatement existing = value as LabeledStatement;
3097 List<LabeledStatement> existing_list;
3098 if (existing != null) {
3099 existing_list = new List<LabeledStatement> ();
3100 existing_list.Add (existing);
3101 labels[name] = existing_list;
3103 existing_list = (List<LabeledStatement>) value;
3107 // A collision checking between labels
3109 for (int i = 0; i < existing_list.Count; ++i) {
3110 existing = existing_list[i];
3111 Block b = existing.Block;
3113 // Collision at same level
3114 if (label.Block == b) {
3115 Report.SymbolRelatedToPreviousError (existing.loc, name);
3116 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3120 // Collision with parent
3122 while ((b = b.Parent) != null) {
3123 if (existing.Block == b) {
3124 Report.Error (158, label.loc,
3125 "The label `{0}' shadows another label by the same name in a contained scope", name);
3126 i = existing_list.Count;
3131 // Collision with with children
3133 while ((b = b.Parent) != null) {
3134 if (label.Block == b) {
3135 Report.Error (158, label.loc,
3136 "The label `{0}' shadows another label by the same name in a contained scope", name);
3137 i = existing_list.Count;
3143 existing_list.Add (label);
3147 // Creates an arguments set from all parameters, useful for method proxy calls
3149 public Arguments GetAllParametersArguments ()
3151 int count = parameters.Count;
3152 Arguments args = new Arguments (count);
3153 for (int i = 0; i < count; ++i) {
3154 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3155 args.Add (new Argument (arg_expr));
3162 // Lookup inside a block, the returned value can represent 3 states
3164 // true+variable: A local name was found and it's valid
3165 // false+variable: A local name was found in a child block only
3166 // false+null: No local name was found
3168 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3174 if (!names.TryGetValue (name, out value))
3177 variable = value as INamedBlockVariable;
3179 if (variable != null) {
3181 if (variable.Block == b.Original)
3185 } while (b != null);
3193 } while (b != null);
3195 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3196 for (int i = 0; i < list.Count; ++i) {
3199 if (variable.Block == b.Original)
3203 } while (b != null);
3211 } while (b != null);
3221 public LabeledStatement GetLabel (string name, Block block)
3227 if (!labels.TryGetValue (name, out value)) {
3231 var label = value as LabeledStatement;
3233 if (label != null) {
3234 if (label.Block == b.Original)
3237 // TODO: Temporary workaround for the switch block implicit label block
3238 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3241 List<LabeledStatement> list = (List<LabeledStatement>) value;
3242 for (int i = 0; i < list.Count; ++i) {
3244 if (label.Block == b.Original)
3247 // TODO: Temporary workaround for the switch block implicit label block
3248 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3257 // Returns the "this" instance variable of this block.
3258 // See AddThisVariable() for more information.
3260 public LocalVariable ThisVariable {
3261 get { return this_variable; }
3265 // This is used by non-static `struct' constructors which do not have an
3266 // initializer - in this case, the constructor must initialize all of the
3267 // struct's fields. To do this, we add a "this" variable and use the flow
3268 // analysis code to ensure that it's been fully initialized before control
3269 // leaves the constructor.
3271 public void AddThisVariable (BlockContext bc)
3273 if (this_variable != null)
3274 throw new InternalErrorException (StartLocation.ToString ());
3276 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3277 this_variable.Type = bc.CurrentType;
3278 this_variable.PrepareForFlowAnalysis (bc);
3281 public bool IsThisAssigned (BlockContext ec)
3283 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3286 public override void Emit (EmitContext ec)
3288 if (Report.Errors > 0)
3294 if (IsCompilerGenerated) {
3295 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3303 // If `HasReturnLabel' is set, then we already emitted a
3304 // jump to the end of the method, so we must emit a `ret'
3307 // Unfortunately, System.Reflection.Emit automatically emits
3308 // a leave to the end of a finally block. This is a problem
3309 // if no code is following the try/finally block since we may
3310 // jump to a point after the end of the method.
3311 // As a workaround, we're always creating a return label in
3314 if (ec.HasReturnLabel || !unreachable) {
3315 if (ec.HasReturnLabel)
3316 ec.MarkLabel (ec.ReturnLabel);
3318 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3319 ec.Mark (EndLocation);
3321 if (ec.ReturnType.Kind != MemberKind.Void)
3322 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3324 ec.Emit (OpCodes.Ret);
3328 } catch (Exception e){
3329 Console.WriteLine ("Exception caught by the compiler while emitting:");
3330 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3332 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3339 public class SwitchLabel {
3342 readonly Location loc;
3347 // if expr == null, then it is the default case.
3349 public SwitchLabel (Expression expr, Location l)
3355 public bool IsDefault {
3357 return label == null;
3361 public Expression Label {
3367 public Location Location {
3373 public Constant Converted {
3382 public Label GetILLabel (EmitContext ec)
3384 if (il_label == null){
3385 il_label = ec.DefineLabel ();
3388 return il_label.Value;
3392 // Resolves the expression, reduces it to a literal if possible
3393 // and then converts it to the requested type.
3395 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3397 Expression e = label.Resolve (ec);
3402 Constant c = e as Constant;
3404 ec.Report.Error (150, loc, "A constant value is expected");
3408 if (allow_nullable && c is NullLiteral) {
3413 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3414 return converted != null;
3417 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3420 if (converted == null)
3423 label = converted.GetValueAsLiteral ();
3425 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3426 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3429 public SwitchLabel Clone (CloneContext clonectx)
3434 return new SwitchLabel (label.Clone (clonectx), loc);
3438 public class SwitchSection {
3439 public readonly List<SwitchLabel> Labels;
3440 public readonly Block Block;
3442 public SwitchSection (List<SwitchLabel> labels, Block block)
3448 public SwitchSection Clone (CloneContext clonectx)
3450 var cloned_labels = new List<SwitchLabel> ();
3452 foreach (SwitchLabel sl in Labels)
3453 cloned_labels.Add (sl.Clone (clonectx));
3455 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3459 public class Switch : Statement
3461 // structure used to hold blocks of keys while calculating table switch
3462 sealed class LabelsRange : IComparable<LabelsRange>
3464 public readonly long min;
3466 public readonly List<long> label_values;
3468 public LabelsRange (long value)
3471 label_values = new List<long> ();
3472 label_values.Add (value);
3475 public LabelsRange (long min, long max, ICollection<long> values)
3479 this.label_values = new List<long> (values);
3484 return max - min + 1;
3488 public bool AddValue (long value)
3490 var gap = value - min + 1;
3491 // Ensure the range has > 50% occupancy
3492 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3496 label_values.Add (value);
3500 public int CompareTo (LabelsRange other)
3502 int nLength = label_values.Count;
3503 int nLengthOther = other.label_values.Count;
3504 if (nLengthOther == nLength)
3505 return (int) (other.min - min);
3507 return nLength - nLengthOther;
3511 sealed class LabelMarker : Statement
3514 readonly List<SwitchLabel> labels;
3516 public LabelMarker (Switch s, List<SwitchLabel> labels)
3519 this.labels = labels;
3522 protected override void CloneTo (CloneContext clonectx, Statement target)
3526 protected override void DoEmit (EmitContext ec)
3528 foreach (var l in labels) {
3530 ec.MarkLabel (s.DefaultLabel);
3532 ec.MarkLabel (l.GetILLabel (ec));
3537 public List<SwitchSection> Sections;
3538 public Expression Expr;
3541 // Mapping of all labels to their SwitchLabels
3543 Dictionary<long, SwitchLabel> labels;
3544 Dictionary<string, SwitchLabel> string_labels;
3547 /// The governing switch type
3549 public TypeSpec SwitchType;
3554 Label default_target;
3556 Expression new_expr;
3559 SwitchSection constant_section;
3560 SwitchSection default_section;
3561 SwitchLabel null_section;
3563 Statement simple_stmt;
3564 VariableReference value;
3565 ExpressionStatement string_dictionary;
3566 FieldExpr switch_cache_field;
3567 ExplicitBlock block;
3570 // Nullable Types support
3572 Nullable.Unwrap unwrap;
3574 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3582 public ExplicitBlock Block {
3588 public Label DefaultLabel {
3590 return default_target;
3594 public bool GotDefault {
3596 return default_section != null;
3600 public bool IsNullable {
3602 return unwrap != null;
3607 // Determines the governing type for a switch. The returned
3608 // expression might be the expression from the switch, or an
3609 // expression that includes any potential conversions to
3611 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3613 switch (expr.Type.BuiltinType) {
3614 case BuiltinTypeSpec.Type.Byte:
3615 case BuiltinTypeSpec.Type.SByte:
3616 case BuiltinTypeSpec.Type.UShort:
3617 case BuiltinTypeSpec.Type.Short:
3618 case BuiltinTypeSpec.Type.UInt:
3619 case BuiltinTypeSpec.Type.Int:
3620 case BuiltinTypeSpec.Type.ULong:
3621 case BuiltinTypeSpec.Type.Long:
3622 case BuiltinTypeSpec.Type.Char:
3623 case BuiltinTypeSpec.Type.String:
3624 case BuiltinTypeSpec.Type.Bool:
3628 if (expr.Type.IsEnum)
3632 // Try to find a *user* defined implicit conversion.
3634 // If there is no implicit conversion, or if there are multiple
3635 // conversions, we have to report an error
3637 Expression converted = null;
3638 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3641 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3646 // Ignore over-worked ImplicitUserConversions that do
3647 // an implicit conversion in addition to the user conversion.
3649 if (!(e is UserCast))
3652 if (converted != null){
3653 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3662 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3664 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3680 // Performs the basic sanity checks on the switch statement
3681 // (looks for duplicate keys and non-constant expressions).
3683 // It also returns a hashtable with the keys that we will later
3684 // use to compute the switch tables
3686 bool CheckSwitch (ResolveContext ec)
3689 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3690 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3692 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3694 foreach (SwitchSection ss in Sections){
3695 foreach (SwitchLabel sl in ss.Labels){
3697 if (default_section != null){
3698 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3701 default_section = ss;
3705 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3711 if (string_labels != null) {
3712 string s = sl.Converted.GetValue () as string;
3716 string_labels.Add (s, sl);
3718 if (sl.Converted is NullLiteral) {
3721 labels.Add (sl.Converted.GetValueAsLong (), sl);
3724 } catch (ArgumentException) {
3725 if (string_labels != null)
3726 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3728 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3738 // This method emits code for a lookup-based switch statement (non-string)
3739 // Basically it groups the cases into blocks that are at least half full,
3740 // and then spits out individual lookup opcodes for each block.
3741 // It emits the longest blocks first, and short blocks are just
3742 // handled with direct compares.
3744 void EmitTableSwitch (EmitContext ec, Expression val)
3746 Label lbl_default = default_target;
3748 if (labels != null && labels.Count > 0) {
3749 List<LabelsRange> ranges;
3750 if (string_labels != null) {
3751 // We have done all hard work for string already
3752 // setup single range only
3753 ranges = new List<LabelsRange> (1);
3754 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3756 var element_keys = new long[labels.Count];
3757 labels.Keys.CopyTo (element_keys, 0);
3758 Array.Sort (element_keys);
3761 // Build possible ranges of switch labes to reduce number
3764 ranges = new List<LabelsRange> (element_keys.Length);
3765 var range = new LabelsRange (element_keys[0]);
3767 for (int i = 1; i < element_keys.Length; ++i) {
3768 var l = element_keys[i];
3769 if (range.AddValue (l))
3772 range = new LabelsRange (l);
3776 // sort the blocks so we can tackle the largest ones first
3780 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3782 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3783 LabelsRange kb = ranges[range_index];
3784 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3786 // Optimize small ranges using simple equality check
3787 if (kb.Range <= 2) {
3788 foreach (var key in kb.label_values) {
3789 SwitchLabel sl = labels[key];
3790 if (sl.Converted.IsDefaultValue) {
3791 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3794 sl.Converted.Emit (ec);
3795 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3799 // TODO: if all the keys in the block are the same and there are
3800 // no gaps/defaults then just use a range-check.
3801 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3802 // TODO: optimize constant/I4 cases
3804 // check block range (could be > 2^31)
3806 ec.EmitLong (kb.min);
3807 ec.Emit (OpCodes.Blt, lbl_default);
3810 ec.EmitLong (kb.max);
3811 ec.Emit (OpCodes.Bgt, lbl_default);
3816 ec.EmitLong (kb.min);
3817 ec.Emit (OpCodes.Sub);
3820 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3824 int first = (int) kb.min;
3827 ec.Emit (OpCodes.Sub);
3828 } else if (first < 0) {
3829 ec.EmitInt (-first);
3830 ec.Emit (OpCodes.Add);
3834 // first, build the list of labels for the switch
3836 long cJumps = kb.Range;
3837 Label[] switch_labels = new Label[cJumps];
3838 for (int iJump = 0; iJump < cJumps; iJump++) {
3839 var key = kb.label_values[iKey];
3840 if (key == kb.min + iJump) {
3841 switch_labels[iJump] = labels[key].GetILLabel (ec);
3844 switch_labels[iJump] = lbl_default;
3848 // emit the switch opcode
3849 ec.Emit (OpCodes.Switch, switch_labels);
3852 // mark the default for this block
3853 if (range_index != 0)
3854 ec.MarkLabel (lbl_default);
3857 // the last default just goes to the end
3858 if (ranges.Count > 0)
3859 ec.Emit (OpCodes.Br, lbl_default);
3862 // now emit the code for the sections
3863 bool found_default = false;
3865 foreach (SwitchSection ss in Sections) {
3866 foreach (SwitchLabel sl in ss.Labels) {
3868 ec.MarkLabel (lbl_default);
3869 found_default = true;
3870 if (null_section == null)
3871 ec.MarkLabel (null_target);
3872 } else if (sl.Converted.IsNull) {
3873 ec.MarkLabel (null_target);
3876 ec.MarkLabel (sl.GetILLabel (ec));
3882 if (!found_default) {
3883 ec.MarkLabel (lbl_default);
3884 if (null_section == null) {
3885 ec.MarkLabel (null_target);
3890 SwitchLabel FindLabel (Constant value)
3892 SwitchLabel sl = null;
3894 if (string_labels != null) {
3895 string s = value.GetValue () as string;
3897 if (null_section != null)
3899 else if (default_section != null)
3900 sl = default_section.Labels[0];
3902 string_labels.TryGetValue (s, out sl);
3905 if (value is NullLiteral) {
3908 labels.TryGetValue (value.GetValueAsLong (), out sl);
3915 SwitchSection FindSection (SwitchLabel label)
3917 foreach (SwitchSection ss in Sections){
3918 foreach (SwitchLabel sl in ss.Labels){
3927 public override bool Resolve (BlockContext ec)
3929 Expr = Expr.Resolve (ec);
3933 new_expr = SwitchGoverningType (ec, Expr);
3935 if (new_expr == null && Expr.Type.IsNullableType) {
3936 unwrap = Nullable.Unwrap.Create (Expr, false);
3940 new_expr = SwitchGoverningType (ec, unwrap);
3943 if (new_expr == null){
3944 ec.Report.Error (151, loc,
3945 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3946 TypeManager.CSharpName (Expr.Type));
3951 SwitchType = new_expr.Type;
3953 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3954 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3958 if (!CheckSwitch (ec))
3961 Switch old_switch = ec.Switch;
3963 ec.Switch.SwitchType = SwitchType;
3965 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3967 var constant = new_expr as Constant;
3968 if (constant != null) {
3970 SwitchLabel label = FindLabel (constant);
3972 constant_section = FindSection (label);
3974 if (constant_section == null)
3975 constant_section = default_section;
3978 // Store switch expression for comparission purposes
3980 value = new_expr as VariableReference;
3982 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
3987 foreach (SwitchSection ss in Sections){
3989 ec.CurrentBranching.CreateSibling (
3990 null, FlowBranching.SiblingType.SwitchSection);
3994 if (is_constant && (ss != constant_section)) {
3995 // If we're a constant switch, we're only emitting
3996 // one single section - mark all the others as
3998 ec.CurrentBranching.CurrentUsageVector.Goto ();
3999 if (!ss.Block.ResolveUnreachable (ec, true)) {
4003 if (!ss.Block.Resolve (ec))
4008 if (default_section == null)
4009 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4011 ec.EndFlowBranching ();
4012 ec.Switch = old_switch;
4018 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4019 if (string_labels.Count < 7)
4020 ResolveSimpleSwitch (ec);
4022 ResolveStringSwitchMap (ec);
4023 } else if (labels.Count < 3 && !IsNullable) {
4024 ResolveSimpleSwitch (ec);
4031 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4033 var sl = FindLabel (value);
4036 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4043 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4045 void ResolveSimpleSwitch (BlockContext bc)
4047 simple_stmt = default_section != null ? default_section.Block : null;
4049 for (int i = Sections.Count - 1; i >= 0; --i) {
4050 var s = Sections[i];
4052 if (s == default_section) {
4053 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4057 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4059 Expression cond = null;
4060 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4061 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4064 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4071 // Compiler generated, hide from symbol file
4073 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4076 // It's null for empty switch
4077 if (simple_stmt != null)
4078 simple_stmt.Resolve (bc);
4082 // Converts string switch into string hashtable
4084 void ResolveStringSwitchMap (ResolveContext ec)
4086 FullNamedExpression string_dictionary_type;
4087 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4088 string_dictionary_type = new TypeExpression (
4089 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4090 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4092 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4093 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4095 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4099 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4100 Field field = new Field (ctype, string_dictionary_type,
4101 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4102 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4103 if (!field.Define ())
4105 ctype.AddField (field);
4107 var init = new List<Expression> ();
4109 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4110 string value = null;
4111 foreach (SwitchSection section in Sections) {
4112 bool contains_label = false;
4113 foreach (SwitchLabel sl in section.Labels) {
4114 if (sl.IsDefault || sl.Converted.IsNull)
4117 if (!contains_label) {
4118 labels.Add (counter, sl);
4119 contains_label = true;
4122 value = (string) sl.Converted.GetValue ();
4123 var init_args = new List<Expression> (2);
4124 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4126 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4127 init_args.Add (sl.Converted);
4129 init.Add (new CollectionElementInitializer (init_args, loc));
4133 // Don't add empty sections
4139 Arguments args = new Arguments (1);
4140 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4141 Expression initializer = new NewInitialize (string_dictionary_type, args,
4142 new CollectionOrObjectInitializers (init, loc), loc);
4144 switch_cache_field = new FieldExpr (field, loc);
4145 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4148 void DoEmitStringSwitch (EmitContext ec)
4150 Label l_initialized = ec.DefineLabel ();
4153 // Skip initialization when value is null
4155 value.EmitBranchable (ec, null_target, false);
4158 // Check if string dictionary is initialized and initialize
4160 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4161 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4162 string_dictionary.EmitStatement (ec);
4164 ec.MarkLabel (l_initialized);
4166 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4168 ResolveContext rc = new ResolveContext (ec.MemberContext);
4170 if (switch_cache_field.Type.IsGeneric) {
4171 Arguments get_value_args = new Arguments (2);
4172 get_value_args.Add (new Argument (value));
4173 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4174 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4175 if (get_item == null)
4179 // A value was not found, go to default case
4181 get_item.EmitBranchable (ec, default_target, false);
4183 Arguments get_value_args = new Arguments (1);
4184 get_value_args.Add (new Argument (value));
4186 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4187 if (get_item == null)
4190 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4191 get_item_object.EmitAssign (ec, get_item, true, false);
4192 ec.Emit (OpCodes.Brfalse, default_target);
4194 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4195 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4197 get_item_int.EmitStatement (ec);
4198 get_item_object.Release (ec);
4201 EmitTableSwitch (ec, string_switch_variable);
4202 string_switch_variable.Release (ec);
4205 protected override void DoEmit (EmitContext ec)
4207 // Workaround broken flow-analysis
4208 block.HasUnreachableClosingBrace = true;
4211 // Needed to emit anonymous storey initialization
4212 // Otherwise it does not contain any statements for now
4216 default_target = ec.DefineLabel ();
4217 null_target = ec.DefineLabel ();
4220 unwrap.EmitCheck (ec);
4221 ec.Emit (OpCodes.Brfalse, null_target);
4222 value.EmitAssign (ec, new_expr, false, false);
4223 } else if (new_expr != value && !is_constant) {
4224 value.EmitAssign (ec, new_expr, false, false);
4228 // Setup the codegen context
4230 Label old_end = ec.LoopEnd;
4231 Switch old_switch = ec.Switch;
4233 ec.LoopEnd = ec.DefineLabel ();
4238 if (constant_section != null)
4239 constant_section.Block.Emit (ec);
4240 } else if (string_dictionary != null) {
4241 DoEmitStringSwitch (ec);
4242 } else if (simple_stmt != null) {
4243 simple_stmt.Emit (ec);
4245 EmitTableSwitch (ec, value);
4248 // Restore context state.
4249 ec.MarkLabel (ec.LoopEnd);
4252 // Restore the previous context
4254 ec.LoopEnd = old_end;
4255 ec.Switch = old_switch;
4258 protected override void CloneTo (CloneContext clonectx, Statement t)
4260 Switch target = (Switch) t;
4262 target.Expr = Expr.Clone (clonectx);
4263 target.Sections = new List<SwitchSection> ();
4264 foreach (SwitchSection ss in Sections){
4265 target.Sections.Add (ss.Clone (clonectx));
4269 public override object Accept (StructuralVisitor visitor)
4271 return visitor.Visit (this);
4275 // A place where execution can restart in an iterator
4276 public abstract class ResumableStatement : Statement
4279 protected Label resume_point;
4281 public Label PrepareForEmit (EmitContext ec)
4285 resume_point = ec.DefineLabel ();
4287 return resume_point;
4290 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4295 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4300 public abstract class TryFinallyBlock : ExceptionStatement
4302 protected Statement stmt;
4303 Label dispose_try_block;
4304 bool prepared_for_dispose, emitted_dispose;
4306 protected TryFinallyBlock (Statement stmt, Location loc)
4314 public Statement Statement {
4322 protected abstract void EmitTryBody (EmitContext ec);
4323 protected abstract void EmitFinallyBody (EmitContext ec);
4325 public override Label PrepareForDispose (EmitContext ec, Label end)
4327 if (!prepared_for_dispose) {
4328 prepared_for_dispose = true;
4329 dispose_try_block = ec.DefineLabel ();
4331 return dispose_try_block;
4334 protected sealed override void DoEmit (EmitContext ec)
4336 EmitTryBodyPrepare (ec);
4339 ec.BeginFinallyBlock ();
4341 Label start_finally = ec.DefineLabel ();
4342 if (resume_points != null) {
4343 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4345 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4346 ec.Emit (OpCodes.Brfalse_S, start_finally);
4347 ec.Emit (OpCodes.Endfinally);
4350 ec.MarkLabel (start_finally);
4351 EmitFinallyBody (ec);
4353 ec.EndExceptionBlock ();
4356 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4358 if (emitted_dispose)
4361 emitted_dispose = true;
4363 Label end_of_try = ec.DefineLabel ();
4365 // Ensure that the only way we can get into this code is through a dispatcher
4366 if (have_dispatcher)
4367 ec.Emit (OpCodes.Br, end);
4369 ec.BeginExceptionBlock ();
4371 ec.MarkLabel (dispose_try_block);
4373 Label[] labels = null;
4374 for (int i = 0; i < resume_points.Count; ++i) {
4375 ResumableStatement s = resume_points[i];
4376 Label ret = s.PrepareForDispose (ec, end_of_try);
4377 if (ret.Equals (end_of_try) && labels == null)
4379 if (labels == null) {
4380 labels = new Label[resume_points.Count];
4381 for (int j = 0; j < i; ++j)
4382 labels[j] = end_of_try;
4387 if (labels != null) {
4389 for (j = 1; j < labels.Length; ++j)
4390 if (!labels[0].Equals (labels[j]))
4392 bool emit_dispatcher = j < labels.Length;
4394 if (emit_dispatcher) {
4395 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4396 ec.Emit (OpCodes.Ldloc, pc);
4397 ec.EmitInt (first_resume_pc);
4398 ec.Emit (OpCodes.Sub);
4399 ec.Emit (OpCodes.Switch, labels);
4400 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4403 foreach (ResumableStatement s in resume_points)
4404 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4407 ec.MarkLabel (end_of_try);
4409 ec.BeginFinallyBlock ();
4411 EmitFinallyBody (ec);
4413 ec.EndExceptionBlock ();
4418 // Base class for blocks using exception handling
4420 public abstract class ExceptionStatement : ResumableStatement
4425 protected List<ResumableStatement> resume_points;
4426 protected int first_resume_pc;
4428 protected ExceptionStatement (Location loc)
4433 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4435 StateMachineInitializer state_machine = null;
4436 if (resume_points != null) {
4437 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4439 ec.EmitInt ((int) IteratorStorey.State.Running);
4440 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4443 ec.BeginExceptionBlock ();
4445 if (resume_points != null) {
4446 ec.MarkLabel (resume_point);
4448 // For normal control flow, we want to fall-through the Switch
4449 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4450 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4451 ec.EmitInt (first_resume_pc);
4452 ec.Emit (OpCodes.Sub);
4454 Label[] labels = new Label[resume_points.Count];
4455 for (int i = 0; i < resume_points.Count; ++i)
4456 labels[i] = resume_points[i].PrepareForEmit (ec);
4457 ec.Emit (OpCodes.Switch, labels);
4461 public void SomeCodeFollows ()
4464 code_follows = true;
4468 public override bool Resolve (BlockContext ec)
4471 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4472 // So, ensure there's some IL code after this statement.
4473 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4474 ec.NeedReturnLabel ();
4479 public void AddResumePoint (ResumableStatement stmt, int pc)
4481 if (resume_points == null) {
4482 resume_points = new List<ResumableStatement> ();
4483 first_resume_pc = pc;
4486 if (pc != first_resume_pc + resume_points.Count)
4487 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4489 resume_points.Add (stmt);
4494 public class Lock : TryFinallyBlock
4497 TemporaryVariableReference expr_copy;
4498 TemporaryVariableReference lock_taken;
4500 public Lock (Expression expr, Statement stmt, Location loc)
4506 public Expression Expr {
4512 public override bool Resolve (BlockContext ec)
4514 expr = expr.Resolve (ec);
4518 if (!TypeSpec.IsReferenceType (expr.Type)) {
4519 ec.Report.Error (185, loc,
4520 "`{0}' is not a reference type as required by the lock statement",
4521 expr.Type.GetSignatureForError ());
4524 if (expr.Type.IsGenericParameter) {
4525 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4528 VariableReference lv = expr as VariableReference;
4531 locked = lv.IsLockedByStatement;
4532 lv.IsLockedByStatement = true;
4538 using (ec.Set (ResolveContext.Options.LockScope)) {
4539 ec.StartFlowBranching (this);
4540 Statement.Resolve (ec);
4541 ec.EndFlowBranching ();
4545 lv.IsLockedByStatement = locked;
4551 // Have to keep original lock value around to unlock same location
4552 // in the case the original has changed or is null
4554 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4555 expr_copy.Resolve (ec);
4558 // Ensure Monitor methods are available
4560 if (ResolvePredefinedMethods (ec) > 1) {
4561 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4562 lock_taken.Resolve (ec);
4568 protected override void EmitTryBodyPrepare (EmitContext ec)
4570 expr_copy.EmitAssign (ec, expr);
4572 if (lock_taken != null) {
4574 // Initialize ref variable
4576 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4579 // Monitor.Enter (expr_copy)
4581 expr_copy.Emit (ec);
4582 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4585 base.EmitTryBodyPrepare (ec);
4588 protected override void EmitTryBody (EmitContext ec)
4591 // Monitor.Enter (expr_copy, ref lock_taken)
4593 if (lock_taken != null) {
4594 expr_copy.Emit (ec);
4595 lock_taken.LocalInfo.CreateBuilder (ec);
4596 lock_taken.AddressOf (ec, AddressOp.Load);
4597 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4600 Statement.Emit (ec);
4603 protected override void EmitFinallyBody (EmitContext ec)
4606 // if (lock_taken) Monitor.Exit (expr_copy)
4608 Label skip = ec.DefineLabel ();
4610 if (lock_taken != null) {
4611 lock_taken.Emit (ec);
4612 ec.Emit (OpCodes.Brfalse_S, skip);
4615 expr_copy.Emit (ec);
4616 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4618 ec.Emit (OpCodes.Call, m);
4620 ec.MarkLabel (skip);
4623 int ResolvePredefinedMethods (ResolveContext rc)
4625 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4626 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4630 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4634 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4638 protected override void CloneTo (CloneContext clonectx, Statement t)
4640 Lock target = (Lock) t;
4642 target.expr = expr.Clone (clonectx);
4643 target.stmt = Statement.Clone (clonectx);
4646 public override object Accept (StructuralVisitor visitor)
4648 return visitor.Visit (this);
4653 public class Unchecked : Statement {
4656 public Unchecked (Block b, Location loc)
4663 public override bool Resolve (BlockContext ec)
4665 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4666 return Block.Resolve (ec);
4669 protected override void DoEmit (EmitContext ec)
4671 using (ec.With (EmitContext.Options.CheckedScope, false))
4675 protected override void CloneTo (CloneContext clonectx, Statement t)
4677 Unchecked target = (Unchecked) t;
4679 target.Block = clonectx.LookupBlock (Block);
4682 public override object Accept (StructuralVisitor visitor)
4684 return visitor.Visit (this);
4688 public class Checked : Statement {
4691 public Checked (Block b, Location loc)
4694 b.Unchecked = false;
4698 public override bool Resolve (BlockContext ec)
4700 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4701 return Block.Resolve (ec);
4704 protected override void DoEmit (EmitContext ec)
4706 using (ec.With (EmitContext.Options.CheckedScope, true))
4710 protected override void CloneTo (CloneContext clonectx, Statement t)
4712 Checked target = (Checked) t;
4714 target.Block = clonectx.LookupBlock (Block);
4717 public override object Accept (StructuralVisitor visitor)
4719 return visitor.Visit (this);
4723 public class Unsafe : Statement {
4726 public Unsafe (Block b, Location loc)
4729 Block.Unsafe = true;
4733 public override bool Resolve (BlockContext ec)
4735 if (ec.CurrentIterator != null)
4736 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4738 using (ec.Set (ResolveContext.Options.UnsafeScope))
4739 return Block.Resolve (ec);
4742 protected override void DoEmit (EmitContext ec)
4747 protected override void CloneTo (CloneContext clonectx, Statement t)
4749 Unsafe target = (Unsafe) t;
4751 target.Block = clonectx.LookupBlock (Block);
4754 public override object Accept (StructuralVisitor visitor)
4756 return visitor.Visit (this);
4763 public class Fixed : Statement
4765 abstract class Emitter : ShimExpression
4767 protected LocalVariable vi;
4769 protected Emitter (Expression expr, LocalVariable li)
4775 public abstract void EmitExit (EmitContext ec);
4778 class ExpressionEmitter : Emitter {
4779 public ExpressionEmitter (Expression converted, LocalVariable li) :
4780 base (converted, li)
4784 protected override Expression DoResolve (ResolveContext rc)
4786 throw new NotImplementedException ();
4789 public override void Emit (EmitContext ec) {
4791 // Store pointer in pinned location
4797 public override void EmitExit (EmitContext ec)
4800 ec.Emit (OpCodes.Conv_U);
4805 class StringEmitter : Emitter
4807 LocalVariable pinned_string;
4809 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4814 protected override Expression DoResolve (ResolveContext rc)
4816 pinned_string = new LocalVariable (vi.Block, "$pinned",
4817 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4819 pinned_string.Type = rc.BuiltinTypes.String;
4821 eclass = ExprClass.Variable;
4822 type = rc.BuiltinTypes.Int;
4826 public override void Emit (EmitContext ec)
4828 pinned_string.CreateBuilder (ec);
4831 pinned_string.EmitAssign (ec);
4833 // TODO: Should use Binary::Add
4834 pinned_string.Emit (ec);
4835 ec.Emit (OpCodes.Conv_I);
4837 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4841 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4842 //pe.InstanceExpression = pinned_string;
4843 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4845 ec.Emit (OpCodes.Add);
4849 public override void EmitExit (EmitContext ec)
4852 pinned_string.EmitAssign (ec);
4856 public class VariableDeclaration : BlockVariableDeclaration
4858 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4863 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4865 if (!Variable.Type.IsPointer && li == Variable) {
4866 bc.Report.Error (209, TypeExpression.Location,
4867 "The type of locals declared in a fixed statement must be a pointer type");
4872 // The rules for the possible declarators are pretty wise,
4873 // but the production on the grammar is more concise.
4875 // So we have to enforce these rules here.
4877 // We do not resolve before doing the case 1 test,
4878 // because the grammar is explicit in that the token &
4879 // is present, so we need to test for this particular case.
4882 if (initializer is Cast) {
4883 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4887 initializer = initializer.Resolve (bc);
4889 if (initializer == null)
4895 if (initializer.Type.IsArray) {
4896 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4899 // Provided that array_type is unmanaged,
4901 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4905 // and T* is implicitly convertible to the
4906 // pointer type given in the fixed statement.
4908 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4910 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4911 if (converted == null)
4915 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4917 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4918 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4919 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4920 new NullLiteral (loc),
4923 converted = converted.Resolve (bc);
4925 return new ExpressionEmitter (converted, li);
4931 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4932 return new StringEmitter (initializer, li, loc).Resolve (bc);
4935 // Case 3: fixed buffer
4936 if (initializer is FixedBufferPtr) {
4937 return new ExpressionEmitter (initializer, li);
4941 // Case 4: & object.
4943 bool already_fixed = true;
4944 Unary u = initializer as Unary;
4945 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4946 IVariableReference vr = u.Expr as IVariableReference;
4947 if (vr == null || !vr.IsFixed) {
4948 already_fixed = false;
4952 if (already_fixed) {
4953 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4956 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4957 return new ExpressionEmitter (initializer, li);
4962 VariableDeclaration decl;
4963 Statement statement;
4966 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
4975 public Statement Statement {
4981 public BlockVariableDeclaration Variables {
4989 public override bool Resolve (BlockContext ec)
4991 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4992 if (!decl.Resolve (ec))
4996 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4997 bool ok = statement.Resolve (ec);
4998 bool flow_unreachable = ec.EndFlowBranching ();
4999 has_ret = flow_unreachable;
5004 protected override void DoEmit (EmitContext ec)
5006 decl.Variable.CreateBuilder (ec);
5007 decl.Initializer.Emit (ec);
5008 if (decl.Declarators != null) {
5009 foreach (var d in decl.Declarators) {
5010 d.Variable.CreateBuilder (ec);
5011 d.Initializer.Emit (ec);
5015 statement.Emit (ec);
5021 // Clear the pinned variable
5023 ((Emitter) decl.Initializer).EmitExit (ec);
5024 if (decl.Declarators != null) {
5025 foreach (var d in decl.Declarators) {
5026 ((Emitter)d.Initializer).EmitExit (ec);
5031 protected override void CloneTo (CloneContext clonectx, Statement t)
5033 Fixed target = (Fixed) t;
5035 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5036 target.statement = statement.Clone (clonectx);
5039 public override object Accept (StructuralVisitor visitor)
5041 return visitor.Visit (this);
5045 public class Catch : Statement
5049 FullNamedExpression type_expr;
5050 CompilerAssign assign;
5053 public Catch (Block block, Location loc)
5061 public Block Block {
5067 public TypeSpec CatchType {
5073 public bool IsGeneral {
5075 return type_expr == null;
5079 public FullNamedExpression TypeExpression {
5088 public LocalVariable Variable {
5099 protected override void DoEmit (EmitContext ec)
5102 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5104 ec.BeginCatchBlock (CatchType);
5107 li.CreateBuilder (ec);
5110 // Special case hoisted catch variable, we have to use a temporary variable
5111 // to pass via anonymous storey initialization with the value still on top
5114 if (li.HoistedVariant != null) {
5115 LocalTemporary lt = new LocalTemporary (li.Type);
5118 // switch to assigning from the temporary variable and not from top of the stack
5119 assign.UpdateSource (lt);
5122 ec.Emit (OpCodes.Pop);
5128 public override bool Resolve (BlockContext ec)
5130 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5131 if (type_expr != null) {
5132 type = type_expr.ResolveAsType (ec);
5136 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5137 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5138 } else if (li != null) {
5140 li.PrepareForFlowAnalysis (ec);
5142 // source variable is at the top of the stack
5143 Expression source = new EmptyExpression (li.Type);
5144 if (li.Type.IsGenericParameter)
5145 source = new UnboxCast (source, li.Type);
5148 // Uses Location.Null to hide from symbol file
5150 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5151 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5155 return Block.Resolve (ec);
5159 protected override void CloneTo (CloneContext clonectx, Statement t)
5161 Catch target = (Catch) t;
5163 if (type_expr != null)
5164 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5166 target.block = clonectx.LookupBlock (block);
5170 public class TryFinally : TryFinallyBlock
5174 public TryFinally (Statement stmt, Block fini, Location loc)
5180 public Block Finallyblock {
5186 public override bool Resolve (BlockContext ec)
5190 ec.StartFlowBranching (this);
5192 if (!stmt.Resolve (ec))
5196 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5197 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5198 if (!fini.Resolve (ec))
5202 ec.EndFlowBranching ();
5204 ok &= base.Resolve (ec);
5209 protected override void EmitTryBody (EmitContext ec)
5214 protected override void EmitFinallyBody (EmitContext ec)
5219 protected override void CloneTo (CloneContext clonectx, Statement t)
5221 TryFinally target = (TryFinally) t;
5223 target.stmt = (Statement) stmt.Clone (clonectx);
5225 target.fini = clonectx.LookupBlock (fini);
5228 public override object Accept (StructuralVisitor visitor)
5230 return visitor.Visit (this);
5234 public class TryCatch : ExceptionStatement
5237 List<Catch> clauses;
5238 readonly bool inside_try_finally;
5240 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5244 this.clauses = catch_clauses;
5245 this.inside_try_finally = inside_try_finally;
5248 public List<Catch> Clauses {
5254 public bool IsTryCatchFinally {
5256 return inside_try_finally;
5260 public override bool Resolve (BlockContext ec)
5264 ec.StartFlowBranching (this);
5266 if (!Block.Resolve (ec))
5269 for (int i = 0; i < clauses.Count; ++i) {
5271 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5273 if (!c.Resolve (ec)) {
5278 TypeSpec resolved_type = c.CatchType;
5279 for (int ii = 0; ii < clauses.Count; ++ii) {
5283 if (clauses[ii].IsGeneral) {
5284 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5287 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5290 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5293 ec.Report.Warning (1058, 1, c.loc,
5294 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5302 var ct = clauses[ii].CatchType;
5306 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5307 ec.Report.Error (160, c.loc,
5308 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5309 ct.GetSignatureForError ());
5315 ec.EndFlowBranching ();
5317 return base.Resolve (ec) && ok;
5320 protected sealed override void DoEmit (EmitContext ec)
5322 if (!inside_try_finally)
5323 EmitTryBodyPrepare (ec);
5327 foreach (Catch c in clauses)
5330 if (!inside_try_finally)
5331 ec.EndExceptionBlock ();
5334 protected override void CloneTo (CloneContext clonectx, Statement t)
5336 TryCatch target = (TryCatch) t;
5338 target.Block = clonectx.LookupBlock (Block);
5339 if (clauses != null){
5340 target.clauses = new List<Catch> ();
5341 foreach (Catch c in clauses)
5342 target.clauses.Add ((Catch) c.Clone (clonectx));
5346 public override object Accept (StructuralVisitor visitor)
5348 return visitor.Visit (this);
5352 public class Using : TryFinallyBlock
5354 public class VariableDeclaration : BlockVariableDeclaration
5356 Statement dispose_call;
5358 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5363 public VariableDeclaration (LocalVariable li, Location loc)
5369 public VariableDeclaration (Expression expr)
5372 loc = expr.Location;
5378 public bool IsNested { get; private set; }
5382 public void EmitDispose (EmitContext ec)
5384 dispose_call.Emit (ec);
5387 public override bool Resolve (BlockContext bc)
5392 return base.Resolve (bc, false);
5395 public Expression ResolveExpression (BlockContext bc)
5397 var e = Initializer.Resolve (bc);
5401 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5402 Initializer = ResolveInitializer (bc, Variable, e);
5406 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5408 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5409 initializer = initializer.Resolve (bc);
5410 if (initializer == null)
5413 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5414 Arguments args = new Arguments (1);
5415 args.Add (new Argument (initializer));
5416 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5417 if (initializer == null)
5420 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5421 dispose_call = CreateDisposeCall (bc, var);
5422 dispose_call.Resolve (bc);
5424 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5427 if (li == Variable) {
5428 CheckIDiposableConversion (bc, li, initializer);
5429 dispose_call = CreateDisposeCall (bc, li);
5430 dispose_call.Resolve (bc);
5433 return base.ResolveInitializer (bc, li, initializer);
5436 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5440 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5441 if (type.IsNullableType) {
5442 // it's handled in CreateDisposeCall
5446 bc.Report.SymbolRelatedToPreviousError (type);
5447 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5448 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5449 type.GetSignatureForError ());
5455 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5457 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5459 var loc = lv.Location;
5461 var idt = bc.BuiltinTypes.IDisposable;
5462 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5464 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5465 dispose_mg.InstanceExpression = type.IsNullableType ?
5466 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5470 // Hide it from symbol file via null location
5472 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5474 // Add conditional call when disposing possible null variable
5475 if (!type.IsStruct || type.IsNullableType)
5476 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, loc);
5481 public void ResolveDeclaratorInitializer (BlockContext bc)
5483 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5486 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5488 for (int i = declarators.Count - 1; i >= 0; --i) {
5489 var d = declarators [i];
5490 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5491 vd.Initializer = d.Initializer;
5493 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5494 vd.dispose_call.Resolve (bc);
5496 stmt = new Using (vd, stmt, d.Variable.Location);
5503 public override object Accept (StructuralVisitor visitor)
5505 return visitor.Visit (this);
5509 VariableDeclaration decl;
5511 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5517 public Using (Expression expr, Statement stmt, Location loc)
5520 this.decl = new VariableDeclaration (expr);
5525 public Expression Expr {
5527 return decl.Variable == null ? decl.Initializer : null;
5531 public BlockVariableDeclaration Variables {
5539 public override void Emit (EmitContext ec)
5542 // Don't emit sequence point it will be set on variable declaration
5547 protected override void EmitTryBodyPrepare (EmitContext ec)
5550 base.EmitTryBodyPrepare (ec);
5553 protected override void EmitTryBody (EmitContext ec)
5558 protected override void EmitFinallyBody (EmitContext ec)
5560 decl.EmitDispose (ec);
5563 public override bool Resolve (BlockContext ec)
5565 VariableReference vr;
5566 bool vr_locked = false;
5568 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5569 if (decl.Variable == null) {
5570 vr = decl.ResolveExpression (ec) as VariableReference;
5572 vr_locked = vr.IsLockedByStatement;
5573 vr.IsLockedByStatement = true;
5576 if (decl.IsNested) {
5577 decl.ResolveDeclaratorInitializer (ec);
5579 if (!decl.Resolve (ec))
5582 if (decl.Declarators != null) {
5583 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5591 ec.StartFlowBranching (this);
5595 ec.EndFlowBranching ();
5598 vr.IsLockedByStatement = vr_locked;
5605 protected override void CloneTo (CloneContext clonectx, Statement t)
5607 Using target = (Using) t;
5609 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5610 target.stmt = stmt.Clone (clonectx);
5613 public override object Accept (StructuralVisitor visitor)
5615 return visitor.Visit (this);
5620 /// Implementation of the foreach C# statement
5622 public class Foreach : Statement
5624 sealed class ArrayForeach : Statement
5626 readonly Foreach for_each;
5627 readonly Statement statement;
5630 TemporaryVariableReference[] lengths;
5631 Expression [] length_exprs;
5632 StatementExpression[] counter;
5633 TemporaryVariableReference[] variables;
5635 TemporaryVariableReference copy;
5637 LocalVariableReference variable;
5639 public ArrayForeach (Foreach @foreach, int rank)
5641 for_each = @foreach;
5642 statement = for_each.statement;
5645 counter = new StatementExpression[rank];
5646 variables = new TemporaryVariableReference[rank];
5647 length_exprs = new Expression [rank];
5650 // Only use temporary length variables when dealing with
5651 // multi-dimensional arrays
5654 lengths = new TemporaryVariableReference [rank];
5657 protected override void CloneTo (CloneContext clonectx, Statement target)
5659 throw new NotImplementedException ();
5662 public override bool Resolve (BlockContext ec)
5664 Block variables_block = for_each.variable.Block;
5665 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5668 int rank = length_exprs.Length;
5669 Arguments list = new Arguments (rank);
5670 for (int i = 0; i < rank; i++) {
5671 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5673 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, loc));
5674 counter[i].Resolve (ec);
5677 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5679 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5680 lengths[i].Resolve (ec);
5682 Arguments args = new Arguments (1);
5683 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5684 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5687 list.Add (new Argument (v));
5690 access = new ElementAccess (copy, list, loc).Resolve (ec);
5695 if (for_each.type is VarExpr) {
5696 // Infer implicitly typed local variable from foreach array type
5697 var_type = access.Type;
5699 var_type = for_each.type.ResolveAsType (ec);
5702 if (var_type == null)
5705 conv = Convert.ExplicitConversion (ec, access, var_type, loc);
5711 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5712 ec.CurrentBranching.CreateSibling ();
5714 for_each.variable.Type = conv.Type;
5715 variable = new LocalVariableReference (for_each.variable, loc);
5716 variable.Resolve (ec);
5718 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5719 if (!statement.Resolve (ec))
5721 ec.EndFlowBranching ();
5723 // There's no direct control flow from the end of the embedded statement to the end of the loop
5724 ec.CurrentBranching.CurrentUsageVector.Goto ();
5726 ec.EndFlowBranching ();
5731 protected override void DoEmit (EmitContext ec)
5733 copy.EmitAssign (ec, for_each.expr);
5735 int rank = length_exprs.Length;
5736 Label[] test = new Label [rank];
5737 Label[] loop = new Label [rank];
5739 for (int i = 0; i < rank; i++) {
5740 test [i] = ec.DefineLabel ();
5741 loop [i] = ec.DefineLabel ();
5743 if (lengths != null)
5744 lengths [i].EmitAssign (ec, length_exprs [i]);
5747 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5748 for (int i = 0; i < rank; i++) {
5749 variables [i].EmitAssign (ec, zero);
5751 ec.Emit (OpCodes.Br, test [i]);
5752 ec.MarkLabel (loop [i]);
5755 variable.local_info.CreateBuilder (ec);
5756 variable.EmitAssign (ec, conv, false, false);
5758 statement.Emit (ec);
5760 ec.MarkLabel (ec.LoopBegin);
5762 for (int i = rank - 1; i >= 0; i--){
5763 counter [i].Emit (ec);
5765 ec.MarkLabel (test [i]);
5766 variables [i].Emit (ec);
5768 if (lengths != null)
5769 lengths [i].Emit (ec);
5771 length_exprs [i].Emit (ec);
5773 ec.Emit (OpCodes.Blt, loop [i]);
5776 ec.MarkLabel (ec.LoopEnd);
5780 sealed class CollectionForeach : Statement, OverloadResolver.IErrorHandler
5782 class Body : Statement
5785 LocalVariableReference variable;
5786 Expression current, conv;
5787 Statement statement;
5789 public Body (TypeSpec type, LocalVariable variable,
5790 Expression current, Statement statement,
5794 this.variable = new LocalVariableReference (variable, loc);
5795 this.current = current;
5796 this.statement = statement;
5800 protected override void CloneTo (CloneContext clonectx, Statement target)
5802 throw new NotImplementedException ();
5805 public override bool Resolve (BlockContext ec)
5807 current = current.Resolve (ec);
5808 if (current == null)
5811 conv = Convert.ExplicitConversion (ec, current, type, loc);
5815 variable.local_info.Type = conv.Type;
5816 variable.Resolve (ec);
5818 if (!statement.Resolve (ec))
5824 protected override void DoEmit (EmitContext ec)
5826 variable.local_info.CreateBuilder (ec);
5827 variable.EmitAssign (ec, conv, false, false);
5829 statement.Emit (ec);
5833 class RuntimeDispose : Using.VariableDeclaration
5835 public RuntimeDispose (LocalVariable lv, Location loc)
5840 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5842 // Defered to runtime check
5845 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5847 var idt = bc.BuiltinTypes.IDisposable;
5850 // Fabricates code like
5852 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5855 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5857 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5858 dispose_variable.CreateReferenceExpression (bc, loc),
5859 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5860 loc), new NullLiteral (loc), loc);
5862 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5864 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5865 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5867 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5868 return new If (idisaposable_test, dispose, loc);
5872 LocalVariable variable;
5874 Statement statement;
5875 Expression var_type;
5876 ExpressionStatement init;
5877 TemporaryVariableReference enumerator_variable;
5878 bool ambiguous_getenumerator_name;
5880 public CollectionForeach (Expression var_type, LocalVariable var, Expression expr, Statement stmt, Location l)
5882 this.var_type = var_type;
5883 this.variable = var;
5889 protected override void CloneTo (CloneContext clonectx, Statement target)
5891 throw new NotImplementedException ();
5894 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5896 rc.Report.SymbolRelatedToPreviousError (enumerator);
5897 rc.Report.Error (202, loc,
5898 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5899 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5902 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5905 // Option 1: Try to match by name GetEnumerator first
5907 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5908 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5910 var mg = mexpr as MethodGroupExpr;
5912 mg.InstanceExpression = expr;
5913 Arguments args = new Arguments (0);
5914 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5916 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5917 if (ambiguous_getenumerator_name)
5920 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5926 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5929 PredefinedMember<MethodSpec> iface_candidate = null;
5930 var ptypes = rc.Module.PredefinedTypes;
5931 var gen_ienumerable = ptypes.IEnumerableGeneric;
5932 if (!gen_ienumerable.Define ())
5933 gen_ienumerable = null;
5936 var ifaces = t.Interfaces;
5937 if (ifaces != null) {
5938 foreach (var iface in ifaces) {
5939 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5940 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5941 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5942 rc.Report.Error (1640, loc,
5943 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5944 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5949 // TODO: Cache this somehow
5950 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5951 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5956 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5957 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5962 if (t.IsGenericParameter)
5967 } while (t != null);
5969 if (iface_candidate == null) {
5970 if (expr.Type != InternalType.ErrorType) {
5971 rc.Report.Error (1579, loc,
5972 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5973 expr.Type.GetSignatureForError (), "GetEnumerator");
5979 var method = iface_candidate.Resolve (loc);
5983 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5984 mg.InstanceExpression = expr;
5988 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5990 var ms = MemberCache.FindMember (enumerator.ReturnType,
5991 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5992 BindingRestriction.InstanceOnly) as MethodSpec;
5994 if (ms == null || !ms.IsPublic) {
5995 Error_WrongEnumerator (rc, enumerator);
5999 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6002 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6004 var ps = MemberCache.FindMember (enumerator.ReturnType,
6005 MemberFilter.Property ("Current", null),
6006 BindingRestriction.InstanceOnly) as PropertySpec;
6008 if (ps == null || !ps.IsPublic) {
6009 Error_WrongEnumerator (rc, enumerator);
6016 public override bool Resolve (BlockContext ec)
6018 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6021 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6022 } else if (expr.Type.IsNullableType) {
6023 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6026 var get_enumerator_mg = ResolveGetEnumerator (ec);
6027 if (get_enumerator_mg == null) {
6031 var get_enumerator = get_enumerator_mg.BestCandidate;
6032 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6033 enumerator_variable.Resolve (ec);
6035 // Prepare bool MoveNext ()
6036 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6037 if (move_next_mg == null) {
6041 move_next_mg.InstanceExpression = enumerator_variable;
6043 // Prepare ~T~ Current { get; }
6044 var current_prop = ResolveCurrent (ec, get_enumerator);
6045 if (current_prop == null) {
6049 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6050 if (current_pe == null)
6053 VarExpr ve = var_type as VarExpr;
6057 // Source type is dynamic, set element type to dynamic too
6058 variable.Type = ec.BuiltinTypes.Dynamic;
6060 // Infer implicitly typed local variable from foreach enumerable type
6061 variable.Type = current_pe.Type;
6065 // Explicit cast of dynamic collection elements has to be done at runtime
6066 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6069 variable.Type = var_type.ResolveAsType (ec);
6072 if (variable.Type == null)
6075 var init = new Invocation (get_enumerator_mg, null);
6077 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6078 new Body (variable.Type, variable, current_pe, statement, variable.Location), Location.Null);
6080 var enum_type = enumerator_variable.Type;
6083 // Add Dispose method call when enumerator can be IDisposable
6085 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6086 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6088 // Runtime Dispose check
6090 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6091 vd.Initializer = init;
6092 statement = new Using (vd, statement, Location.Null);
6095 // No Dispose call needed
6097 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6098 this.init.Resolve (ec);
6102 // Static Dispose check
6104 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6105 vd.Initializer = init;
6106 statement = new Using (vd, statement, Location.Null);
6109 return statement.Resolve (ec);
6112 protected override void DoEmit (EmitContext ec)
6114 enumerator_variable.LocalInfo.CreateBuilder (ec);
6117 init.EmitStatement (ec);
6119 statement.Emit (ec);
6122 #region IErrorHandler Members
6124 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6126 ec.Report.SymbolRelatedToPreviousError (best);
6127 ec.Report.Warning (278, 2, expr.Location,
6128 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6129 expr.Type.GetSignatureForError (), "enumerable",
6130 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6132 ambiguous_getenumerator_name = true;
6136 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6141 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6146 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6155 LocalVariable variable;
6157 Statement statement;
6159 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Location l)
6162 this.variable = var;
6168 public Expression Expr {
6169 get { return expr; }
6172 public Statement Statement {
6173 get { return statement; }
6176 public Expression TypeExpression {
6177 get { return type; }
6180 public LocalVariable Variable {
6181 get { return variable; }
6185 public override bool Resolve (BlockContext ec)
6187 expr = expr.Resolve (ec);
6192 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6196 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6197 statement = new ArrayForeach (this, 1);
6198 } else if (expr.Type is ArrayContainer) {
6199 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6201 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6202 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6203 expr.ExprClassName);
6207 statement = new CollectionForeach (type, variable, expr, statement, loc);
6210 return statement.Resolve (ec);
6213 protected override void DoEmit (EmitContext ec)
6215 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6216 ec.LoopBegin = ec.DefineLabel ();
6217 ec.LoopEnd = ec.DefineLabel ();
6219 statement.Emit (ec);
6221 ec.LoopBegin = old_begin;
6222 ec.LoopEnd = old_end;
6225 protected override void CloneTo (CloneContext clonectx, Statement t)
6227 Foreach target = (Foreach) t;
6229 target.type = type.Clone (clonectx);
6230 target.expr = expr.Clone (clonectx);
6231 target.statement = statement.Clone (clonectx);
6234 public override object Accept (StructuralVisitor visitor)
6236 return visitor.Visit (this);