2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
52 bool unreachable = false;
53 if (warn && !ec.UnreachableReported) {
55 // TODO: This is wrong, need to form of flow-analysis branch specific flag
56 // or multiple unrelared unreachable code won't be reported
57 // if (false) { // ok } if (false) { // not reported }
58 ec.UnreachableReported = true;
60 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
63 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
64 ec.CurrentBranching.CurrentUsageVector.Goto ();
65 bool ok = Resolve (ec);
66 ec.KillFlowBranching ();
69 ec.UnreachableReported = false;
76 /// Return value indicates whether all code paths emitted return.
78 protected abstract void DoEmit (EmitContext ec);
80 public virtual void Emit (EmitContext ec)
85 if (ec.StatementEpilogue != null) {
91 // This routine must be overrided in derived classes and make copies
92 // of all the data that might be modified if resolved
94 protected abstract void CloneTo (CloneContext clonectx, Statement target);
96 public Statement Clone (CloneContext clonectx)
98 Statement s = (Statement) this.MemberwiseClone ();
99 CloneTo (clonectx, s);
103 public virtual Expression CreateExpressionTree (ResolveContext ec)
105 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
109 public virtual object Accept (StructuralVisitor visitor)
111 return visitor.Visit (this);
115 public sealed class EmptyStatement : Statement
117 public EmptyStatement (Location loc)
122 public override bool Resolve (BlockContext ec)
127 public override bool ResolveUnreachable (BlockContext ec, bool warn)
132 public override void Emit (EmitContext ec)
136 protected override void DoEmit (EmitContext ec)
138 throw new NotSupportedException ();
141 protected override void CloneTo (CloneContext clonectx, Statement target)
146 public override object Accept (StructuralVisitor visitor)
148 return visitor.Visit (this);
152 public class If : Statement {
154 public Statement TrueStatement;
155 public Statement FalseStatement;
159 public If (Expression bool_expr, Statement true_statement, Location l)
160 : this (bool_expr, true_statement, null, l)
164 public If (Expression bool_expr,
165 Statement true_statement,
166 Statement false_statement,
169 this.expr = bool_expr;
170 TrueStatement = true_statement;
171 FalseStatement = false_statement;
175 public Expression Expr {
181 public override bool Resolve (BlockContext ec)
185 expr = expr.Resolve (ec);
190 // Dead code elimination
192 if (expr is Constant) {
193 bool take = !((Constant) expr).IsDefaultValue;
195 if (!TrueStatement.Resolve (ec))
198 if ((FalseStatement != null) &&
199 !FalseStatement.ResolveUnreachable (ec, true))
201 FalseStatement = null;
203 if (!TrueStatement.ResolveUnreachable (ec, true))
205 TrueStatement = null;
207 if ((FalseStatement != null) &&
208 !FalseStatement.Resolve (ec))
216 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
218 ok &= TrueStatement.Resolve (ec);
220 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
222 ec.CurrentBranching.CreateSibling ();
224 if (FalseStatement != null)
225 ok &= FalseStatement.Resolve (ec);
227 ec.EndFlowBranching ();
232 protected override void DoEmit (EmitContext ec)
234 Label false_target = ec.DefineLabel ();
238 // If we're a boolean constant, Resolve() already
239 // eliminated dead code for us.
241 Constant c = expr as Constant;
243 c.EmitSideEffect (ec);
245 if (!c.IsDefaultValue)
246 TrueStatement.Emit (ec);
247 else if (FalseStatement != null)
248 FalseStatement.Emit (ec);
253 expr.EmitBranchable (ec, false_target, false);
255 TrueStatement.Emit (ec);
257 if (FalseStatement != null){
258 bool branch_emitted = false;
260 end = ec.DefineLabel ();
262 ec.Emit (OpCodes.Br, end);
263 branch_emitted = true;
266 ec.MarkLabel (false_target);
267 FalseStatement.Emit (ec);
272 ec.MarkLabel (false_target);
276 protected override void CloneTo (CloneContext clonectx, Statement t)
280 target.expr = expr.Clone (clonectx);
281 target.TrueStatement = TrueStatement.Clone (clonectx);
282 if (FalseStatement != null)
283 target.FalseStatement = FalseStatement.Clone (clonectx);
286 public override object Accept (StructuralVisitor visitor)
288 return visitor.Visit (this);
292 public class Do : Statement {
293 public Expression expr;
294 public Statement EmbeddedStatement;
296 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
299 EmbeddedStatement = statement;
301 WhileLocation = whileLocation;
304 public Location WhileLocation {
308 public override bool Resolve (BlockContext ec)
312 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
314 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
316 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
317 if (!EmbeddedStatement.Resolve (ec))
319 ec.EndFlowBranching ();
321 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
322 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
324 expr = expr.Resolve (ec);
327 else if (expr is Constant){
328 bool infinite = !((Constant) expr).IsDefaultValue;
330 ec.CurrentBranching.CurrentUsageVector.Goto ();
333 ec.EndFlowBranching ();
338 protected override void DoEmit (EmitContext ec)
340 Label loop = ec.DefineLabel ();
341 Label old_begin = ec.LoopBegin;
342 Label old_end = ec.LoopEnd;
344 ec.LoopBegin = ec.DefineLabel ();
345 ec.LoopEnd = ec.DefineLabel ();
348 EmbeddedStatement.Emit (ec);
349 ec.MarkLabel (ec.LoopBegin);
351 // Mark start of while condition
352 ec.Mark (WhileLocation);
355 // Dead code elimination
357 if (expr is Constant) {
358 bool res = !((Constant) expr).IsDefaultValue;
360 expr.EmitSideEffect (ec);
362 ec.Emit (OpCodes.Br, loop);
364 expr.EmitBranchable (ec, loop, true);
367 ec.MarkLabel (ec.LoopEnd);
369 ec.LoopBegin = old_begin;
370 ec.LoopEnd = old_end;
373 protected override void CloneTo (CloneContext clonectx, Statement t)
377 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
378 target.expr = expr.Clone (clonectx);
381 public override object Accept (StructuralVisitor visitor)
383 return visitor.Visit (this);
387 public class While : Statement {
388 public Expression expr;
389 public Statement Statement;
390 bool infinite, empty;
392 public While (BooleanExpression bool_expr, Statement statement, Location l)
394 this.expr = bool_expr;
395 Statement = statement;
399 public override bool Resolve (BlockContext ec)
403 expr = expr.Resolve (ec);
408 // Inform whether we are infinite or not
410 if (expr is Constant){
411 bool value = !((Constant) expr).IsDefaultValue;
414 if (!Statement.ResolveUnreachable (ec, true))
423 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
425 ec.CurrentBranching.CreateSibling ();
427 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
428 if (!Statement.Resolve (ec))
430 ec.EndFlowBranching ();
432 // There's no direct control flow from the end of the embedded statement to the end of the loop
433 ec.CurrentBranching.CurrentUsageVector.Goto ();
435 ec.EndFlowBranching ();
440 protected override void DoEmit (EmitContext ec)
443 expr.EmitSideEffect (ec);
447 Label old_begin = ec.LoopBegin;
448 Label old_end = ec.LoopEnd;
450 ec.LoopBegin = ec.DefineLabel ();
451 ec.LoopEnd = ec.DefineLabel ();
454 // Inform whether we are infinite or not
456 if (expr is Constant) {
457 // expr is 'true', since the 'empty' case above handles the 'false' case
458 ec.MarkLabel (ec.LoopBegin);
460 if (ec.EmitAccurateDebugInfo)
461 ec.Emit (OpCodes.Nop);
463 expr.EmitSideEffect (ec);
465 ec.Emit (OpCodes.Br, ec.LoopBegin);
468 // Inform that we are infinite (ie, `we return'), only
469 // if we do not `break' inside the code.
471 ec.MarkLabel (ec.LoopEnd);
473 Label while_loop = ec.DefineLabel ();
475 ec.Emit (OpCodes.Br, ec.LoopBegin);
476 ec.MarkLabel (while_loop);
480 ec.MarkLabel (ec.LoopBegin);
483 expr.EmitBranchable (ec, while_loop, true);
485 ec.MarkLabel (ec.LoopEnd);
488 ec.LoopBegin = old_begin;
489 ec.LoopEnd = old_end;
492 protected override void CloneTo (CloneContext clonectx, Statement t)
494 While target = (While) t;
496 target.expr = expr.Clone (clonectx);
497 target.Statement = Statement.Clone (clonectx);
500 public override object Accept (StructuralVisitor visitor)
502 return visitor.Visit (this);
506 public class For : Statement
508 bool infinite, empty;
510 public For (Location l)
515 public Statement Initializer {
519 public Expression Condition {
523 public Statement Iterator {
527 public Statement Statement {
531 public override bool Resolve (BlockContext ec)
535 if (Initializer != null) {
536 if (!Initializer.Resolve (ec))
540 if (Condition != null) {
541 Condition = Condition.Resolve (ec);
542 if (Condition == null)
544 else if (Condition is Constant) {
545 bool value = !((Constant) Condition).IsDefaultValue;
548 if (!Statement.ResolveUnreachable (ec, true))
550 if ((Iterator != null) &&
551 !Iterator.ResolveUnreachable (ec, false))
562 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
564 ec.CurrentBranching.CreateSibling ();
566 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
568 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
569 if (!Statement.Resolve (ec))
571 ec.EndFlowBranching ();
573 if (Iterator != null){
574 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
575 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
578 if (!Iterator.Resolve (ec))
583 // There's no direct control flow from the end of the embedded statement to the end of the loop
584 ec.CurrentBranching.CurrentUsageVector.Goto ();
586 ec.EndFlowBranching ();
591 protected override void DoEmit (EmitContext ec)
593 if (Initializer != null)
594 Initializer.Emit (ec);
597 Condition.EmitSideEffect (ec);
601 Label old_begin = ec.LoopBegin;
602 Label old_end = ec.LoopEnd;
603 Label loop = ec.DefineLabel ();
604 Label test = ec.DefineLabel ();
606 ec.LoopBegin = ec.DefineLabel ();
607 ec.LoopEnd = ec.DefineLabel ();
609 ec.Emit (OpCodes.Br, test);
613 ec.MarkLabel (ec.LoopBegin);
618 // If test is null, there is no test, and we are just
621 if (Condition != null) {
622 ec.Mark (Condition.Location);
625 // The Resolve code already catches the case for
626 // Test == Constant (false) so we know that
629 if (Condition is Constant) {
630 Condition.EmitSideEffect (ec);
631 ec.Emit (OpCodes.Br, loop);
633 Condition.EmitBranchable (ec, loop, true);
637 ec.Emit (OpCodes.Br, loop);
638 ec.MarkLabel (ec.LoopEnd);
640 ec.LoopBegin = old_begin;
641 ec.LoopEnd = old_end;
644 protected override void CloneTo (CloneContext clonectx, Statement t)
646 For target = (For) t;
648 if (Initializer != null)
649 target.Initializer = Initializer.Clone (clonectx);
650 if (Condition != null)
651 target.Condition = Condition.Clone (clonectx);
652 if (Iterator != null)
653 target.Iterator = Iterator.Clone (clonectx);
654 target.Statement = Statement.Clone (clonectx);
657 public override object Accept (StructuralVisitor visitor)
659 return visitor.Visit (this);
663 public class StatementExpression : Statement
665 ExpressionStatement expr;
667 public StatementExpression (ExpressionStatement expr)
670 loc = expr.StartLocation;
673 public StatementExpression (ExpressionStatement expr, Location loc)
679 public ExpressionStatement Expr {
685 protected override void CloneTo (CloneContext clonectx, Statement t)
687 StatementExpression target = (StatementExpression) t;
688 target.expr = (ExpressionStatement) expr.Clone (clonectx);
691 protected override void DoEmit (EmitContext ec)
693 expr.EmitStatement (ec);
696 public override bool Resolve (BlockContext ec)
698 expr = expr.ResolveStatement (ec);
702 public override object Accept (StructuralVisitor visitor)
704 return visitor.Visit (this);
708 public class StatementErrorExpression : Statement
712 public StatementErrorExpression (Expression expr)
715 this.loc = expr.StartLocation;
718 public Expression Expr {
724 public override bool Resolve (BlockContext bc)
726 expr.Error_InvalidExpressionStatement (bc);
730 protected override void DoEmit (EmitContext ec)
732 throw new NotSupportedException ();
735 protected override void CloneTo (CloneContext clonectx, Statement target)
737 var t = (StatementErrorExpression) target;
739 t.expr = expr.Clone (clonectx);
742 public override object Accept (StructuralVisitor visitor)
744 return visitor.Visit (this);
749 // Simple version of statement list not requiring a block
751 public class StatementList : Statement
753 List<Statement> statements;
755 public StatementList (Statement first, Statement second)
757 statements = new List<Statement> { first, second };
761 public IList<Statement> Statements {
768 public void Add (Statement statement)
770 statements.Add (statement);
773 public override bool Resolve (BlockContext ec)
775 foreach (var s in statements)
781 protected override void DoEmit (EmitContext ec)
783 foreach (var s in statements)
787 protected override void CloneTo (CloneContext clonectx, Statement target)
789 StatementList t = (StatementList) target;
791 t.statements = new List<Statement> (statements.Count);
792 foreach (Statement s in statements)
793 t.statements.Add (s.Clone (clonectx));
796 public override object Accept (StructuralVisitor visitor)
798 return visitor.Visit (this);
802 // A 'return' or a 'yield break'
803 public abstract class ExitStatement : Statement
805 protected bool unwind_protect;
806 protected abstract bool DoResolve (BlockContext ec);
808 public virtual void Error_FinallyClause (Report Report)
810 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
813 public sealed override bool Resolve (BlockContext ec)
815 var res = DoResolve (ec);
816 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
817 ec.CurrentBranching.CurrentUsageVector.Goto ();
823 /// Implements the return statement
825 public class Return : ExitStatement
829 public Return (Expression expr, Location l)
837 public Expression Expr {
848 protected override bool DoResolve (BlockContext ec)
851 if (ec.ReturnType.Kind == MemberKind.Void)
855 // Return must not be followed by an expression when
856 // the method return type is Task
858 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
859 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
860 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
862 // Extra trick not to emit ret/leave inside awaiter body
864 expr = EmptyExpression.Null;
869 if (ec.CurrentIterator != null) {
870 Error_ReturnFromIterator (ec);
871 } else if (ec.ReturnType != InternalType.ErrorType) {
872 ec.Report.Error (126, loc,
873 "An object of a type convertible to `{0}' is required for the return statement",
874 ec.ReturnType.GetSignatureForError ());
880 expr = expr.Resolve (ec);
881 TypeSpec block_return_type = ec.ReturnType;
883 AnonymousExpression am = ec.CurrentAnonymousMethod;
885 if (block_return_type.Kind == MemberKind.Void) {
886 ec.Report.Error (127, loc,
887 "`{0}': A return keyword must not be followed by any expression when method returns void",
888 ec.GetSignatureForError ());
894 Error_ReturnFromIterator (ec);
898 var async_block = am as AsyncInitializer;
899 if (async_block != null) {
901 var storey = (AsyncTaskStorey) am.Storey;
902 var async_type = storey.ReturnType;
904 if (async_type == null && async_block.ReturnTypeInference != null) {
905 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
909 if (async_type.Kind == MemberKind.Void) {
910 ec.Report.Error (127, loc,
911 "`{0}': A return keyword must not be followed by any expression when method returns void",
912 ec.GetSignatureForError ());
917 if (!async_type.IsGenericTask) {
918 if (this is ContextualReturn)
921 // Same error code as .NET but better error message
922 if (async_block.DelegateType != null) {
923 ec.Report.Error (1997, loc,
924 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
925 async_block.DelegateType.GetSignatureForError ());
927 ec.Report.Error (1997, loc,
928 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
929 ec.GetSignatureForError ());
937 // The return type is actually Task<T> type argument
939 if (expr.Type == async_type) {
940 ec.Report.Error (4016, loc,
941 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
942 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
944 block_return_type = async_type.TypeArguments[0];
948 // Same error code as .NET but better error message
949 if (block_return_type.Kind == MemberKind.Void) {
950 ec.Report.Error (127, loc,
951 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
952 am.GetSignatureForError ());
957 var l = am as AnonymousMethodBody;
958 if (l != null && expr != null) {
959 if (l.ReturnTypeInference != null) {
960 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
965 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
966 // unexpected debugging experience
968 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
969 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
978 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
979 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
982 if (am != null && block_return_type == ec.ReturnType) {
983 ec.Report.Error (1662, loc,
984 "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",
985 am.ContainerType, am.GetSignatureForError ());
994 protected override void DoEmit (EmitContext ec)
999 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1000 if (async_body != null) {
1001 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
1003 // It's null for await without async
1004 if (async_return != null) {
1005 async_return.EmitAssign (ec);
1010 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
1016 if (unwind_protect || ec.EmitAccurateDebugInfo)
1017 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1020 if (unwind_protect) {
1021 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1022 } else if (ec.EmitAccurateDebugInfo) {
1023 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1025 ec.Emit (OpCodes.Ret);
1029 void Error_ReturnFromIterator (ResolveContext rc)
1031 rc.Report.Error (1622, loc,
1032 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1035 protected override void CloneTo (CloneContext clonectx, Statement t)
1037 Return target = (Return) t;
1038 // It's null for simple return;
1040 target.expr = expr.Clone (clonectx);
1043 public override object Accept (StructuralVisitor visitor)
1045 return visitor.Visit (this);
1049 public class Goto : Statement {
1051 LabeledStatement label;
1052 bool unwind_protect;
1054 public override bool Resolve (BlockContext ec)
1056 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1057 ec.CurrentBranching.CurrentUsageVector.Goto ();
1061 public Goto (string label, Location l)
1067 public string Target {
1068 get { return target; }
1071 public void SetResolvedTarget (LabeledStatement label)
1074 label.AddReference ();
1077 protected override void CloneTo (CloneContext clonectx, Statement target)
1082 protected override void DoEmit (EmitContext ec)
1085 throw new InternalErrorException ("goto emitted before target resolved");
1086 Label l = label.LabelTarget (ec);
1087 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1090 public override object Accept (StructuralVisitor visitor)
1092 return visitor.Visit (this);
1096 public class LabeledStatement : Statement {
1103 FlowBranching.UsageVector vectors;
1105 public LabeledStatement (string name, Block block, Location l)
1112 public Label LabelTarget (EmitContext ec)
1117 label = ec.DefineLabel ();
1122 public Block Block {
1128 public string Name {
1129 get { return name; }
1132 public bool IsDefined {
1133 get { return defined; }
1136 public bool HasBeenReferenced {
1137 get { return referenced; }
1140 public FlowBranching.UsageVector JumpOrigins {
1141 get { return vectors; }
1144 public void AddUsageVector (FlowBranching.UsageVector vector)
1146 vector = vector.Clone ();
1147 vector.Next = vectors;
1151 protected override void CloneTo (CloneContext clonectx, Statement target)
1156 public override bool Resolve (BlockContext ec)
1158 // this flow-branching will be terminated when the surrounding block ends
1159 ec.StartFlowBranching (this);
1163 protected override void DoEmit (EmitContext ec)
1165 if (!HasBeenReferenced)
1166 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1169 ec.MarkLabel (label);
1172 public void AddReference ()
1177 public override object Accept (StructuralVisitor visitor)
1179 return visitor.Visit (this);
1185 /// `goto default' statement
1187 public class GotoDefault : Statement {
1189 public GotoDefault (Location l)
1194 protected override void CloneTo (CloneContext clonectx, Statement target)
1199 public override bool Resolve (BlockContext ec)
1201 ec.CurrentBranching.CurrentUsageVector.Goto ();
1203 if (ec.Switch == null) {
1204 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1208 ec.Switch.RegisterGotoCase (null, null);
1213 protected override void DoEmit (EmitContext ec)
1215 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1218 public override object Accept (StructuralVisitor visitor)
1220 return visitor.Visit (this);
1225 /// `goto case' statement
1227 public class GotoCase : Statement {
1230 public GotoCase (Expression e, Location l)
1236 public Expression Expr {
1242 public SwitchLabel Label { get; set; }
1244 public override bool Resolve (BlockContext ec)
1246 if (ec.Switch == null){
1247 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1251 ec.CurrentBranching.CurrentUsageVector.Goto ();
1253 Constant c = expr.ResolveLabelConstant (ec);
1259 if (ec.Switch.IsNullable && c is NullLiteral) {
1262 TypeSpec type = ec.Switch.SwitchType;
1263 res = c.Reduce (ec, type);
1265 c.Error_ValueCannotBeConverted (ec, type, true);
1269 if (!Convert.ImplicitStandardConversionExists (c, type))
1270 ec.Report.Warning (469, 2, loc,
1271 "The `goto case' value is not implicitly convertible to type `{0}'",
1272 type.GetSignatureForError ());
1276 ec.Switch.RegisterGotoCase (this, res);
1280 protected override void DoEmit (EmitContext ec)
1282 ec.Emit (OpCodes.Br, Label.GetILLabel (ec));
1285 protected override void CloneTo (CloneContext clonectx, Statement t)
1287 GotoCase target = (GotoCase) t;
1289 target.expr = expr.Clone (clonectx);
1292 public override object Accept (StructuralVisitor visitor)
1294 return visitor.Visit (this);
1298 public class Throw : Statement {
1301 public Throw (Expression expr, Location l)
1307 public Expression Expr {
1313 public override bool Resolve (BlockContext ec)
1316 ec.CurrentBranching.CurrentUsageVector.Goto ();
1317 return ec.CurrentBranching.CheckRethrow (loc);
1320 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1321 ec.CurrentBranching.CurrentUsageVector.Goto ();
1326 var et = ec.BuiltinTypes.Exception;
1327 if (Convert.ImplicitConversionExists (ec, expr, et))
1328 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1330 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1335 protected override void DoEmit (EmitContext ec)
1338 ec.Emit (OpCodes.Rethrow);
1342 ec.Emit (OpCodes.Throw);
1346 protected override void CloneTo (CloneContext clonectx, Statement t)
1348 Throw target = (Throw) t;
1351 target.expr = expr.Clone (clonectx);
1354 public override object Accept (StructuralVisitor visitor)
1356 return visitor.Visit (this);
1360 public class Break : Statement {
1362 public Break (Location l)
1367 bool unwind_protect;
1369 public override bool Resolve (BlockContext ec)
1371 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1372 ec.CurrentBranching.CurrentUsageVector.Goto ();
1376 protected override void DoEmit (EmitContext ec)
1378 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1381 protected override void CloneTo (CloneContext clonectx, Statement t)
1386 public override object Accept (StructuralVisitor visitor)
1388 return visitor.Visit (this);
1392 public class Continue : Statement {
1394 public Continue (Location l)
1399 bool unwind_protect;
1401 public override bool Resolve (BlockContext ec)
1403 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1404 ec.CurrentBranching.CurrentUsageVector.Goto ();
1408 protected override void DoEmit (EmitContext ec)
1410 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1413 protected override void CloneTo (CloneContext clonectx, Statement t)
1418 public override object Accept (StructuralVisitor visitor)
1420 return visitor.Visit (this);
1424 public interface ILocalVariable
1426 void Emit (EmitContext ec);
1427 void EmitAssign (EmitContext ec);
1428 void EmitAddressOf (EmitContext ec);
1431 public interface INamedBlockVariable
1433 Block Block { get; }
1434 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1435 bool IsDeclared { get; }
1436 bool IsParameter { get; }
1437 Location Location { get; }
1440 public class BlockVariableDeclarator
1443 Expression initializer;
1445 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1447 if (li.Type != null)
1448 throw new ArgumentException ("Expected null variable type");
1451 this.initializer = initializer;
1456 public LocalVariable Variable {
1462 public Expression Initializer {
1467 initializer = value;
1473 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1475 var t = (BlockVariableDeclarator) MemberwiseClone ();
1476 if (initializer != null)
1477 t.initializer = initializer.Clone (cloneCtx);
1483 public class BlockVariable : Statement
1485 Expression initializer;
1486 protected FullNamedExpression type_expr;
1487 protected LocalVariable li;
1488 protected List<BlockVariableDeclarator> declarators;
1491 public BlockVariable (FullNamedExpression type, LocalVariable li)
1493 this.type_expr = type;
1495 this.loc = type_expr.Location;
1498 protected BlockVariable (LocalVariable li)
1505 public List<BlockVariableDeclarator> Declarators {
1511 public Expression Initializer {
1516 initializer = value;
1520 public FullNamedExpression TypeExpression {
1526 public LocalVariable Variable {
1534 public void AddDeclarator (BlockVariableDeclarator decl)
1536 if (declarators == null)
1537 declarators = new List<BlockVariableDeclarator> ();
1539 declarators.Add (decl);
1542 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1544 if (bc.Report.Errors != 0)
1547 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1549 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1550 new MemberName (li.Name, li.Location), null);
1552 container.AddField (f);
1555 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1559 public override bool Resolve (BlockContext bc)
1561 return Resolve (bc, true);
1564 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1566 if (type == null && !li.IsCompilerGenerated) {
1567 var vexpr = type_expr as VarExpr;
1570 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1571 // same name exists or as a keyword when no type was found
1573 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1574 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1575 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1578 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1582 if (li.IsConstant) {
1583 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1587 if (Initializer == null) {
1588 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1592 if (declarators != null) {
1593 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1597 Initializer = Initializer.Resolve (bc);
1598 if (Initializer != null) {
1599 ((VarExpr) type_expr).InferType (bc, Initializer);
1600 type = type_expr.Type;
1602 // Set error type to indicate the var was placed correctly but could
1605 // var a = missing ();
1607 type = InternalType.ErrorType;
1612 type = type_expr.ResolveAsType (bc);
1616 if (li.IsConstant && !type.IsConstantCompatible) {
1617 Const.Error_InvalidConstantType (type, loc, bc.Report);
1622 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1627 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1629 CreateEvaluatorVariable (bc, li);
1630 } else if (type != InternalType.ErrorType) {
1631 li.PrepareAssignmentAnalysis (bc);
1634 if (initializer != null) {
1635 initializer = ResolveInitializer (bc, li, initializer);
1636 // li.Variable.DefinitelyAssigned
1639 if (declarators != null) {
1640 foreach (var d in declarators) {
1641 d.Variable.Type = li.Type;
1643 CreateEvaluatorVariable (bc, d.Variable);
1644 } else if (type != InternalType.ErrorType) {
1645 d.Variable.PrepareAssignmentAnalysis (bc);
1648 if (d.Initializer != null && resolveDeclaratorInitializers) {
1649 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1650 // d.Variable.DefinitelyAssigned
1658 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1660 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1661 return a.ResolveStatement (bc);
1664 protected override void DoEmit (EmitContext ec)
1666 li.CreateBuilder (ec);
1668 if (Initializer != null)
1669 ((ExpressionStatement) Initializer).EmitStatement (ec);
1671 if (declarators != null) {
1672 foreach (var d in declarators) {
1673 d.Variable.CreateBuilder (ec);
1674 if (d.Initializer != null) {
1675 ec.Mark (d.Variable.Location);
1676 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1682 protected override void CloneTo (CloneContext clonectx, Statement target)
1684 BlockVariable t = (BlockVariable) target;
1686 if (type_expr != null)
1687 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1689 if (initializer != null)
1690 t.initializer = initializer.Clone (clonectx);
1692 if (declarators != null) {
1693 t.declarators = null;
1694 foreach (var d in declarators)
1695 t.AddDeclarator (d.Clone (clonectx));
1699 public override object Accept (StructuralVisitor visitor)
1701 return visitor.Visit (this);
1705 public class BlockConstant : BlockVariable
1707 public BlockConstant (FullNamedExpression type, LocalVariable li)
1712 public override void Emit (EmitContext ec)
1714 // Nothing to emit, not even sequence point
1717 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1719 initializer = initializer.Resolve (bc);
1720 if (initializer == null)
1723 var c = initializer as Constant;
1725 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1729 c = c.ConvertImplicitly (li.Type);
1731 if (TypeSpec.IsReferenceType (li.Type))
1732 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1734 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1739 li.ConstantValue = c;
1743 public override object Accept (StructuralVisitor visitor)
1745 return visitor.Visit (this);
1750 // The information about a user-perceived local variable
1752 public class LocalVariable : INamedBlockVariable, ILocalVariable
1759 AddressTaken = 1 << 2,
1760 CompilerGenerated = 1 << 3,
1762 ForeachVariable = 1 << 5,
1763 FixedVariable = 1 << 6,
1764 UsingVariable = 1 << 7,
1765 // DefinitelyAssigned = 1 << 8,
1768 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1772 readonly string name;
1773 readonly Location loc;
1774 readonly Block block;
1776 Constant const_value;
1778 public VariableInfo VariableInfo;
1779 HoistedVariable hoisted_variant;
1781 LocalBuilder builder;
1783 public LocalVariable (Block block, string name, Location loc)
1790 public LocalVariable (Block block, string name, Flags flags, Location loc)
1791 : this (block, name, loc)
1797 // Used by variable declarators
1799 public LocalVariable (LocalVariable li, string name, Location loc)
1800 : this (li.block, name, li.flags, loc)
1806 public bool AddressTaken {
1808 return (flags & Flags.AddressTaken) != 0;
1812 public Block Block {
1818 public Constant ConstantValue {
1823 const_value = value;
1828 // Hoisted local variable variant
1830 public HoistedVariable HoistedVariant {
1832 return hoisted_variant;
1835 hoisted_variant = value;
1839 public bool IsDeclared {
1841 return type != null;
1845 public bool IsCompilerGenerated {
1847 return (flags & Flags.CompilerGenerated) != 0;
1851 public bool IsConstant {
1853 return (flags & Flags.Constant) != 0;
1857 public bool IsLocked {
1859 return (flags & Flags.IsLocked) != 0;
1862 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1866 public bool IsThis {
1868 return (flags & Flags.IsThis) != 0;
1872 public bool IsFixed {
1874 return (flags & Flags.FixedVariable) != 0;
1878 bool INamedBlockVariable.IsParameter {
1884 public bool IsReadonly {
1886 return (flags & Flags.ReadonlyMask) != 0;
1890 public Location Location {
1896 public string Name {
1902 public TypeSpec Type {
1913 public void CreateBuilder (EmitContext ec)
1915 if ((flags & Flags.Used) == 0) {
1916 if (VariableInfo == null) {
1917 // Missing flow analysis or wrong variable flags
1918 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1921 if (VariableInfo.IsEverAssigned)
1922 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1924 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1927 if (HoistedVariant != null)
1930 if (builder != null) {
1931 if ((flags & Flags.CompilerGenerated) != 0)
1934 // To avoid Used warning duplicates
1935 throw new InternalErrorException ("Already created variable `{0}'", name);
1939 // All fixed variabled are pinned, a slot has to be alocated
1941 builder = ec.DeclareLocal (Type, IsFixed);
1942 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1943 ec.DefineLocalVariable (name, builder);
1946 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1948 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1953 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1955 if (IsConstant && const_value != null)
1956 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1958 return new LocalVariableReference (this, loc);
1961 public void Emit (EmitContext ec)
1963 // TODO: Need something better for temporary variables
1964 if ((flags & Flags.CompilerGenerated) != 0)
1967 ec.Emit (OpCodes.Ldloc, builder);
1970 public void EmitAssign (EmitContext ec)
1972 // TODO: Need something better for temporary variables
1973 if ((flags & Flags.CompilerGenerated) != 0)
1976 ec.Emit (OpCodes.Stloc, builder);
1979 public void EmitAddressOf (EmitContext ec)
1981 ec.Emit (OpCodes.Ldloca, builder);
1984 public static string GetCompilerGeneratedName (Block block)
1986 // HACK: Debugger depends on the name semantics
1987 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1990 public string GetReadOnlyContext ()
1992 switch (flags & Flags.ReadonlyMask) {
1993 case Flags.FixedVariable:
1994 return "fixed variable";
1995 case Flags.ForeachVariable:
1996 return "foreach iteration variable";
1997 case Flags.UsingVariable:
1998 return "using variable";
2001 throw new InternalErrorException ("Variable is not readonly");
2004 public bool IsThisAssigned (BlockContext ec, Block block)
2006 if (VariableInfo == null)
2007 throw new Exception ();
2009 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
2012 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
2015 public bool IsAssigned (BlockContext ec)
2017 if (VariableInfo == null)
2018 throw new Exception ();
2020 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
2023 public void PrepareAssignmentAnalysis (BlockContext bc)
2026 // No need to run assignment analysis for these guys
2028 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2031 VariableInfo = VariableInfo.Create (bc, this);
2035 // Mark the variables as referenced in the user code
2037 public void SetIsUsed ()
2039 flags |= Flags.Used;
2042 public void SetHasAddressTaken ()
2044 flags |= (Flags.AddressTaken | Flags.Used);
2047 public override string ToString ()
2049 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2054 /// Block represents a C# block.
2058 /// This class is used in a number of places: either to represent
2059 /// explicit blocks that the programmer places or implicit blocks.
2061 /// Implicit blocks are used as labels or to introduce variable
2064 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2065 /// they contain extra information that is not necessary on normal blocks.
2067 public class Block : Statement {
2074 HasCapturedVariable = 64,
2075 HasCapturedThis = 1 << 7,
2076 IsExpressionTree = 1 << 8,
2077 CompilerGenerated = 1 << 9,
2078 HasAsyncModifier = 1 << 10,
2080 YieldBlock = 1 << 12,
2081 AwaitBlock = 1 << 13,
2085 public Block Parent;
2086 public Location StartLocation;
2087 public Location EndLocation;
2089 public ExplicitBlock Explicit;
2090 public ParametersBlock ParametersBlock;
2092 protected Flags flags;
2095 // The statements in this block
2097 protected List<Statement> statements;
2099 protected List<Statement> scope_initializers;
2101 int? resolving_init_idx;
2107 public int ID = id++;
2109 static int clone_id_counter;
2113 // int assignable_slots;
2115 public Block (Block parent, Location start, Location end)
2116 : this (parent, 0, start, end)
2120 public Block (Block parent, Flags flags, Location start, Location end)
2122 if (parent != null) {
2123 // the appropriate constructors will fixup these fields
2124 ParametersBlock = parent.ParametersBlock;
2125 Explicit = parent.Explicit;
2128 this.Parent = parent;
2130 this.StartLocation = start;
2131 this.EndLocation = end;
2133 statements = new List<Statement> (4);
2135 this.original = this;
2140 public bool HasUnreachableClosingBrace {
2142 return (flags & Flags.HasRet) != 0;
2145 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2149 public Block Original {
2158 public bool IsCompilerGenerated {
2159 get { return (flags & Flags.CompilerGenerated) != 0; }
2160 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2163 public bool Unchecked {
2164 get { return (flags & Flags.Unchecked) != 0; }
2165 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2168 public bool Unsafe {
2169 get { return (flags & Flags.Unsafe) != 0; }
2170 set { flags |= Flags.Unsafe; }
2173 public List<Statement> Statements {
2174 get { return statements; }
2179 public void SetEndLocation (Location loc)
2184 public void AddLabel (LabeledStatement target)
2186 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2189 public void AddLocalName (LocalVariable li)
2191 AddLocalName (li.Name, li);
2194 public void AddLocalName (string name, INamedBlockVariable li)
2196 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2199 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2201 if (reason == null) {
2202 Error_AlreadyDeclared (name, variable);
2206 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2207 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2208 "to `{0}', which is already used in a `{1}' scope to denote something else",
2212 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2214 var pi = variable as ParametersBlock.ParameterInfo;
2216 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2218 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2219 "A local variable named `{0}' is already defined in this scope", name);
2223 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2225 ParametersBlock.TopBlock.Report.Error (412, loc,
2226 "The type parameter name `{0}' is the same as local variable or parameter name",
2231 // It should be used by expressions which require to
2232 // register a statement during resolve process.
2234 public void AddScopeStatement (Statement s)
2236 if (scope_initializers == null)
2237 scope_initializers = new List<Statement> ();
2240 // Simple recursive helper, when resolve scope initializer another
2241 // new scope initializer can be added, this ensures it's initialized
2242 // before existing one. For now this can happen with expression trees
2243 // in base ctor initializer only
2245 if (resolving_init_idx.HasValue) {
2246 scope_initializers.Insert (resolving_init_idx.Value, s);
2247 ++resolving_init_idx;
2249 scope_initializers.Add (s);
2253 public void InsertStatement (int index, Statement s)
2255 statements.Insert (index, s);
2258 public void AddStatement (Statement s)
2263 public int AssignableSlots {
2265 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2267 // return assignable_slots;
2271 public LabeledStatement LookupLabel (string name)
2273 return ParametersBlock.TopBlock.GetLabel (name, this);
2276 public override bool Resolve (BlockContext ec)
2278 if ((flags & Flags.Resolved) != 0)
2281 Block prev_block = ec.CurrentBlock;
2283 bool unreachable = ec.IsUnreachable;
2284 bool prev_unreachable = unreachable;
2286 ec.CurrentBlock = this;
2287 ec.StartFlowBranching (this);
2290 // Compiler generated scope statements
2292 if (scope_initializers != null) {
2293 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2294 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2297 resolving_init_idx = null;
2301 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2302 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2303 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2304 // responsible for handling the situation.
2306 int statement_count = statements.Count;
2307 for (int ix = 0; ix < statement_count; ix++){
2308 Statement s = statements [ix];
2311 // Warn if we detect unreachable code.
2314 if (s is EmptyStatement)
2317 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2318 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2319 ec.UnreachableReported = true;
2324 // Note that we're not using ResolveUnreachable() for unreachable
2325 // statements here. ResolveUnreachable() creates a temporary
2326 // flow branching and kills it afterwards. This leads to problems
2327 // if you have two unreachable statements where the first one
2328 // assigns a variable and the second one tries to access it.
2331 if (!s.Resolve (ec)) {
2333 if (!ec.IsInProbingMode)
2334 statements [ix] = new EmptyStatement (s.loc);
2339 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2340 statements [ix] = new EmptyStatement (s.loc);
2342 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2344 ec.IsUnreachable = true;
2345 } else if (ec.IsUnreachable)
2346 ec.IsUnreachable = false;
2349 if (unreachable != prev_unreachable) {
2350 ec.IsUnreachable = prev_unreachable;
2351 ec.UnreachableReported = false;
2354 while (ec.CurrentBranching is FlowBranchingLabeled)
2355 ec.EndFlowBranching ();
2357 bool flow_unreachable = ec.EndFlowBranching ();
2359 ec.CurrentBlock = prev_block;
2361 if (flow_unreachable)
2362 flags |= Flags.HasRet;
2364 // If we're a non-static `struct' constructor which doesn't have an
2365 // initializer, then we must initialize all of the struct's fields.
2366 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2369 flags |= Flags.Resolved;
2373 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2375 bool unreachable = false;
2376 if (warn && !ec.UnreachableReported) {
2377 ec.UnreachableReported = true;
2379 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2382 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2383 fb.CurrentUsageVector.IsUnreachable = true;
2384 bool ok = Resolve (ec);
2385 ec.KillFlowBranching ();
2388 ec.UnreachableReported = false;
2393 protected override void DoEmit (EmitContext ec)
2395 for (int ix = 0; ix < statements.Count; ix++){
2396 statements [ix].Emit (ec);
2400 public override void Emit (EmitContext ec)
2402 if (scope_initializers != null)
2403 EmitScopeInitializers (ec);
2408 protected void EmitScopeInitializers (EmitContext ec)
2410 foreach (Statement s in scope_initializers)
2415 public override string ToString ()
2417 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2421 protected override void CloneTo (CloneContext clonectx, Statement t)
2423 Block target = (Block) t;
2425 target.clone_id = clone_id_counter++;
2428 clonectx.AddBlockMap (this, target);
2429 if (original != this)
2430 clonectx.AddBlockMap (original, target);
2432 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2433 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2436 target.Parent = clonectx.RemapBlockCopy (Parent);
2438 target.statements = new List<Statement> (statements.Count);
2439 foreach (Statement s in statements)
2440 target.statements.Add (s.Clone (clonectx));
2443 public override object Accept (StructuralVisitor visitor)
2445 return visitor.Visit (this);
2449 public class ExplicitBlock : Block
2451 protected AnonymousMethodStorey am_storey;
2453 public ExplicitBlock (Block parent, Location start, Location end)
2454 : this (parent, (Flags) 0, start, end)
2458 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2459 : base (parent, flags, start, end)
2461 this.Explicit = this;
2466 public AnonymousMethodStorey AnonymousMethodStorey {
2472 public bool HasAwait {
2474 return (flags & Flags.AwaitBlock) != 0;
2478 public bool HasCapturedThis {
2480 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2483 return (flags & Flags.HasCapturedThis) != 0;
2488 // Used to indicate that the block has reference to parent
2489 // block and cannot be made static when defining anonymous method
2491 public bool HasCapturedVariable {
2493 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2496 return (flags & Flags.HasCapturedVariable) != 0;
2500 public bool HasYield {
2502 return (flags & Flags.YieldBlock) != 0;
2509 // Creates anonymous method storey in current block
2511 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2514 // Return same story for iterator and async blocks unless we are
2515 // in nested anonymous method
2517 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2518 return ec.CurrentAnonymousMethod.Storey;
2520 if (am_storey == null) {
2521 MemberBase mc = ec.MemberContext as MemberBase;
2524 // Creates anonymous method storey for this block
2526 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2532 public override void Emit (EmitContext ec)
2534 if (am_storey != null) {
2535 DefineStoreyContainer (ec, am_storey);
2536 am_storey.EmitStoreyInstantiation (ec, this);
2539 if (scope_initializers != null)
2540 EmitScopeInitializers (ec);
2542 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2543 ec.Emit (OpCodes.Nop);
2554 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2555 ec.Emit (OpCodes.Nop);
2559 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2561 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2562 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2563 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2567 // Creates anonymous method storey
2569 storey.CreateContainer ();
2570 storey.DefineContainer ();
2572 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2575 // Only first storey in path will hold this reference. All children blocks will
2576 // reference it indirectly using $ref field
2578 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2579 if (b.Parent != null) {
2580 var s = b.Parent.Explicit.AnonymousMethodStorey;
2582 storey.HoistedThis = s.HoistedThis;
2587 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2588 if (storey.HoistedThis == null)
2589 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2591 if (storey.HoistedThis != null)
2597 // We are the first storey on path and 'this' has to be hoisted
2599 if (storey.HoistedThis == null) {
2600 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2602 // ThisReferencesFromChildrenBlock holds all reference even if they
2603 // are not on this path. It saves some memory otherwise it'd have to
2604 // be in every explicit block. We run this check to see if the reference
2605 // is valid for this storey
2607 Block block_on_path = ref_block;
2608 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2610 if (block_on_path == null)
2613 if (storey.HoistedThis == null) {
2614 storey.AddCapturedThisField (ec, null);
2617 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2620 if (b.AnonymousMethodStorey != null) {
2622 // Don't add storey cross reference for `this' when the storey ends up not
2623 // beeing attached to any parent
2625 if (b.ParametersBlock.StateMachine == null) {
2626 AnonymousMethodStorey s = null;
2627 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
2628 s = ab.Explicit.AnonymousMethodStorey;
2633 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
2635 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
2636 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
2641 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2642 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2645 // Stop propagation inside same top block
2647 if (b.ParametersBlock == ParametersBlock.Original)
2650 b = b.ParametersBlock;
2653 pb = b as ParametersBlock;
2654 if (pb != null && pb.StateMachine != null) {
2655 if (pb.StateMachine == storey)
2659 // If we are state machine with no parent. We can hook into parent without additional
2660 // reference and capture this directly
2662 ExplicitBlock parent_storey_block = pb;
2663 while (parent_storey_block.Parent != null) {
2664 parent_storey_block = parent_storey_block.Parent.Explicit;
2665 if (parent_storey_block.AnonymousMethodStorey != null) {
2670 if (parent_storey_block.AnonymousMethodStorey == null) {
2671 pb.StateMachine.AddCapturedThisField (ec, null);
2672 b.HasCapturedThis = true;
2676 pb.StateMachine.AddParentStoreyReference (ec, storey);
2679 b.HasCapturedVariable = true;
2685 var ref_blocks = storey.ReferencesFromChildrenBlock;
2686 if (ref_blocks != null) {
2687 foreach (ExplicitBlock ref_block in ref_blocks) {
2688 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2689 if (b.AnonymousMethodStorey != null) {
2690 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2693 // Stop propagation inside same top block
2695 if (b.ParametersBlock == ParametersBlock.Original)
2698 b = b.ParametersBlock;
2701 var pb = b as ParametersBlock;
2702 if (pb != null && pb.StateMachine != null) {
2703 if (pb.StateMachine == storey)
2706 pb.StateMachine.AddParentStoreyReference (ec, storey);
2709 b.HasCapturedVariable = true;
2715 storey.PrepareEmit ();
2716 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2719 public void RegisterAsyncAwait ()
2722 while ((block.flags & Flags.AwaitBlock) == 0) {
2723 block.flags |= Flags.AwaitBlock;
2725 if (block is ParametersBlock)
2728 block = block.Parent.Explicit;
2732 public void RegisterIteratorYield ()
2734 ParametersBlock.TopBlock.IsIterator = true;
2737 while ((block.flags & Flags.YieldBlock) == 0) {
2738 block.flags |= Flags.YieldBlock;
2740 if (block.Parent == null)
2743 block = block.Parent.Explicit;
2747 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2749 tryBlock.statements = statements;
2750 statements = new List<Statement> (1);
2751 statements.Add (tf);
2756 // ParametersBlock was introduced to support anonymous methods
2757 // and lambda expressions
2759 public class ParametersBlock : ExplicitBlock
2761 public class ParameterInfo : INamedBlockVariable
2763 readonly ParametersBlock block;
2765 public VariableInfo VariableInfo;
2768 public ParameterInfo (ParametersBlock block, int index)
2776 public ParametersBlock Block {
2782 Block INamedBlockVariable.Block {
2788 public bool IsDeclared {
2794 public bool IsParameter {
2800 public bool IsLocked {
2809 public Location Location {
2811 return Parameter.Location;
2815 public Parameter Parameter {
2817 return block.Parameters [index];
2821 public TypeSpec ParameterType {
2823 return Parameter.Type;
2829 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2831 return new ParameterReference (this, loc);
2836 // Block is converted into an expression
2838 sealed class BlockScopeExpression : Expression
2841 readonly ParametersBlock block;
2843 public BlockScopeExpression (Expression child, ParametersBlock block)
2849 public override bool ContainsEmitWithAwait ()
2851 return child.ContainsEmitWithAwait ();
2854 public override Expression CreateExpressionTree (ResolveContext ec)
2856 throw new NotSupportedException ();
2859 protected override Expression DoResolve (ResolveContext ec)
2864 child = child.Resolve (ec);
2868 eclass = child.eclass;
2873 public override void Emit (EmitContext ec)
2875 block.EmitScopeInitializers (ec);
2880 protected ParametersCompiled parameters;
2881 protected ParameterInfo[] parameter_info;
2883 protected bool unreachable;
2884 protected ToplevelBlock top_block;
2885 protected StateMachine state_machine;
2887 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2888 : base (parent, 0, start, start)
2890 if (parameters == null)
2891 throw new ArgumentNullException ("parameters");
2893 this.parameters = parameters;
2894 ParametersBlock = this;
2896 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2898 this.top_block = parent.ParametersBlock.top_block;
2899 ProcessParameters ();
2902 protected ParametersBlock (ParametersCompiled parameters, Location start)
2903 : base (null, 0, start, start)
2905 if (parameters == null)
2906 throw new ArgumentNullException ("parameters");
2908 this.parameters = parameters;
2909 ParametersBlock = this;
2913 // It's supposed to be used by method body implementation of anonymous methods
2915 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2916 : base (null, 0, source.StartLocation, source.EndLocation)
2918 this.parameters = parameters;
2919 this.statements = source.statements;
2920 this.scope_initializers = source.scope_initializers;
2922 this.resolved = true;
2923 this.unreachable = source.unreachable;
2924 this.am_storey = source.am_storey;
2925 this.state_machine = source.state_machine;
2927 ParametersBlock = this;
2930 // Overwrite original for comparison purposes when linking cross references
2931 // between anonymous methods
2933 Original = source.Original;
2938 public bool IsAsync {
2940 return (flags & Flags.HasAsyncModifier) != 0;
2943 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2948 // Block has been converted to expression tree
2950 public bool IsExpressionTree {
2952 return (flags & Flags.IsExpressionTree) != 0;
2957 // The parameters for the block.
2959 public ParametersCompiled Parameters {
2965 public StateMachine StateMachine {
2967 return state_machine;
2971 public ToplevelBlock TopBlock {
2977 public bool Resolved {
2979 return (flags & Flags.Resolved) != 0;
2983 public int TemporaryLocalsCount { get; set; }
2988 // Check whether all `out' parameters have been assigned.
2990 public void CheckOutParameters (FlowBranching.UsageVector vector)
2992 if (vector.IsUnreachable)
2995 int n = parameter_info == null ? 0 : parameter_info.Length;
2997 for (int i = 0; i < n; i++) {
2998 VariableInfo var = parameter_info[i].VariableInfo;
3003 if (vector.IsAssigned (var, false))
3006 var p = parameter_info[i].Parameter;
3007 TopBlock.Report.Error (177, p.Location,
3008 "The out parameter `{0}' must be assigned to before control leaves the current method",
3013 public override Expression CreateExpressionTree (ResolveContext ec)
3015 if (statements.Count == 1) {
3016 Expression expr = statements[0].CreateExpressionTree (ec);
3017 if (scope_initializers != null)
3018 expr = new BlockScopeExpression (expr, this);
3023 return base.CreateExpressionTree (ec);
3026 public override void Emit (EmitContext ec)
3028 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3029 DefineStoreyContainer (ec, state_machine);
3030 state_machine.EmitStoreyInstantiation (ec, this);
3036 public void EmitEmbedded (EmitContext ec)
3038 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3039 DefineStoreyContainer (ec, state_machine);
3040 state_machine.EmitStoreyInstantiation (ec, this);
3046 public ParameterInfo GetParameterInfo (Parameter p)
3048 for (int i = 0; i < parameters.Count; ++i) {
3049 if (parameters[i] == p)
3050 return parameter_info[i];
3053 throw new ArgumentException ("Invalid parameter");
3056 public ParameterReference GetParameterReference (int index, Location loc)
3058 return new ParameterReference (parameter_info[index], loc);
3061 public Statement PerformClone ()
3063 CloneContext clonectx = new CloneContext ();
3064 return Clone (clonectx);
3067 protected void ProcessParameters ()
3069 if (parameters.Count == 0)
3072 parameter_info = new ParameterInfo[parameters.Count];
3073 for (int i = 0; i < parameter_info.Length; ++i) {
3074 var p = parameters.FixedParameters[i];
3078 // TODO: Should use Parameter only and more block there
3079 parameter_info[i] = new ParameterInfo (this, i);
3081 AddLocalName (p.Name, parameter_info[i]);
3085 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3092 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3093 flags |= Flags.IsExpressionTree;
3096 PrepareAssignmentAnalysis (rc);
3098 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3099 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3104 unreachable = top_level.End ();
3106 } catch (Exception e) {
3107 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException || rc.Report.Printer is NullReportPrinter)
3110 if (rc.CurrentBlock != null) {
3111 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3113 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3116 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3120 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3121 if (rc.CurrentAnonymousMethod == null) {
3122 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3123 if (md is StateMachineMethod) {
3126 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3131 // If an asynchronous body of F is either an expression classified as nothing, or a
3132 // statement block where no return statements have expressions, the inferred return type is Task
3135 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3136 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3137 am.ReturnTypeInference = null;
3138 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3143 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3144 rc.CurrentAnonymousMethod.GetSignatureForError ());
3152 void PrepareAssignmentAnalysis (BlockContext bc)
3154 for (int i = 0; i < parameters.Count; ++i) {
3155 var par = parameters.FixedParameters[i];
3157 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3160 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3164 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3166 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3167 var stateMachine = new IteratorStorey (iterator);
3169 state_machine = stateMachine;
3170 iterator.SetStateMachine (stateMachine);
3172 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3173 tlb.Original = this;
3174 tlb.IsCompilerGenerated = true;
3175 tlb.state_machine = stateMachine;
3176 tlb.AddStatement (new Return (iterator, iterator.Location));
3180 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3182 for (int i = 0; i < parameters.Count; i++) {
3183 Parameter p = parameters[i];
3184 Parameter.Modifier mod = p.ModFlags;
3185 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3186 host.Compiler.Report.Error (1988, p.Location,
3187 "Async methods cannot have ref or out parameters");
3191 if (p is ArglistParameter) {
3192 host.Compiler.Report.Error (4006, p.Location,
3193 "__arglist is not allowed in parameter list of async methods");
3197 if (parameters.Types[i].IsPointer) {
3198 host.Compiler.Report.Error (4005, p.Location,
3199 "Async methods cannot have unsafe parameters");
3205 host.Compiler.Report.Warning (1998, 1, loc,
3206 "Async block lacks `await' operator and will run synchronously");
3209 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3210 var initializer = new AsyncInitializer (this, host, block_type);
3211 initializer.Type = block_type;
3212 initializer.DelegateType = delegateType;
3214 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3216 state_machine = stateMachine;
3217 initializer.SetStateMachine (stateMachine);
3219 var b = this is ToplevelBlock ?
3220 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3221 new ParametersBlock (Parent, parameters, Location.Null) {
3226 b.IsCompilerGenerated = true;
3227 b.state_machine = stateMachine;
3228 b.AddStatement (new StatementExpression (initializer));
3236 public class ToplevelBlock : ParametersBlock
3238 LocalVariable this_variable;
3239 CompilerContext compiler;
3240 Dictionary<string, object> names;
3241 Dictionary<string, object> labels;
3243 List<ExplicitBlock> this_references;
3245 public ToplevelBlock (CompilerContext ctx, Location loc)
3246 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3250 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3251 : base (parameters, start)
3253 this.compiler = ctx;
3255 flags |= Flags.HasRet;
3257 ProcessParameters ();
3261 // Recreates a top level block from parameters block. Used for
3262 // compiler generated methods where the original block comes from
3263 // explicit child block. This works for already resolved blocks
3264 // only to ensure we resolve them in the correct flow order
3266 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3267 : base (source, parameters)
3269 this.compiler = source.TopBlock.compiler;
3271 flags |= Flags.HasRet;
3274 public bool IsIterator {
3276 return (flags & Flags.Iterator) != 0;
3279 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3283 public Report Report {
3285 return compiler.Report;
3290 // Used by anonymous blocks to track references of `this' variable
3292 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3294 return this_references;
3299 // Returns the "this" instance variable of this block.
3300 // See AddThisVariable() for more information.
3302 public LocalVariable ThisVariable {
3304 return this_variable;
3308 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3311 names = new Dictionary<string, object> ();
3314 if (!names.TryGetValue (name, out value)) {
3315 names.Add (name, li);
3319 INamedBlockVariable existing = value as INamedBlockVariable;
3320 List<INamedBlockVariable> existing_list;
3321 if (existing != null) {
3322 existing_list = new List<INamedBlockVariable> ();
3323 existing_list.Add (existing);
3324 names[name] = existing_list;
3326 existing_list = (List<INamedBlockVariable>) value;
3330 // A collision checking between local names
3332 var variable_block = li.Block.Explicit;
3333 for (int i = 0; i < existing_list.Count; ++i) {
3334 existing = existing_list[i];
3335 Block b = existing.Block.Explicit;
3337 // Collision at same level
3338 if (variable_block == b) {
3339 li.Block.Error_AlreadyDeclared (name, li);
3343 // Collision with parent
3344 Block parent = variable_block;
3345 while ((parent = parent.Parent) != null) {
3347 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3348 i = existing_list.Count;
3353 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3354 // Collision with children
3355 while ((b = b.Parent) != null) {
3356 if (variable_block == b) {
3357 li.Block.Error_AlreadyDeclared (name, li, "child");
3358 i = existing_list.Count;
3365 existing_list.Add (li);
3368 public void AddLabel (string name, LabeledStatement label)
3371 labels = new Dictionary<string, object> ();
3374 if (!labels.TryGetValue (name, out value)) {
3375 labels.Add (name, label);
3379 LabeledStatement existing = value as LabeledStatement;
3380 List<LabeledStatement> existing_list;
3381 if (existing != null) {
3382 existing_list = new List<LabeledStatement> ();
3383 existing_list.Add (existing);
3384 labels[name] = existing_list;
3386 existing_list = (List<LabeledStatement>) value;
3390 // A collision checking between labels
3392 for (int i = 0; i < existing_list.Count; ++i) {
3393 existing = existing_list[i];
3394 Block b = existing.Block;
3396 // Collision at same level
3397 if (label.Block == b) {
3398 Report.SymbolRelatedToPreviousError (existing.loc, name);
3399 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3403 // Collision with parent
3405 while ((b = b.Parent) != null) {
3406 if (existing.Block == b) {
3407 Report.Error (158, label.loc,
3408 "The label `{0}' shadows another label by the same name in a contained scope", name);
3409 i = existing_list.Count;
3414 // Collision with with children
3416 while ((b = b.Parent) != null) {
3417 if (label.Block == b) {
3418 Report.Error (158, label.loc,
3419 "The label `{0}' shadows another label by the same name in a contained scope", name);
3420 i = existing_list.Count;
3426 existing_list.Add (label);
3429 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3431 if (this_references == null)
3432 this_references = new List<ExplicitBlock> ();
3434 if (!this_references.Contains (block))
3435 this_references.Add (block);
3438 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3440 this_references.Remove (block);
3444 // Creates an arguments set from all parameters, useful for method proxy calls
3446 public Arguments GetAllParametersArguments ()
3448 int count = parameters.Count;
3449 Arguments args = new Arguments (count);
3450 for (int i = 0; i < count; ++i) {
3451 var pi = parameter_info[i];
3452 var arg_expr = GetParameterReference (i, pi.Location);
3454 Argument.AType atype_modifier;
3455 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
3456 case Parameter.Modifier.REF:
3457 atype_modifier = Argument.AType.Ref;
3459 case Parameter.Modifier.OUT:
3460 atype_modifier = Argument.AType.Out;
3467 args.Add (new Argument (arg_expr, atype_modifier));
3474 // Lookup inside a block, the returned value can represent 3 states
3476 // true+variable: A local name was found and it's valid
3477 // false+variable: A local name was found in a child block only
3478 // false+null: No local name was found
3480 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3486 if (!names.TryGetValue (name, out value))
3489 variable = value as INamedBlockVariable;
3491 if (variable != null) {
3493 if (variable.Block == b.Original)
3497 } while (b != null);
3505 } while (b != null);
3507 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3508 for (int i = 0; i < list.Count; ++i) {
3511 if (variable.Block == b.Original)
3515 } while (b != null);
3523 } while (b != null);
3533 public LabeledStatement GetLabel (string name, Block block)
3539 if (!labels.TryGetValue (name, out value)) {
3543 var label = value as LabeledStatement;
3545 if (label != null) {
3546 if (label.Block == b.Original)
3549 List<LabeledStatement> list = (List<LabeledStatement>) value;
3550 for (int i = 0; i < list.Count; ++i) {
3552 if (label.Block == b.Original)
3561 // This is used by non-static `struct' constructors which do not have an
3562 // initializer - in this case, the constructor must initialize all of the
3563 // struct's fields. To do this, we add a "this" variable and use the flow
3564 // analysis code to ensure that it's been fully initialized before control
3565 // leaves the constructor.
3567 public void AddThisVariable (BlockContext bc)
3569 if (this_variable != null)
3570 throw new InternalErrorException (StartLocation.ToString ());
3572 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3573 this_variable.Type = bc.CurrentType;
3574 this_variable.PrepareAssignmentAnalysis (bc);
3577 public bool IsThisAssigned (BlockContext ec)
3579 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3582 public override void Emit (EmitContext ec)
3584 if (Report.Errors > 0)
3588 if (IsCompilerGenerated) {
3589 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3597 // If `HasReturnLabel' is set, then we already emitted a
3598 // jump to the end of the method, so we must emit a `ret'
3601 // Unfortunately, System.Reflection.Emit automatically emits
3602 // a leave to the end of a finally block. This is a problem
3603 // if no code is following the try/finally block since we may
3604 // jump to a point after the end of the method.
3605 // As a workaround, we're always creating a return label in
3608 if (ec.HasReturnLabel || !unreachable) {
3609 if (ec.HasReturnLabel)
3610 ec.MarkLabel (ec.ReturnLabel);
3612 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3613 ec.Mark (EndLocation);
3615 if (ec.ReturnType.Kind != MemberKind.Void)
3616 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3618 ec.Emit (OpCodes.Ret);
3621 } catch (Exception e) {
3622 throw new InternalErrorException (e, StartLocation);
3627 public class SwitchLabel : Statement
3635 // if expr == null, then it is the default case.
3637 public SwitchLabel (Expression expr, Location l)
3643 public bool IsDefault {
3645 return label == null;
3649 public Expression Label {
3655 public Location Location {
3661 public Constant Converted {
3670 public bool SectionStart { get; set; }
3672 public Label GetILLabel (EmitContext ec)
3674 if (il_label == null){
3675 il_label = ec.DefineLabel ();
3678 return il_label.Value;
3681 protected override void DoEmit (EmitContext ec)
3683 ec.MarkLabel (GetILLabel (ec));
3686 public override bool Resolve (BlockContext bc)
3688 if (ResolveAndReduce (bc))
3689 bc.Switch.RegisterLabel (bc, this);
3691 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3693 return base.Resolve (bc);
3697 // Resolves the expression, reduces it to a literal if possible
3698 // and then converts it to the requested type.
3700 bool ResolveAndReduce (ResolveContext rc)
3705 var c = label.ResolveLabelConstant (rc);
3709 if (rc.Switch.IsNullable && c is NullLiteral) {
3714 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType);
3715 return converted != null;
3718 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
3721 if (converted == null)
3724 label = converted.GetValueAsLiteral ();
3726 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3727 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3730 protected override void CloneTo (CloneContext clonectx, Statement target)
3732 var t = (SwitchLabel) target;
3734 t.label = label.Clone (clonectx);
3737 public override object Accept (StructuralVisitor visitor)
3739 return visitor.Visit (this);
3743 public class Switch : Statement
3745 // structure used to hold blocks of keys while calculating table switch
3746 sealed class LabelsRange : IComparable<LabelsRange>
3748 public readonly long min;
3750 public readonly List<long> label_values;
3752 public LabelsRange (long value)
3755 label_values = new List<long> ();
3756 label_values.Add (value);
3759 public LabelsRange (long min, long max, ICollection<long> values)
3763 this.label_values = new List<long> (values);
3768 return max - min + 1;
3772 public bool AddValue (long value)
3774 var gap = value - min + 1;
3775 // Ensure the range has > 50% occupancy
3776 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3780 label_values.Add (value);
3784 public int CompareTo (LabelsRange other)
3786 int nLength = label_values.Count;
3787 int nLengthOther = other.label_values.Count;
3788 if (nLengthOther == nLength)
3789 return (int) (other.min - min);
3791 return nLength - nLengthOther;
3795 sealed class DispatchStatement : Statement
3797 readonly Switch body;
3799 public DispatchStatement (Switch body)
3804 protected override void CloneTo (CloneContext clonectx, Statement target)
3806 throw new NotImplementedException ();
3809 protected override void DoEmit (EmitContext ec)
3811 body.EmitDispatch (ec);
3815 public Expression Expr;
3818 // Mapping of all labels to their SwitchLabels
3820 Dictionary<long, SwitchLabel> labels;
3821 Dictionary<string, SwitchLabel> string_labels;
3822 List<SwitchLabel> case_labels;
3824 List<Tuple<GotoCase, Constant>> goto_cases;
3827 /// The governing switch type
3829 public TypeSpec SwitchType;
3831 Expression new_expr;
3833 SwitchLabel case_null;
3834 SwitchLabel case_default;
3836 Label defaultLabel, nullLabel;
3837 VariableReference value;
3838 ExpressionStatement string_dictionary;
3839 FieldExpr switch_cache_field;
3840 ExplicitBlock block;
3843 // Nullable Types support
3845 Nullable.Unwrap unwrap;
3847 public Switch (Expression e, ExplicitBlock block, Location l)
3854 public ExplicitBlock Block {
3860 public SwitchLabel DefaultLabel {
3862 return case_default;
3866 public bool IsNullable {
3868 return unwrap != null;
3873 // Determines the governing type for a switch. The returned
3874 // expression might be the expression from the switch, or an
3875 // expression that includes any potential conversions to
3877 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3879 switch (expr.Type.BuiltinType) {
3880 case BuiltinTypeSpec.Type.Byte:
3881 case BuiltinTypeSpec.Type.SByte:
3882 case BuiltinTypeSpec.Type.UShort:
3883 case BuiltinTypeSpec.Type.Short:
3884 case BuiltinTypeSpec.Type.UInt:
3885 case BuiltinTypeSpec.Type.Int:
3886 case BuiltinTypeSpec.Type.ULong:
3887 case BuiltinTypeSpec.Type.Long:
3888 case BuiltinTypeSpec.Type.Char:
3889 case BuiltinTypeSpec.Type.String:
3890 case BuiltinTypeSpec.Type.Bool:
3894 if (expr.Type.IsEnum)
3898 // Try to find a *user* defined implicit conversion.
3900 // If there is no implicit conversion, or if there are multiple
3901 // conversions, we have to report an error
3903 Expression converted = null;
3904 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3907 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3912 // Ignore over-worked ImplicitUserConversions that do
3913 // an implicit conversion in addition to the user conversion.
3915 if (!(e is UserCast))
3918 if (converted != null){
3919 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3928 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3930 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3945 public void RegisterLabel (ResolveContext rc, SwitchLabel sl)
3947 case_labels.Add (sl);
3950 if (case_default != null) {
3951 sl.Error_AlreadyOccurs (rc, case_default);
3960 if (string_labels != null) {
3961 string string_value = sl.Converted.GetValue () as string;
3962 if (string_value == null)
3965 string_labels.Add (string_value, sl);
3967 if (sl.Converted is NullLiteral) {
3970 labels.Add (sl.Converted.GetValueAsLong (), sl);
3973 } catch (ArgumentException) {
3974 if (string_labels != null)
3975 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
3977 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
3982 // This method emits code for a lookup-based switch statement (non-string)
3983 // Basically it groups the cases into blocks that are at least half full,
3984 // and then spits out individual lookup opcodes for each block.
3985 // It emits the longest blocks first, and short blocks are just
3986 // handled with direct compares.
3988 void EmitTableSwitch (EmitContext ec, Expression val)
3990 if (labels != null && labels.Count > 0) {
3991 List<LabelsRange> ranges;
3992 if (string_labels != null) {
3993 // We have done all hard work for string already
3994 // setup single range only
3995 ranges = new List<LabelsRange> (1);
3996 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3998 var element_keys = new long[labels.Count];
3999 labels.Keys.CopyTo (element_keys, 0);
4000 Array.Sort (element_keys);
4003 // Build possible ranges of switch labes to reduce number
4006 ranges = new List<LabelsRange> (element_keys.Length);
4007 var range = new LabelsRange (element_keys[0]);
4009 for (int i = 1; i < element_keys.Length; ++i) {
4010 var l = element_keys[i];
4011 if (range.AddValue (l))
4014 range = new LabelsRange (l);
4018 // sort the blocks so we can tackle the largest ones first
4022 Label lbl_default = defaultLabel;
4023 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4025 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4026 LabelsRange kb = ranges[range_index];
4027 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4029 // Optimize small ranges using simple equality check
4030 if (kb.Range <= 2) {
4031 foreach (var key in kb.label_values) {
4032 SwitchLabel sl = labels[key];
4033 if (sl == case_default || sl == case_null)
4036 if (sl.Converted.IsZeroInteger) {
4037 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4040 sl.Converted.Emit (ec);
4041 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4045 // TODO: if all the keys in the block are the same and there are
4046 // no gaps/defaults then just use a range-check.
4047 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4048 // TODO: optimize constant/I4 cases
4050 // check block range (could be > 2^31)
4052 ec.EmitLong (kb.min);
4053 ec.Emit (OpCodes.Blt, lbl_default);
4056 ec.EmitLong (kb.max);
4057 ec.Emit (OpCodes.Bgt, lbl_default);
4062 ec.EmitLong (kb.min);
4063 ec.Emit (OpCodes.Sub);
4066 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4070 int first = (int) kb.min;
4073 ec.Emit (OpCodes.Sub);
4074 } else if (first < 0) {
4075 ec.EmitInt (-first);
4076 ec.Emit (OpCodes.Add);
4080 // first, build the list of labels for the switch
4082 long cJumps = kb.Range;
4083 Label[] switch_labels = new Label[cJumps];
4084 for (int iJump = 0; iJump < cJumps; iJump++) {
4085 var key = kb.label_values[iKey];
4086 if (key == kb.min + iJump) {
4087 switch_labels[iJump] = labels[key].GetILLabel (ec);
4090 switch_labels[iJump] = lbl_default;
4094 // emit the switch opcode
4095 ec.Emit (OpCodes.Switch, switch_labels);
4098 // mark the default for this block
4099 if (range_index != 0)
4100 ec.MarkLabel (lbl_default);
4103 // the last default just goes to the end
4104 if (ranges.Count > 0)
4105 ec.Emit (OpCodes.Br, lbl_default);
4109 SwitchLabel FindLabel (Constant value)
4111 SwitchLabel sl = null;
4113 if (string_labels != null) {
4114 string s = value.GetValue () as string;
4116 if (case_null != null)
4118 else if (case_default != null)
4121 string_labels.TryGetValue (s, out sl);
4124 if (value is NullLiteral) {
4127 labels.TryGetValue (value.GetValueAsLong (), out sl);
4134 public override bool Resolve (BlockContext ec)
4136 Expr = Expr.Resolve (ec);
4140 new_expr = SwitchGoverningType (ec, Expr);
4142 if (new_expr == null && Expr.Type.IsNullableType) {
4143 unwrap = Nullable.Unwrap.Create (Expr, false);
4147 new_expr = SwitchGoverningType (ec, unwrap);
4150 if (new_expr == null) {
4151 if (Expr.Type != InternalType.ErrorType) {
4152 ec.Report.Error (151, loc,
4153 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4154 Expr.Type.GetSignatureForError ());
4161 SwitchType = new_expr.Type;
4163 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4164 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4168 if (block.Statements.Count == 0)
4171 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4172 string_labels = new Dictionary<string, SwitchLabel> ();
4174 labels = new Dictionary<long, SwitchLabel> ();
4177 case_labels = new List<SwitchLabel> ();
4179 var constant = new_expr as Constant;
4182 // Don't need extra variable for constant switch or switch with
4183 // only default case
4185 if (constant == null) {
4187 // Store switch expression for comparison purposes
4189 value = new_expr as VariableReference;
4190 if (value == null && !HasOnlyDefaultSection ()) {
4191 var current_block = ec.CurrentBlock;
4192 ec.CurrentBlock = Block;
4193 // Create temporary variable inside switch scope
4194 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4196 ec.CurrentBlock = current_block;
4200 Switch old_switch = ec.Switch;
4202 ec.Switch.SwitchType = SwitchType;
4204 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4206 ec.CurrentBranching.CurrentUsageVector.Goto ();
4208 var ok = block.Resolve (ec);
4210 if (case_default == null)
4211 ec.CurrentBranching.CreateSibling (null, FlowBranching.SiblingType.SwitchSection);
4213 if (ec.IsUnreachable)
4214 ec.KillFlowBranching ();
4216 ec.EndFlowBranching ();
4218 ec.Switch = old_switch;
4221 // Check if all goto cases are valid. Needs to be done after switch
4222 // is resolved becuase goto can jump forward in the scope.
4224 if (goto_cases != null) {
4225 foreach (var gc in goto_cases) {
4226 if (gc.Item1 == null) {
4227 if (DefaultLabel == null) {
4228 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
4234 var sl = FindLabel (gc.Item2);
4236 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + gc.Item2.GetValueAsLiteral (), ec.Report);
4238 gc.Item1.Label = sl;
4243 if (constant != null) {
4244 ResolveUnreachableSections (ec, constant);
4250 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4251 ResolveStringSwitchMap (ec);
4255 // Anonymous storey initialization has to happen before
4256 // any generated switch dispatch
4258 block.InsertStatement (0, new DispatchStatement (this));
4263 bool HasOnlyDefaultSection ()
4265 for (int i = 0; i < block.Statements.Count; ++i) {
4266 var s = block.Statements[i] as SwitchLabel;
4268 if (s == null || s.IsDefault)
4277 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
4279 if (goto_cases == null)
4280 goto_cases = new List<Tuple<GotoCase, Constant>> ();
4282 goto_cases.Add (Tuple.Create (gotoCase, value));
4286 // Converts string switch into string hashtable
4288 void ResolveStringSwitchMap (ResolveContext ec)
4290 FullNamedExpression string_dictionary_type;
4291 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4292 string_dictionary_type = new TypeExpression (
4293 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4294 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4296 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4297 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4299 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4303 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4304 Field field = new Field (ctype, string_dictionary_type,
4305 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4306 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4307 if (!field.Define ())
4309 ctype.AddField (field);
4311 var init = new List<Expression> ();
4313 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4314 string value = null;
4316 foreach (SwitchLabel sl in case_labels) {
4318 if (sl.SectionStart)
4319 labels.Add (++counter, sl);
4321 if (sl == case_default || sl == case_null)
4324 value = (string) sl.Converted.GetValue ();
4325 var init_args = new List<Expression> (2);
4326 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4328 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4329 init_args.Add (sl.Converted);
4331 init.Add (new CollectionElementInitializer (init_args, loc));
4334 Arguments args = new Arguments (1);
4335 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4336 Expression initializer = new NewInitialize (string_dictionary_type, args,
4337 new CollectionOrObjectInitializers (init, loc), loc);
4339 switch_cache_field = new FieldExpr (field, loc);
4340 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4343 void ResolveUnreachableSections (BlockContext bc, Constant value)
4345 var constant_label = FindLabel (value) ?? case_default;
4348 bool unreachable_reported = false;
4349 for (int i = 0; i < block.Statements.Count; ++i) {
4350 var s = block.Statements[i];
4352 if (s is SwitchLabel) {
4353 if (unreachable_reported) {
4354 found = unreachable_reported = false;
4357 found |= s == constant_label;
4362 unreachable_reported = true;
4366 if (!unreachable_reported) {
4367 unreachable_reported = true;
4368 if (!bc.IsUnreachable) {
4369 bc.Report.Warning (162, 2, s.loc, "Unreachable code detected");
4373 block.Statements[i] = new EmptyStatement (s.loc);
4377 void DoEmitStringSwitch (EmitContext ec)
4379 Label l_initialized = ec.DefineLabel ();
4382 // Skip initialization when value is null
4384 value.EmitBranchable (ec, nullLabel, false);
4387 // Check if string dictionary is initialized and initialize
4389 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4390 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4391 string_dictionary.EmitStatement (ec);
4393 ec.MarkLabel (l_initialized);
4395 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4397 ResolveContext rc = new ResolveContext (ec.MemberContext);
4399 if (switch_cache_field.Type.IsGeneric) {
4400 Arguments get_value_args = new Arguments (2);
4401 get_value_args.Add (new Argument (value));
4402 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4403 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4404 if (get_item == null)
4408 // A value was not found, go to default case
4410 get_item.EmitBranchable (ec, defaultLabel, false);
4412 Arguments get_value_args = new Arguments (1);
4413 get_value_args.Add (new Argument (value));
4415 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4416 if (get_item == null)
4419 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4420 get_item_object.EmitAssign (ec, get_item, true, false);
4421 ec.Emit (OpCodes.Brfalse, defaultLabel);
4423 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4424 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4426 get_item_int.EmitStatement (ec);
4427 get_item_object.Release (ec);
4430 EmitTableSwitch (ec, string_switch_variable);
4431 string_switch_variable.Release (ec);
4435 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4437 void EmitShortSwitch (EmitContext ec)
4439 MethodSpec equal_method = null;
4440 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4441 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4444 if (equal_method != null) {
4445 value.EmitBranchable (ec, nullLabel, false);
4448 for (int i = 0; i < case_labels.Count; ++i) {
4449 var label = case_labels [i];
4450 if (label == case_default || label == case_null)
4453 var constant = label.Converted;
4455 if (equal_method != null) {
4459 var call = new CallEmitter ();
4460 call.EmitPredefined (ec, equal_method, new Arguments (0));
4461 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4465 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4466 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4472 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4475 ec.Emit (OpCodes.Br, defaultLabel);
4478 void EmitDispatch (EmitContext ec)
4480 if (value == null) {
4482 // Constant switch, we already done the work
4487 if (string_dictionary != null) {
4488 DoEmitStringSwitch (ec);
4489 } else if (case_labels.Count < 4 || string_labels != null) {
4490 EmitShortSwitch (ec);
4492 EmitTableSwitch (ec, value);
4496 protected override void DoEmit (EmitContext ec)
4498 // Workaround broken flow-analysis
4499 block.HasUnreachableClosingBrace = true;
4502 // Setup the codegen context
4504 Label old_end = ec.LoopEnd;
4505 Switch old_switch = ec.Switch;
4507 ec.LoopEnd = ec.DefineLabel ();
4510 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4511 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4513 if (value != null) {
4516 unwrap.EmitCheck (ec);
4517 ec.Emit (OpCodes.Brfalse, nullLabel);
4518 value.EmitAssign (ec, new_expr, false, false);
4519 } else if (new_expr != value) {
4520 value.EmitAssign (ec, new_expr, false, false);
4525 // Next statement is compiler generated we don't need extra
4526 // nop when we can use the statement for sequence point
4528 ec.Mark (block.StartLocation);
4529 block.IsCompilerGenerated = true;
4534 // Restore context state.
4535 ec.MarkLabel (ec.LoopEnd);
4538 // Restore the previous context
4540 ec.LoopEnd = old_end;
4541 ec.Switch = old_switch;
4544 protected override void CloneTo (CloneContext clonectx, Statement t)
4546 Switch target = (Switch) t;
4548 target.Expr = Expr.Clone (clonectx);
4549 target.block = (ExplicitBlock) block.Clone (clonectx);
4552 public override object Accept (StructuralVisitor visitor)
4554 return visitor.Visit (this);
4558 // A place where execution can restart in an iterator
4559 public abstract class ResumableStatement : Statement
4562 protected Label resume_point;
4564 public Label PrepareForEmit (EmitContext ec)
4568 resume_point = ec.DefineLabel ();
4570 return resume_point;
4573 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4578 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4583 public abstract class TryFinallyBlock : ExceptionStatement
4585 protected Statement stmt;
4586 Label dispose_try_block;
4587 bool prepared_for_dispose, emitted_dispose;
4588 Method finally_host;
4590 protected TryFinallyBlock (Statement stmt, Location loc)
4598 public Statement Statement {
4606 protected abstract void EmitTryBody (EmitContext ec);
4607 public abstract void EmitFinallyBody (EmitContext ec);
4609 public override Label PrepareForDispose (EmitContext ec, Label end)
4611 if (!prepared_for_dispose) {
4612 prepared_for_dispose = true;
4613 dispose_try_block = ec.DefineLabel ();
4615 return dispose_try_block;
4618 protected sealed override void DoEmit (EmitContext ec)
4620 EmitTryBodyPrepare (ec);
4623 ec.BeginFinallyBlock ();
4625 Label start_finally = ec.DefineLabel ();
4626 if (resume_points != null) {
4627 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4629 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4630 ec.Emit (OpCodes.Brfalse_S, start_finally);
4631 ec.Emit (OpCodes.Endfinally);
4634 ec.MarkLabel (start_finally);
4636 if (finally_host != null) {
4637 finally_host.Define ();
4638 finally_host.PrepareEmit ();
4639 finally_host.Emit ();
4641 // Now it's safe to add, to close it properly and emit sequence points
4642 finally_host.Parent.AddMember (finally_host);
4644 var ce = new CallEmitter ();
4645 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4646 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4648 EmitFinallyBody (ec);
4651 ec.EndExceptionBlock ();
4654 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4656 if (emitted_dispose)
4659 emitted_dispose = true;
4661 Label end_of_try = ec.DefineLabel ();
4663 // Ensure that the only way we can get into this code is through a dispatcher
4664 if (have_dispatcher)
4665 ec.Emit (OpCodes.Br, end);
4667 ec.BeginExceptionBlock ();
4669 ec.MarkLabel (dispose_try_block);
4671 Label[] labels = null;
4672 for (int i = 0; i < resume_points.Count; ++i) {
4673 ResumableStatement s = resume_points[i];
4674 Label ret = s.PrepareForDispose (ec, end_of_try);
4675 if (ret.Equals (end_of_try) && labels == null)
4677 if (labels == null) {
4678 labels = new Label[resume_points.Count];
4679 for (int j = 0; j < i; ++j)
4680 labels[j] = end_of_try;
4685 if (labels != null) {
4687 for (j = 1; j < labels.Length; ++j)
4688 if (!labels[0].Equals (labels[j]))
4690 bool emit_dispatcher = j < labels.Length;
4692 if (emit_dispatcher) {
4693 ec.Emit (OpCodes.Ldloc, pc);
4694 ec.EmitInt (first_resume_pc);
4695 ec.Emit (OpCodes.Sub);
4696 ec.Emit (OpCodes.Switch, labels);
4699 foreach (ResumableStatement s in resume_points)
4700 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4703 ec.MarkLabel (end_of_try);
4705 ec.BeginFinallyBlock ();
4707 if (finally_host != null) {
4708 var ce = new CallEmitter ();
4709 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4710 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4712 EmitFinallyBody (ec);
4715 ec.EndExceptionBlock ();
4718 public override bool Resolve (BlockContext bc)
4721 // Finally block inside iterator is called from MoveNext and
4722 // Dispose methods that means we need to lift the block into
4723 // newly created host method to emit the body only once. The
4724 // original block then simply calls the newly generated method.
4726 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4727 var b = stmt as Block;
4728 if (b != null && b.Explicit.HasYield) {
4729 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4733 return base.Resolve (bc);
4738 // Base class for blocks using exception handling
4740 public abstract class ExceptionStatement : ResumableStatement
4745 protected List<ResumableStatement> resume_points;
4746 protected int first_resume_pc;
4748 protected ExceptionStatement (Location loc)
4753 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4755 StateMachineInitializer state_machine = null;
4756 if (resume_points != null) {
4757 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4759 ec.EmitInt ((int) IteratorStorey.State.Running);
4760 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4763 ec.BeginExceptionBlock ();
4765 if (resume_points != null) {
4766 ec.MarkLabel (resume_point);
4768 // For normal control flow, we want to fall-through the Switch
4769 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4770 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4771 ec.EmitInt (first_resume_pc);
4772 ec.Emit (OpCodes.Sub);
4774 Label[] labels = new Label[resume_points.Count];
4775 for (int i = 0; i < resume_points.Count; ++i)
4776 labels[i] = resume_points[i].PrepareForEmit (ec);
4777 ec.Emit (OpCodes.Switch, labels);
4781 public void SomeCodeFollows ()
4784 code_follows = true;
4788 public override bool Resolve (BlockContext ec)
4791 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4792 // So, ensure there's some IL code after this statement.
4793 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4794 ec.NeedReturnLabel ();
4799 public void AddResumePoint (ResumableStatement stmt, int pc)
4801 if (resume_points == null) {
4802 resume_points = new List<ResumableStatement> ();
4803 first_resume_pc = pc;
4806 if (pc != first_resume_pc + resume_points.Count)
4807 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4809 resume_points.Add (stmt);
4814 public class Lock : TryFinallyBlock
4817 TemporaryVariableReference expr_copy;
4818 TemporaryVariableReference lock_taken;
4820 public Lock (Expression expr, Statement stmt, Location loc)
4826 public Expression Expr {
4832 public override bool Resolve (BlockContext ec)
4834 expr = expr.Resolve (ec);
4838 if (!TypeSpec.IsReferenceType (expr.Type)) {
4839 ec.Report.Error (185, loc,
4840 "`{0}' is not a reference type as required by the lock statement",
4841 expr.Type.GetSignatureForError ());
4844 if (expr.Type.IsGenericParameter) {
4845 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4848 VariableReference lv = expr as VariableReference;
4851 locked = lv.IsLockedByStatement;
4852 lv.IsLockedByStatement = true;
4859 // Have to keep original lock value around to unlock same location
4860 // in the case of original value has changed or is null
4862 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4863 expr_copy.Resolve (ec);
4866 // Ensure Monitor methods are available
4868 if (ResolvePredefinedMethods (ec) > 1) {
4869 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4870 lock_taken.Resolve (ec);
4873 using (ec.Set (ResolveContext.Options.LockScope)) {
4874 ec.StartFlowBranching (this);
4875 Statement.Resolve (ec);
4876 ec.EndFlowBranching ();
4880 lv.IsLockedByStatement = locked;
4888 protected override void EmitTryBodyPrepare (EmitContext ec)
4890 expr_copy.EmitAssign (ec, expr);
4892 if (lock_taken != null) {
4894 // Initialize ref variable
4896 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4899 // Monitor.Enter (expr_copy)
4901 expr_copy.Emit (ec);
4902 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4905 base.EmitTryBodyPrepare (ec);
4908 protected override void EmitTryBody (EmitContext ec)
4911 // Monitor.Enter (expr_copy, ref lock_taken)
4913 if (lock_taken != null) {
4914 expr_copy.Emit (ec);
4915 lock_taken.LocalInfo.CreateBuilder (ec);
4916 lock_taken.AddressOf (ec, AddressOp.Load);
4917 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4920 Statement.Emit (ec);
4923 public override void EmitFinallyBody (EmitContext ec)
4926 // if (lock_taken) Monitor.Exit (expr_copy)
4928 Label skip = ec.DefineLabel ();
4930 if (lock_taken != null) {
4931 lock_taken.Emit (ec);
4932 ec.Emit (OpCodes.Brfalse_S, skip);
4935 expr_copy.Emit (ec);
4936 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4938 ec.Emit (OpCodes.Call, m);
4940 ec.MarkLabel (skip);
4943 int ResolvePredefinedMethods (ResolveContext rc)
4945 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4946 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4950 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4954 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4958 protected override void CloneTo (CloneContext clonectx, Statement t)
4960 Lock target = (Lock) t;
4962 target.expr = expr.Clone (clonectx);
4963 target.stmt = Statement.Clone (clonectx);
4966 public override object Accept (StructuralVisitor visitor)
4968 return visitor.Visit (this);
4973 public class Unchecked : Statement {
4976 public Unchecked (Block b, Location loc)
4983 public override bool Resolve (BlockContext ec)
4985 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4986 return Block.Resolve (ec);
4989 protected override void DoEmit (EmitContext ec)
4991 using (ec.With (EmitContext.Options.CheckedScope, false))
4995 protected override void CloneTo (CloneContext clonectx, Statement t)
4997 Unchecked target = (Unchecked) t;
4999 target.Block = clonectx.LookupBlock (Block);
5002 public override object Accept (StructuralVisitor visitor)
5004 return visitor.Visit (this);
5008 public class Checked : Statement {
5011 public Checked (Block b, Location loc)
5014 b.Unchecked = false;
5018 public override bool Resolve (BlockContext ec)
5020 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
5021 return Block.Resolve (ec);
5024 protected override void DoEmit (EmitContext ec)
5026 using (ec.With (EmitContext.Options.CheckedScope, true))
5030 protected override void CloneTo (CloneContext clonectx, Statement t)
5032 Checked target = (Checked) t;
5034 target.Block = clonectx.LookupBlock (Block);
5037 public override object Accept (StructuralVisitor visitor)
5039 return visitor.Visit (this);
5043 public class Unsafe : Statement {
5046 public Unsafe (Block b, Location loc)
5049 Block.Unsafe = true;
5053 public override bool Resolve (BlockContext ec)
5055 if (ec.CurrentIterator != null)
5056 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5058 using (ec.Set (ResolveContext.Options.UnsafeScope))
5059 return Block.Resolve (ec);
5062 protected override void DoEmit (EmitContext ec)
5067 protected override void CloneTo (CloneContext clonectx, Statement t)
5069 Unsafe target = (Unsafe) t;
5071 target.Block = clonectx.LookupBlock (Block);
5074 public override object Accept (StructuralVisitor visitor)
5076 return visitor.Visit (this);
5083 public class Fixed : Statement
5085 abstract class Emitter : ShimExpression
5087 protected LocalVariable vi;
5089 protected Emitter (Expression expr, LocalVariable li)
5095 public abstract void EmitExit (EmitContext ec);
5098 class ExpressionEmitter : Emitter {
5099 public ExpressionEmitter (Expression converted, LocalVariable li) :
5100 base (converted, li)
5104 protected override Expression DoResolve (ResolveContext rc)
5106 throw new NotImplementedException ();
5109 public override void Emit (EmitContext ec) {
5111 // Store pointer in pinned location
5117 public override void EmitExit (EmitContext ec)
5120 ec.Emit (OpCodes.Conv_U);
5125 class StringEmitter : Emitter
5127 LocalVariable pinned_string;
5129 public StringEmitter (Expression expr, LocalVariable li)
5134 protected override Expression DoResolve (ResolveContext rc)
5136 pinned_string = new LocalVariable (vi.Block, "$pinned",
5137 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5139 pinned_string.Type = rc.BuiltinTypes.String;
5141 eclass = ExprClass.Variable;
5142 type = rc.BuiltinTypes.Int;
5146 public override void Emit (EmitContext ec)
5148 pinned_string.CreateBuilder (ec);
5151 pinned_string.EmitAssign (ec);
5153 // TODO: Should use Binary::Add
5154 pinned_string.Emit (ec);
5155 ec.Emit (OpCodes.Conv_I);
5157 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5161 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5162 //pe.InstanceExpression = pinned_string;
5163 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5165 ec.Emit (OpCodes.Add);
5169 public override void EmitExit (EmitContext ec)
5172 pinned_string.EmitAssign (ec);
5176 public class VariableDeclaration : BlockVariable
5178 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5183 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5185 if (!Variable.Type.IsPointer && li == Variable) {
5186 bc.Report.Error (209, TypeExpression.Location,
5187 "The type of locals declared in a fixed statement must be a pointer type");
5192 // The rules for the possible declarators are pretty wise,
5193 // but the production on the grammar is more concise.
5195 // So we have to enforce these rules here.
5197 // We do not resolve before doing the case 1 test,
5198 // because the grammar is explicit in that the token &
5199 // is present, so we need to test for this particular case.
5202 if (initializer is Cast) {
5203 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5207 initializer = initializer.Resolve (bc);
5209 if (initializer == null)
5215 if (initializer.Type.IsArray) {
5216 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5219 // Provided that array_type is unmanaged,
5221 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5225 // and T* is implicitly convertible to the
5226 // pointer type given in the fixed statement.
5228 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5230 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5231 if (converted == null)
5235 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5237 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5238 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5239 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5240 new NullLiteral (loc),
5243 converted = converted.Resolve (bc);
5245 return new ExpressionEmitter (converted, li);
5251 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5252 return new StringEmitter (initializer, li).Resolve (bc);
5255 // Case 3: fixed buffer
5256 if (initializer is FixedBufferPtr) {
5257 return new ExpressionEmitter (initializer, li);
5261 // Case 4: & object.
5263 bool already_fixed = true;
5264 Unary u = initializer as Unary;
5265 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5266 IVariableReference vr = u.Expr as IVariableReference;
5267 if (vr == null || !vr.IsFixed) {
5268 already_fixed = false;
5272 if (already_fixed) {
5273 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5276 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5277 return new ExpressionEmitter (initializer, li);
5282 VariableDeclaration decl;
5283 Statement statement;
5286 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5295 public Statement Statement {
5301 public BlockVariable Variables {
5309 public override bool Resolve (BlockContext ec)
5311 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5312 if (!decl.Resolve (ec))
5316 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5317 bool ok = statement.Resolve (ec);
5318 bool flow_unreachable = ec.EndFlowBranching ();
5319 has_ret = flow_unreachable;
5324 protected override void DoEmit (EmitContext ec)
5326 decl.Variable.CreateBuilder (ec);
5327 decl.Initializer.Emit (ec);
5328 if (decl.Declarators != null) {
5329 foreach (var d in decl.Declarators) {
5330 d.Variable.CreateBuilder (ec);
5331 d.Initializer.Emit (ec);
5335 statement.Emit (ec);
5341 // Clear the pinned variable
5343 ((Emitter) decl.Initializer).EmitExit (ec);
5344 if (decl.Declarators != null) {
5345 foreach (var d in decl.Declarators) {
5346 ((Emitter)d.Initializer).EmitExit (ec);
5351 protected override void CloneTo (CloneContext clonectx, Statement t)
5353 Fixed target = (Fixed) t;
5355 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5356 target.statement = statement.Clone (clonectx);
5359 public override object Accept (StructuralVisitor visitor)
5361 return visitor.Visit (this);
5365 public class Catch : Statement
5369 FullNamedExpression type_expr;
5370 CompilerAssign assign;
5373 public Catch (Block block, Location loc)
5381 public Block Block {
5387 public TypeSpec CatchType {
5393 public bool IsGeneral {
5395 return type_expr == null;
5399 public FullNamedExpression TypeExpression {
5408 public LocalVariable Variable {
5419 protected override void DoEmit (EmitContext ec)
5422 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5424 ec.BeginCatchBlock (CatchType);
5427 li.CreateBuilder (ec);
5430 // Special case hoisted catch variable, we have to use a temporary variable
5431 // to pass via anonymous storey initialization with the value still on top
5434 if (li.HoistedVariant != null) {
5435 LocalTemporary lt = new LocalTemporary (li.Type);
5438 // switch to assigning from the temporary variable and not from top of the stack
5439 assign.UpdateSource (lt);
5442 ec.Emit (OpCodes.Pop);
5448 public override bool Resolve (BlockContext ec)
5450 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5451 if (type_expr != null) {
5452 type = type_expr.ResolveAsType (ec);
5456 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5457 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5458 } else if (li != null) {
5460 li.PrepareAssignmentAnalysis (ec);
5462 // source variable is at the top of the stack
5463 Expression source = new EmptyExpression (li.Type);
5464 if (li.Type.IsGenericParameter)
5465 source = new UnboxCast (source, li.Type);
5468 // Uses Location.Null to hide from symbol file
5470 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5471 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5475 return Block.Resolve (ec);
5479 protected override void CloneTo (CloneContext clonectx, Statement t)
5481 Catch target = (Catch) t;
5483 if (type_expr != null)
5484 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5486 target.block = clonectx.LookupBlock (block);
5490 public class TryFinally : TryFinallyBlock
5494 public TryFinally (Statement stmt, Block fini, Location loc)
5500 public Block Finallyblock {
5506 public override bool Resolve (BlockContext ec)
5510 ec.StartFlowBranching (this);
5512 if (!stmt.Resolve (ec))
5516 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5518 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5519 if (!fini.Resolve (ec))
5523 ec.EndFlowBranching ();
5525 ok &= base.Resolve (ec);
5530 protected override void EmitTryBody (EmitContext ec)
5535 public override void EmitFinallyBody (EmitContext ec)
5540 protected override void CloneTo (CloneContext clonectx, Statement t)
5542 TryFinally target = (TryFinally) t;
5544 target.stmt = stmt.Clone (clonectx);
5546 target.fini = clonectx.LookupBlock (fini);
5549 public override object Accept (StructuralVisitor visitor)
5551 return visitor.Visit (this);
5555 public class TryCatch : ExceptionStatement
5558 List<Catch> clauses;
5559 readonly bool inside_try_finally;
5561 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5565 this.clauses = catch_clauses;
5566 this.inside_try_finally = inside_try_finally;
5569 public List<Catch> Clauses {
5575 public bool IsTryCatchFinally {
5577 return inside_try_finally;
5581 public override bool Resolve (BlockContext ec)
5585 ec.StartFlowBranching (this);
5587 if (!Block.Resolve (ec))
5590 for (int i = 0; i < clauses.Count; ++i) {
5592 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5594 if (!c.Resolve (ec)) {
5599 TypeSpec resolved_type = c.CatchType;
5600 for (int ii = 0; ii < clauses.Count; ++ii) {
5604 if (clauses[ii].IsGeneral) {
5605 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5608 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5611 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5614 ec.Report.Warning (1058, 1, c.loc,
5615 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5623 var ct = clauses[ii].CatchType;
5627 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5628 ec.Report.Error (160, c.loc,
5629 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5630 ct.GetSignatureForError ());
5636 ec.EndFlowBranching ();
5638 return base.Resolve (ec) && ok;
5641 protected sealed override void DoEmit (EmitContext ec)
5643 if (!inside_try_finally)
5644 EmitTryBodyPrepare (ec);
5648 foreach (Catch c in clauses)
5651 if (!inside_try_finally)
5652 ec.EndExceptionBlock ();
5655 protected override void CloneTo (CloneContext clonectx, Statement t)
5657 TryCatch target = (TryCatch) t;
5659 target.Block = clonectx.LookupBlock (Block);
5660 if (clauses != null){
5661 target.clauses = new List<Catch> ();
5662 foreach (Catch c in clauses)
5663 target.clauses.Add ((Catch) c.Clone (clonectx));
5667 public override object Accept (StructuralVisitor visitor)
5669 return visitor.Visit (this);
5673 public class Using : TryFinallyBlock
5675 public class VariableDeclaration : BlockVariable
5677 Statement dispose_call;
5679 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5684 public VariableDeclaration (LocalVariable li, Location loc)
5690 public VariableDeclaration (Expression expr)
5693 loc = expr.Location;
5699 public bool IsNested { get; private set; }
5703 public void EmitDispose (EmitContext ec)
5705 dispose_call.Emit (ec);
5708 public override bool Resolve (BlockContext bc)
5713 return base.Resolve (bc, false);
5716 public Expression ResolveExpression (BlockContext bc)
5718 var e = Initializer.Resolve (bc);
5722 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5723 Initializer = ResolveInitializer (bc, Variable, e);
5727 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5729 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5730 initializer = initializer.Resolve (bc);
5731 if (initializer == null)
5734 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5735 Arguments args = new Arguments (1);
5736 args.Add (new Argument (initializer));
5737 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5738 if (initializer == null)
5741 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5742 dispose_call = CreateDisposeCall (bc, var);
5743 dispose_call.Resolve (bc);
5745 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5748 if (li == Variable) {
5749 CheckIDiposableConversion (bc, li, initializer);
5750 dispose_call = CreateDisposeCall (bc, li);
5751 dispose_call.Resolve (bc);
5754 return base.ResolveInitializer (bc, li, initializer);
5757 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5761 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5762 if (type.IsNullableType) {
5763 // it's handled in CreateDisposeCall
5767 bc.Report.SymbolRelatedToPreviousError (type);
5768 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5769 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5770 type.GetSignatureForError ());
5776 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5778 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5780 var loc = lv.Location;
5782 var idt = bc.BuiltinTypes.IDisposable;
5783 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5785 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5786 dispose_mg.InstanceExpression = type.IsNullableType ?
5787 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5791 // Hide it from symbol file via null location
5793 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5795 // Add conditional call when disposing possible null variable
5796 if (!type.IsStruct || type.IsNullableType)
5797 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5802 public void ResolveDeclaratorInitializer (BlockContext bc)
5804 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5807 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5809 for (int i = declarators.Count - 1; i >= 0; --i) {
5810 var d = declarators [i];
5811 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5812 vd.Initializer = d.Initializer;
5814 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5815 vd.dispose_call.Resolve (bc);
5817 stmt = new Using (vd, stmt, d.Variable.Location);
5824 public override object Accept (StructuralVisitor visitor)
5826 return visitor.Visit (this);
5830 VariableDeclaration decl;
5832 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5838 public Using (Expression expr, Statement stmt, Location loc)
5841 this.decl = new VariableDeclaration (expr);
5846 public Expression Expr {
5848 return decl.Variable == null ? decl.Initializer : null;
5852 public BlockVariable Variables {
5860 public override void Emit (EmitContext ec)
5863 // Don't emit sequence point it will be set on variable declaration
5868 protected override void EmitTryBodyPrepare (EmitContext ec)
5871 base.EmitTryBodyPrepare (ec);
5874 protected override void EmitTryBody (EmitContext ec)
5879 public override void EmitFinallyBody (EmitContext ec)
5881 decl.EmitDispose (ec);
5884 public override bool Resolve (BlockContext ec)
5886 VariableReference vr;
5887 bool vr_locked = false;
5889 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5890 if (decl.Variable == null) {
5891 vr = decl.ResolveExpression (ec) as VariableReference;
5893 vr_locked = vr.IsLockedByStatement;
5894 vr.IsLockedByStatement = true;
5897 if (decl.IsNested) {
5898 decl.ResolveDeclaratorInitializer (ec);
5900 if (!decl.Resolve (ec))
5903 if (decl.Declarators != null) {
5904 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5912 ec.StartFlowBranching (this);
5916 ec.EndFlowBranching ();
5919 vr.IsLockedByStatement = vr_locked;
5926 protected override void CloneTo (CloneContext clonectx, Statement t)
5928 Using target = (Using) t;
5930 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5931 target.stmt = stmt.Clone (clonectx);
5934 public override object Accept (StructuralVisitor visitor)
5936 return visitor.Visit (this);
5941 /// Implementation of the foreach C# statement
5943 public class Foreach : Statement
5945 abstract class IteratorStatement : Statement
5947 protected readonly Foreach for_each;
5949 protected IteratorStatement (Foreach @foreach)
5951 this.for_each = @foreach;
5952 this.loc = @foreach.expr.Location;
5955 protected override void CloneTo (CloneContext clonectx, Statement target)
5957 throw new NotImplementedException ();
5960 public override void Emit (EmitContext ec)
5962 if (ec.EmitAccurateDebugInfo) {
5963 ec.Emit (OpCodes.Nop);
5970 sealed class ArrayForeach : IteratorStatement
5972 TemporaryVariableReference[] lengths;
5973 Expression [] length_exprs;
5974 StatementExpression[] counter;
5975 TemporaryVariableReference[] variables;
5977 TemporaryVariableReference copy;
5979 public ArrayForeach (Foreach @foreach, int rank)
5982 counter = new StatementExpression[rank];
5983 variables = new TemporaryVariableReference[rank];
5984 length_exprs = new Expression [rank];
5987 // Only use temporary length variables when dealing with
5988 // multi-dimensional arrays
5991 lengths = new TemporaryVariableReference [rank];
5994 public override bool Resolve (BlockContext ec)
5996 Block variables_block = for_each.variable.Block;
5997 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
6000 int rank = length_exprs.Length;
6001 Arguments list = new Arguments (rank);
6002 for (int i = 0; i < rank; i++) {
6003 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6005 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
6006 counter[i].Resolve (ec);
6009 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
6011 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6012 lengths[i].Resolve (ec);
6014 Arguments args = new Arguments (1);
6015 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
6016 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
6019 list.Add (new Argument (v));
6022 var access = new ElementAccess (copy, list, loc).Resolve (ec);
6027 if (for_each.type is VarExpr) {
6028 // Infer implicitly typed local variable from foreach array type
6029 var_type = access.Type;
6031 var_type = for_each.type.ResolveAsType (ec);
6033 if (var_type == null)
6036 access = Convert.ExplicitConversion (ec, access, var_type, loc);
6041 for_each.variable.Type = var_type;
6043 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
6044 if (variable_ref == null)
6047 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
6051 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
6052 ec.CurrentBranching.CreateSibling ();
6054 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
6055 if (!for_each.body.Resolve (ec))
6057 ec.EndFlowBranching ();
6059 // There's no direct control flow from the end of the embedded statement to the end of the loop
6060 ec.CurrentBranching.CurrentUsageVector.Goto ();
6062 ec.EndFlowBranching ();
6067 protected override void DoEmit (EmitContext ec)
6069 copy.EmitAssign (ec, for_each.expr);
6071 int rank = length_exprs.Length;
6072 Label[] test = new Label [rank];
6073 Label[] loop = new Label [rank];
6075 for (int i = 0; i < rank; i++) {
6076 test [i] = ec.DefineLabel ();
6077 loop [i] = ec.DefineLabel ();
6079 if (lengths != null)
6080 lengths [i].EmitAssign (ec, length_exprs [i]);
6083 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
6084 for (int i = 0; i < rank; i++) {
6085 variables [i].EmitAssign (ec, zero);
6087 ec.Emit (OpCodes.Br, test [i]);
6088 ec.MarkLabel (loop [i]);
6091 for_each.body.Emit (ec);
6093 ec.MarkLabel (ec.LoopBegin);
6094 ec.Mark (for_each.expr.Location);
6096 for (int i = rank - 1; i >= 0; i--){
6097 counter [i].Emit (ec);
6099 ec.MarkLabel (test [i]);
6100 variables [i].Emit (ec);
6102 if (lengths != null)
6103 lengths [i].Emit (ec);
6105 length_exprs [i].Emit (ec);
6107 ec.Emit (OpCodes.Blt, loop [i]);
6110 ec.MarkLabel (ec.LoopEnd);
6114 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6116 class RuntimeDispose : Using.VariableDeclaration
6118 public RuntimeDispose (LocalVariable lv, Location loc)
6123 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6125 // Defered to runtime check
6128 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6130 var idt = bc.BuiltinTypes.IDisposable;
6133 // Fabricates code like
6135 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6138 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6140 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6141 dispose_variable.CreateReferenceExpression (bc, loc),
6142 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6143 loc), new NullLiteral (loc));
6145 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6147 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6148 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6150 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6151 return new If (idisaposable_test, dispose, loc);
6155 LocalVariable variable;
6157 Statement statement;
6158 ExpressionStatement init;
6159 TemporaryVariableReference enumerator_variable;
6160 bool ambiguous_getenumerator_name;
6162 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6165 this.variable = var;
6169 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6171 rc.Report.SymbolRelatedToPreviousError (enumerator);
6172 rc.Report.Error (202, loc,
6173 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6174 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6177 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6180 // Option 1: Try to match by name GetEnumerator first
6182 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6183 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6185 var mg = mexpr as MethodGroupExpr;
6187 mg.InstanceExpression = expr;
6188 Arguments args = new Arguments (0);
6189 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6191 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6192 if (ambiguous_getenumerator_name)
6195 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6201 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6204 PredefinedMember<MethodSpec> iface_candidate = null;
6205 var ptypes = rc.Module.PredefinedTypes;
6206 var gen_ienumerable = ptypes.IEnumerableGeneric;
6207 if (!gen_ienumerable.Define ())
6208 gen_ienumerable = null;
6210 var ifaces = t.Interfaces;
6211 if (ifaces != null) {
6212 foreach (var iface in ifaces) {
6213 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6214 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6215 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6216 rc.Report.Error (1640, loc,
6217 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6218 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6223 // TODO: Cache this somehow
6224 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6225 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6230 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6231 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6236 if (iface_candidate == null) {
6237 if (expr.Type != InternalType.ErrorType) {
6238 rc.Report.Error (1579, loc,
6239 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6240 expr.Type.GetSignatureForError (), "GetEnumerator");
6246 var method = iface_candidate.Resolve (loc);
6250 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6251 mg.InstanceExpression = expr;
6255 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6257 var ms = MemberCache.FindMember (enumerator.ReturnType,
6258 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6259 BindingRestriction.InstanceOnly) as MethodSpec;
6261 if (ms == null || !ms.IsPublic) {
6262 Error_WrongEnumerator (rc, enumerator);
6266 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6269 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6271 var ps = MemberCache.FindMember (enumerator.ReturnType,
6272 MemberFilter.Property ("Current", null),
6273 BindingRestriction.InstanceOnly) as PropertySpec;
6275 if (ps == null || !ps.IsPublic) {
6276 Error_WrongEnumerator (rc, enumerator);
6283 public override bool Resolve (BlockContext ec)
6285 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6288 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6289 } else if (expr.Type.IsNullableType) {
6290 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6293 var get_enumerator_mg = ResolveGetEnumerator (ec);
6294 if (get_enumerator_mg == null) {
6298 var get_enumerator = get_enumerator_mg.BestCandidate;
6299 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6300 enumerator_variable.Resolve (ec);
6302 // Prepare bool MoveNext ()
6303 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6304 if (move_next_mg == null) {
6308 move_next_mg.InstanceExpression = enumerator_variable;
6310 // Prepare ~T~ Current { get; }
6311 var current_prop = ResolveCurrent (ec, get_enumerator);
6312 if (current_prop == null) {
6316 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6317 if (current_pe == null)
6320 VarExpr ve = for_each.type as VarExpr;
6324 // Source type is dynamic, set element type to dynamic too
6325 variable.Type = ec.BuiltinTypes.Dynamic;
6327 // Infer implicitly typed local variable from foreach enumerable type
6328 variable.Type = current_pe.Type;
6332 // Explicit cast of dynamic collection elements has to be done at runtime
6333 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6336 variable.Type = for_each.type.ResolveAsType (ec);
6338 if (variable.Type == null)
6341 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6342 if (current_pe == null)
6346 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6347 if (variable_ref == null)
6350 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6352 var init = new Invocation (get_enumerator_mg, null);
6354 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6355 for_each.body, Location.Null);
6357 var enum_type = enumerator_variable.Type;
6360 // Add Dispose method call when enumerator can be IDisposable
6362 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6363 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6365 // Runtime Dispose check
6367 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6368 vd.Initializer = init;
6369 statement = new Using (vd, statement, Location.Null);
6372 // No Dispose call needed
6374 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6375 this.init.Resolve (ec);
6379 // Static Dispose check
6381 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6382 vd.Initializer = init;
6383 statement = new Using (vd, statement, Location.Null);
6386 return statement.Resolve (ec);
6389 protected override void DoEmit (EmitContext ec)
6391 enumerator_variable.LocalInfo.CreateBuilder (ec);
6394 init.EmitStatement (ec);
6396 statement.Emit (ec);
6399 #region IErrorHandler Members
6401 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6403 ec.Report.SymbolRelatedToPreviousError (best);
6404 ec.Report.Warning (278, 2, expr.Location,
6405 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6406 expr.Type.GetSignatureForError (), "enumerable",
6407 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6409 ambiguous_getenumerator_name = true;
6413 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6418 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6423 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6432 LocalVariable variable;
6434 Statement statement;
6437 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6440 this.variable = var;
6442 this.statement = stmt;
6447 public Expression Expr {
6448 get { return expr; }
6451 public Statement Statement {
6452 get { return statement; }
6455 public Expression TypeExpression {
6456 get { return type; }
6459 public LocalVariable Variable {
6460 get { return variable; }
6463 public override bool Resolve (BlockContext ec)
6465 expr = expr.Resolve (ec);
6470 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6474 body.AddStatement (statement);
6476 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6477 statement = new ArrayForeach (this, 1);
6478 } else if (expr.Type is ArrayContainer) {
6479 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6481 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6482 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6483 expr.ExprClassName);
6487 statement = new CollectionForeach (this, variable, expr);
6490 return statement.Resolve (ec);
6493 protected override void DoEmit (EmitContext ec)
6495 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6496 ec.LoopBegin = ec.DefineLabel ();
6497 ec.LoopEnd = ec.DefineLabel ();
6499 if (!(statement is Block))
6500 ec.BeginCompilerScope ();
6502 variable.CreateBuilder (ec);
6504 statement.Emit (ec);
6506 if (!(statement is Block))
6509 ec.LoopBegin = old_begin;
6510 ec.LoopEnd = old_end;
6513 protected override void CloneTo (CloneContext clonectx, Statement t)
6515 Foreach target = (Foreach) t;
6517 target.type = type.Clone (clonectx);
6518 target.expr = expr.Clone (clonectx);
6519 target.body = (Block) body.Clone (clonectx);
6520 target.statement = statement.Clone (clonectx);
6523 public override object Accept (StructuralVisitor visitor)
6525 return visitor.Visit (this);