2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
53 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
55 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
56 bool ok = Resolve (ec);
57 ec.KillFlowBranching ();
63 /// Return value indicates whether all code paths emitted return.
65 protected abstract void DoEmit (EmitContext ec);
67 public virtual void Emit (EmitContext ec)
72 if (ec.StatementEpilogue != null) {
78 // This routine must be overrided in derived classes and make copies
79 // of all the data that might be modified if resolved
81 protected abstract void CloneTo (CloneContext clonectx, Statement target);
83 public Statement Clone (CloneContext clonectx)
85 Statement s = (Statement) this.MemberwiseClone ();
86 CloneTo (clonectx, s);
90 public virtual Expression CreateExpressionTree (ResolveContext ec)
92 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
96 public virtual object Accept (StructuralVisitor visitor)
98 return visitor.Visit (this);
102 public sealed class EmptyStatement : Statement
104 public EmptyStatement (Location loc)
109 public override bool Resolve (BlockContext ec)
114 public override bool ResolveUnreachable (BlockContext ec, bool warn)
119 public override void Emit (EmitContext ec)
123 protected override void DoEmit (EmitContext ec)
125 throw new NotSupportedException ();
128 protected override void CloneTo (CloneContext clonectx, Statement target)
133 public override object Accept (StructuralVisitor visitor)
135 return visitor.Visit (this);
139 public class If : Statement {
141 public Statement TrueStatement;
142 public Statement FalseStatement;
146 public If (Expression bool_expr, Statement true_statement, Location l)
147 : this (bool_expr, true_statement, null, l)
151 public If (Expression bool_expr,
152 Statement true_statement,
153 Statement false_statement,
156 this.expr = bool_expr;
157 TrueStatement = true_statement;
158 FalseStatement = false_statement;
162 public Expression Expr {
168 public override bool Resolve (BlockContext ec)
172 expr = expr.Resolve (ec);
177 // Dead code elimination
179 if (expr is Constant) {
180 bool take = !((Constant) expr).IsDefaultValue;
183 if (!TrueStatement.Resolve (ec))
186 if ((FalseStatement != null) &&
187 !FalseStatement.ResolveUnreachable (ec, true))
189 FalseStatement = null;
191 if (!TrueStatement.ResolveUnreachable (ec, true))
193 TrueStatement = null;
195 if ((FalseStatement != null) &&
196 !FalseStatement.Resolve (ec))
204 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
206 ok &= TrueStatement.Resolve (ec);
208 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
210 ec.CurrentBranching.CreateSibling ();
212 if (FalseStatement != null)
213 ok &= FalseStatement.Resolve (ec);
215 ec.EndFlowBranching ();
220 protected override void DoEmit (EmitContext ec)
222 Label false_target = ec.DefineLabel ();
226 // If we're a boolean constant, Resolve() already
227 // eliminated dead code for us.
229 Constant c = expr as Constant;
231 c.EmitSideEffect (ec);
233 if (!c.IsDefaultValue)
234 TrueStatement.Emit (ec);
235 else if (FalseStatement != null)
236 FalseStatement.Emit (ec);
241 expr.EmitBranchable (ec, false_target, false);
243 TrueStatement.Emit (ec);
245 if (FalseStatement != null){
246 bool branch_emitted = false;
248 end = ec.DefineLabel ();
250 ec.Emit (OpCodes.Br, end);
251 branch_emitted = true;
254 ec.MarkLabel (false_target);
255 FalseStatement.Emit (ec);
260 ec.MarkLabel (false_target);
264 protected override void CloneTo (CloneContext clonectx, Statement t)
268 target.expr = expr.Clone (clonectx);
269 target.TrueStatement = TrueStatement.Clone (clonectx);
270 if (FalseStatement != null)
271 target.FalseStatement = FalseStatement.Clone (clonectx);
274 public override object Accept (StructuralVisitor visitor)
276 return visitor.Visit (this);
280 public class Do : Statement {
281 public Expression expr;
282 public Statement EmbeddedStatement;
284 public Do (Statement statement, BooleanExpression bool_expr, Location l)
287 EmbeddedStatement = statement;
291 public override bool Resolve (BlockContext ec)
295 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
297 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
299 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
300 if (!EmbeddedStatement.Resolve (ec))
302 ec.EndFlowBranching ();
304 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
305 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
307 expr = expr.Resolve (ec);
310 else if (expr is Constant){
311 bool infinite = !((Constant) expr).IsDefaultValue;
313 ec.CurrentBranching.CurrentUsageVector.Goto ();
316 ec.EndFlowBranching ();
321 protected override void DoEmit (EmitContext ec)
323 Label loop = ec.DefineLabel ();
324 Label old_begin = ec.LoopBegin;
325 Label old_end = ec.LoopEnd;
327 ec.LoopBegin = ec.DefineLabel ();
328 ec.LoopEnd = ec.DefineLabel ();
331 EmbeddedStatement.Emit (ec);
332 ec.MarkLabel (ec.LoopBegin);
334 // Mark start of while condition
335 ec.Mark (expr.Location);
338 // Dead code elimination
340 if (expr is Constant) {
341 bool res = !((Constant) expr).IsDefaultValue;
343 expr.EmitSideEffect (ec);
345 ec.Emit (OpCodes.Br, loop);
347 expr.EmitBranchable (ec, loop, true);
350 ec.MarkLabel (ec.LoopEnd);
352 ec.LoopBegin = old_begin;
353 ec.LoopEnd = old_end;
356 protected override void CloneTo (CloneContext clonectx, Statement t)
360 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
361 target.expr = expr.Clone (clonectx);
364 public override object Accept (StructuralVisitor visitor)
366 return visitor.Visit (this);
370 public class While : Statement {
371 public Expression expr;
372 public Statement Statement;
373 bool infinite, empty;
375 public While (BooleanExpression bool_expr, Statement statement, Location l)
377 this.expr = bool_expr;
378 Statement = statement;
382 public override bool Resolve (BlockContext ec)
386 expr = expr.Resolve (ec);
391 // Inform whether we are infinite or not
393 if (expr is Constant){
394 bool value = !((Constant) expr).IsDefaultValue;
397 if (!Statement.ResolveUnreachable (ec, true))
405 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
407 ec.CurrentBranching.CreateSibling ();
409 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
410 if (!Statement.Resolve (ec))
412 ec.EndFlowBranching ();
414 // There's no direct control flow from the end of the embedded statement to the end of the loop
415 ec.CurrentBranching.CurrentUsageVector.Goto ();
417 ec.EndFlowBranching ();
422 protected override void DoEmit (EmitContext ec)
425 expr.EmitSideEffect (ec);
429 Label old_begin = ec.LoopBegin;
430 Label old_end = ec.LoopEnd;
432 ec.LoopBegin = ec.DefineLabel ();
433 ec.LoopEnd = ec.DefineLabel ();
436 // Inform whether we are infinite or not
438 if (expr is Constant) {
439 // expr is 'true', since the 'empty' case above handles the 'false' case
440 ec.MarkLabel (ec.LoopBegin);
442 if (ec.EmitAccurateDebugInfo)
443 ec.Emit (OpCodes.Nop);
445 expr.EmitSideEffect (ec);
447 ec.Emit (OpCodes.Br, ec.LoopBegin);
450 // Inform that we are infinite (ie, `we return'), only
451 // if we do not `break' inside the code.
453 ec.MarkLabel (ec.LoopEnd);
455 Label while_loop = ec.DefineLabel ();
457 ec.Emit (OpCodes.Br, ec.LoopBegin);
458 ec.MarkLabel (while_loop);
462 ec.MarkLabel (ec.LoopBegin);
464 ec.Mark (expr.Location);
465 expr.EmitBranchable (ec, while_loop, true);
467 ec.MarkLabel (ec.LoopEnd);
470 ec.LoopBegin = old_begin;
471 ec.LoopEnd = old_end;
474 protected override void CloneTo (CloneContext clonectx, Statement t)
476 While target = (While) t;
478 target.expr = expr.Clone (clonectx);
479 target.Statement = Statement.Clone (clonectx);
482 public override object Accept (StructuralVisitor visitor)
484 return visitor.Visit (this);
488 public class For : Statement
490 bool infinite, empty;
492 public For (Location l)
497 public Statement Initializer {
501 public Expression Condition {
505 public Statement Iterator {
509 public Statement Statement {
513 public override bool Resolve (BlockContext ec)
517 if (Initializer != null) {
518 if (!Initializer.Resolve (ec))
522 if (Condition != null) {
523 Condition = Condition.Resolve (ec);
524 if (Condition == null)
526 else if (Condition is Constant) {
527 bool value = !((Constant) Condition).IsDefaultValue;
530 if (!Statement.ResolveUnreachable (ec, true))
532 if ((Iterator != null) &&
533 !Iterator.ResolveUnreachable (ec, false))
543 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
545 ec.CurrentBranching.CreateSibling ();
547 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
549 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
550 if (!Statement.Resolve (ec))
552 ec.EndFlowBranching ();
554 if (Iterator != null){
555 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
556 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
559 if (!Iterator.Resolve (ec))
564 // There's no direct control flow from the end of the embedded statement to the end of the loop
565 ec.CurrentBranching.CurrentUsageVector.Goto ();
567 ec.EndFlowBranching ();
572 protected override void DoEmit (EmitContext ec)
574 if (Initializer != null)
575 Initializer.Emit (ec);
578 Condition.EmitSideEffect (ec);
582 Label old_begin = ec.LoopBegin;
583 Label old_end = ec.LoopEnd;
584 Label loop = ec.DefineLabel ();
585 Label test = ec.DefineLabel ();
587 ec.LoopBegin = ec.DefineLabel ();
588 ec.LoopEnd = ec.DefineLabel ();
590 ec.Emit (OpCodes.Br, test);
594 ec.MarkLabel (ec.LoopBegin);
599 // If test is null, there is no test, and we are just
602 if (Condition != null) {
603 ec.Mark (Condition.Location);
606 // The Resolve code already catches the case for
607 // Test == Constant (false) so we know that
610 if (Condition is Constant) {
611 Condition.EmitSideEffect (ec);
612 ec.Emit (OpCodes.Br, loop);
614 Condition.EmitBranchable (ec, loop, true);
618 ec.Emit (OpCodes.Br, loop);
619 ec.MarkLabel (ec.LoopEnd);
621 ec.LoopBegin = old_begin;
622 ec.LoopEnd = old_end;
625 protected override void CloneTo (CloneContext clonectx, Statement t)
627 For target = (For) t;
629 if (Initializer != null)
630 target.Initializer = Initializer.Clone (clonectx);
631 if (Condition != null)
632 target.Condition = Condition.Clone (clonectx);
633 if (Iterator != null)
634 target.Iterator = Iterator.Clone (clonectx);
635 target.Statement = Statement.Clone (clonectx);
638 public override object Accept (StructuralVisitor visitor)
640 return visitor.Visit (this);
644 public class StatementExpression : Statement
646 ExpressionStatement expr;
648 public StatementExpression (ExpressionStatement expr)
654 public StatementExpression (ExpressionStatement expr, Location loc)
660 public ExpressionStatement Expr {
666 protected override void CloneTo (CloneContext clonectx, Statement t)
668 StatementExpression target = (StatementExpression) t;
669 target.expr = (ExpressionStatement) expr.Clone (clonectx);
672 protected override void DoEmit (EmitContext ec)
674 expr.EmitStatement (ec);
677 public override bool Resolve (BlockContext ec)
679 expr = expr.ResolveStatement (ec);
683 public override object Accept (StructuralVisitor visitor)
685 return visitor.Visit (this);
689 public class StatementErrorExpression : Statement
691 readonly Expression expr;
693 public StatementErrorExpression (Expression expr)
698 public Expression Expr {
704 protected override void DoEmit (EmitContext ec)
706 throw new NotSupportedException ();
709 protected override void CloneTo (CloneContext clonectx, Statement target)
711 throw new NotImplementedException ();
714 public override object Accept (StructuralVisitor visitor)
716 return visitor.Visit (this);
721 // Simple version of statement list not requiring a block
723 public class StatementList : Statement
725 List<Statement> statements;
727 public StatementList (Statement first, Statement second)
729 statements = new List<Statement> () { first, second };
733 public IList<Statement> Statements {
740 public void Add (Statement statement)
742 statements.Add (statement);
745 public override bool Resolve (BlockContext ec)
747 foreach (var s in statements)
753 protected override void DoEmit (EmitContext ec)
755 foreach (var s in statements)
759 protected override void CloneTo (CloneContext clonectx, Statement target)
761 StatementList t = (StatementList) target;
763 t.statements = new List<Statement> (statements.Count);
764 foreach (Statement s in statements)
765 t.statements.Add (s.Clone (clonectx));
768 public override object Accept (StructuralVisitor visitor)
770 return visitor.Visit (this);
774 // A 'return' or a 'yield break'
775 public abstract class ExitStatement : Statement
777 protected bool unwind_protect;
778 protected abstract bool DoResolve (BlockContext ec);
780 public virtual void Error_FinallyClause (Report Report)
782 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
785 public sealed override bool Resolve (BlockContext ec)
790 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
791 ec.CurrentBranching.CurrentUsageVector.Goto ();
797 /// Implements the return statement
799 public class Return : ExitStatement
803 public Return (Expression expr, Location l)
811 public Expression Expr {
822 protected override bool DoResolve (BlockContext ec)
825 if (ec.ReturnType.Kind == MemberKind.Void)
829 // Return must not be followed by an expression when
830 // the method return type is Task
832 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
833 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
834 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
836 // Extra trick not to emit ret/leave inside awaiter body
838 expr = EmptyExpression.Null;
843 if (ec.CurrentIterator != null) {
844 Error_ReturnFromIterator (ec);
846 ec.Report.Error (126, loc,
847 "An object of a type convertible to `{0}' is required for the return statement",
848 ec.ReturnType.GetSignatureForError ());
854 expr = expr.Resolve (ec);
855 TypeSpec block_return_type = ec.ReturnType;
857 AnonymousExpression am = ec.CurrentAnonymousMethod;
859 if (block_return_type.Kind == MemberKind.Void) {
860 ec.Report.Error (127, loc,
861 "`{0}': A return keyword must not be followed by any expression when method returns void",
862 ec.GetSignatureForError ());
868 Error_ReturnFromIterator (ec);
872 var async_block = am as AsyncInitializer;
873 if (async_block != null) {
875 var storey = (AsyncTaskStorey) am.Storey;
876 var async_type = storey.ReturnType;
878 if (async_type == null && async_block.ReturnTypeInference != null) {
879 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
883 // TODO: Better error message
884 if (async_type.Kind == MemberKind.Void) {
885 ec.Report.Error (127, loc,
886 "`{0}': A return keyword must not be followed by any expression when method returns void",
887 ec.GetSignatureForError ());
892 if (!async_type.IsGenericTask) {
893 if (this is ContextualReturn)
896 ec.Report.Error (1997, loc,
897 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
898 ec.GetSignatureForError ());
903 // The return type is actually Task<T> type argument
905 if (expr.Type == async_type) {
906 ec.Report.Error (4016, loc,
907 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
908 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
910 block_return_type = async_type.TypeArguments[0];
914 var l = am as AnonymousMethodBody;
915 if (l != null && l.ReturnTypeInference != null && expr != null) {
916 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
925 if (expr.Type != block_return_type) {
926 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
929 if (am != null && block_return_type == ec.ReturnType) {
930 ec.Report.Error (1662, loc,
931 "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",
932 am.ContainerType, am.GetSignatureForError ());
941 protected override void DoEmit (EmitContext ec)
946 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
947 if (async_body != null) {
948 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
950 // It's null for await without async
951 if (async_return != null) {
952 async_return.EmitAssign (ec);
956 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
964 if (unwind_protect || ec.EmitAccurateDebugInfo)
965 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
968 if (unwind_protect) {
969 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
970 } else if (ec.EmitAccurateDebugInfo) {
971 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
973 ec.Emit (OpCodes.Ret);
977 void Error_ReturnFromIterator (ResolveContext rc)
979 rc.Report.Error (1622, loc,
980 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
983 protected override void CloneTo (CloneContext clonectx, Statement t)
985 Return target = (Return) t;
986 // It's null for simple return;
988 target.expr = expr.Clone (clonectx);
991 public override object Accept (StructuralVisitor visitor)
993 return visitor.Visit (this);
997 public class Goto : Statement {
999 LabeledStatement label;
1000 bool unwind_protect;
1002 public override bool Resolve (BlockContext ec)
1004 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1005 ec.CurrentBranching.CurrentUsageVector.Goto ();
1009 public Goto (string label, Location l)
1015 public string Target {
1016 get { return target; }
1019 public void SetResolvedTarget (LabeledStatement label)
1022 label.AddReference ();
1025 protected override void CloneTo (CloneContext clonectx, Statement target)
1030 protected override void DoEmit (EmitContext ec)
1033 throw new InternalErrorException ("goto emitted before target resolved");
1034 Label l = label.LabelTarget (ec);
1035 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1038 public override object Accept (StructuralVisitor visitor)
1040 return visitor.Visit (this);
1044 public class LabeledStatement : Statement {
1051 FlowBranching.UsageVector vectors;
1053 public LabeledStatement (string name, Block block, Location l)
1060 public Label LabelTarget (EmitContext ec)
1065 label = ec.DefineLabel ();
1070 public Block Block {
1076 public string Name {
1077 get { return name; }
1080 public bool IsDefined {
1081 get { return defined; }
1084 public bool HasBeenReferenced {
1085 get { return referenced; }
1088 public FlowBranching.UsageVector JumpOrigins {
1089 get { return vectors; }
1092 public void AddUsageVector (FlowBranching.UsageVector vector)
1094 vector = vector.Clone ();
1095 vector.Next = vectors;
1099 protected override void CloneTo (CloneContext clonectx, Statement target)
1104 public override bool Resolve (BlockContext ec)
1106 // this flow-branching will be terminated when the surrounding block ends
1107 ec.StartFlowBranching (this);
1111 protected override void DoEmit (EmitContext ec)
1113 if (!HasBeenReferenced)
1114 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1117 ec.MarkLabel (label);
1120 public void AddReference ()
1125 public override object Accept (StructuralVisitor visitor)
1127 return visitor.Visit (this);
1133 /// `goto default' statement
1135 public class GotoDefault : Statement {
1137 public GotoDefault (Location l)
1142 protected override void CloneTo (CloneContext clonectx, Statement target)
1147 public override bool Resolve (BlockContext ec)
1149 ec.CurrentBranching.CurrentUsageVector.Goto ();
1151 if (ec.Switch == null) {
1152 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1156 if (!ec.Switch.GotDefault) {
1157 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
1164 protected override void DoEmit (EmitContext ec)
1166 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel);
1169 public override object Accept (StructuralVisitor visitor)
1171 return visitor.Visit (this);
1176 /// `goto case' statement
1178 public class GotoCase : Statement {
1182 public GotoCase (Expression e, Location l)
1188 public Expression Expr {
1194 public override bool Resolve (BlockContext ec)
1196 if (ec.Switch == null){
1197 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1201 ec.CurrentBranching.CurrentUsageVector.Goto ();
1203 expr = expr.Resolve (ec);
1207 Constant c = expr as Constant;
1209 ec.Report.Error (150, expr.Location, "A constant value is expected");
1214 if (ec.Switch.IsNullable && c is NullLiteral) {
1217 TypeSpec type = ec.Switch.SwitchType;
1218 res = c.TryReduce (ec, type, c.Location);
1220 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1224 if (!Convert.ImplicitStandardConversionExists (c, type))
1225 ec.Report.Warning (469, 2, loc,
1226 "The `goto case' value is not implicitly convertible to type `{0}'",
1227 TypeManager.CSharpName (type));
1231 sl = ec.Switch.ResolveGotoCase (ec, res);
1235 protected override void DoEmit (EmitContext ec)
1237 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1240 protected override void CloneTo (CloneContext clonectx, Statement t)
1242 GotoCase target = (GotoCase) t;
1244 target.expr = expr.Clone (clonectx);
1247 public override object Accept (StructuralVisitor visitor)
1249 return visitor.Visit (this);
1253 public class Throw : Statement {
1256 public Throw (Expression expr, Location l)
1262 public Expression Expr {
1268 public override bool Resolve (BlockContext ec)
1271 ec.CurrentBranching.CurrentUsageVector.Goto ();
1272 return ec.CurrentBranching.CheckRethrow (loc);
1275 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1276 ec.CurrentBranching.CurrentUsageVector.Goto ();
1281 var et = ec.BuiltinTypes.Exception;
1282 if (Convert.ImplicitConversionExists (ec, expr, et))
1283 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1285 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1290 protected override void DoEmit (EmitContext ec)
1293 ec.Emit (OpCodes.Rethrow);
1297 ec.Emit (OpCodes.Throw);
1301 protected override void CloneTo (CloneContext clonectx, Statement t)
1303 Throw target = (Throw) t;
1306 target.expr = expr.Clone (clonectx);
1309 public override object Accept (StructuralVisitor visitor)
1311 return visitor.Visit (this);
1315 public class Break : Statement {
1317 public Break (Location l)
1322 bool unwind_protect;
1324 public override bool Resolve (BlockContext ec)
1326 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1327 ec.CurrentBranching.CurrentUsageVector.Goto ();
1331 protected override void DoEmit (EmitContext ec)
1333 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1336 protected override void CloneTo (CloneContext clonectx, Statement t)
1341 public override object Accept (StructuralVisitor visitor)
1343 return visitor.Visit (this);
1347 public class Continue : Statement {
1349 public Continue (Location l)
1354 bool unwind_protect;
1356 public override bool Resolve (BlockContext ec)
1358 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1359 ec.CurrentBranching.CurrentUsageVector.Goto ();
1363 protected override void DoEmit (EmitContext ec)
1365 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1368 protected override void CloneTo (CloneContext clonectx, Statement t)
1373 public override object Accept (StructuralVisitor visitor)
1375 return visitor.Visit (this);
1379 public interface ILocalVariable
1381 void Emit (EmitContext ec);
1382 void EmitAssign (EmitContext ec);
1383 void EmitAddressOf (EmitContext ec);
1386 public interface INamedBlockVariable
1388 Block Block { get; }
1389 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1390 bool IsDeclared { get; }
1391 bool IsParameter { get; }
1392 Location Location { get; }
1395 public class BlockVariableDeclaration : Statement
1397 public class Declarator
1400 Expression initializer;
1402 public Declarator (LocalVariable li, Expression initializer)
1404 if (li.Type != null)
1405 throw new ArgumentException ("Expected null variable type");
1408 this.initializer = initializer;
1411 public Declarator (Declarator clone, Expression initializer)
1414 this.initializer = initializer;
1419 public LocalVariable Variable {
1425 public Expression Initializer {
1430 initializer = value;
1437 Expression initializer;
1438 protected FullNamedExpression type_expr;
1439 protected LocalVariable li;
1440 protected List<Declarator> declarators;
1442 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1444 this.type_expr = type;
1446 this.loc = type_expr.Location;
1449 protected BlockVariableDeclaration (LocalVariable li)
1456 public List<Declarator> Declarators {
1462 public Expression Initializer {
1467 initializer = value;
1471 public FullNamedExpression TypeExpression {
1477 public LocalVariable Variable {
1485 public void AddDeclarator (Declarator decl)
1487 if (declarators == null)
1488 declarators = new List<Declarator> ();
1490 declarators.Add (decl);
1493 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1495 if (bc.Report.Errors != 0)
1498 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1500 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1501 new MemberName (li.Name, li.Location), null);
1503 container.AddField (f);
1506 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1510 public override bool Resolve (BlockContext bc)
1512 return Resolve (bc, true);
1515 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1517 if (li.Type == null) {
1518 TypeSpec type = null;
1519 var vexpr = type_expr as VarExpr;
1522 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1523 // same name exists or as a keyword when no type was found
1525 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1526 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1527 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1530 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1534 if (li.IsConstant) {
1535 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1539 if (Initializer == null) {
1540 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1544 if (declarators != null) {
1545 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1549 Initializer = Initializer.Resolve (bc);
1550 if (Initializer != null) {
1551 ((VarExpr) type_expr).InferType (bc, Initializer);
1552 type = type_expr.Type;
1554 // Set error type to indicate the var was placed correctly but could
1557 // var a = missing ();
1559 type = InternalType.ErrorType;
1564 type = type_expr.ResolveAsType (bc);
1568 if (li.IsConstant && !type.IsConstantCompatible) {
1569 Const.Error_InvalidConstantType (type, loc, bc.Report);
1574 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1579 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1581 CreateEvaluatorVariable (bc, li);
1583 li.PrepareForFlowAnalysis (bc);
1586 if (initializer != null) {
1587 initializer = ResolveInitializer (bc, li, initializer);
1588 // li.Variable.DefinitelyAssigned
1591 if (declarators != null) {
1592 foreach (var d in declarators) {
1593 d.Variable.Type = li.Type;
1595 CreateEvaluatorVariable (bc, d.Variable);
1597 d.Variable.PrepareForFlowAnalysis (bc);
1600 if (d.Initializer != null && resolveDeclaratorInitializers) {
1601 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1602 // d.Variable.DefinitelyAssigned
1610 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1612 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1613 return a.ResolveStatement (bc);
1616 protected override void DoEmit (EmitContext ec)
1618 li.CreateBuilder (ec);
1620 if (Initializer != null)
1621 ((ExpressionStatement) Initializer).EmitStatement (ec);
1623 if (declarators != null) {
1624 foreach (var d in declarators) {
1625 d.Variable.CreateBuilder (ec);
1626 if (d.Initializer != null)
1627 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1632 protected override void CloneTo (CloneContext clonectx, Statement target)
1634 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1636 if (type_expr != null)
1637 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1639 if (initializer != null)
1640 t.initializer = initializer.Clone (clonectx);
1642 if (declarators != null) {
1643 t.declarators = null;
1644 foreach (var d in declarators)
1645 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1649 public override object Accept (StructuralVisitor visitor)
1651 return visitor.Visit (this);
1655 public class BlockConstantDeclaration : BlockVariableDeclaration
1657 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1662 public override void Emit (EmitContext ec)
1664 // Nothing to emit, not even sequence point
1667 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1669 initializer = initializer.Resolve (bc);
1670 if (initializer == null)
1673 var c = initializer as Constant;
1675 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1679 c = c.ConvertImplicitly (li.Type);
1681 if (TypeSpec.IsReferenceType (li.Type))
1682 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1684 initializer.Error_ValueCannotBeConverted (bc, initializer.Location, li.Type, false);
1689 li.ConstantValue = c;
1693 public override object Accept (StructuralVisitor visitor)
1695 return visitor.Visit (this);
1700 // The information about a user-perceived local variable
1702 public class LocalVariable : INamedBlockVariable, ILocalVariable
1709 AddressTaken = 1 << 2,
1710 CompilerGenerated = 1 << 3,
1712 ForeachVariable = 1 << 5,
1713 FixedVariable = 1 << 6,
1714 UsingVariable = 1 << 7,
1715 // DefinitelyAssigned = 1 << 8,
1718 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1722 readonly string name;
1723 readonly Location loc;
1724 readonly Block block;
1726 Constant const_value;
1728 public VariableInfo VariableInfo;
1729 HoistedVariable hoisted_variant;
1731 LocalBuilder builder;
1733 public LocalVariable (Block block, string name, Location loc)
1740 public LocalVariable (Block block, string name, Flags flags, Location loc)
1741 : this (block, name, loc)
1747 // Used by variable declarators
1749 public LocalVariable (LocalVariable li, string name, Location loc)
1750 : this (li.block, name, li.flags, loc)
1756 public bool AddressTaken {
1758 return (flags & Flags.AddressTaken) != 0;
1762 public Block Block {
1768 public Constant ConstantValue {
1773 const_value = value;
1778 // Hoisted local variable variant
1780 public HoistedVariable HoistedVariant {
1782 return hoisted_variant;
1785 hoisted_variant = value;
1789 public bool IsDeclared {
1791 return type != null;
1795 public bool IsCompilerGenerated {
1797 return (flags & Flags.CompilerGenerated) != 0;
1801 public bool IsConstant {
1803 return (flags & Flags.Constant) != 0;
1807 public bool IsLocked {
1809 return (flags & Flags.IsLocked) != 0;
1812 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1816 public bool IsThis {
1818 return (flags & Flags.IsThis) != 0;
1822 public bool IsFixed {
1824 return (flags & Flags.FixedVariable) != 0;
1828 bool INamedBlockVariable.IsParameter {
1834 public bool IsReadonly {
1836 return (flags & Flags.ReadonlyMask) != 0;
1840 public Location Location {
1846 public string Name {
1852 public TypeSpec Type {
1863 public void CreateBuilder (EmitContext ec)
1865 if ((flags & Flags.Used) == 0) {
1866 if (VariableInfo == null) {
1867 // Missing flow analysis or wrong variable flags
1868 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1871 if (VariableInfo.IsEverAssigned)
1872 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1874 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1877 if (HoistedVariant != null)
1880 if (builder != null) {
1881 if ((flags & Flags.CompilerGenerated) != 0)
1884 // To avoid Used warning duplicates
1885 throw new InternalErrorException ("Already created variable `{0}'", name);
1889 // All fixed variabled are pinned, a slot has to be alocated
1891 builder = ec.DeclareLocal (Type, IsFixed);
1892 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1893 ec.DefineLocalVariable (name, builder);
1896 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1898 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1903 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1905 if (IsConstant && const_value != null)
1906 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1908 return new LocalVariableReference (this, loc);
1911 public void Emit (EmitContext ec)
1913 // TODO: Need something better for temporary variables
1914 if ((flags & Flags.CompilerGenerated) != 0)
1917 ec.Emit (OpCodes.Ldloc, builder);
1920 public void EmitAssign (EmitContext ec)
1922 // TODO: Need something better for temporary variables
1923 if ((flags & Flags.CompilerGenerated) != 0)
1926 ec.Emit (OpCodes.Stloc, builder);
1929 public void EmitAddressOf (EmitContext ec)
1931 ec.Emit (OpCodes.Ldloca, builder);
1934 public static string GetCompilerGeneratedName (Block block)
1936 // HACK: Debugger depends on the name semantics
1937 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1940 public string GetReadOnlyContext ()
1942 switch (flags & Flags.ReadonlyMask) {
1943 case Flags.FixedVariable:
1944 return "fixed variable";
1945 case Flags.ForeachVariable:
1946 return "foreach iteration variable";
1947 case Flags.UsingVariable:
1948 return "using variable";
1951 throw new InternalErrorException ("Variable is not readonly");
1954 public bool IsThisAssigned (BlockContext ec, Block block)
1956 if (VariableInfo == null)
1957 throw new Exception ();
1959 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1962 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
1965 public bool IsAssigned (BlockContext ec)
1967 if (VariableInfo == null)
1968 throw new Exception ();
1970 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1973 public void PrepareForFlowAnalysis (BlockContext bc)
1976 // No need for definitely assigned check for these guys
1978 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
1981 VariableInfo = new VariableInfo (this, bc.FlowOffset);
1982 bc.FlowOffset += VariableInfo.Length;
1986 // Mark the variables as referenced in the user code
1988 public void SetIsUsed ()
1990 flags |= Flags.Used;
1993 public void SetHasAddressTaken ()
1995 flags |= (Flags.AddressTaken | Flags.Used);
1998 public override string ToString ()
2000 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2005 /// Block represents a C# block.
2009 /// This class is used in a number of places: either to represent
2010 /// explicit blocks that the programmer places or implicit blocks.
2012 /// Implicit blocks are used as labels or to introduce variable
2015 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2016 /// they contain extra information that is not necessary on normal blocks.
2018 public class Block : Statement {
2025 HasCapturedVariable = 64,
2026 HasCapturedThis = 1 << 7,
2027 IsExpressionTree = 1 << 8,
2028 CompilerGenerated = 1 << 9,
2029 HasAsyncModifier = 1 << 10,
2031 YieldBlock = 1 << 12,
2032 AwaitBlock = 1 << 13
2035 public Block Parent;
2036 public Location StartLocation;
2037 public Location EndLocation;
2039 public ExplicitBlock Explicit;
2040 public ParametersBlock ParametersBlock;
2042 protected Flags flags;
2045 // The statements in this block
2047 protected List<Statement> statements;
2049 protected List<Statement> scope_initializers;
2051 int? resolving_init_idx;
2057 public int ID = id++;
2059 static int clone_id_counter;
2063 // int assignable_slots;
2064 bool unreachable_shown;
2067 public Block (Block parent, Location start, Location end)
2068 : this (parent, 0, start, end)
2072 public Block (Block parent, Flags flags, Location start, Location end)
2074 if (parent != null) {
2075 // the appropriate constructors will fixup these fields
2076 ParametersBlock = parent.ParametersBlock;
2077 Explicit = parent.Explicit;
2080 this.Parent = parent;
2082 this.StartLocation = start;
2083 this.EndLocation = end;
2085 statements = new List<Statement> (4);
2087 this.original = this;
2092 public bool HasUnreachableClosingBrace {
2094 return (flags & Flags.HasRet) != 0;
2097 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2101 public Block Original {
2110 public bool IsCompilerGenerated {
2111 get { return (flags & Flags.CompilerGenerated) != 0; }
2112 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2115 public bool Unchecked {
2116 get { return (flags & Flags.Unchecked) != 0; }
2117 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2120 public bool Unsafe {
2121 get { return (flags & Flags.Unsafe) != 0; }
2122 set { flags |= Flags.Unsafe; }
2125 public List<Statement> Statements {
2126 get { return statements; }
2131 public Block CreateSwitchBlock (Location start)
2133 // FIXME: Only explicit block should be created
2134 var new_block = new Block (this, start, start);
2135 new_block.IsCompilerGenerated = true;
2139 public void SetEndLocation (Location loc)
2144 public void AddLabel (LabeledStatement target)
2146 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2149 public void AddLocalName (LocalVariable li)
2151 AddLocalName (li.Name, li);
2154 public void AddLocalName (string name, INamedBlockVariable li)
2156 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2159 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2161 if (reason == null) {
2162 Error_AlreadyDeclared (name, variable);
2166 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2167 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2168 "to `{0}', which is already used in a `{1}' scope to denote something else",
2172 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2174 var pi = variable as ParametersBlock.ParameterInfo;
2176 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2178 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2179 "A local variable named `{0}' is already defined in this scope", name);
2183 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2185 ParametersBlock.TopBlock.Report.Error (412, loc,
2186 "The type parameter name `{0}' is the same as local variable or parameter name",
2191 // It should be used by expressions which require to
2192 // register a statement during resolve process.
2194 public void AddScopeStatement (Statement s)
2196 if (scope_initializers == null)
2197 scope_initializers = new List<Statement> ();
2200 // Simple recursive helper, when resolve scope initializer another
2201 // new scope initializer can be added, this ensures it's initialized
2202 // before existing one. For now this can happen with expression trees
2203 // in base ctor initializer only
2205 if (resolving_init_idx.HasValue) {
2206 scope_initializers.Insert (resolving_init_idx.Value, s);
2207 ++resolving_init_idx;
2209 scope_initializers.Add (s);
2213 public void AddStatement (Statement s)
2218 public int AssignableSlots {
2220 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2222 // return assignable_slots;
2226 public LabeledStatement LookupLabel (string name)
2228 return ParametersBlock.TopBlock.GetLabel (name, this);
2231 public override bool Resolve (BlockContext ec)
2233 if ((flags & Flags.Resolved) != 0)
2236 Block prev_block = ec.CurrentBlock;
2239 ec.CurrentBlock = this;
2240 ec.StartFlowBranching (this);
2243 // Compiler generated scope statements
2245 if (scope_initializers != null) {
2246 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2247 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2250 resolving_init_idx = null;
2254 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2255 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2256 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2257 // responsible for handling the situation.
2259 int statement_count = statements.Count;
2260 for (int ix = 0; ix < statement_count; ix++){
2261 Statement s = statements [ix];
2264 // Warn if we detect unreachable code.
2267 if (s is EmptyStatement)
2270 if (!unreachable_shown && !(s is LabeledStatement)) {
2271 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2272 unreachable_shown = true;
2275 Block c_block = s as Block;
2276 if (c_block != null)
2277 c_block.unreachable = c_block.unreachable_shown = true;
2281 // Note that we're not using ResolveUnreachable() for unreachable
2282 // statements here. ResolveUnreachable() creates a temporary
2283 // flow branching and kills it afterwards. This leads to problems
2284 // if you have two unreachable statements where the first one
2285 // assigns a variable and the second one tries to access it.
2288 if (!s.Resolve (ec)) {
2290 if (ec.IsInProbingMode)
2293 statements [ix] = new EmptyStatement (s.loc);
2297 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2298 statements [ix] = new EmptyStatement (s.loc);
2300 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2301 if (unreachable && s is LabeledStatement)
2302 throw new InternalErrorException ("should not happen");
2305 while (ec.CurrentBranching is FlowBranchingLabeled)
2306 ec.EndFlowBranching ();
2308 bool flow_unreachable = ec.EndFlowBranching ();
2310 ec.CurrentBlock = prev_block;
2312 if (flow_unreachable)
2313 flags |= Flags.HasRet;
2315 // If we're a non-static `struct' constructor which doesn't have an
2316 // initializer, then we must initialize all of the struct's fields.
2317 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2320 flags |= Flags.Resolved;
2324 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2326 unreachable_shown = true;
2330 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2332 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2333 fb.CurrentUsageVector.IsUnreachable = true;
2334 bool ok = Resolve (ec);
2335 ec.KillFlowBranching ();
2340 protected override void DoEmit (EmitContext ec)
2342 for (int ix = 0; ix < statements.Count; ix++){
2343 statements [ix].Emit (ec);
2347 public override void Emit (EmitContext ec)
2349 if (scope_initializers != null)
2350 EmitScopeInitializers (ec);
2355 protected void EmitScopeInitializers (EmitContext ec)
2357 foreach (Statement s in scope_initializers)
2362 public override string ToString ()
2364 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2368 protected override void CloneTo (CloneContext clonectx, Statement t)
2370 Block target = (Block) t;
2372 target.clone_id = clone_id_counter++;
2375 clonectx.AddBlockMap (this, target);
2376 if (original != this)
2377 clonectx.AddBlockMap (original, target);
2379 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2380 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2383 target.Parent = clonectx.RemapBlockCopy (Parent);
2385 target.statements = new List<Statement> (statements.Count);
2386 foreach (Statement s in statements)
2387 target.statements.Add (s.Clone (clonectx));
2390 public override object Accept (StructuralVisitor visitor)
2392 return visitor.Visit (this);
2396 public class ExplicitBlock : Block
2398 protected AnonymousMethodStorey am_storey;
2400 public ExplicitBlock (Block parent, Location start, Location end)
2401 : this (parent, (Flags) 0, start, end)
2405 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2406 : base (parent, flags, start, end)
2408 this.Explicit = this;
2413 public AnonymousMethodStorey AnonymousMethodStorey {
2419 public bool HasAwait {
2421 return (flags & Flags.AwaitBlock) != 0;
2425 public bool HasCapturedThis {
2426 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2428 return (flags & Flags.HasCapturedThis) != 0;
2432 public bool HasCapturedVariable {
2433 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2435 return (flags & Flags.HasCapturedVariable) != 0;
2439 public bool HasYield {
2441 return (flags & Flags.YieldBlock) != 0;
2448 // Creates anonymous method storey in current block
2450 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2453 // Return same story for iterator and async blocks unless we are
2454 // in nested anonymous method
2456 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2457 return ec.CurrentAnonymousMethod.Storey;
2460 // When referencing a variable in parent iterator/async storey
2461 // from nested anonymous method
2463 if (ParametersBlock.am_storey is StateMachine) {
2464 return ParametersBlock.am_storey;
2467 if (am_storey == null) {
2468 MemberBase mc = ec.MemberContext as MemberBase;
2471 // Creates anonymous method storey for this block
2473 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2479 public override void Emit (EmitContext ec)
2481 if (am_storey != null) {
2482 DefineAnonymousStorey (ec);
2483 am_storey.EmitStoreyInstantiation (ec, this);
2486 if (scope_initializers != null)
2487 EmitScopeInitializers (ec);
2489 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2490 ec.Emit (OpCodes.Nop);
2501 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2502 ec.Emit (OpCodes.Nop);
2506 void DefineAnonymousStorey (EmitContext ec)
2509 // Creates anonymous method storey
2511 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2513 // Creates parent storey reference when hoisted this is accessible
2515 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2516 ExplicitBlock parent = am_storey.OriginalSourceBlock.Explicit.Parent.Explicit;
2519 // Hoisted this exists in top-level parent storey only
2521 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2522 parent = parent.Parent.Explicit;
2524 am_storey.AddParentStoreyReference (ec, parent.am_storey);
2527 am_storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2529 // TODO MemberCache: Review
2530 am_storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2533 am_storey.CreateContainer ();
2534 am_storey.DefineContainer ();
2536 var ref_blocks = am_storey.ReferencesFromChildrenBlock;
2537 if (ref_blocks != null) {
2538 foreach (ExplicitBlock ref_block in ref_blocks) {
2539 for (ExplicitBlock b = ref_block.Explicit; b.am_storey != am_storey; b = b.Parent.Explicit) {
2540 if (b.am_storey != null) {
2541 b.am_storey.AddParentStoreyReference (ec, am_storey);
2543 // Stop propagation inside same top block
2544 if (b.ParametersBlock.Original == ParametersBlock.Original)
2547 b = b.ParametersBlock;
2550 b.HasCapturedVariable = true;
2555 am_storey.Define ();
2556 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2559 public void RegisterAsyncAwait ()
2562 while ((block.flags & Flags.AwaitBlock) == 0) {
2563 block.flags |= Flags.AwaitBlock;
2565 if (block.Parent == null)
2568 block = block.Parent.Explicit;
2572 public void RegisterIteratorYield ()
2575 while ((block.flags & Flags.YieldBlock) == 0) {
2576 block.flags |= Flags.YieldBlock;
2578 if (block.Parent == null)
2581 block = block.Parent.Explicit;
2585 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2587 tryBlock.statements = statements;
2588 statements = new List<Statement> (1);
2589 statements.Add (tf);
2594 // ParametersBlock was introduced to support anonymous methods
2595 // and lambda expressions
2597 public class ParametersBlock : ExplicitBlock
2599 public class ParameterInfo : INamedBlockVariable
2601 readonly ParametersBlock block;
2603 public VariableInfo VariableInfo;
2606 public ParameterInfo (ParametersBlock block, int index)
2614 public Block Block {
2620 public bool IsDeclared {
2626 public bool IsParameter {
2632 public bool IsLocked {
2641 public Location Location {
2643 return Parameter.Location;
2647 public Parameter Parameter {
2649 return block.Parameters [index];
2653 public TypeSpec ParameterType {
2655 return Parameter.Type;
2661 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2663 return new ParameterReference (this, loc);
2668 // Block is converted into an expression
2670 sealed class BlockScopeExpression : Expression
2673 readonly ParametersBlock block;
2675 public BlockScopeExpression (Expression child, ParametersBlock block)
2681 public override bool ContainsEmitWithAwait ()
2683 return child.ContainsEmitWithAwait ();
2686 public override Expression CreateExpressionTree (ResolveContext ec)
2688 throw new NotSupportedException ();
2691 protected override Expression DoResolve (ResolveContext ec)
2696 child = child.Resolve (ec);
2700 eclass = child.eclass;
2705 public override void Emit (EmitContext ec)
2707 block.EmitScopeInitializers (ec);
2712 protected ParametersCompiled parameters;
2713 protected ParameterInfo[] parameter_info;
2715 protected bool unreachable;
2716 protected ToplevelBlock top_block;
2718 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2719 : base (parent, 0, start, start)
2721 if (parameters == null)
2722 throw new ArgumentNullException ("parameters");
2724 this.parameters = parameters;
2725 ParametersBlock = this;
2727 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2729 this.top_block = parent.ParametersBlock.top_block;
2730 ProcessParameters ();
2733 protected ParametersBlock (ParametersCompiled parameters, Location start)
2734 : base (null, 0, start, start)
2736 if (parameters == null)
2737 throw new ArgumentNullException ("parameters");
2739 this.parameters = parameters;
2740 ParametersBlock = this;
2744 // It's supposed to be used by method body implementation of anonymous methods
2746 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2747 : base (null, 0, source.StartLocation, source.EndLocation)
2749 this.parameters = parameters;
2750 this.statements = source.statements;
2751 this.scope_initializers = source.scope_initializers;
2753 this.resolved = true;
2754 this.unreachable = source.unreachable;
2755 this.am_storey = source.am_storey;
2757 ParametersBlock = this;
2760 // Overwrite original for comparison purposes when linking cross references
2761 // between anonymous methods
2768 public bool IsAsync {
2770 return (flags & Flags.HasAsyncModifier) != 0;
2773 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2778 // Block has been converted to expression tree
2780 public bool IsExpressionTree {
2782 return (flags & Flags.IsExpressionTree) != 0;
2787 // The parameters for the block.
2789 public ParametersCompiled Parameters {
2795 public ToplevelBlock TopBlock {
2801 public bool Resolved {
2803 return (flags & Flags.Resolved) != 0;
2807 public int TemporaryLocalsCount { get; set; }
2812 // Check whether all `out' parameters have been assigned.
2814 public void CheckOutParameters (FlowBranching.UsageVector vector)
2816 if (vector.IsUnreachable)
2819 int n = parameter_info == null ? 0 : parameter_info.Length;
2821 for (int i = 0; i < n; i++) {
2822 VariableInfo var = parameter_info[i].VariableInfo;
2827 if (vector.IsAssigned (var, false))
2830 var p = parameter_info[i].Parameter;
2831 TopBlock.Report.Error (177, p.Location,
2832 "The out parameter `{0}' must be assigned to before control leaves the current method",
2837 public override Expression CreateExpressionTree (ResolveContext ec)
2839 if (statements.Count == 1) {
2840 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2841 if (scope_initializers != null)
2842 expr = new BlockScopeExpression (expr, this);
2847 return base.CreateExpressionTree (ec);
2850 public ParameterInfo GetParameterInfo (Parameter p)
2852 for (int i = 0; i < parameters.Count; ++i) {
2853 if (parameters[i] == p)
2854 return parameter_info[i];
2857 throw new ArgumentException ("Invalid parameter");
2860 public ParameterReference GetParameterReference (int index, Location loc)
2862 return new ParameterReference (parameter_info[index], loc);
2865 public Statement PerformClone ()
2867 CloneContext clonectx = new CloneContext ();
2868 return Clone (clonectx);
2871 protected void ProcessParameters ()
2873 if (parameters.Count == 0)
2876 parameter_info = new ParameterInfo[parameters.Count];
2877 for (int i = 0; i < parameter_info.Length; ++i) {
2878 var p = parameters.FixedParameters[i];
2882 // TODO: Should use Parameter only and more block there
2883 parameter_info[i] = new ParameterInfo (this, i);
2885 AddLocalName (p.Name, parameter_info[i]);
2889 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
2896 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
2897 flags |= Flags.IsExpressionTree;
2902 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2903 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2908 unreachable = top_level.End ();
2910 } catch (Exception e) {
2911 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
2914 if (rc.CurrentBlock != null) {
2915 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
2917 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
2920 if (rc.Module.Compiler.Settings.DebugFlags > 0)
2924 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
2925 if (rc.CurrentAnonymousMethod == null) {
2926 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
2927 if (md is StateMachineMethod) {
2930 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2935 // If an asynchronous body of F is either an expression classified as nothing, or a
2936 // statement block where no return statements have expressions, the inferred return type is Task
2939 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
2940 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
2941 am.ReturnTypeInference = null;
2942 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
2947 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2948 rc.CurrentAnonymousMethod.GetSignatureForError ());
2956 void ResolveMeta (BlockContext ec)
2958 int orig_count = parameters.Count;
2960 for (int i = 0; i < orig_count; ++i) {
2961 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
2963 if ((mod & Parameter.Modifier.OUT) == 0)
2966 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
2967 parameter_info[i].VariableInfo = vi;
2968 ec.FlowOffset += vi.Length;
2972 public void WrapIntoIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
2974 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, Location.Null);
2975 pb.statements = statements;
2978 var iterator = new Iterator (pb, method, host, iterator_type, is_enumerable);
2979 am_storey = new IteratorStorey (iterator);
2981 statements = new List<Statement> (1);
2982 AddStatement (new Return (iterator, iterator.Location));
2983 flags &= ~Flags.YieldBlock;
2984 IsCompilerGenerated = true;
2987 public void WrapIntoAsyncTask (IMemberContext context, TypeDefinition host, TypeSpec returnType)
2989 ParametersBlock pb = new ParametersBlock (this, ParametersCompiled.EmptyReadOnlyParameters, Location.Null);
2990 pb.statements = statements;
2993 var block_type = host.Module.Compiler.BuiltinTypes.Void;
2994 var initializer = new AsyncInitializer (pb, host, block_type);
2995 initializer.Type = block_type;
2997 am_storey = new AsyncTaskStorey (context, initializer, returnType);
2999 statements = new List<Statement> (1);
3000 AddStatement (new StatementExpression (initializer));
3001 flags &= ~Flags.AwaitBlock;
3002 IsCompilerGenerated = true;
3009 public class ToplevelBlock : ParametersBlock
3011 LocalVariable this_variable;
3012 CompilerContext compiler;
3013 Dictionary<string, object> names;
3014 Dictionary<string, object> labels;
3016 public HoistedVariable HoistedThisVariable;
3018 public Report Report {
3019 get { return compiler.Report; }
3022 public ToplevelBlock (CompilerContext ctx, Location loc)
3023 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3027 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3028 : base (parameters, start)
3030 this.compiler = ctx;
3032 flags |= Flags.HasRet;
3034 ProcessParameters ();
3038 // Recreates a top level block from parameters block. Used for
3039 // compiler generated methods where the original block comes from
3040 // explicit child block. This works for already resolved blocks
3041 // only to ensure we resolve them in the correct flow order
3043 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3044 : base (source, parameters)
3046 this.compiler = source.TopBlock.compiler;
3048 flags |= Flags.HasRet;
3051 public bool IsIterator {
3057 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3060 names = new Dictionary<string, object> ();
3063 if (!names.TryGetValue (name, out value)) {
3064 names.Add (name, li);
3068 INamedBlockVariable existing = value as INamedBlockVariable;
3069 List<INamedBlockVariable> existing_list;
3070 if (existing != null) {
3071 existing_list = new List<INamedBlockVariable> ();
3072 existing_list.Add (existing);
3073 names[name] = existing_list;
3075 existing_list = (List<INamedBlockVariable>) value;
3079 // A collision checking between local names
3081 for (int i = 0; i < existing_list.Count; ++i) {
3082 existing = existing_list[i];
3083 Block b = existing.Block.Explicit;
3085 // Collision at same level
3086 if (li.Block.Explicit == b) {
3087 li.Block.Error_AlreadyDeclared (name, li);
3091 // Collision with parent
3092 Block parent = li.Block.Explicit;
3093 while ((parent = parent.Parent) != null) {
3095 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3096 i = existing_list.Count;
3101 if (!ignoreChildrenBlocks) {
3102 // Collision with children
3103 while ((b = b.Parent) != null) {
3104 if (li.Block.Explicit == b) {
3105 li.Block.Error_AlreadyDeclared (name, li, "child");
3106 i = existing_list.Count;
3113 existing_list.Add (li);
3116 public void AddLabel (string name, LabeledStatement label)
3119 labels = new Dictionary<string, object> ();
3122 if (!labels.TryGetValue (name, out value)) {
3123 labels.Add (name, label);
3127 LabeledStatement existing = value as LabeledStatement;
3128 List<LabeledStatement> existing_list;
3129 if (existing != null) {
3130 existing_list = new List<LabeledStatement> ();
3131 existing_list.Add (existing);
3132 labels[name] = existing_list;
3134 existing_list = (List<LabeledStatement>) value;
3138 // A collision checking between labels
3140 for (int i = 0; i < existing_list.Count; ++i) {
3141 existing = existing_list[i];
3142 Block b = existing.Block;
3144 // Collision at same level
3145 if (label.Block == b) {
3146 Report.SymbolRelatedToPreviousError (existing.loc, name);
3147 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3151 // Collision with parent
3153 while ((b = b.Parent) != null) {
3154 if (existing.Block == b) {
3155 Report.Error (158, label.loc,
3156 "The label `{0}' shadows another label by the same name in a contained scope", name);
3157 i = existing_list.Count;
3162 // Collision with with children
3164 while ((b = b.Parent) != null) {
3165 if (label.Block == b) {
3166 Report.Error (158, label.loc,
3167 "The label `{0}' shadows another label by the same name in a contained scope", name);
3168 i = existing_list.Count;
3174 existing_list.Add (label);
3178 // Creates an arguments set from all parameters, useful for method proxy calls
3180 public Arguments GetAllParametersArguments ()
3182 int count = parameters.Count;
3183 Arguments args = new Arguments (count);
3184 for (int i = 0; i < count; ++i) {
3185 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3186 args.Add (new Argument (arg_expr));
3193 // Lookup inside a block, the returned value can represent 3 states
3195 // true+variable: A local name was found and it's valid
3196 // false+variable: A local name was found in a child block only
3197 // false+null: No local name was found
3199 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3205 if (!names.TryGetValue (name, out value))
3208 variable = value as INamedBlockVariable;
3210 if (variable != null) {
3212 if (variable.Block == b.Original)
3216 } while (b != null);
3224 } while (b != null);
3226 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3227 for (int i = 0; i < list.Count; ++i) {
3230 if (variable.Block == b.Original)
3234 } while (b != null);
3242 } while (b != null);
3252 public LabeledStatement GetLabel (string name, Block block)
3258 if (!labels.TryGetValue (name, out value)) {
3262 var label = value as LabeledStatement;
3264 if (label != null) {
3265 if (label.Block == b.Original)
3268 // TODO: Temporary workaround for the switch block implicit label block
3269 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3272 List<LabeledStatement> list = (List<LabeledStatement>) value;
3273 for (int i = 0; i < list.Count; ++i) {
3275 if (label.Block == b.Original)
3278 // TODO: Temporary workaround for the switch block implicit label block
3279 if (label.Block.IsCompilerGenerated && label.Block.Parent == b.Original)
3288 // Returns the "this" instance variable of this block.
3289 // See AddThisVariable() for more information.
3291 public LocalVariable ThisVariable {
3292 get { return this_variable; }
3296 // This is used by non-static `struct' constructors which do not have an
3297 // initializer - in this case, the constructor must initialize all of the
3298 // struct's fields. To do this, we add a "this" variable and use the flow
3299 // analysis code to ensure that it's been fully initialized before control
3300 // leaves the constructor.
3302 public void AddThisVariable (BlockContext bc)
3304 if (this_variable != null)
3305 throw new InternalErrorException (StartLocation.ToString ());
3307 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3308 this_variable.Type = bc.CurrentType;
3309 this_variable.PrepareForFlowAnalysis (bc);
3312 public bool IsThisAssigned (BlockContext ec)
3314 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3317 public override void Emit (EmitContext ec)
3319 if (Report.Errors > 0)
3325 if (IsCompilerGenerated) {
3326 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3334 // If `HasReturnLabel' is set, then we already emitted a
3335 // jump to the end of the method, so we must emit a `ret'
3338 // Unfortunately, System.Reflection.Emit automatically emits
3339 // a leave to the end of a finally block. This is a problem
3340 // if no code is following the try/finally block since we may
3341 // jump to a point after the end of the method.
3342 // As a workaround, we're always creating a return label in
3345 if (ec.HasReturnLabel || !unreachable) {
3346 if (ec.HasReturnLabel)
3347 ec.MarkLabel (ec.ReturnLabel);
3349 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3350 ec.Mark (EndLocation);
3352 if (ec.ReturnType.Kind != MemberKind.Void)
3353 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3355 ec.Emit (OpCodes.Ret);
3359 } catch (Exception e){
3360 Console.WriteLine ("Exception caught by the compiler while emitting:");
3361 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3363 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3370 public class SwitchLabel {
3373 readonly Location loc;
3378 // if expr == null, then it is the default case.
3380 public SwitchLabel (Expression expr, Location l)
3386 public bool IsDefault {
3388 return label == null;
3392 public Expression Label {
3398 public Location Location {
3404 public Constant Converted {
3413 public Label GetILLabel (EmitContext ec)
3415 if (il_label == null){
3416 il_label = ec.DefineLabel ();
3419 return il_label.Value;
3423 // Resolves the expression, reduces it to a literal if possible
3424 // and then converts it to the requested type.
3426 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3428 Expression e = label.Resolve (ec);
3433 Constant c = e as Constant;
3435 ec.Report.Error (150, loc, "A constant value is expected");
3439 if (allow_nullable && c is NullLiteral) {
3444 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3445 return converted != null;
3448 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3451 if (converted == null)
3454 label = converted.GetValueAsLiteral ();
3456 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3457 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3460 public SwitchLabel Clone (CloneContext clonectx)
3465 return new SwitchLabel (label.Clone (clonectx), loc);
3469 public class SwitchSection {
3470 public readonly List<SwitchLabel> Labels;
3471 public readonly Block Block;
3473 public SwitchSection (List<SwitchLabel> labels, Block block)
3479 public SwitchSection Clone (CloneContext clonectx)
3481 var cloned_labels = new List<SwitchLabel> ();
3483 foreach (SwitchLabel sl in Labels)
3484 cloned_labels.Add (sl.Clone (clonectx));
3486 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3490 public class Switch : Statement
3492 // structure used to hold blocks of keys while calculating table switch
3493 sealed class LabelsRange : IComparable<LabelsRange>
3495 public readonly long min;
3497 public readonly List<long> label_values;
3499 public LabelsRange (long value)
3502 label_values = new List<long> ();
3503 label_values.Add (value);
3506 public LabelsRange (long min, long max, ICollection<long> values)
3510 this.label_values = new List<long> (values);
3515 return max - min + 1;
3519 public bool AddValue (long value)
3521 var gap = value - min + 1;
3522 // Ensure the range has > 50% occupancy
3523 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3527 label_values.Add (value);
3531 public int CompareTo (LabelsRange other)
3533 int nLength = label_values.Count;
3534 int nLengthOther = other.label_values.Count;
3535 if (nLengthOther == nLength)
3536 return (int) (other.min - min);
3538 return nLength - nLengthOther;
3542 sealed class LabelMarker : Statement
3545 readonly List<SwitchLabel> labels;
3547 public LabelMarker (Switch s, List<SwitchLabel> labels)
3550 this.labels = labels;
3553 protected override void CloneTo (CloneContext clonectx, Statement target)
3557 protected override void DoEmit (EmitContext ec)
3559 foreach (var l in labels) {
3561 ec.MarkLabel (s.DefaultLabel);
3563 ec.MarkLabel (l.GetILLabel (ec));
3568 public List<SwitchSection> Sections;
3569 public Expression Expr;
3572 // Mapping of all labels to their SwitchLabels
3574 Dictionary<long, SwitchLabel> labels;
3575 Dictionary<string, SwitchLabel> string_labels;
3578 /// The governing switch type
3580 public TypeSpec SwitchType;
3585 Label default_target;
3587 Expression new_expr;
3590 SwitchSection constant_section;
3591 SwitchSection default_section;
3592 SwitchLabel null_section;
3594 Statement simple_stmt;
3595 VariableReference value;
3596 ExpressionStatement string_dictionary;
3597 FieldExpr switch_cache_field;
3598 ExplicitBlock block;
3601 // Nullable Types support
3603 Nullable.Unwrap unwrap;
3605 public Switch (Expression e, ExplicitBlock block, List<SwitchSection> sects, Location l)
3613 public ExplicitBlock Block {
3619 public Label DefaultLabel {
3621 return default_target;
3625 public bool GotDefault {
3627 return default_section != null;
3631 public bool IsNullable {
3633 return unwrap != null;
3638 // Determines the governing type for a switch. The returned
3639 // expression might be the expression from the switch, or an
3640 // expression that includes any potential conversions to
3642 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3644 switch (expr.Type.BuiltinType) {
3645 case BuiltinTypeSpec.Type.Byte:
3646 case BuiltinTypeSpec.Type.SByte:
3647 case BuiltinTypeSpec.Type.UShort:
3648 case BuiltinTypeSpec.Type.Short:
3649 case BuiltinTypeSpec.Type.UInt:
3650 case BuiltinTypeSpec.Type.Int:
3651 case BuiltinTypeSpec.Type.ULong:
3652 case BuiltinTypeSpec.Type.Long:
3653 case BuiltinTypeSpec.Type.Char:
3654 case BuiltinTypeSpec.Type.String:
3655 case BuiltinTypeSpec.Type.Bool:
3659 if (expr.Type.IsEnum)
3663 // Try to find a *user* defined implicit conversion.
3665 // If there is no implicit conversion, or if there are multiple
3666 // conversions, we have to report an error
3668 Expression converted = null;
3669 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3672 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3677 // Ignore over-worked ImplicitUserConversions that do
3678 // an implicit conversion in addition to the user conversion.
3680 if (!(e is UserCast))
3683 if (converted != null){
3684 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3693 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3695 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3711 // Performs the basic sanity checks on the switch statement
3712 // (looks for duplicate keys and non-constant expressions).
3714 // It also returns a hashtable with the keys that we will later
3715 // use to compute the switch tables
3717 bool CheckSwitch (ResolveContext ec)
3720 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String)
3721 string_labels = new Dictionary<string, SwitchLabel> (Sections.Count + 1);
3723 labels = new Dictionary<long, SwitchLabel> (Sections.Count + 1);
3725 foreach (SwitchSection ss in Sections){
3726 foreach (SwitchLabel sl in ss.Labels){
3728 if (default_section != null){
3729 sl.Error_AlreadyOccurs (ec, SwitchType, default_section.Labels [0]);
3732 default_section = ss;
3736 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3742 if (string_labels != null) {
3743 string s = sl.Converted.GetValue () as string;
3747 string_labels.Add (s, sl);
3749 if (sl.Converted is NullLiteral) {
3752 labels.Add (sl.Converted.GetValueAsLong (), sl);
3755 } catch (ArgumentException) {
3756 if (string_labels != null)
3757 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3759 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3769 // This method emits code for a lookup-based switch statement (non-string)
3770 // Basically it groups the cases into blocks that are at least half full,
3771 // and then spits out individual lookup opcodes for each block.
3772 // It emits the longest blocks first, and short blocks are just
3773 // handled with direct compares.
3775 void EmitTableSwitch (EmitContext ec, Expression val)
3777 Label lbl_default = default_target;
3779 if (labels != null && labels.Count > 0) {
3780 List<LabelsRange> ranges;
3781 if (string_labels != null) {
3782 // We have done all hard work for string already
3783 // setup single range only
3784 ranges = new List<LabelsRange> (1);
3785 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3787 var element_keys = new long[labels.Count];
3788 labels.Keys.CopyTo (element_keys, 0);
3789 Array.Sort (element_keys);
3792 // Build possible ranges of switch labes to reduce number
3795 ranges = new List<LabelsRange> (element_keys.Length);
3796 var range = new LabelsRange (element_keys[0]);
3798 for (int i = 1; i < element_keys.Length; ++i) {
3799 var l = element_keys[i];
3800 if (range.AddValue (l))
3803 range = new LabelsRange (l);
3807 // sort the blocks so we can tackle the largest ones first
3811 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3813 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3814 LabelsRange kb = ranges[range_index];
3815 lbl_default = (range_index == 0) ? default_target : ec.DefineLabel ();
3817 // Optimize small ranges using simple equality check
3818 if (kb.Range <= 2) {
3819 foreach (var key in kb.label_values) {
3820 SwitchLabel sl = labels[key];
3821 if (sl.Converted.IsDefaultValue) {
3822 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3825 sl.Converted.Emit (ec);
3826 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3830 // TODO: if all the keys in the block are the same and there are
3831 // no gaps/defaults then just use a range-check.
3832 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
3833 // TODO: optimize constant/I4 cases
3835 // check block range (could be > 2^31)
3837 ec.EmitLong (kb.min);
3838 ec.Emit (OpCodes.Blt, lbl_default);
3841 ec.EmitLong (kb.max);
3842 ec.Emit (OpCodes.Bgt, lbl_default);
3847 ec.EmitLong (kb.min);
3848 ec.Emit (OpCodes.Sub);
3851 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3855 int first = (int) kb.min;
3858 ec.Emit (OpCodes.Sub);
3859 } else if (first < 0) {
3860 ec.EmitInt (-first);
3861 ec.Emit (OpCodes.Add);
3865 // first, build the list of labels for the switch
3867 long cJumps = kb.Range;
3868 Label[] switch_labels = new Label[cJumps];
3869 for (int iJump = 0; iJump < cJumps; iJump++) {
3870 var key = kb.label_values[iKey];
3871 if (key == kb.min + iJump) {
3872 switch_labels[iJump] = labels[key].GetILLabel (ec);
3875 switch_labels[iJump] = lbl_default;
3879 // emit the switch opcode
3880 ec.Emit (OpCodes.Switch, switch_labels);
3883 // mark the default for this block
3884 if (range_index != 0)
3885 ec.MarkLabel (lbl_default);
3888 // the last default just goes to the end
3889 if (ranges.Count > 0)
3890 ec.Emit (OpCodes.Br, lbl_default);
3893 // now emit the code for the sections
3894 bool found_default = false;
3896 foreach (SwitchSection ss in Sections) {
3897 foreach (SwitchLabel sl in ss.Labels) {
3899 ec.MarkLabel (lbl_default);
3900 found_default = true;
3901 if (null_section == null)
3902 ec.MarkLabel (null_target);
3903 } else if (sl.Converted.IsNull) {
3904 ec.MarkLabel (null_target);
3907 ec.MarkLabel (sl.GetILLabel (ec));
3913 if (!found_default) {
3914 ec.MarkLabel (lbl_default);
3915 if (null_section == null) {
3916 ec.MarkLabel (null_target);
3921 SwitchLabel FindLabel (Constant value)
3923 SwitchLabel sl = null;
3925 if (string_labels != null) {
3926 string s = value.GetValue () as string;
3928 if (null_section != null)
3930 else if (default_section != null)
3931 sl = default_section.Labels[0];
3933 string_labels.TryGetValue (s, out sl);
3936 if (value is NullLiteral) {
3939 labels.TryGetValue (value.GetValueAsLong (), out sl);
3946 SwitchSection FindSection (SwitchLabel label)
3948 foreach (SwitchSection ss in Sections){
3949 foreach (SwitchLabel sl in ss.Labels){
3958 public override bool Resolve (BlockContext ec)
3960 Expr = Expr.Resolve (ec);
3964 new_expr = SwitchGoverningType (ec, Expr);
3966 if (new_expr == null && Expr.Type.IsNullableType) {
3967 unwrap = Nullable.Unwrap.Create (Expr, false);
3971 new_expr = SwitchGoverningType (ec, unwrap);
3974 if (new_expr == null){
3975 ec.Report.Error (151, loc,
3976 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3977 TypeManager.CSharpName (Expr.Type));
3982 SwitchType = new_expr.Type;
3984 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
3985 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
3989 if (!CheckSwitch (ec))
3992 Switch old_switch = ec.Switch;
3994 ec.Switch.SwitchType = SwitchType;
3996 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3998 var constant = new_expr as Constant;
3999 if (constant != null) {
4001 SwitchLabel label = FindLabel (constant);
4003 constant_section = FindSection (label);
4005 if (constant_section == null)
4006 constant_section = default_section;
4009 // Store switch expression for comparission purposes
4011 value = new_expr as VariableReference;
4013 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4018 foreach (SwitchSection ss in Sections){
4020 ec.CurrentBranching.CreateSibling (
4021 null, FlowBranching.SiblingType.SwitchSection);
4025 if (is_constant && (ss != constant_section)) {
4026 // If we're a constant switch, we're only emitting
4027 // one single section - mark all the others as
4029 ec.CurrentBranching.CurrentUsageVector.Goto ();
4030 if (!ss.Block.ResolveUnreachable (ec, true)) {
4034 if (!ss.Block.Resolve (ec))
4039 if (default_section == null)
4040 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4042 ec.EndFlowBranching ();
4043 ec.Switch = old_switch;
4049 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4050 if (string_labels.Count < 7)
4051 ResolveSimpleSwitch (ec);
4053 ResolveStringSwitchMap (ec);
4054 } else if (labels.Count < 3 && !IsNullable) {
4055 ResolveSimpleSwitch (ec);
4062 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4064 var sl = FindLabel (value);
4067 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4074 // Prepares switch using simple if/else comparison for small label count (4 + optional default)
4076 void ResolveSimpleSwitch (BlockContext bc)
4078 simple_stmt = default_section != null ? default_section.Block : null;
4080 for (int i = Sections.Count - 1; i >= 0; --i) {
4081 var s = Sections[i];
4083 if (s == default_section) {
4084 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4088 s.Block.AddScopeStatement (new LabelMarker (this, s.Labels));
4090 Expression cond = null;
4091 for (int ci = 0; ci < s.Labels.Count; ++ci) {
4092 var e = new Binary (Binary.Operator.Equality, value, s.Labels[ci].Converted, loc);
4095 cond = new Binary (Binary.Operator.LogicalOr, cond, e, loc);
4102 // Compiler generated, hide from symbol file
4104 simple_stmt = new If (cond, s.Block, simple_stmt, Location.Null);
4107 // It's null for empty switch
4108 if (simple_stmt != null)
4109 simple_stmt.Resolve (bc);
4113 // Converts string switch into string hashtable
4115 void ResolveStringSwitchMap (ResolveContext ec)
4117 FullNamedExpression string_dictionary_type;
4118 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4119 string_dictionary_type = new TypeExpression (
4120 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4121 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4123 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4124 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4126 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4130 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4131 Field field = new Field (ctype, string_dictionary_type,
4132 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4133 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4134 if (!field.Define ())
4136 ctype.AddField (field);
4138 var init = new List<Expression> ();
4140 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4141 string value = null;
4142 foreach (SwitchSection section in Sections) {
4143 bool contains_label = false;
4144 foreach (SwitchLabel sl in section.Labels) {
4145 if (sl.IsDefault || sl.Converted.IsNull)
4148 if (!contains_label) {
4149 labels.Add (counter, sl);
4150 contains_label = true;
4153 value = (string) sl.Converted.GetValue ();
4154 var init_args = new List<Expression> (2);
4155 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4157 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4158 init_args.Add (sl.Converted);
4160 init.Add (new CollectionElementInitializer (init_args, loc));
4164 // Don't add empty sections
4170 Arguments args = new Arguments (1);
4171 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4172 Expression initializer = new NewInitialize (string_dictionary_type, args,
4173 new CollectionOrObjectInitializers (init, loc), loc);
4175 switch_cache_field = new FieldExpr (field, loc);
4176 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4179 void DoEmitStringSwitch (EmitContext ec)
4181 Label l_initialized = ec.DefineLabel ();
4184 // Skip initialization when value is null
4186 value.EmitBranchable (ec, null_target, false);
4189 // Check if string dictionary is initialized and initialize
4191 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4192 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4193 string_dictionary.EmitStatement (ec);
4195 ec.MarkLabel (l_initialized);
4197 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4199 ResolveContext rc = new ResolveContext (ec.MemberContext);
4201 if (switch_cache_field.Type.IsGeneric) {
4202 Arguments get_value_args = new Arguments (2);
4203 get_value_args.Add (new Argument (value));
4204 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4205 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4206 if (get_item == null)
4210 // A value was not found, go to default case
4212 get_item.EmitBranchable (ec, default_target, false);
4214 Arguments get_value_args = new Arguments (1);
4215 get_value_args.Add (new Argument (value));
4217 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4218 if (get_item == null)
4221 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4222 get_item_object.EmitAssign (ec, get_item, true, false);
4223 ec.Emit (OpCodes.Brfalse, default_target);
4225 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4226 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4228 get_item_int.EmitStatement (ec);
4229 get_item_object.Release (ec);
4232 EmitTableSwitch (ec, string_switch_variable);
4233 string_switch_variable.Release (ec);
4236 protected override void DoEmit (EmitContext ec)
4238 // Workaround broken flow-analysis
4239 block.HasUnreachableClosingBrace = true;
4242 // Needed to emit anonymous storey initialization
4243 // Otherwise it does not contain any statements for now
4247 default_target = ec.DefineLabel ();
4248 null_target = ec.DefineLabel ();
4251 unwrap.EmitCheck (ec);
4252 ec.Emit (OpCodes.Brfalse, null_target);
4253 value.EmitAssign (ec, new_expr, false, false);
4254 } else if (new_expr != value && !is_constant) {
4255 value.EmitAssign (ec, new_expr, false, false);
4259 // Setup the codegen context
4261 Label old_end = ec.LoopEnd;
4262 Switch old_switch = ec.Switch;
4264 ec.LoopEnd = ec.DefineLabel ();
4269 if (constant_section != null)
4270 constant_section.Block.Emit (ec);
4271 } else if (string_dictionary != null) {
4272 DoEmitStringSwitch (ec);
4273 } else if (simple_stmt != null) {
4274 simple_stmt.Emit (ec);
4276 EmitTableSwitch (ec, value);
4279 // Restore context state.
4280 ec.MarkLabel (ec.LoopEnd);
4283 // Restore the previous context
4285 ec.LoopEnd = old_end;
4286 ec.Switch = old_switch;
4289 protected override void CloneTo (CloneContext clonectx, Statement t)
4291 Switch target = (Switch) t;
4293 target.Expr = Expr.Clone (clonectx);
4294 target.Sections = new List<SwitchSection> ();
4295 foreach (SwitchSection ss in Sections){
4296 target.Sections.Add (ss.Clone (clonectx));
4300 public override object Accept (StructuralVisitor visitor)
4302 return visitor.Visit (this);
4306 // A place where execution can restart in an iterator
4307 public abstract class ResumableStatement : Statement
4310 protected Label resume_point;
4312 public Label PrepareForEmit (EmitContext ec)
4316 resume_point = ec.DefineLabel ();
4318 return resume_point;
4321 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4326 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4331 public abstract class TryFinallyBlock : ExceptionStatement
4333 protected Statement stmt;
4334 Label dispose_try_block;
4335 bool prepared_for_dispose, emitted_dispose;
4337 protected TryFinallyBlock (Statement stmt, Location loc)
4345 public Statement Statement {
4353 protected abstract void EmitTryBody (EmitContext ec);
4354 protected abstract void EmitFinallyBody (EmitContext ec);
4356 public override Label PrepareForDispose (EmitContext ec, Label end)
4358 if (!prepared_for_dispose) {
4359 prepared_for_dispose = true;
4360 dispose_try_block = ec.DefineLabel ();
4362 return dispose_try_block;
4365 protected sealed override void DoEmit (EmitContext ec)
4367 EmitTryBodyPrepare (ec);
4370 ec.BeginFinallyBlock ();
4372 Label start_finally = ec.DefineLabel ();
4373 if (resume_points != null) {
4374 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4376 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4377 ec.Emit (OpCodes.Brfalse_S, start_finally);
4378 ec.Emit (OpCodes.Endfinally);
4381 ec.MarkLabel (start_finally);
4382 EmitFinallyBody (ec);
4384 ec.EndExceptionBlock ();
4387 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4389 if (emitted_dispose)
4392 emitted_dispose = true;
4394 Label end_of_try = ec.DefineLabel ();
4396 // Ensure that the only way we can get into this code is through a dispatcher
4397 if (have_dispatcher)
4398 ec.Emit (OpCodes.Br, end);
4400 ec.BeginExceptionBlock ();
4402 ec.MarkLabel (dispose_try_block);
4404 Label[] labels = null;
4405 for (int i = 0; i < resume_points.Count; ++i) {
4406 ResumableStatement s = resume_points[i];
4407 Label ret = s.PrepareForDispose (ec, end_of_try);
4408 if (ret.Equals (end_of_try) && labels == null)
4410 if (labels == null) {
4411 labels = new Label[resume_points.Count];
4412 for (int j = 0; j < i; ++j)
4413 labels[j] = end_of_try;
4418 if (labels != null) {
4420 for (j = 1; j < labels.Length; ++j)
4421 if (!labels[0].Equals (labels[j]))
4423 bool emit_dispatcher = j < labels.Length;
4425 if (emit_dispatcher) {
4426 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4427 ec.Emit (OpCodes.Ldloc, pc);
4428 ec.EmitInt (first_resume_pc);
4429 ec.Emit (OpCodes.Sub);
4430 ec.Emit (OpCodes.Switch, labels);
4431 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4434 foreach (ResumableStatement s in resume_points)
4435 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4438 ec.MarkLabel (end_of_try);
4440 ec.BeginFinallyBlock ();
4442 EmitFinallyBody (ec);
4444 ec.EndExceptionBlock ();
4449 // Base class for blocks using exception handling
4451 public abstract class ExceptionStatement : ResumableStatement
4456 protected List<ResumableStatement> resume_points;
4457 protected int first_resume_pc;
4459 protected ExceptionStatement (Location loc)
4464 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4466 StateMachineInitializer state_machine = null;
4467 if (resume_points != null) {
4468 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4470 ec.EmitInt ((int) IteratorStorey.State.Running);
4471 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4474 ec.BeginExceptionBlock ();
4476 if (resume_points != null) {
4477 ec.MarkLabel (resume_point);
4479 // For normal control flow, we want to fall-through the Switch
4480 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4481 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4482 ec.EmitInt (first_resume_pc);
4483 ec.Emit (OpCodes.Sub);
4485 Label[] labels = new Label[resume_points.Count];
4486 for (int i = 0; i < resume_points.Count; ++i)
4487 labels[i] = resume_points[i].PrepareForEmit (ec);
4488 ec.Emit (OpCodes.Switch, labels);
4492 public void SomeCodeFollows ()
4495 code_follows = true;
4499 public override bool Resolve (BlockContext ec)
4502 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4503 // So, ensure there's some IL code after this statement.
4504 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4505 ec.NeedReturnLabel ();
4510 public void AddResumePoint (ResumableStatement stmt, int pc)
4512 if (resume_points == null) {
4513 resume_points = new List<ResumableStatement> ();
4514 first_resume_pc = pc;
4517 if (pc != first_resume_pc + resume_points.Count)
4518 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4520 resume_points.Add (stmt);
4525 public class Lock : TryFinallyBlock
4528 TemporaryVariableReference expr_copy;
4529 TemporaryVariableReference lock_taken;
4531 public Lock (Expression expr, Statement stmt, Location loc)
4537 public Expression Expr {
4543 public override bool Resolve (BlockContext ec)
4545 expr = expr.Resolve (ec);
4549 if (!TypeSpec.IsReferenceType (expr.Type)) {
4550 ec.Report.Error (185, loc,
4551 "`{0}' is not a reference type as required by the lock statement",
4552 expr.Type.GetSignatureForError ());
4555 if (expr.Type.IsGenericParameter) {
4556 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4559 VariableReference lv = expr as VariableReference;
4562 locked = lv.IsLockedByStatement;
4563 lv.IsLockedByStatement = true;
4569 using (ec.Set (ResolveContext.Options.LockScope)) {
4570 ec.StartFlowBranching (this);
4571 Statement.Resolve (ec);
4572 ec.EndFlowBranching ();
4576 lv.IsLockedByStatement = locked;
4582 // Have to keep original lock value around to unlock same location
4583 // in the case the original has changed or is null
4585 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4586 expr_copy.Resolve (ec);
4589 // Ensure Monitor methods are available
4591 if (ResolvePredefinedMethods (ec) > 1) {
4592 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4593 lock_taken.Resolve (ec);
4599 protected override void EmitTryBodyPrepare (EmitContext ec)
4601 expr_copy.EmitAssign (ec, expr);
4603 if (lock_taken != null) {
4605 // Initialize ref variable
4607 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4610 // Monitor.Enter (expr_copy)
4612 expr_copy.Emit (ec);
4613 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4616 base.EmitTryBodyPrepare (ec);
4619 protected override void EmitTryBody (EmitContext ec)
4622 // Monitor.Enter (expr_copy, ref lock_taken)
4624 if (lock_taken != null) {
4625 expr_copy.Emit (ec);
4626 lock_taken.LocalInfo.CreateBuilder (ec);
4627 lock_taken.AddressOf (ec, AddressOp.Load);
4628 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4631 Statement.Emit (ec);
4634 protected override void EmitFinallyBody (EmitContext ec)
4637 // if (lock_taken) Monitor.Exit (expr_copy)
4639 Label skip = ec.DefineLabel ();
4641 if (lock_taken != null) {
4642 lock_taken.Emit (ec);
4643 ec.Emit (OpCodes.Brfalse_S, skip);
4646 expr_copy.Emit (ec);
4647 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4649 ec.Emit (OpCodes.Call, m);
4651 ec.MarkLabel (skip);
4654 int ResolvePredefinedMethods (ResolveContext rc)
4656 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4657 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4661 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4665 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4669 protected override void CloneTo (CloneContext clonectx, Statement t)
4671 Lock target = (Lock) t;
4673 target.expr = expr.Clone (clonectx);
4674 target.stmt = Statement.Clone (clonectx);
4677 public override object Accept (StructuralVisitor visitor)
4679 return visitor.Visit (this);
4684 public class Unchecked : Statement {
4687 public Unchecked (Block b, Location loc)
4694 public override bool Resolve (BlockContext ec)
4696 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4697 return Block.Resolve (ec);
4700 protected override void DoEmit (EmitContext ec)
4702 using (ec.With (EmitContext.Options.CheckedScope, false))
4706 protected override void CloneTo (CloneContext clonectx, Statement t)
4708 Unchecked target = (Unchecked) t;
4710 target.Block = clonectx.LookupBlock (Block);
4713 public override object Accept (StructuralVisitor visitor)
4715 return visitor.Visit (this);
4719 public class Checked : Statement {
4722 public Checked (Block b, Location loc)
4725 b.Unchecked = false;
4729 public override bool Resolve (BlockContext ec)
4731 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4732 return Block.Resolve (ec);
4735 protected override void DoEmit (EmitContext ec)
4737 using (ec.With (EmitContext.Options.CheckedScope, true))
4741 protected override void CloneTo (CloneContext clonectx, Statement t)
4743 Checked target = (Checked) t;
4745 target.Block = clonectx.LookupBlock (Block);
4748 public override object Accept (StructuralVisitor visitor)
4750 return visitor.Visit (this);
4754 public class Unsafe : Statement {
4757 public Unsafe (Block b, Location loc)
4760 Block.Unsafe = true;
4764 public override bool Resolve (BlockContext ec)
4766 if (ec.CurrentIterator != null)
4767 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4769 using (ec.Set (ResolveContext.Options.UnsafeScope))
4770 return Block.Resolve (ec);
4773 protected override void DoEmit (EmitContext ec)
4778 protected override void CloneTo (CloneContext clonectx, Statement t)
4780 Unsafe target = (Unsafe) t;
4782 target.Block = clonectx.LookupBlock (Block);
4785 public override object Accept (StructuralVisitor visitor)
4787 return visitor.Visit (this);
4794 public class Fixed : Statement
4796 abstract class Emitter : ShimExpression
4798 protected LocalVariable vi;
4800 protected Emitter (Expression expr, LocalVariable li)
4806 public abstract void EmitExit (EmitContext ec);
4809 class ExpressionEmitter : Emitter {
4810 public ExpressionEmitter (Expression converted, LocalVariable li) :
4811 base (converted, li)
4815 protected override Expression DoResolve (ResolveContext rc)
4817 throw new NotImplementedException ();
4820 public override void Emit (EmitContext ec) {
4822 // Store pointer in pinned location
4828 public override void EmitExit (EmitContext ec)
4831 ec.Emit (OpCodes.Conv_U);
4836 class StringEmitter : Emitter
4838 LocalVariable pinned_string;
4840 public StringEmitter (Expression expr, LocalVariable li, Location loc)
4845 protected override Expression DoResolve (ResolveContext rc)
4847 pinned_string = new LocalVariable (vi.Block, "$pinned",
4848 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
4850 pinned_string.Type = rc.BuiltinTypes.String;
4852 eclass = ExprClass.Variable;
4853 type = rc.BuiltinTypes.Int;
4857 public override void Emit (EmitContext ec)
4859 pinned_string.CreateBuilder (ec);
4862 pinned_string.EmitAssign (ec);
4864 // TODO: Should use Binary::Add
4865 pinned_string.Emit (ec);
4866 ec.Emit (OpCodes.Conv_I);
4868 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
4872 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
4873 //pe.InstanceExpression = pinned_string;
4874 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4876 ec.Emit (OpCodes.Add);
4880 public override void EmitExit (EmitContext ec)
4883 pinned_string.EmitAssign (ec);
4887 public class VariableDeclaration : BlockVariableDeclaration
4889 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
4894 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
4896 if (!Variable.Type.IsPointer && li == Variable) {
4897 bc.Report.Error (209, TypeExpression.Location,
4898 "The type of locals declared in a fixed statement must be a pointer type");
4903 // The rules for the possible declarators are pretty wise,
4904 // but the production on the grammar is more concise.
4906 // So we have to enforce these rules here.
4908 // We do not resolve before doing the case 1 test,
4909 // because the grammar is explicit in that the token &
4910 // is present, so we need to test for this particular case.
4913 if (initializer is Cast) {
4914 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
4918 initializer = initializer.Resolve (bc);
4920 if (initializer == null)
4926 if (initializer.Type.IsArray) {
4927 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
4930 // Provided that array_type is unmanaged,
4932 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
4936 // and T* is implicitly convertible to the
4937 // pointer type given in the fixed statement.
4939 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
4941 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
4942 if (converted == null)
4946 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4948 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
4949 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc), loc),
4950 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc), loc), loc)),
4951 new NullLiteral (loc),
4954 converted = converted.Resolve (bc);
4956 return new ExpressionEmitter (converted, li);
4962 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
4963 return new StringEmitter (initializer, li, loc).Resolve (bc);
4966 // Case 3: fixed buffer
4967 if (initializer is FixedBufferPtr) {
4968 return new ExpressionEmitter (initializer, li);
4972 // Case 4: & object.
4974 bool already_fixed = true;
4975 Unary u = initializer as Unary;
4976 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4977 IVariableReference vr = u.Expr as IVariableReference;
4978 if (vr == null || !vr.IsFixed) {
4979 already_fixed = false;
4983 if (already_fixed) {
4984 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
4987 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
4988 return new ExpressionEmitter (initializer, li);
4993 VariableDeclaration decl;
4994 Statement statement;
4997 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5006 public Statement Statement {
5012 public BlockVariableDeclaration Variables {
5020 public override bool Resolve (BlockContext ec)
5022 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5023 if (!decl.Resolve (ec))
5027 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5028 bool ok = statement.Resolve (ec);
5029 bool flow_unreachable = ec.EndFlowBranching ();
5030 has_ret = flow_unreachable;
5035 protected override void DoEmit (EmitContext ec)
5037 decl.Variable.CreateBuilder (ec);
5038 decl.Initializer.Emit (ec);
5039 if (decl.Declarators != null) {
5040 foreach (var d in decl.Declarators) {
5041 d.Variable.CreateBuilder (ec);
5042 d.Initializer.Emit (ec);
5046 statement.Emit (ec);
5052 // Clear the pinned variable
5054 ((Emitter) decl.Initializer).EmitExit (ec);
5055 if (decl.Declarators != null) {
5056 foreach (var d in decl.Declarators) {
5057 ((Emitter)d.Initializer).EmitExit (ec);
5062 protected override void CloneTo (CloneContext clonectx, Statement t)
5064 Fixed target = (Fixed) t;
5066 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5067 target.statement = statement.Clone (clonectx);
5070 public override object Accept (StructuralVisitor visitor)
5072 return visitor.Visit (this);
5076 public class Catch : Statement
5080 FullNamedExpression type_expr;
5081 CompilerAssign assign;
5084 public Catch (Block block, Location loc)
5092 public Block Block {
5098 public TypeSpec CatchType {
5104 public bool IsGeneral {
5106 return type_expr == null;
5110 public FullNamedExpression TypeExpression {
5119 public LocalVariable Variable {
5130 protected override void DoEmit (EmitContext ec)
5133 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5135 ec.BeginCatchBlock (CatchType);
5138 li.CreateBuilder (ec);
5141 // Special case hoisted catch variable, we have to use a temporary variable
5142 // to pass via anonymous storey initialization with the value still on top
5145 if (li.HoistedVariant != null) {
5146 LocalTemporary lt = new LocalTemporary (li.Type);
5149 // switch to assigning from the temporary variable and not from top of the stack
5150 assign.UpdateSource (lt);
5153 ec.Emit (OpCodes.Pop);
5159 public override bool Resolve (BlockContext ec)
5161 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5162 if (type_expr != null) {
5163 type = type_expr.ResolveAsType (ec);
5167 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5168 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5169 } else if (li != null) {
5171 li.PrepareForFlowAnalysis (ec);
5173 // source variable is at the top of the stack
5174 Expression source = new EmptyExpression (li.Type);
5175 if (li.Type.IsGenericParameter)
5176 source = new UnboxCast (source, li.Type);
5179 // Uses Location.Null to hide from symbol file
5181 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5182 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5186 return Block.Resolve (ec);
5190 protected override void CloneTo (CloneContext clonectx, Statement t)
5192 Catch target = (Catch) t;
5194 if (type_expr != null)
5195 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5197 target.block = clonectx.LookupBlock (block);
5201 public class TryFinally : TryFinallyBlock
5205 public TryFinally (Statement stmt, Block fini, Location loc)
5211 public Block Finallyblock {
5217 public override bool Resolve (BlockContext ec)
5221 ec.StartFlowBranching (this);
5223 if (!stmt.Resolve (ec))
5227 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5228 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5229 if (!fini.Resolve (ec))
5233 ec.EndFlowBranching ();
5235 ok &= base.Resolve (ec);
5240 protected override void EmitTryBody (EmitContext ec)
5245 protected override void EmitFinallyBody (EmitContext ec)
5250 protected override void CloneTo (CloneContext clonectx, Statement t)
5252 TryFinally target = (TryFinally) t;
5254 target.stmt = (Statement) stmt.Clone (clonectx);
5256 target.fini = clonectx.LookupBlock (fini);
5259 public override object Accept (StructuralVisitor visitor)
5261 return visitor.Visit (this);
5265 public class TryCatch : ExceptionStatement
5268 List<Catch> clauses;
5269 readonly bool inside_try_finally;
5271 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5275 this.clauses = catch_clauses;
5276 this.inside_try_finally = inside_try_finally;
5279 public List<Catch> Clauses {
5285 public bool IsTryCatchFinally {
5287 return inside_try_finally;
5291 public override bool Resolve (BlockContext ec)
5295 ec.StartFlowBranching (this);
5297 if (!Block.Resolve (ec))
5300 for (int i = 0; i < clauses.Count; ++i) {
5302 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5304 if (!c.Resolve (ec)) {
5309 TypeSpec resolved_type = c.CatchType;
5310 for (int ii = 0; ii < clauses.Count; ++ii) {
5314 if (clauses[ii].IsGeneral) {
5315 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5318 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5321 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5324 ec.Report.Warning (1058, 1, c.loc,
5325 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5333 var ct = clauses[ii].CatchType;
5337 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5338 ec.Report.Error (160, c.loc,
5339 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5340 ct.GetSignatureForError ());
5346 ec.EndFlowBranching ();
5348 return base.Resolve (ec) && ok;
5351 protected sealed override void DoEmit (EmitContext ec)
5353 if (!inside_try_finally)
5354 EmitTryBodyPrepare (ec);
5358 foreach (Catch c in clauses)
5361 if (!inside_try_finally)
5362 ec.EndExceptionBlock ();
5365 protected override void CloneTo (CloneContext clonectx, Statement t)
5367 TryCatch target = (TryCatch) t;
5369 target.Block = clonectx.LookupBlock (Block);
5370 if (clauses != null){
5371 target.clauses = new List<Catch> ();
5372 foreach (Catch c in clauses)
5373 target.clauses.Add ((Catch) c.Clone (clonectx));
5377 public override object Accept (StructuralVisitor visitor)
5379 return visitor.Visit (this);
5383 public class Using : TryFinallyBlock
5385 public class VariableDeclaration : BlockVariableDeclaration
5387 Statement dispose_call;
5389 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5394 public VariableDeclaration (LocalVariable li, Location loc)
5400 public VariableDeclaration (Expression expr)
5403 loc = expr.Location;
5409 public bool IsNested { get; private set; }
5413 public void EmitDispose (EmitContext ec)
5415 dispose_call.Emit (ec);
5418 public override bool Resolve (BlockContext bc)
5423 return base.Resolve (bc, false);
5426 public Expression ResolveExpression (BlockContext bc)
5428 var e = Initializer.Resolve (bc);
5432 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5433 Initializer = ResolveInitializer (bc, Variable, e);
5437 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5439 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5440 initializer = initializer.Resolve (bc);
5441 if (initializer == null)
5444 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5445 Arguments args = new Arguments (1);
5446 args.Add (new Argument (initializer));
5447 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5448 if (initializer == null)
5451 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5452 dispose_call = CreateDisposeCall (bc, var);
5453 dispose_call.Resolve (bc);
5455 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5458 if (li == Variable) {
5459 CheckIDiposableConversion (bc, li, initializer);
5460 dispose_call = CreateDisposeCall (bc, li);
5461 dispose_call.Resolve (bc);
5464 return base.ResolveInitializer (bc, li, initializer);
5467 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5471 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5472 if (type.IsNullableType) {
5473 // it's handled in CreateDisposeCall
5477 bc.Report.SymbolRelatedToPreviousError (type);
5478 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5479 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5480 type.GetSignatureForError ());
5486 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5488 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5490 var loc = lv.Location;
5492 var idt = bc.BuiltinTypes.IDisposable;
5493 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5495 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5496 dispose_mg.InstanceExpression = type.IsNullableType ?
5497 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5501 // Hide it from symbol file via null location
5503 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5505 // Add conditional call when disposing possible null variable
5506 if (!type.IsStruct || type.IsNullableType)
5507 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc), loc), dispose, dispose.loc);
5512 public void ResolveDeclaratorInitializer (BlockContext bc)
5514 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5517 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5519 for (int i = declarators.Count - 1; i >= 0; --i) {
5520 var d = declarators [i];
5521 var vd = new VariableDeclaration (d.Variable, type_expr.Location);
5522 vd.Initializer = d.Initializer;
5524 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5525 vd.dispose_call.Resolve (bc);
5527 stmt = new Using (vd, stmt, d.Variable.Location);
5534 public override object Accept (StructuralVisitor visitor)
5536 return visitor.Visit (this);
5540 VariableDeclaration decl;
5542 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5548 public Using (Expression expr, Statement stmt, Location loc)
5551 this.decl = new VariableDeclaration (expr);
5556 public Expression Expr {
5558 return decl.Variable == null ? decl.Initializer : null;
5562 public BlockVariableDeclaration Variables {
5570 public override void Emit (EmitContext ec)
5573 // Don't emit sequence point it will be set on variable declaration
5578 protected override void EmitTryBodyPrepare (EmitContext ec)
5581 base.EmitTryBodyPrepare (ec);
5584 protected override void EmitTryBody (EmitContext ec)
5589 protected override void EmitFinallyBody (EmitContext ec)
5591 decl.EmitDispose (ec);
5594 public override bool Resolve (BlockContext ec)
5596 VariableReference vr;
5597 bool vr_locked = false;
5599 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5600 if (decl.Variable == null) {
5601 vr = decl.ResolveExpression (ec) as VariableReference;
5603 vr_locked = vr.IsLockedByStatement;
5604 vr.IsLockedByStatement = true;
5607 if (decl.IsNested) {
5608 decl.ResolveDeclaratorInitializer (ec);
5610 if (!decl.Resolve (ec))
5613 if (decl.Declarators != null) {
5614 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5622 ec.StartFlowBranching (this);
5626 ec.EndFlowBranching ();
5629 vr.IsLockedByStatement = vr_locked;
5636 protected override void CloneTo (CloneContext clonectx, Statement t)
5638 Using target = (Using) t;
5640 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5641 target.stmt = stmt.Clone (clonectx);
5644 public override object Accept (StructuralVisitor visitor)
5646 return visitor.Visit (this);
5651 /// Implementation of the foreach C# statement
5653 public class Foreach : Statement
5655 abstract class IteratorStatement : Statement
5657 protected readonly Foreach for_each;
5659 protected IteratorStatement (Foreach @foreach)
5661 this.for_each = @foreach;
5662 this.loc = @foreach.expr.Location;
5665 protected override void CloneTo (CloneContext clonectx, Statement target)
5667 throw new NotImplementedException ();
5670 public override void Emit (EmitContext ec)
5672 if (ec.EmitAccurateDebugInfo) {
5673 ec.Emit (OpCodes.Nop);
5680 sealed class ArrayForeach : IteratorStatement
5682 TemporaryVariableReference[] lengths;
5683 Expression [] length_exprs;
5684 StatementExpression[] counter;
5685 TemporaryVariableReference[] variables;
5687 TemporaryVariableReference copy;
5689 public ArrayForeach (Foreach @foreach, int rank)
5692 counter = new StatementExpression[rank];
5693 variables = new TemporaryVariableReference[rank];
5694 length_exprs = new Expression [rank];
5697 // Only use temporary length variables when dealing with
5698 // multi-dimensional arrays
5701 lengths = new TemporaryVariableReference [rank];
5704 public override bool Resolve (BlockContext ec)
5706 Block variables_block = for_each.variable.Block;
5707 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5710 int rank = length_exprs.Length;
5711 Arguments list = new Arguments (rank);
5712 for (int i = 0; i < rank; i++) {
5713 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5715 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5716 counter[i].Resolve (ec);
5719 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5721 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5722 lengths[i].Resolve (ec);
5724 Arguments args = new Arguments (1);
5725 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5726 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5729 list.Add (new Argument (v));
5732 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5737 if (for_each.type is VarExpr) {
5738 // Infer implicitly typed local variable from foreach array type
5739 var_type = access.Type;
5741 var_type = for_each.type.ResolveAsType (ec);
5743 if (var_type == null)
5746 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5751 for_each.variable.Type = var_type;
5753 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5754 if (variable_ref == null)
5757 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.variable.Location));
5761 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5762 ec.CurrentBranching.CreateSibling ();
5764 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5765 if (!for_each.body.Resolve (ec))
5767 ec.EndFlowBranching ();
5769 // There's no direct control flow from the end of the embedded statement to the end of the loop
5770 ec.CurrentBranching.CurrentUsageVector.Goto ();
5772 ec.EndFlowBranching ();
5777 protected override void DoEmit (EmitContext ec)
5779 copy.EmitAssign (ec, for_each.expr);
5781 int rank = length_exprs.Length;
5782 Label[] test = new Label [rank];
5783 Label[] loop = new Label [rank];
5785 for (int i = 0; i < rank; i++) {
5786 test [i] = ec.DefineLabel ();
5787 loop [i] = ec.DefineLabel ();
5789 if (lengths != null)
5790 lengths [i].EmitAssign (ec, length_exprs [i]);
5793 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5794 for (int i = 0; i < rank; i++) {
5795 variables [i].EmitAssign (ec, zero);
5797 ec.Emit (OpCodes.Br, test [i]);
5798 ec.MarkLabel (loop [i]);
5801 for_each.body.Emit (ec);
5803 ec.MarkLabel (ec.LoopBegin);
5804 ec.Mark (for_each.expr.Location);
5806 for (int i = rank - 1; i >= 0; i--){
5807 counter [i].Emit (ec);
5809 ec.MarkLabel (test [i]);
5810 variables [i].Emit (ec);
5812 if (lengths != null)
5813 lengths [i].Emit (ec);
5815 length_exprs [i].Emit (ec);
5817 ec.Emit (OpCodes.Blt, loop [i]);
5820 ec.MarkLabel (ec.LoopEnd);
5824 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
5826 class RuntimeDispose : Using.VariableDeclaration
5828 public RuntimeDispose (LocalVariable lv, Location loc)
5833 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5835 // Defered to runtime check
5838 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5840 var idt = bc.BuiltinTypes.IDisposable;
5843 // Fabricates code like
5845 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
5848 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
5850 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
5851 dispose_variable.CreateReferenceExpression (bc, loc),
5852 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
5853 loc), new NullLiteral (loc), loc);
5855 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5857 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5858 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
5860 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
5861 return new If (idisaposable_test, dispose, loc);
5865 LocalVariable variable;
5867 Statement statement;
5868 ExpressionStatement init;
5869 TemporaryVariableReference enumerator_variable;
5870 bool ambiguous_getenumerator_name;
5872 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
5875 this.variable = var;
5879 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
5881 rc.Report.SymbolRelatedToPreviousError (enumerator);
5882 rc.Report.Error (202, loc,
5883 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5884 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
5887 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
5890 // Option 1: Try to match by name GetEnumerator first
5892 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
5893 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
5895 var mg = mexpr as MethodGroupExpr;
5897 mg.InstanceExpression = expr;
5898 Arguments args = new Arguments (0);
5899 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.None);
5901 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
5902 if (ambiguous_getenumerator_name)
5905 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
5911 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
5914 PredefinedMember<MethodSpec> iface_candidate = null;
5915 var ptypes = rc.Module.PredefinedTypes;
5916 var gen_ienumerable = ptypes.IEnumerableGeneric;
5917 if (!gen_ienumerable.Define ())
5918 gen_ienumerable = null;
5921 var ifaces = t.Interfaces;
5922 if (ifaces != null) {
5923 foreach (var iface in ifaces) {
5924 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
5925 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
5926 rc.Report.SymbolRelatedToPreviousError (expr.Type);
5927 rc.Report.Error (1640, loc,
5928 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5929 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
5934 // TODO: Cache this somehow
5935 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
5936 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
5941 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
5942 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
5947 if (t.IsGenericParameter)
5952 } while (t != null);
5954 if (iface_candidate == null) {
5955 if (expr.Type != InternalType.ErrorType) {
5956 rc.Report.Error (1579, loc,
5957 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
5958 expr.Type.GetSignatureForError (), "GetEnumerator");
5964 var method = iface_candidate.Resolve (loc);
5968 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
5969 mg.InstanceExpression = expr;
5973 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
5975 var ms = MemberCache.FindMember (enumerator.ReturnType,
5976 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
5977 BindingRestriction.InstanceOnly) as MethodSpec;
5979 if (ms == null || !ms.IsPublic) {
5980 Error_WrongEnumerator (rc, enumerator);
5984 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
5987 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
5989 var ps = MemberCache.FindMember (enumerator.ReturnType,
5990 MemberFilter.Property ("Current", null),
5991 BindingRestriction.InstanceOnly) as PropertySpec;
5993 if (ps == null || !ps.IsPublic) {
5994 Error_WrongEnumerator (rc, enumerator);
6001 public override bool Resolve (BlockContext ec)
6003 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6006 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6007 } else if (expr.Type.IsNullableType) {
6008 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6011 var get_enumerator_mg = ResolveGetEnumerator (ec);
6012 if (get_enumerator_mg == null) {
6016 var get_enumerator = get_enumerator_mg.BestCandidate;
6017 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6018 enumerator_variable.Resolve (ec);
6020 // Prepare bool MoveNext ()
6021 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6022 if (move_next_mg == null) {
6026 move_next_mg.InstanceExpression = enumerator_variable;
6028 // Prepare ~T~ Current { get; }
6029 var current_prop = ResolveCurrent (ec, get_enumerator);
6030 if (current_prop == null) {
6034 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6035 if (current_pe == null)
6038 VarExpr ve = for_each.type as VarExpr;
6042 // Source type is dynamic, set element type to dynamic too
6043 variable.Type = ec.BuiltinTypes.Dynamic;
6045 // Infer implicitly typed local variable from foreach enumerable type
6046 variable.Type = current_pe.Type;
6050 // Explicit cast of dynamic collection elements has to be done at runtime
6051 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6054 variable.Type = for_each.type.ResolveAsType (ec);
6056 if (variable.Type == null)
6059 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6060 if (current_pe == null)
6064 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6065 if (variable_ref == null)
6068 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), variable.Location));
6070 var init = new Invocation (get_enumerator_mg, null);
6072 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6073 for_each.body, Location.Null);
6075 var enum_type = enumerator_variable.Type;
6078 // Add Dispose method call when enumerator can be IDisposable
6080 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6081 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6083 // Runtime Dispose check
6085 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6086 vd.Initializer = init;
6087 statement = new Using (vd, statement, Location.Null);
6090 // No Dispose call needed
6092 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6093 this.init.Resolve (ec);
6097 // Static Dispose check
6099 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6100 vd.Initializer = init;
6101 statement = new Using (vd, statement, Location.Null);
6104 return statement.Resolve (ec);
6107 protected override void DoEmit (EmitContext ec)
6109 enumerator_variable.LocalInfo.CreateBuilder (ec);
6112 init.EmitStatement (ec);
6114 statement.Emit (ec);
6117 #region IErrorHandler Members
6119 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6121 ec.Report.SymbolRelatedToPreviousError (best);
6122 ec.Report.Warning (278, 2, expr.Location,
6123 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6124 expr.Type.GetSignatureForError (), "enumerable",
6125 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6127 ambiguous_getenumerator_name = true;
6131 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6136 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6141 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6150 LocalVariable variable;
6152 Statement statement;
6155 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6158 this.variable = var;
6160 this.statement = stmt;
6165 public Expression Expr {
6166 get { return expr; }
6169 public Statement Statement {
6170 get { return statement; }
6173 public Expression TypeExpression {
6174 get { return type; }
6177 public LocalVariable Variable {
6178 get { return variable; }
6181 public override bool Resolve (BlockContext ec)
6183 expr = expr.Resolve (ec);
6188 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6192 body.AddStatement (statement);
6194 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6195 statement = new ArrayForeach (this, 1);
6196 } else if (expr.Type is ArrayContainer) {
6197 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6199 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6200 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6201 expr.ExprClassName);
6205 statement = new CollectionForeach (this, variable, expr);
6208 return statement.Resolve (ec);
6211 protected override void DoEmit (EmitContext ec)
6213 variable.CreateBuilder (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.body = (Block) body.Clone (clonectx);
6234 public override object Accept (StructuralVisitor visitor)
6236 return visitor.Visit (this);