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 {
27 protected bool reachable;
29 public bool IsUnreachable {
36 /// Resolves the statement, true means that all sub-statements
39 public virtual bool Resolve (BlockContext bc)
45 /// Return value indicates whether all code paths emitted return.
47 protected abstract void DoEmit (EmitContext ec);
49 public virtual void Emit (EmitContext ec)
54 if (ec.StatementEpilogue != null) {
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
63 protected abstract void CloneTo (CloneContext clonectx, Statement target);
65 public Statement Clone (CloneContext clonectx)
67 Statement s = (Statement) this.MemberwiseClone ();
68 CloneTo (clonectx, s);
72 public virtual Expression CreateExpressionTree (ResolveContext ec)
74 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
78 public virtual object Accept (StructuralVisitor visitor)
80 return visitor.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
88 public bool FlowAnalysis (FlowAnalysisContext fc)
91 fc.UnreachableReported = false;
92 var res = DoFlowAnalysis (fc);
97 // Special handling cases
100 return DoFlowAnalysis (fc);
103 if (this is EmptyStatement || loc.IsNull)
106 if (fc.UnreachableReported)
109 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
110 fc.UnreachableReported = true;
114 public virtual Reachability MarkReachable (Reachability rc)
116 if (!rc.IsUnreachable)
122 protected void CheckExitBoundaries (BlockContext bc, Block scope)
124 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
125 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
129 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
130 if (b.IsFinallyBlock) {
131 Error_FinallyClauseExit (bc);
137 protected void Error_FinallyClauseExit (BlockContext bc)
139 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
143 public sealed class EmptyStatement : Statement
145 public EmptyStatement (Location loc)
150 public override bool Resolve (BlockContext ec)
155 public override void Emit (EmitContext ec)
159 protected override void DoEmit (EmitContext ec)
161 throw new NotSupportedException ();
164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
169 protected override void CloneTo (CloneContext clonectx, Statement target)
174 public override object Accept (StructuralVisitor visitor)
176 return visitor.Visit (this);
180 public class If : Statement {
182 public Statement TrueStatement;
183 public Statement FalseStatement;
185 bool true_returns, false_returns;
187 public If (Expression bool_expr, Statement true_statement, Location l)
188 : this (bool_expr, true_statement, null, l)
192 public If (Expression bool_expr,
193 Statement true_statement,
194 Statement false_statement,
197 this.expr = bool_expr;
198 TrueStatement = true_statement;
199 FalseStatement = false_statement;
203 public Expression Expr {
209 public override bool Resolve (BlockContext ec)
211 expr = expr.Resolve (ec);
213 var ok = TrueStatement.Resolve (ec);
215 if (FalseStatement != null) {
216 ok &= FalseStatement.Resolve (ec);
222 protected override void DoEmit (EmitContext ec)
224 Label false_target = ec.DefineLabel ();
228 // If we're a boolean constant, Resolve() already
229 // eliminated dead code for us.
231 Constant c = expr as Constant;
233 c.EmitSideEffect (ec);
235 if (!c.IsDefaultValue)
236 TrueStatement.Emit (ec);
237 else if (FalseStatement != null)
238 FalseStatement.Emit (ec);
243 expr.EmitBranchable (ec, false_target, false);
245 TrueStatement.Emit (ec);
247 if (FalseStatement != null){
248 bool branch_emitted = false;
250 end = ec.DefineLabel ();
252 ec.Emit (OpCodes.Br, end);
253 branch_emitted = true;
256 ec.MarkLabel (false_target);
257 FalseStatement.Emit (ec);
262 ec.MarkLabel (false_target);
266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
268 expr.FlowAnalysisConditional (fc);
270 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
272 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
273 var labels = fc.CopyLabelStack ();
275 var res = TrueStatement.FlowAnalysis (fc);
277 fc.SetLabelStack (labels);
279 if (FalseStatement == null) {
281 var c = expr as Constant;
282 if (c != null && !c.IsDefaultValue)
286 fc.DefiniteAssignment = da_false;
288 fc.DefiniteAssignment &= da_false;
294 fc.DefiniteAssignment = da_false;
296 res = FalseStatement.FlowAnalysis (fc);
297 fc.SetLabelStack (labels);
301 var da_true = fc.DefiniteAssignment;
303 fc.DefiniteAssignment = da_false;
305 res &= FalseStatement.FlowAnalysis (fc);
307 fc.SetLabelStack (labels);
309 if (!TrueStatement.IsUnreachable) {
310 if (false_returns || FalseStatement.IsUnreachable)
311 fc.DefiniteAssignment = da_true;
313 fc.DefiniteAssignment &= da_true;
319 public override Reachability MarkReachable (Reachability rc)
321 if (rc.IsUnreachable)
324 base.MarkReachable (rc);
326 var c = expr as Constant;
328 bool take = !c.IsDefaultValue;
330 rc = TrueStatement.MarkReachable (rc);
332 if (FalseStatement != null)
333 rc = FalseStatement.MarkReachable (rc);
339 var true_rc = TrueStatement.MarkReachable (rc);
340 true_returns = true_rc.IsUnreachable;
342 if (FalseStatement == null)
345 var false_rc = FalseStatement.MarkReachable (rc);
346 false_returns = false_rc.IsUnreachable;
348 return true_rc & false_rc;
351 protected override void CloneTo (CloneContext clonectx, Statement t)
355 target.expr = expr.Clone (clonectx);
356 target.TrueStatement = TrueStatement.Clone (clonectx);
357 if (FalseStatement != null)
358 target.FalseStatement = FalseStatement.Clone (clonectx);
361 public override object Accept (StructuralVisitor visitor)
363 return visitor.Visit (this);
367 public class Do : LoopStatement
369 public Expression expr;
370 bool iterator_reachable, end_reachable;
372 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
377 WhileLocation = whileLocation;
380 public Location WhileLocation {
384 public override bool Resolve (BlockContext bc)
386 var ok = base.Resolve (bc);
388 expr = expr.Resolve (bc);
393 protected override void DoEmit (EmitContext ec)
395 Label loop = ec.DefineLabel ();
396 Label old_begin = ec.LoopBegin;
397 Label old_end = ec.LoopEnd;
399 ec.LoopBegin = ec.DefineLabel ();
400 ec.LoopEnd = ec.DefineLabel ();
404 ec.MarkLabel (ec.LoopBegin);
406 // Mark start of while condition
407 ec.Mark (WhileLocation);
410 // Dead code elimination
412 if (expr is Constant) {
413 bool res = !((Constant) expr).IsDefaultValue;
415 expr.EmitSideEffect (ec);
417 ec.Emit (OpCodes.Br, loop);
419 expr.EmitBranchable (ec, loop, true);
422 ec.MarkLabel (ec.LoopEnd);
424 ec.LoopBegin = old_begin;
425 ec.LoopEnd = old_end;
428 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
430 var res = Statement.FlowAnalysis (fc);
432 expr.FlowAnalysisConditional (fc);
434 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
436 if (res && !iterator_reachable)
437 return !end_reachable;
439 if (!end_reachable) {
440 var c = expr as Constant;
441 if (c != null && !c.IsDefaultValue)
448 public override Reachability MarkReachable (Reachability rc)
450 base.MarkReachable (rc);
452 var body_rc = Statement.MarkReachable (rc);
454 if (body_rc.IsUnreachable && !iterator_reachable) {
455 expr = new UnreachableExpression (expr);
456 return end_reachable ? rc : Reachability.CreateUnreachable ();
459 if (!end_reachable) {
460 var c = expr as Constant;
461 if (c != null && !c.IsDefaultValue)
462 return Reachability.CreateUnreachable ();
468 protected override void CloneTo (CloneContext clonectx, Statement t)
472 target.Statement = Statement.Clone (clonectx);
473 target.expr = expr.Clone (clonectx);
476 public override object Accept (StructuralVisitor visitor)
478 return visitor.Visit (this);
481 public override void SetEndReachable ()
483 end_reachable = true;
486 public override void SetIteratorReachable ()
488 iterator_reachable = true;
492 public class While : LoopStatement
494 public Expression expr;
495 bool empty, infinite, end_reachable;
496 List<DefiniteAssignmentBitSet> end_reachable_das;
498 public While (BooleanExpression bool_expr, Statement statement, Location l)
501 this.expr = bool_expr;
505 public override bool Resolve (BlockContext bc)
509 expr = expr.Resolve (bc);
513 var c = expr as Constant;
515 empty = c.IsDefaultValue;
519 ok &= base.Resolve (bc);
523 protected override void DoEmit (EmitContext ec)
526 expr.EmitSideEffect (ec);
530 Label old_begin = ec.LoopBegin;
531 Label old_end = ec.LoopEnd;
533 ec.LoopBegin = ec.DefineLabel ();
534 ec.LoopEnd = ec.DefineLabel ();
537 // Inform whether we are infinite or not
539 if (expr is Constant) {
540 // expr is 'true', since the 'empty' case above handles the 'false' case
541 ec.MarkLabel (ec.LoopBegin);
543 if (ec.EmitAccurateDebugInfo)
544 ec.Emit (OpCodes.Nop);
546 expr.EmitSideEffect (ec);
548 ec.Emit (OpCodes.Br, ec.LoopBegin);
551 // Inform that we are infinite (ie, `we return'), only
552 // if we do not `break' inside the code.
554 ec.MarkLabel (ec.LoopEnd);
556 Label while_loop = ec.DefineLabel ();
558 ec.Emit (OpCodes.Br, ec.LoopBegin);
559 ec.MarkLabel (while_loop);
563 ec.MarkLabel (ec.LoopBegin);
566 expr.EmitBranchable (ec, while_loop, true);
568 ec.MarkLabel (ec.LoopEnd);
571 ec.LoopBegin = old_begin;
572 ec.LoopEnd = old_end;
575 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
577 expr.FlowAnalysisConditional (fc);
579 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
580 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
582 Statement.FlowAnalysis (fc);
585 // Special case infinite while with breaks
587 if (end_reachable_das != null) {
588 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
589 end_reachable_das = null;
592 fc.DefiniteAssignment = da_false;
594 if (infinite && !end_reachable)
600 public override Reachability MarkReachable (Reachability rc)
602 if (rc.IsUnreachable)
605 base.MarkReachable (rc);
608 // Special case unreachable while body
611 Statement.MarkReachable (Reachability.CreateUnreachable ());
615 Statement.MarkReachable (rc);
618 // When infinite while end is unreachable via break anything what follows is unreachable too
620 if (infinite && !end_reachable)
621 return Reachability.CreateUnreachable ();
626 protected override void CloneTo (CloneContext clonectx, Statement t)
628 While target = (While) t;
630 target.expr = expr.Clone (clonectx);
631 target.Statement = Statement.Clone (clonectx);
634 public override object Accept (StructuralVisitor visitor)
636 return visitor.Visit (this);
639 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
644 if (end_reachable_das == null)
645 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
647 end_reachable_das.Add (fc.DefiniteAssignment);
650 public override void SetEndReachable ()
652 end_reachable = true;
656 public class For : LoopStatement
658 bool infinite, empty, iterator_reachable, end_reachable;
659 List<DefiniteAssignmentBitSet> end_reachable_das;
661 public For (Location l)
667 public Statement Initializer {
671 public Expression Condition {
675 public Statement Iterator {
679 public override bool Resolve (BlockContext bc)
681 Initializer.Resolve (bc);
683 if (Condition != null) {
684 Condition = Condition.Resolve (bc);
685 var condition_constant = Condition as Constant;
686 if (condition_constant != null) {
687 if (condition_constant.IsDefaultValue) {
697 return base.Resolve (bc) && Iterator.Resolve (bc);
700 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
702 Initializer.FlowAnalysis (fc);
704 DefiniteAssignmentBitSet da_false;
705 if (Condition != null) {
706 Condition.FlowAnalysisConditional (fc);
707 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
708 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
710 da_false = fc.BranchDefiniteAssignment ();
713 Statement.FlowAnalysis (fc);
715 Iterator.FlowAnalysis (fc);
718 // Special case infinite for with breaks
720 if (end_reachable_das != null) {
721 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
722 end_reachable_das = null;
725 fc.DefiniteAssignment = da_false;
727 if (infinite && !end_reachable)
733 public override Reachability MarkReachable (Reachability rc)
735 base.MarkReachable (rc);
737 Initializer.MarkReachable (rc);
739 var body_rc = Statement.MarkReachable (rc);
740 if (!body_rc.IsUnreachable || iterator_reachable) {
741 Iterator.MarkReachable (rc);
745 // When infinite for end is unreachable via break anything what follows is unreachable too
747 if (infinite && !end_reachable) {
748 return Reachability.CreateUnreachable ();
754 protected override void DoEmit (EmitContext ec)
756 if (Initializer != null)
757 Initializer.Emit (ec);
760 Condition.EmitSideEffect (ec);
764 Label old_begin = ec.LoopBegin;
765 Label old_end = ec.LoopEnd;
766 Label loop = ec.DefineLabel ();
767 Label test = ec.DefineLabel ();
769 ec.LoopBegin = ec.DefineLabel ();
770 ec.LoopEnd = ec.DefineLabel ();
772 ec.Emit (OpCodes.Br, test);
776 ec.MarkLabel (ec.LoopBegin);
781 // If test is null, there is no test, and we are just
784 if (Condition != null) {
785 ec.Mark (Condition.Location);
788 // The Resolve code already catches the case for
789 // Test == Constant (false) so we know that
792 if (Condition is Constant) {
793 Condition.EmitSideEffect (ec);
794 ec.Emit (OpCodes.Br, loop);
796 Condition.EmitBranchable (ec, loop, true);
800 ec.Emit (OpCodes.Br, loop);
801 ec.MarkLabel (ec.LoopEnd);
803 ec.LoopBegin = old_begin;
804 ec.LoopEnd = old_end;
807 protected override void CloneTo (CloneContext clonectx, Statement t)
809 For target = (For) t;
811 if (Initializer != null)
812 target.Initializer = Initializer.Clone (clonectx);
813 if (Condition != null)
814 target.Condition = Condition.Clone (clonectx);
815 if (Iterator != null)
816 target.Iterator = Iterator.Clone (clonectx);
817 target.Statement = Statement.Clone (clonectx);
820 public override object Accept (StructuralVisitor visitor)
822 return visitor.Visit (this);
825 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
830 if (end_reachable_das == null)
831 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
833 end_reachable_das.Add (fc.DefiniteAssignment);
836 public override void SetEndReachable ()
838 end_reachable = true;
841 public override void SetIteratorReachable ()
843 iterator_reachable = true;
847 public abstract class LoopStatement : Statement
849 protected LoopStatement (Statement statement)
851 Statement = statement;
854 public Statement Statement { get; set; }
856 public override bool Resolve (BlockContext bc)
858 var prev_loop = bc.EnclosingLoop;
859 var prev_los = bc.EnclosingLoopOrSwitch;
860 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
861 var ok = Statement.Resolve (bc);
862 bc.EnclosingLoopOrSwitch = prev_los;
863 bc.EnclosingLoop = prev_loop;
869 // Needed by possibly infinite loops statements (for, while) and switch statment
871 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
875 public virtual void SetEndReachable ()
879 public virtual void SetIteratorReachable ()
884 public class StatementExpression : Statement
886 ExpressionStatement expr;
888 public StatementExpression (ExpressionStatement expr)
891 loc = expr.StartLocation;
894 public StatementExpression (ExpressionStatement expr, Location loc)
900 public ExpressionStatement Expr {
906 protected override void CloneTo (CloneContext clonectx, Statement t)
908 StatementExpression target = (StatementExpression) t;
909 target.expr = (ExpressionStatement) expr.Clone (clonectx);
912 protected override void DoEmit (EmitContext ec)
914 expr.EmitStatement (ec);
917 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
919 expr.FlowAnalysis (fc);
923 public override Reachability MarkReachable (Reachability rc)
925 base.MarkReachable (rc);
926 expr.MarkReachable (rc);
930 public override bool Resolve (BlockContext ec)
932 expr = expr.ResolveStatement (ec);
936 public override object Accept (StructuralVisitor visitor)
938 return visitor.Visit (this);
942 public class StatementErrorExpression : Statement
946 public StatementErrorExpression (Expression expr)
949 this.loc = expr.StartLocation;
952 public Expression Expr {
958 public override bool Resolve (BlockContext bc)
960 expr.Error_InvalidExpressionStatement (bc);
964 protected override void DoEmit (EmitContext ec)
966 throw new NotSupportedException ();
969 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
974 protected override void CloneTo (CloneContext clonectx, Statement target)
976 var t = (StatementErrorExpression) target;
978 t.expr = expr.Clone (clonectx);
981 public override object Accept (StructuralVisitor visitor)
983 return visitor.Visit (this);
988 // Simple version of statement list not requiring a block
990 public class StatementList : Statement
992 List<Statement> statements;
994 public StatementList (Statement first, Statement second)
996 statements = new List<Statement> { first, second };
1000 public IList<Statement> Statements {
1007 public void Add (Statement statement)
1009 statements.Add (statement);
1012 public override bool Resolve (BlockContext ec)
1014 foreach (var s in statements)
1020 protected override void DoEmit (EmitContext ec)
1022 foreach (var s in statements)
1026 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1028 foreach (var s in statements)
1029 s.FlowAnalysis (fc);
1034 public override Reachability MarkReachable (Reachability rc)
1036 base.MarkReachable (rc);
1038 Reachability res = rc;
1039 foreach (var s in statements)
1040 res = s.MarkReachable (rc);
1045 protected override void CloneTo (CloneContext clonectx, Statement target)
1047 StatementList t = (StatementList) target;
1049 t.statements = new List<Statement> (statements.Count);
1050 foreach (Statement s in statements)
1051 t.statements.Add (s.Clone (clonectx));
1054 public override object Accept (StructuralVisitor visitor)
1056 return visitor.Visit (this);
1061 // For statements which require special handling when inside try or catch block
1063 public abstract class ExitStatement : Statement
1065 protected bool unwind_protect;
1067 protected abstract bool DoResolve (BlockContext bc);
1068 protected abstract bool IsLocalExit { get; }
1070 public override bool Resolve (BlockContext bc)
1072 var res = DoResolve (bc);
1076 // We are inside finally scope but is it the scope we are exiting
1078 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1080 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1081 if (b.IsFinallyBlock) {
1082 Error_FinallyClauseExit (bc);
1086 if (b is ParametersBlock)
1092 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1096 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1101 if (fc.TryFinally != null) {
1102 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1104 fc.ParametersBlock.CheckControlExit (fc);
1112 /// Implements the return statement
1114 public class Return : ExitStatement
1119 public Return (Expression expr, Location l)
1127 public Expression Expr {
1136 protected override bool IsLocalExit {
1144 protected override bool DoResolve (BlockContext ec)
1146 var block_return_type = ec.ReturnType;
1149 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1153 // Return must not be followed by an expression when
1154 // the method return type is Task
1156 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1157 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1158 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1160 // Extra trick not to emit ret/leave inside awaiter body
1162 expr = EmptyExpression.Null;
1166 if (storey.ReturnType.IsGenericTask)
1167 block_return_type = storey.ReturnType.TypeArguments[0];
1170 if (ec.CurrentIterator != null) {
1171 Error_ReturnFromIterator (ec);
1172 } else if (block_return_type != InternalType.ErrorType) {
1173 ec.Report.Error (126, loc,
1174 "An object of a type convertible to `{0}' is required for the return statement",
1175 block_return_type.GetSignatureForError ());
1181 expr = expr.Resolve (ec);
1183 AnonymousExpression am = ec.CurrentAnonymousMethod;
1185 if (block_return_type.Kind == MemberKind.Void) {
1186 ec.Report.Error (127, loc,
1187 "`{0}': A return keyword must not be followed by any expression when method returns void",
1188 ec.GetSignatureForError ());
1193 if (am.IsIterator) {
1194 Error_ReturnFromIterator (ec);
1198 var async_block = am as AsyncInitializer;
1199 if (async_block != null) {
1201 var storey = (AsyncTaskStorey) am.Storey;
1202 var async_type = storey.ReturnType;
1204 if (async_type == null && async_block.ReturnTypeInference != null) {
1205 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1206 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1208 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1212 if (async_type.Kind == MemberKind.Void) {
1213 ec.Report.Error (8030, loc,
1214 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1218 if (!async_type.IsGenericTask) {
1219 if (this is ContextualReturn)
1222 if (async_block.DelegateType != null) {
1223 ec.Report.Error (8031, loc,
1224 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1226 ec.Report.Error (1997, loc,
1227 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1228 ec.GetSignatureForError ());
1234 // The return type is actually Task<T> type argument
1236 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1237 ec.Report.Error (4016, loc,
1238 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1239 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1241 block_return_type = async_type.TypeArguments[0];
1245 if (block_return_type.Kind == MemberKind.Void) {
1246 ec.Report.Error (8030, loc,
1247 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1251 var l = am as AnonymousMethodBody;
1252 if (l != null && expr != null) {
1253 if (l.ReturnTypeInference != null) {
1254 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1259 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1260 // unexpected debugging experience
1262 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1263 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1272 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1273 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1276 if (am != null && block_return_type == ec.ReturnType) {
1277 ec.Report.Error (1662, loc,
1278 "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",
1279 am.ContainerType, am.GetSignatureForError ());
1288 protected override void DoEmit (EmitContext ec)
1292 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1293 if (async_body != null) {
1294 var storey = (AsyncTaskStorey)async_body.Storey;
1295 Label exit_label = async_body.BodyEnd;
1298 // It's null for await without async
1300 if (storey.HoistedReturnValue != null) {
1302 // Special case hoisted return value (happens in try/finally scenario)
1304 if (ec.TryFinallyUnwind != null) {
1305 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1308 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1309 async_return.EmitAssign (ec, expr, false, false);
1314 if (ec.TryFinallyUnwind != null)
1315 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1318 ec.Emit (OpCodes.Leave, exit_label);
1325 if (unwind_protect || ec.EmitAccurateDebugInfo)
1326 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1329 if (unwind_protect) {
1330 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1331 } else if (ec.EmitAccurateDebugInfo) {
1332 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1334 ec.Emit (OpCodes.Ret);
1338 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1341 expr.FlowAnalysis (fc);
1344 base.DoFlowAnalysis (fc);
1349 void Error_ReturnFromIterator (ResolveContext rc)
1351 rc.Report.Error (1622, loc,
1352 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1355 public override Reachability MarkReachable (Reachability rc)
1357 base.MarkReachable (rc);
1360 rc = Expr.MarkReachable (rc);
1361 expr_returns = rc.IsUnreachable;
1364 return Reachability.CreateUnreachable ();
1367 protected override void CloneTo (CloneContext clonectx, Statement t)
1369 Return target = (Return) t;
1370 // It's null for simple return;
1372 target.expr = expr.Clone (clonectx);
1375 public override object Accept (StructuralVisitor visitor)
1377 return visitor.Visit (this);
1381 public class Goto : ExitStatement
1384 LabeledStatement label;
1385 TryFinally try_finally;
1387 public Goto (string label, Location l)
1393 public string Target {
1394 get { return target; }
1397 protected override bool IsLocalExit {
1403 protected override bool DoResolve (BlockContext bc)
1405 label = bc.CurrentBlock.LookupLabel (target);
1406 if (label == null) {
1407 Error_UnknownLabel (bc, target, loc);
1411 try_finally = bc.CurrentTryBlock as TryFinally;
1413 CheckExitBoundaries (bc, label.Block);
1418 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1420 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1424 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1426 // Goto to unreachable label
1430 if (fc.AddReachedLabel (label))
1433 label.Block.ScanGotoJump (label, fc);
1437 public override Reachability MarkReachable (Reachability rc)
1439 if (rc.IsUnreachable)
1442 base.MarkReachable (rc);
1444 if (try_finally != null) {
1445 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1446 label.AddGotoReference (rc);
1451 label.AddGotoReference (rc);
1454 return Reachability.CreateUnreachable ();
1457 protected override void CloneTo (CloneContext clonectx, Statement target)
1462 protected override void DoEmit (EmitContext ec)
1464 // This should only happen for goto from try block to unrechable label
1468 Label l = label.LabelTarget (ec);
1470 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1471 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1472 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1475 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1478 bool IsLeavingFinally (Block labelBlock)
1480 var b = try_finally.Statement as Block;
1482 if (b == labelBlock)
1491 public override object Accept (StructuralVisitor visitor)
1493 return visitor.Visit (this);
1497 public class LabeledStatement : Statement {
1504 public LabeledStatement (string name, Block block, Location l)
1511 public Label LabelTarget (EmitContext ec)
1516 label = ec.DefineLabel ();
1521 public Block Block {
1527 public string Name {
1528 get { return name; }
1531 protected override void CloneTo (CloneContext clonectx, Statement target)
1533 var t = (LabeledStatement) target;
1535 t.block = clonectx.RemapBlockCopy (block);
1538 public override bool Resolve (BlockContext bc)
1543 protected override void DoEmit (EmitContext ec)
1546 ec.MarkLabel (label);
1549 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1552 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1558 public override Reachability MarkReachable (Reachability rc)
1560 base.MarkReachable (rc);
1563 rc = new Reachability ();
1568 public void AddGotoReference (Reachability rc)
1576 block.ScanGotoJump (this);
1579 public override object Accept (StructuralVisitor visitor)
1581 return visitor.Visit (this);
1587 /// `goto default' statement
1589 public class GotoDefault : SwitchGoto
1591 public GotoDefault (Location l)
1596 public override bool Resolve (BlockContext bc)
1598 if (bc.Switch == null) {
1599 Error_GotoCaseRequiresSwitchBlock (bc);
1603 bc.Switch.RegisterGotoCase (null, null);
1609 protected override void DoEmit (EmitContext ec)
1611 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1614 public override Reachability MarkReachable (Reachability rc)
1616 if (!rc.IsUnreachable) {
1617 var label = switch_statement.DefaultLabel;
1618 if (label.IsUnreachable) {
1619 label.MarkReachable (rc);
1620 switch_statement.Block.ScanGotoJump (label);
1624 return base.MarkReachable (rc);
1627 public override object Accept (StructuralVisitor visitor)
1629 return visitor.Visit (this);
1634 /// `goto case' statement
1636 public class GotoCase : SwitchGoto
1640 public GotoCase (Expression e, Location l)
1646 public Expression Expr {
1652 public SwitchLabel Label { get; set; }
1654 public override bool Resolve (BlockContext ec)
1656 if (ec.Switch == null) {
1657 Error_GotoCaseRequiresSwitchBlock (ec);
1661 Constant c = expr.ResolveLabelConstant (ec);
1667 if (ec.Switch.IsNullable && c is NullLiteral) {
1670 TypeSpec type = ec.Switch.SwitchType;
1671 res = c.Reduce (ec, type);
1673 c.Error_ValueCannotBeConverted (ec, type, true);
1677 if (!Convert.ImplicitStandardConversionExists (c, type))
1678 ec.Report.Warning (469, 2, loc,
1679 "The `goto case' value is not implicitly convertible to type `{0}'",
1680 type.GetSignatureForError ());
1684 ec.Switch.RegisterGotoCase (this, res);
1691 protected override void DoEmit (EmitContext ec)
1693 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1696 protected override void CloneTo (CloneContext clonectx, Statement t)
1698 GotoCase target = (GotoCase) t;
1700 target.expr = expr.Clone (clonectx);
1703 public override Reachability MarkReachable (Reachability rc)
1705 if (!rc.IsUnreachable) {
1706 var label = switch_statement.FindLabel ((Constant) expr);
1707 if (label.IsUnreachable) {
1708 label.MarkReachable (rc);
1709 switch_statement.Block.ScanGotoJump (label);
1713 return base.MarkReachable (rc);
1716 public override object Accept (StructuralVisitor visitor)
1718 return visitor.Visit (this);
1722 public abstract class SwitchGoto : Statement
1724 protected bool unwind_protect;
1725 protected Switch switch_statement;
1727 protected SwitchGoto (Location loc)
1732 protected override void CloneTo (CloneContext clonectx, Statement target)
1737 public override bool Resolve (BlockContext bc)
1739 CheckExitBoundaries (bc, bc.Switch.Block);
1741 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1742 switch_statement = bc.Switch;
1747 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1752 public override Reachability MarkReachable (Reachability rc)
1754 base.MarkReachable (rc);
1755 return Reachability.CreateUnreachable ();
1758 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1760 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1764 public class Throw : Statement {
1767 public Throw (Expression expr, Location l)
1773 public Expression Expr {
1779 public static Expression ConvertType (ResolveContext rc, Expression expr)
1781 var et = rc.BuiltinTypes.Exception;
1782 if (Convert.ImplicitConversionExists (rc, expr, et))
1783 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1785 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1786 expr = EmptyCast.Create (expr, et);
1792 public override bool Resolve (BlockContext ec)
1795 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1796 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1797 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1798 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1799 if (b.IsFinallyBlock) {
1800 ec.Report.Error (724, loc,
1801 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1810 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1815 expr = ConvertType (ec, expr);
1820 protected override void DoEmit (EmitContext ec)
1823 var atv = ec.AsyncThrowVariable;
1825 if (atv.HoistedVariant != null) {
1826 atv.HoistedVariant.Emit (ec);
1831 ec.Emit (OpCodes.Throw);
1833 ec.Emit (OpCodes.Rethrow);
1838 ec.Emit (OpCodes.Throw);
1842 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1845 expr.FlowAnalysis (fc);
1850 public override Reachability MarkReachable (Reachability rc)
1852 base.MarkReachable (rc);
1853 return Reachability.CreateUnreachable ();
1856 protected override void CloneTo (CloneContext clonectx, Statement t)
1858 Throw target = (Throw) t;
1861 target.expr = expr.Clone (clonectx);
1864 public override object Accept (StructuralVisitor visitor)
1866 return visitor.Visit (this);
1870 public class Break : LocalExitStatement
1872 public Break (Location l)
1877 public override object Accept (StructuralVisitor visitor)
1879 return visitor.Visit (this);
1882 protected override void DoEmit (EmitContext ec)
1886 if (ec.TryFinallyUnwind != null) {
1887 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1888 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1891 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1894 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1896 enclosing_loop.AddEndDefiniteAssignment (fc);
1900 protected override bool DoResolve (BlockContext bc)
1902 enclosing_loop = bc.EnclosingLoopOrSwitch;
1903 return base.DoResolve (bc);
1906 public override Reachability MarkReachable (Reachability rc)
1908 base.MarkReachable (rc);
1910 if (!rc.IsUnreachable)
1911 enclosing_loop.SetEndReachable ();
1913 return Reachability.CreateUnreachable ();
1917 public class Continue : LocalExitStatement
1919 public Continue (Location l)
1924 public override object Accept (StructuralVisitor visitor)
1926 return visitor.Visit (this);
1930 protected override void DoEmit (EmitContext ec)
1932 var l = ec.LoopBegin;
1934 if (ec.TryFinallyUnwind != null) {
1935 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1936 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1939 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1942 protected override bool DoResolve (BlockContext bc)
1944 enclosing_loop = bc.EnclosingLoop;
1945 return base.DoResolve (bc);
1948 public override Reachability MarkReachable (Reachability rc)
1950 base.MarkReachable (rc);
1952 if (!rc.IsUnreachable)
1953 enclosing_loop.SetIteratorReachable ();
1955 return Reachability.CreateUnreachable ();
1959 public abstract class LocalExitStatement : ExitStatement
1961 protected LoopStatement enclosing_loop;
1963 protected LocalExitStatement (Location loc)
1968 protected override bool IsLocalExit {
1974 protected override void CloneTo (CloneContext clonectx, Statement t)
1979 protected override bool DoResolve (BlockContext bc)
1981 if (enclosing_loop == null) {
1982 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1986 var block = enclosing_loop.Statement as Block;
1988 // Don't need to do extra checks for simple statements loops
1989 if (block != null) {
1990 CheckExitBoundaries (bc, block);
1997 public interface ILocalVariable
1999 void Emit (EmitContext ec);
2000 void EmitAssign (EmitContext ec);
2001 void EmitAddressOf (EmitContext ec);
2004 public interface INamedBlockVariable
2006 Block Block { get; }
2007 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2008 bool IsDeclared { get; }
2009 bool IsParameter { get; }
2010 Location Location { get; }
2013 public class BlockVariableDeclarator
2016 Expression initializer;
2018 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2020 if (li.Type != null)
2021 throw new ArgumentException ("Expected null variable type");
2024 this.initializer = initializer;
2029 public LocalVariable Variable {
2035 public Expression Initializer {
2040 initializer = value;
2046 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2048 var t = (BlockVariableDeclarator) MemberwiseClone ();
2049 if (initializer != null)
2050 t.initializer = initializer.Clone (cloneCtx);
2056 public class BlockVariable : Statement
2058 Expression initializer;
2059 protected FullNamedExpression type_expr;
2060 protected LocalVariable li;
2061 protected List<BlockVariableDeclarator> declarators;
2064 public BlockVariable (FullNamedExpression type, LocalVariable li)
2066 this.type_expr = type;
2068 this.loc = type_expr.Location;
2071 protected BlockVariable (LocalVariable li)
2078 public List<BlockVariableDeclarator> Declarators {
2084 public Expression Initializer {
2089 initializer = value;
2093 public FullNamedExpression TypeExpression {
2099 public LocalVariable Variable {
2107 public void AddDeclarator (BlockVariableDeclarator decl)
2109 if (declarators == null)
2110 declarators = new List<BlockVariableDeclarator> ();
2112 declarators.Add (decl);
2115 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2117 if (bc.Report.Errors != 0)
2120 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2122 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2123 new MemberName (li.Name, li.Location), null);
2125 container.AddField (f);
2128 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2132 public override bool Resolve (BlockContext bc)
2134 return Resolve (bc, true);
2137 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2139 if (type == null && !li.IsCompilerGenerated) {
2140 var vexpr = type_expr as VarExpr;
2143 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2144 // same name exists or as a keyword when no type was found
2146 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2147 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2148 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2151 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2155 if (li.IsConstant) {
2156 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2160 if (Initializer == null) {
2161 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2165 if (declarators != null) {
2166 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2170 Initializer = Initializer.Resolve (bc);
2171 if (Initializer != null) {
2172 ((VarExpr) type_expr).InferType (bc, Initializer);
2173 type = type_expr.Type;
2175 // Set error type to indicate the var was placed correctly but could
2178 // var a = missing ();
2180 type = InternalType.ErrorType;
2185 type = type_expr.ResolveAsType (bc);
2189 if (li.IsConstant && !type.IsConstantCompatible) {
2190 Const.Error_InvalidConstantType (type, loc, bc.Report);
2195 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2200 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2202 CreateEvaluatorVariable (bc, li);
2203 } else if (type != InternalType.ErrorType) {
2204 li.PrepareAssignmentAnalysis (bc);
2207 if (initializer != null) {
2208 initializer = ResolveInitializer (bc, li, initializer);
2209 // li.Variable.DefinitelyAssigned
2212 if (declarators != null) {
2213 foreach (var d in declarators) {
2214 d.Variable.Type = li.Type;
2216 CreateEvaluatorVariable (bc, d.Variable);
2217 } else if (type != InternalType.ErrorType) {
2218 d.Variable.PrepareAssignmentAnalysis (bc);
2221 if (d.Initializer != null && resolveDeclaratorInitializers) {
2222 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2223 // d.Variable.DefinitelyAssigned
2231 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2233 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2234 return a.ResolveStatement (bc);
2237 protected override void DoEmit (EmitContext ec)
2239 li.CreateBuilder (ec);
2241 if (Initializer != null && !IsUnreachable)
2242 ((ExpressionStatement) Initializer).EmitStatement (ec);
2244 if (declarators != null) {
2245 foreach (var d in declarators) {
2246 d.Variable.CreateBuilder (ec);
2247 if (d.Initializer != null && !IsUnreachable) {
2248 ec.Mark (d.Variable.Location);
2249 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2255 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2257 if (Initializer != null)
2258 Initializer.FlowAnalysis (fc);
2260 if (declarators != null) {
2261 foreach (var d in declarators) {
2262 if (d.Initializer != null)
2263 d.Initializer.FlowAnalysis (fc);
2270 public override Reachability MarkReachable (Reachability rc)
2272 base.MarkReachable (rc);
2273 return initializer == null ? rc : initializer.MarkReachable (rc);
2276 protected override void CloneTo (CloneContext clonectx, Statement target)
2278 BlockVariable t = (BlockVariable) target;
2280 if (type_expr != null)
2281 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2283 if (initializer != null)
2284 t.initializer = initializer.Clone (clonectx);
2286 if (declarators != null) {
2287 t.declarators = null;
2288 foreach (var d in declarators)
2289 t.AddDeclarator (d.Clone (clonectx));
2293 public override object Accept (StructuralVisitor visitor)
2295 return visitor.Visit (this);
2299 public class BlockConstant : BlockVariable
2301 public BlockConstant (FullNamedExpression type, LocalVariable li)
2306 public override void Emit (EmitContext ec)
2308 if (!Variable.IsUsed)
2309 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2311 // Nothing to emit, not even sequence point
2314 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2316 initializer = initializer.Resolve (bc);
2317 if (initializer == null)
2320 var c = initializer as Constant;
2322 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2326 c = c.ConvertImplicitly (li.Type);
2328 if (TypeSpec.IsReferenceType (li.Type))
2329 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2331 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2336 li.ConstantValue = c;
2340 public override object Accept (StructuralVisitor visitor)
2342 return visitor.Visit (this);
2347 // The information about a user-perceived local variable
2349 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2356 AddressTaken = 1 << 2,
2357 CompilerGenerated = 1 << 3,
2359 ForeachVariable = 1 << 5,
2360 FixedVariable = 1 << 6,
2361 UsingVariable = 1 << 7,
2363 SymbolFileHidden = 1 << 9,
2365 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2369 readonly string name;
2370 readonly Location loc;
2371 readonly Block block;
2373 Constant const_value;
2375 public VariableInfo VariableInfo;
2376 HoistedVariable hoisted_variant;
2378 LocalBuilder builder;
2380 public LocalVariable (Block block, string name, Location loc)
2387 public LocalVariable (Block block, string name, Flags flags, Location loc)
2388 : this (block, name, loc)
2394 // Used by variable declarators
2396 public LocalVariable (LocalVariable li, string name, Location loc)
2397 : this (li.block, name, li.flags, loc)
2403 public bool AddressTaken {
2405 return (flags & Flags.AddressTaken) != 0;
2409 public Block Block {
2415 public Constant ConstantValue {
2420 const_value = value;
2425 // Hoisted local variable variant
2427 public HoistedVariable HoistedVariant {
2429 return hoisted_variant;
2432 hoisted_variant = value;
2436 public bool IsDeclared {
2438 return type != null;
2442 public bool IsCompilerGenerated {
2444 return (flags & Flags.CompilerGenerated) != 0;
2448 public bool IsConstant {
2450 return (flags & Flags.Constant) != 0;
2454 public bool IsLocked {
2456 return (flags & Flags.IsLocked) != 0;
2459 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2463 public bool IsThis {
2465 return (flags & Flags.IsThis) != 0;
2469 public bool IsUsed {
2471 return (flags & Flags.Used) != 0;
2475 public bool IsFixed {
2477 return (flags & Flags.FixedVariable) != 0;
2480 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2484 bool INamedBlockVariable.IsParameter {
2490 public bool IsReadonly {
2492 return (flags & Flags.ReadonlyMask) != 0;
2496 public Location Location {
2502 public string Name {
2508 public TypeSpec Type {
2519 public void CreateBuilder (EmitContext ec)
2521 if ((flags & Flags.Used) == 0) {
2522 if (VariableInfo == null) {
2523 // Missing flow analysis or wrong variable flags
2524 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2527 if (VariableInfo.IsEverAssigned)
2528 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2530 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2533 if (HoistedVariant != null)
2536 if (builder != null) {
2537 if ((flags & Flags.CompilerGenerated) != 0)
2540 // To avoid Used warning duplicates
2541 throw new InternalErrorException ("Already created variable `{0}'", name);
2545 // All fixed variabled are pinned, a slot has to be alocated
2547 builder = ec.DeclareLocal (Type, IsFixed);
2548 if ((flags & Flags.SymbolFileHidden) == 0)
2549 ec.DefineLocalVariable (name, builder);
2552 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2554 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2555 if (!writeToSymbolFile)
2556 li.flags |= Flags.SymbolFileHidden;
2562 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2564 if (IsConstant && const_value != null) {
2566 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2569 return new LocalVariableReference (this, loc);
2572 public void Emit (EmitContext ec)
2574 // TODO: Need something better for temporary variables
2575 if ((flags & Flags.CompilerGenerated) != 0)
2578 ec.Emit (OpCodes.Ldloc, builder);
2581 public void EmitAssign (EmitContext ec)
2583 // TODO: Need something better for temporary variables
2584 if ((flags & Flags.CompilerGenerated) != 0)
2587 ec.Emit (OpCodes.Stloc, builder);
2590 public void EmitAddressOf (EmitContext ec)
2592 // TODO: Need something better for temporary variables
2593 if ((flags & Flags.CompilerGenerated) != 0)
2596 ec.Emit (OpCodes.Ldloca, builder);
2599 public static string GetCompilerGeneratedName (Block block)
2601 // HACK: Debugger depends on the name semantics
2602 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2605 public string GetReadOnlyContext ()
2607 switch (flags & Flags.ReadonlyMask) {
2608 case Flags.FixedVariable:
2609 return "fixed variable";
2610 case Flags.ForeachVariable:
2611 return "foreach iteration variable";
2612 case Flags.UsingVariable:
2613 return "using variable";
2616 throw new InternalErrorException ("Variable is not readonly");
2619 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2621 if (VariableInfo == null)
2622 throw new Exception ();
2624 if (IsAssigned (fc))
2627 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2630 public bool IsAssigned (FlowAnalysisContext fc)
2632 return fc.IsDefinitelyAssigned (VariableInfo);
2635 public void PrepareAssignmentAnalysis (BlockContext bc)
2638 // No need to run assignment analysis for these guys
2640 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2643 VariableInfo = VariableInfo.Create (bc, this);
2647 // Mark the variables as referenced in the user code
2649 public void SetIsUsed ()
2651 flags |= Flags.Used;
2654 public void SetHasAddressTaken ()
2656 flags |= (Flags.AddressTaken | Flags.Used);
2659 public override string ToString ()
2661 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2666 /// Block represents a C# block.
2670 /// This class is used in a number of places: either to represent
2671 /// explicit blocks that the programmer places or implicit blocks.
2673 /// Implicit blocks are used as labels or to introduce variable
2676 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2677 /// they contain extra information that is not necessary on normal blocks.
2679 public class Block : Statement {
2686 HasCapturedVariable = 64,
2687 HasCapturedThis = 1 << 7,
2688 IsExpressionTree = 1 << 8,
2689 CompilerGenerated = 1 << 9,
2690 HasAsyncModifier = 1 << 10,
2692 YieldBlock = 1 << 12,
2693 AwaitBlock = 1 << 13,
2694 FinallyBlock = 1 << 14,
2695 CatchBlock = 1 << 15,
2696 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2698 NoFlowAnalysis = 1 << 21,
2699 InitializationEmitted = 1 << 22
2702 public Block Parent;
2703 public Location StartLocation;
2704 public Location EndLocation;
2706 public ExplicitBlock Explicit;
2707 public ParametersBlock ParametersBlock;
2709 protected Flags flags;
2712 // The statements in this block
2714 protected List<Statement> statements;
2716 protected List<Statement> scope_initializers;
2718 int? resolving_init_idx;
2724 public int ID = id++;
2726 static int clone_id_counter;
2730 // int assignable_slots;
2732 public Block (Block parent, Location start, Location end)
2733 : this (parent, 0, start, end)
2737 public Block (Block parent, Flags flags, Location start, Location end)
2739 if (parent != null) {
2740 // the appropriate constructors will fixup these fields
2741 ParametersBlock = parent.ParametersBlock;
2742 Explicit = parent.Explicit;
2745 this.Parent = parent;
2747 this.StartLocation = start;
2748 this.EndLocation = end;
2750 statements = new List<Statement> (4);
2752 this.original = this;
2757 public Block Original {
2766 public bool IsCompilerGenerated {
2767 get { return (flags & Flags.CompilerGenerated) != 0; }
2768 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2772 public bool IsCatchBlock {
2774 return (flags & Flags.CatchBlock) != 0;
2778 public bool IsFinallyBlock {
2780 return (flags & Flags.FinallyBlock) != 0;
2784 public bool Unchecked {
2785 get { return (flags & Flags.Unchecked) != 0; }
2786 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2789 public bool Unsafe {
2790 get { return (flags & Flags.Unsafe) != 0; }
2791 set { flags |= Flags.Unsafe; }
2794 public List<Statement> Statements {
2795 get { return statements; }
2800 public void SetEndLocation (Location loc)
2805 public void AddLabel (LabeledStatement target)
2807 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2810 public void AddLocalName (LocalVariable li)
2812 AddLocalName (li.Name, li);
2815 public void AddLocalName (string name, INamedBlockVariable li)
2817 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2820 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2822 if (reason == null) {
2823 Error_AlreadyDeclared (name, variable);
2827 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2828 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2829 "to `{0}', which is already used in a `{1}' scope to denote something else",
2833 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2835 var pi = variable as ParametersBlock.ParameterInfo;
2837 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2839 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2840 "A local variable named `{0}' is already defined in this scope", name);
2844 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2846 ParametersBlock.TopBlock.Report.Error (412, loc,
2847 "The type parameter name `{0}' is the same as local variable or parameter name",
2852 // It should be used by expressions which require to
2853 // register a statement during resolve process.
2855 public void AddScopeStatement (Statement s)
2857 if (scope_initializers == null)
2858 scope_initializers = new List<Statement> ();
2861 // Simple recursive helper, when resolve scope initializer another
2862 // new scope initializer can be added, this ensures it's initialized
2863 // before existing one. For now this can happen with expression trees
2864 // in base ctor initializer only
2866 if (resolving_init_idx.HasValue) {
2867 scope_initializers.Insert (resolving_init_idx.Value, s);
2868 ++resolving_init_idx;
2870 scope_initializers.Add (s);
2874 public void InsertStatement (int index, Statement s)
2876 statements.Insert (index, s);
2879 public void AddStatement (Statement s)
2884 public LabeledStatement LookupLabel (string name)
2886 return ParametersBlock.GetLabel (name, this);
2889 public override Reachability MarkReachable (Reachability rc)
2891 if (rc.IsUnreachable)
2894 MarkReachableScope (rc);
2896 foreach (var s in statements) {
2897 rc = s.MarkReachable (rc);
2898 if (rc.IsUnreachable) {
2899 if ((flags & Flags.ReachableEnd) != 0)
2900 return new Reachability ();
2906 flags |= Flags.ReachableEnd;
2911 public void MarkReachableScope (Reachability rc)
2913 base.MarkReachable (rc);
2915 if (scope_initializers != null) {
2916 foreach (var si in scope_initializers)
2917 si.MarkReachable (rc);
2921 public override bool Resolve (BlockContext bc)
2923 if ((flags & Flags.Resolved) != 0)
2926 Block prev_block = bc.CurrentBlock;
2927 bc.CurrentBlock = this;
2930 // Compiler generated scope statements
2932 if (scope_initializers != null) {
2933 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2934 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2937 resolving_init_idx = null;
2941 int statement_count = statements.Count;
2942 for (int ix = 0; ix < statement_count; ix++){
2943 Statement s = statements [ix];
2945 if (!s.Resolve (bc)) {
2947 statements [ix] = new EmptyStatement (s.loc);
2952 bc.CurrentBlock = prev_block;
2954 flags |= Flags.Resolved;
2958 protected override void DoEmit (EmitContext ec)
2960 for (int ix = 0; ix < statements.Count; ix++){
2961 statements [ix].Emit (ec);
2965 public override void Emit (EmitContext ec)
2967 if (scope_initializers != null)
2968 EmitScopeInitializers (ec);
2973 protected void EmitScopeInitializers (EmitContext ec)
2975 foreach (Statement s in scope_initializers)
2979 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2981 if (scope_initializers != null) {
2982 foreach (var si in scope_initializers)
2983 si.FlowAnalysis (fc);
2986 return DoFlowAnalysis (fc, 0);
2989 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2991 bool end_unreachable = !reachable;
2992 bool goto_flow_analysis = startIndex != 0;
2993 for (; startIndex < statements.Count; ++startIndex) {
2994 var s = statements[startIndex];
2996 end_unreachable = s.FlowAnalysis (fc);
2997 if (s.IsUnreachable) {
2998 statements [startIndex] = RewriteUnreachableStatement (s);
3003 // Statement end reachability is needed mostly due to goto support. Consider
3012 // X label is reachable only via goto not as another statement after if. We need
3013 // this for flow-analysis only to carry variable info correctly.
3015 if (end_unreachable) {
3016 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3018 var f = s as TryFinally;
3019 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3021 // Special case for try-finally with unreachable code after
3022 // finally block. Try block has to include leave opcode but there is
3023 // no label to leave to after unreachable finally block closing
3024 // brace. This sentinel ensures there is always IL instruction to
3025 // leave to even if we know it'll never be reached.
3027 statements.Insert (startIndex + 1, new SentinelStatement ());
3029 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3030 s = statements [startIndex];
3031 if (s is SwitchLabel) {
3032 if (!after_goto_case)
3033 s.FlowAnalysis (fc);
3038 if (s.IsUnreachable) {
3039 s.FlowAnalysis (fc);
3040 statements [startIndex] = RewriteUnreachableStatement (s);
3046 // Idea is to stop after goto case because goto case will always have at least same
3047 // variable assigned as switch case label. This saves a lot for complex goto case tests
3049 if (after_goto_case)
3055 var lb = s as LabeledStatement;
3056 if (lb != null && fc.AddReachedLabel (lb))
3061 // The condition should be true unless there is forward jumping goto
3063 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3066 return !Explicit.HasReachableClosingBrace;
3069 static Statement RewriteUnreachableStatement (Statement s)
3071 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3072 // analysis. Even csc report unreachable warning for it but it's actually used hence
3073 // we try to emulate this behaviour
3081 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3084 return new EmptyStatement (s.loc);
3087 public void ScanGotoJump (Statement label)
3090 for (i = 0; i < statements.Count; ++i) {
3091 if (statements[i] == label)
3095 var rc = new Reachability ();
3096 for (++i; i < statements.Count; ++i) {
3097 var s = statements[i];
3098 rc = s.MarkReachable (rc);
3099 if (rc.IsUnreachable)
3103 flags |= Flags.ReachableEnd;
3106 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3109 for (i = 0; i < statements.Count; ++i) {
3110 if (statements[i] == label)
3114 DoFlowAnalysis (fc, ++i);
3118 public override string ToString ()
3120 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3124 protected override void CloneTo (CloneContext clonectx, Statement t)
3126 Block target = (Block) t;
3128 target.clone_id = ++clone_id_counter;
3131 clonectx.AddBlockMap (this, target);
3132 if (original != this)
3133 clonectx.AddBlockMap (original, target);
3135 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3136 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3139 target.Parent = clonectx.RemapBlockCopy (Parent);
3141 target.statements = new List<Statement> (statements.Count);
3142 foreach (Statement s in statements)
3143 target.statements.Add (s.Clone (clonectx));
3146 public override object Accept (StructuralVisitor visitor)
3148 return visitor.Visit (this);
3152 public class ExplicitBlock : Block
3154 protected AnonymousMethodStorey am_storey;
3155 int debug_scope_index;
3157 public ExplicitBlock (Block parent, Location start, Location end)
3158 : this (parent, (Flags) 0, start, end)
3162 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3163 : base (parent, flags, start, end)
3165 this.Explicit = this;
3170 public AnonymousMethodStorey AnonymousMethodStorey {
3176 public bool HasAwait {
3178 return (flags & Flags.AwaitBlock) != 0;
3182 public bool HasCapturedThis {
3184 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3187 return (flags & Flags.HasCapturedThis) != 0;
3192 // Used to indicate that the block has reference to parent
3193 // block and cannot be made static when defining anonymous method
3195 public bool HasCapturedVariable {
3197 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3200 return (flags & Flags.HasCapturedVariable) != 0;
3204 public bool HasReachableClosingBrace {
3206 return (flags & Flags.ReachableEnd) != 0;
3209 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3213 public bool HasYield {
3215 return (flags & Flags.YieldBlock) != 0;
3222 // Creates anonymous method storey in current block
3224 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3227 // Return same story for iterator and async blocks unless we are
3228 // in nested anonymous method
3230 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3231 return ec.CurrentAnonymousMethod.Storey;
3233 if (am_storey == null) {
3234 MemberBase mc = ec.MemberContext as MemberBase;
3237 // Creates anonymous method storey for this block
3239 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3245 public void EmitScopeInitialization (EmitContext ec)
3247 if ((flags & Flags.InitializationEmitted) != 0)
3250 if (am_storey != null) {
3251 DefineStoreyContainer (ec, am_storey);
3252 am_storey.EmitStoreyInstantiation (ec, this);
3255 if (scope_initializers != null)
3256 EmitScopeInitializers (ec);
3258 flags |= Flags.InitializationEmitted;
3261 public override void Emit (EmitContext ec)
3263 // TODO: It's needed only when scope has variable (normal or lifted)
3264 var scopeIndex = GetDebugSymbolScopeIndex ();
3265 if (scopeIndex > 0) {
3266 ec.BeginScope (scopeIndex);
3269 EmitScopeInitialization (ec);
3271 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3272 ec.Emit (OpCodes.Nop);
3280 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3281 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3282 ec.Emit (OpCodes.Nop);
3286 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3288 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3289 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3290 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3294 // Creates anonymous method storey
3296 storey.CreateContainer ();
3297 storey.DefineContainer ();
3298 storey.ExpandBaseInterfaces ();
3300 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3303 // Only first storey in path will hold this reference. All children blocks will
3304 // reference it indirectly using $ref field
3306 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3307 if (b.Parent != null) {
3308 var s = b.Parent.Explicit.AnonymousMethodStorey;
3310 storey.HoistedThis = s.HoistedThis;
3315 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3316 if (storey.HoistedThis == null)
3317 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3319 if (storey.HoistedThis != null)
3325 // We are the first storey on path and 'this' has to be hoisted
3327 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3328 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3330 // ThisReferencesFromChildrenBlock holds all reference even if they
3331 // are not on this path. It saves some memory otherwise it'd have to
3332 // be in every explicit block. We run this check to see if the reference
3333 // is valid for this storey
3335 Block block_on_path = ref_block;
3336 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3338 if (block_on_path == null)
3341 if (storey.HoistedThis == null) {
3342 storey.AddCapturedThisField (ec, null);
3345 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3347 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3349 if (b_storey != null) {
3351 // Don't add storey cross reference for `this' when the storey ends up not
3352 // beeing attached to any parent
3354 if (b.ParametersBlock.StateMachine == null) {
3355 AnonymousMethodStorey s = null;
3356 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3357 s = ab.Explicit.AnonymousMethodStorey;
3362 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3364 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3365 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3372 // Stop propagation inside same top block
3374 if (b.ParametersBlock == ParametersBlock.Original) {
3375 b_storey.AddParentStoreyReference (ec, storey);
3376 // b_storey.HoistedThis = storey.HoistedThis;
3380 b = pb = b.ParametersBlock;
3382 pb = b as ParametersBlock;
3385 if (pb != null && pb.StateMachine != null) {
3386 if (pb.StateMachine == storey)
3390 // If we are state machine with no parent. We can hook into parent without additional
3391 // reference and capture this directly
3393 ExplicitBlock parent_storey_block = pb;
3394 while (parent_storey_block.Parent != null) {
3395 parent_storey_block = parent_storey_block.Parent.Explicit;
3396 if (parent_storey_block.AnonymousMethodStorey != null) {
3401 if (parent_storey_block.AnonymousMethodStorey == null) {
3402 if (pb.StateMachine.HoistedThis == null) {
3403 pb.StateMachine.AddCapturedThisField (ec, null);
3404 b.HasCapturedThis = true;
3410 var parent_this_block = pb;
3411 while (parent_this_block.Parent != null) {
3412 parent_this_block = parent_this_block.Parent.ParametersBlock;
3413 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3419 // Add reference to closest storey which holds captured this
3421 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3425 // Add parent storey reference only when this is not captured directly
3427 if (b_storey != null) {
3428 b_storey.AddParentStoreyReference (ec, storey);
3429 b_storey.HoistedThis = storey.HoistedThis;
3436 var ref_blocks = storey.ReferencesFromChildrenBlock;
3437 if (ref_blocks != null) {
3438 foreach (ExplicitBlock ref_block in ref_blocks) {
3439 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3440 if (b.AnonymousMethodStorey != null) {
3441 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3444 // Stop propagation inside same top block
3446 if (b.ParametersBlock == ParametersBlock.Original)
3449 b = b.ParametersBlock;
3452 var pb = b as ParametersBlock;
3453 if (pb != null && pb.StateMachine != null) {
3454 if (pb.StateMachine == storey)
3457 pb.StateMachine.AddParentStoreyReference (ec, storey);
3460 b.HasCapturedVariable = true;
3466 storey.PrepareEmit ();
3467 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3470 public void DisableDebugScopeIndex ()
3472 debug_scope_index = -1;
3475 public virtual int GetDebugSymbolScopeIndex ()
3477 if (debug_scope_index == 0)
3478 debug_scope_index = ++ParametersBlock.debug_scope_index;
3480 return debug_scope_index;
3483 public void RegisterAsyncAwait ()
3486 while ((block.flags & Flags.AwaitBlock) == 0) {
3487 block.flags |= Flags.AwaitBlock;
3489 if (block is ParametersBlock)
3492 block = block.Parent.Explicit;
3496 public void RegisterIteratorYield ()
3498 ParametersBlock.TopBlock.IsIterator = true;
3501 while ((block.flags & Flags.YieldBlock) == 0) {
3502 block.flags |= Flags.YieldBlock;
3504 if (block.Parent == null)
3507 block = block.Parent.Explicit;
3511 public void SetCatchBlock ()
3513 flags |= Flags.CatchBlock;
3516 public void SetFinallyBlock ()
3518 flags |= Flags.FinallyBlock;
3521 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3523 tryBlock.statements = statements;
3524 statements = new List<Statement> (1);
3525 statements.Add (tf);
3530 // ParametersBlock was introduced to support anonymous methods
3531 // and lambda expressions
3533 public class ParametersBlock : ExplicitBlock
3535 public class ParameterInfo : INamedBlockVariable
3537 readonly ParametersBlock block;
3539 public VariableInfo VariableInfo;
3542 public ParameterInfo (ParametersBlock block, int index)
3550 public ParametersBlock Block {
3556 Block INamedBlockVariable.Block {
3562 public bool IsDeclared {
3568 public bool IsParameter {
3574 public bool IsLocked {
3583 public Location Location {
3585 return Parameter.Location;
3589 public Parameter Parameter {
3591 return block.Parameters [index];
3595 public TypeSpec ParameterType {
3597 return Parameter.Type;
3603 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3605 return new ParameterReference (this, loc);
3610 // Block is converted into an expression
3612 sealed class BlockScopeExpression : Expression
3615 readonly ParametersBlock block;
3617 public BlockScopeExpression (Expression child, ParametersBlock block)
3623 public override bool ContainsEmitWithAwait ()
3625 return child.ContainsEmitWithAwait ();
3628 public override Expression CreateExpressionTree (ResolveContext ec)
3630 throw new NotSupportedException ();
3633 protected override Expression DoResolve (ResolveContext ec)
3638 child = child.Resolve (ec);
3642 eclass = child.eclass;
3647 public override void Emit (EmitContext ec)
3649 block.EmitScopeInitializers (ec);
3654 protected ParametersCompiled parameters;
3655 protected ParameterInfo[] parameter_info;
3656 protected bool resolved;
3657 protected ToplevelBlock top_block;
3658 protected StateMachine state_machine;
3659 protected Dictionary<string, object> labels;
3661 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3662 : base (parent, 0, start, start)
3664 if (parameters == null)
3665 throw new ArgumentNullException ("parameters");
3667 this.parameters = parameters;
3668 ParametersBlock = this;
3670 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3672 this.top_block = parent.ParametersBlock.top_block;
3673 ProcessParameters ();
3676 protected ParametersBlock (ParametersCompiled parameters, Location start)
3677 : base (null, 0, start, start)
3679 if (parameters == null)
3680 throw new ArgumentNullException ("parameters");
3682 this.parameters = parameters;
3683 ParametersBlock = this;
3687 // It's supposed to be used by method body implementation of anonymous methods
3689 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3690 : base (null, 0, source.StartLocation, source.EndLocation)
3692 this.parameters = parameters;
3693 this.statements = source.statements;
3694 this.scope_initializers = source.scope_initializers;
3696 this.resolved = true;
3697 this.reachable = source.reachable;
3698 this.am_storey = source.am_storey;
3699 this.state_machine = source.state_machine;
3700 this.flags = source.flags & Flags.ReachableEnd;
3702 ParametersBlock = this;
3705 // Overwrite original for comparison purposes when linking cross references
3706 // between anonymous methods
3708 Original = source.Original;
3713 public bool HasReferenceToStoreyForInstanceLambdas {
3715 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3718 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3722 public bool IsAsync {
3724 return (flags & Flags.HasAsyncModifier) != 0;
3727 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3732 // Block has been converted to expression tree
3734 public bool IsExpressionTree {
3736 return (flags & Flags.IsExpressionTree) != 0;
3741 // The parameters for the block.
3743 public ParametersCompiled Parameters {
3749 public StateMachine StateMachine {
3751 return state_machine;
3755 public ToplevelBlock TopBlock {
3764 public bool Resolved {
3766 return (flags & Flags.Resolved) != 0;
3770 public int TemporaryLocalsCount { get; set; }
3775 // Checks whether all `out' parameters have been assigned.
3777 public void CheckControlExit (FlowAnalysisContext fc)
3779 CheckControlExit (fc, fc.DefiniteAssignment);
3782 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3784 if (parameter_info == null)
3787 foreach (var p in parameter_info) {
3788 if (p.VariableInfo == null)
3791 if (p.VariableInfo.IsAssigned (dat))
3794 fc.Report.Error (177, p.Location,
3795 "The out parameter `{0}' must be assigned to before control leaves the current method",
3800 protected override void CloneTo (CloneContext clonectx, Statement t)
3802 base.CloneTo (clonectx, t);
3804 var target = (ParametersBlock) t;
3807 // Clone label statements as well as they contain block reference
3811 if (pb.labels != null) {
3812 target.labels = new Dictionary<string, object> ();
3814 foreach (var entry in pb.labels) {
3815 var list = entry.Value as List<LabeledStatement>;
3818 var list_clone = new List<LabeledStatement> ();
3819 foreach (var lentry in list) {
3820 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3823 target.labels.Add (entry.Key, list_clone);
3825 var labeled = (LabeledStatement) entry.Value;
3826 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3833 if (pb.Parent == null)
3836 pb = pb.Parent.ParametersBlock;
3840 public override Expression CreateExpressionTree (ResolveContext ec)
3842 if (statements.Count == 1) {
3843 Expression expr = statements[0].CreateExpressionTree (ec);
3844 if (scope_initializers != null)
3845 expr = new BlockScopeExpression (expr, this);
3850 return base.CreateExpressionTree (ec);
3853 public override void Emit (EmitContext ec)
3855 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3856 DefineStoreyContainer (ec, state_machine);
3857 state_machine.EmitStoreyInstantiation (ec, this);
3863 public void EmitEmbedded (EmitContext ec)
3865 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3866 DefineStoreyContainer (ec, state_machine);
3867 state_machine.EmitStoreyInstantiation (ec, this);
3873 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3875 var res = base.DoFlowAnalysis (fc);
3877 if (HasReachableClosingBrace)
3878 CheckControlExit (fc);
3883 public override int GetDebugSymbolScopeIndex ()
3888 public LabeledStatement GetLabel (string name, Block block)
3891 // Cloned parameters blocks can have their own cloned version of top-level labels
3893 if (labels == null) {
3895 return Parent.ParametersBlock.GetLabel (name, block);
3901 if (!labels.TryGetValue (name, out value)) {
3905 var label = value as LabeledStatement;
3907 if (label != null) {
3908 if (IsLabelVisible (label, b))
3912 List<LabeledStatement> list = (List<LabeledStatement>) value;
3913 for (int i = 0; i < list.Count; ++i) {
3915 if (IsLabelVisible (label, b))
3923 static bool IsLabelVisible (LabeledStatement label, Block b)
3926 if (label.Block == b)
3929 } while (b != null);
3934 public ParameterInfo GetParameterInfo (Parameter p)
3936 for (int i = 0; i < parameters.Count; ++i) {
3937 if (parameters[i] == p)
3938 return parameter_info[i];
3941 throw new ArgumentException ("Invalid parameter");
3944 public ParameterReference GetParameterReference (int index, Location loc)
3946 return new ParameterReference (parameter_info[index], loc);
3949 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3951 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3953 CloneContext clonectx = new CloneContext ();
3954 return Clone (clonectx);
3957 protected void ProcessParameters ()
3959 if (parameters.Count == 0)
3962 parameter_info = new ParameterInfo[parameters.Count];
3963 for (int i = 0; i < parameter_info.Length; ++i) {
3964 var p = parameters.FixedParameters[i];
3968 // TODO: Should use Parameter only and more block there
3969 parameter_info[i] = new ParameterInfo (this, i);
3971 AddLocalName (p.Name, parameter_info[i]);
3975 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3977 var src = stmt.Block;
3980 // Cannot remap label block if the label was not yet cloned which
3981 // can happen in case of anonymous method inside anoynymous method
3982 // with a label. But in this case we don't care because goto cannot
3983 // jump of out anonymous method
3985 if (src.ParametersBlock != this)
3988 var src_stmts = src.Statements;
3989 for (int i = 0; i < src_stmts.Count; ++i) {
3990 if (src_stmts[i] == stmt)
3991 return (LabeledStatement) dst.Statements[i];
3994 throw new InternalErrorException ("Should never be reached");
3997 public override bool Resolve (BlockContext bc)
3999 // TODO: if ((flags & Flags.Resolved) != 0)
4006 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4007 flags |= Flags.IsExpressionTree;
4010 PrepareAssignmentAnalysis (bc);
4012 if (!base.Resolve (bc))
4015 } catch (Exception e) {
4016 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4019 if (bc.CurrentBlock != null) {
4020 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4022 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4027 // If an asynchronous body of F is either an expression classified as nothing, or a
4028 // statement block where no return statements have expressions, the inferred return type is Task
4031 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4032 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4033 am.ReturnTypeInference = null;
4034 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4042 void PrepareAssignmentAnalysis (BlockContext bc)
4044 for (int i = 0; i < parameters.Count; ++i) {
4045 var par = parameters.FixedParameters[i];
4047 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4050 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4054 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4056 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4057 var stateMachine = new IteratorStorey (iterator);
4059 state_machine = stateMachine;
4060 iterator.SetStateMachine (stateMachine);
4062 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4063 tlb.Original = this;
4064 tlb.state_machine = stateMachine;
4065 tlb.AddStatement (new Return (iterator, iterator.Location));
4069 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4071 for (int i = 0; i < parameters.Count; i++) {
4072 Parameter p = parameters[i];
4073 Parameter.Modifier mod = p.ModFlags;
4074 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4075 host.Compiler.Report.Error (1988, p.Location,
4076 "Async methods cannot have ref or out parameters");
4080 if (p is ArglistParameter) {
4081 host.Compiler.Report.Error (4006, p.Location,
4082 "__arglist is not allowed in parameter list of async methods");
4086 if (parameters.Types[i].IsPointer) {
4087 host.Compiler.Report.Error (4005, p.Location,
4088 "Async methods cannot have unsafe parameters");
4094 host.Compiler.Report.Warning (1998, 1, loc,
4095 "Async block lacks `await' operator and will run synchronously");
4098 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4099 var initializer = new AsyncInitializer (this, host, block_type);
4100 initializer.Type = block_type;
4101 initializer.DelegateType = delegateType;
4103 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4105 state_machine = stateMachine;
4106 initializer.SetStateMachine (stateMachine);
4108 const Flags flags = Flags.CompilerGenerated;
4110 var b = this is ToplevelBlock ?
4111 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4112 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4115 b.state_machine = stateMachine;
4116 b.AddStatement (new AsyncInitializerStatement (initializer));
4124 public class ToplevelBlock : ParametersBlock
4126 LocalVariable this_variable;
4127 CompilerContext compiler;
4128 Dictionary<string, object> names;
4130 List<ExplicitBlock> this_references;
4132 public ToplevelBlock (CompilerContext ctx, Location loc)
4133 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4137 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4138 : base (parameters, start)
4140 this.compiler = ctx;
4144 ProcessParameters ();
4148 // Recreates a top level block from parameters block. Used for
4149 // compiler generated methods where the original block comes from
4150 // explicit child block. This works for already resolved blocks
4151 // only to ensure we resolve them in the correct flow order
4153 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4154 : base (source, parameters)
4156 this.compiler = source.TopBlock.compiler;
4160 public bool IsIterator {
4162 return (flags & Flags.Iterator) != 0;
4165 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4169 public Report Report {
4171 return compiler.Report;
4176 // Used by anonymous blocks to track references of `this' variable
4178 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4180 return this_references;
4185 // Returns the "this" instance variable of this block.
4186 // See AddThisVariable() for more information.
4188 public LocalVariable ThisVariable {
4190 return this_variable;
4194 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4197 names = new Dictionary<string, object> ();
4200 if (!names.TryGetValue (name, out value)) {
4201 names.Add (name, li);
4205 INamedBlockVariable existing = value as INamedBlockVariable;
4206 List<INamedBlockVariable> existing_list;
4207 if (existing != null) {
4208 existing_list = new List<INamedBlockVariable> ();
4209 existing_list.Add (existing);
4210 names[name] = existing_list;
4212 existing_list = (List<INamedBlockVariable>) value;
4216 // A collision checking between local names
4218 var variable_block = li.Block.Explicit;
4219 for (int i = 0; i < existing_list.Count; ++i) {
4220 existing = existing_list[i];
4221 Block b = existing.Block.Explicit;
4223 // Collision at same level
4224 if (variable_block == b) {
4225 li.Block.Error_AlreadyDeclared (name, li);
4229 // Collision with parent
4230 Block parent = variable_block;
4231 while ((parent = parent.Parent) != null) {
4233 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4234 i = existing_list.Count;
4239 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4240 // Collision with children
4241 while ((b = b.Parent) != null) {
4242 if (variable_block == b) {
4243 li.Block.Error_AlreadyDeclared (name, li, "child");
4244 i = existing_list.Count;
4251 existing_list.Add (li);
4254 public void AddLabel (string name, LabeledStatement label)
4257 labels = new Dictionary<string, object> ();
4260 if (!labels.TryGetValue (name, out value)) {
4261 labels.Add (name, label);
4265 LabeledStatement existing = value as LabeledStatement;
4266 List<LabeledStatement> existing_list;
4267 if (existing != null) {
4268 existing_list = new List<LabeledStatement> ();
4269 existing_list.Add (existing);
4270 labels[name] = existing_list;
4272 existing_list = (List<LabeledStatement>) value;
4276 // A collision checking between labels
4278 for (int i = 0; i < existing_list.Count; ++i) {
4279 existing = existing_list[i];
4280 Block b = existing.Block;
4282 // Collision at same level
4283 if (label.Block == b) {
4284 Report.SymbolRelatedToPreviousError (existing.loc, name);
4285 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4289 // Collision with parent
4291 while ((b = b.Parent) != null) {
4292 if (existing.Block == b) {
4293 Report.Error (158, label.loc,
4294 "The label `{0}' shadows another label by the same name in a contained scope", name);
4295 i = existing_list.Count;
4300 // Collision with with children
4302 while ((b = b.Parent) != null) {
4303 if (label.Block == b) {
4304 Report.Error (158, label.loc,
4305 "The label `{0}' shadows another label by the same name in a contained scope", name);
4306 i = existing_list.Count;
4312 existing_list.Add (label);
4315 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4317 if (this_references == null)
4318 this_references = new List<ExplicitBlock> ();
4320 if (!this_references.Contains (block))
4321 this_references.Add (block);
4324 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4326 this_references.Remove (block);
4330 // Creates an arguments set from all parameters, useful for method proxy calls
4332 public Arguments GetAllParametersArguments ()
4334 int count = parameters.Count;
4335 Arguments args = new Arguments (count);
4336 for (int i = 0; i < count; ++i) {
4337 var pi = parameter_info[i];
4338 var arg_expr = GetParameterReference (i, pi.Location);
4340 Argument.AType atype_modifier;
4341 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4342 case Parameter.Modifier.REF:
4343 atype_modifier = Argument.AType.Ref;
4345 case Parameter.Modifier.OUT:
4346 atype_modifier = Argument.AType.Out;
4353 args.Add (new Argument (arg_expr, atype_modifier));
4360 // Lookup inside a block, the returned value can represent 3 states
4362 // true+variable: A local name was found and it's valid
4363 // false+variable: A local name was found in a child block only
4364 // false+null: No local name was found
4366 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4372 if (!names.TryGetValue (name, out value))
4375 variable = value as INamedBlockVariable;
4377 if (variable != null) {
4379 if (variable.Block == b.Original)
4383 } while (b != null);
4391 } while (b != null);
4393 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4394 for (int i = 0; i < list.Count; ++i) {
4397 if (variable.Block == b.Original)
4401 } while (b != null);
4409 } while (b != null);
4419 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4421 if (block.names != null) {
4422 foreach (var n in block.names) {
4423 var variable = n.Value as INamedBlockVariable;
4424 if (variable != null) {
4425 if (variable.Block.ParametersBlock == pb)
4426 AddLocalName (n.Key, variable, false);
4430 foreach (var v in (List<INamedBlockVariable>) n.Value)
4431 if (v.Block.ParametersBlock == pb)
4432 AddLocalName (n.Key, v, false);
4438 // This is used by non-static `struct' constructors which do not have an
4439 // initializer - in this case, the constructor must initialize all of the
4440 // struct's fields. To do this, we add a "this" variable and use the flow
4441 // analysis code to ensure that it's been fully initialized before control
4442 // leaves the constructor.
4444 public void AddThisVariable (BlockContext bc)
4446 if (this_variable != null)
4447 throw new InternalErrorException (StartLocation.ToString ());
4449 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4450 this_variable.Type = bc.CurrentType;
4451 this_variable.PrepareAssignmentAnalysis (bc);
4454 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4457 // If we're a non-static struct constructor which doesn't have an
4458 // initializer, then we must initialize all of the struct's fields.
4460 if (this_variable != null)
4461 this_variable.IsThisAssigned (fc, this);
4463 base.CheckControlExit (fc, dat);
4466 public HashSet<LocalVariable> GetUndeclaredVariables ()
4471 HashSet<LocalVariable> variables = null;
4473 foreach (var entry in names) {
4474 var complex = entry.Value as List<INamedBlockVariable>;
4475 if (complex != null) {
4476 foreach (var centry in complex) {
4477 if (IsUndeclaredVariable (centry)) {
4478 if (variables == null)
4479 variables = new HashSet<LocalVariable> ();
4481 variables.Add ((LocalVariable) centry);
4484 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4485 if (variables == null)
4486 variables = new HashSet<LocalVariable> ();
4488 variables.Add ((LocalVariable)entry.Value);
4495 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4497 var lv = namedBlockVariable as LocalVariable;
4498 return lv != null && !lv.IsDeclared;
4501 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4506 foreach (var entry in names) {
4507 var complex = entry.Value as List<INamedBlockVariable>;
4508 if (complex != null) {
4509 foreach (var centry in complex) {
4510 var lv = centry as LocalVariable;
4511 if (lv != null && undeclaredVariables.Contains (lv)) {
4516 var lv = entry.Value as LocalVariable;
4517 if (lv != null && undeclaredVariables.Contains (lv))
4523 public override void Emit (EmitContext ec)
4525 if (Report.Errors > 0)
4529 if (IsCompilerGenerated) {
4530 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4538 // If `HasReturnLabel' is set, then we already emitted a
4539 // jump to the end of the method, so we must emit a `ret'
4542 // Unfortunately, System.Reflection.Emit automatically emits
4543 // a leave to the end of a finally block. This is a problem
4544 // if no code is following the try/finally block since we may
4545 // jump to a point after the end of the method.
4546 // As a workaround, we're always creating a return label in
4549 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4550 if (ec.HasReturnLabel)
4551 ec.MarkLabel (ec.ReturnLabel);
4553 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4554 ec.Mark (EndLocation);
4556 if (ec.ReturnType.Kind != MemberKind.Void)
4557 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4559 ec.Emit (OpCodes.Ret);
4562 } catch (Exception e) {
4563 throw new InternalErrorException (e, StartLocation);
4567 public bool Resolve (BlockContext bc, IMethodData md)
4572 var errors = bc.Report.Errors;
4576 if (bc.Report.Errors > errors)
4579 MarkReachable (new Reachability ());
4581 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4582 // TODO: var md = bc.CurrentMemberDefinition;
4583 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4586 if ((flags & Flags.NoFlowAnalysis) != 0)
4589 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4592 } catch (Exception e) {
4593 throw new InternalErrorException (e, StartLocation);
4600 public class SwitchLabel : Statement
4608 // if expr == null, then it is the default case.
4610 public SwitchLabel (Expression expr, Location l)
4616 public bool IsDefault {
4618 return label == null;
4622 public Expression Label {
4628 public Location Location {
4634 public Constant Converted {
4643 public bool PatternMatching { get; set; }
4645 public bool SectionStart { get; set; }
4647 public Label GetILLabel (EmitContext ec)
4649 if (il_label == null){
4650 il_label = ec.DefineLabel ();
4653 return il_label.Value;
4656 protected override void DoEmit (EmitContext ec)
4658 ec.MarkLabel (GetILLabel (ec));
4661 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4666 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4670 public override bool Resolve (BlockContext bc)
4672 if (ResolveAndReduce (bc))
4673 bc.Switch.RegisterLabel (bc, this);
4679 // Resolves the expression, reduces it to a literal if possible
4680 // and then converts it to the requested type.
4682 bool ResolveAndReduce (BlockContext bc)
4687 var switch_statement = bc.Switch;
4689 if (PatternMatching) {
4690 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4691 return label != null;
4694 var c = label.ResolveLabelConstant (bc);
4698 if (switch_statement.IsNullable && c is NullLiteral) {
4703 if (switch_statement.IsPatternMatching) {
4704 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4708 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4709 return converted != null;
4712 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4714 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4715 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4718 protected override void CloneTo (CloneContext clonectx, Statement target)
4720 var t = (SwitchLabel) target;
4722 t.label = label.Clone (clonectx);
4725 public override object Accept (StructuralVisitor visitor)
4727 return visitor.Visit (this);
4730 public string GetSignatureForError ()
4733 if (converted == null)
4736 label = converted.GetValueAsLiteral ();
4738 return string.Format ("case {0}:", label);
4742 public class Switch : LoopStatement
4744 // structure used to hold blocks of keys while calculating table switch
4745 sealed class LabelsRange : IComparable<LabelsRange>
4747 public readonly long min;
4749 public readonly List<long> label_values;
4751 public LabelsRange (long value)
4754 label_values = new List<long> ();
4755 label_values.Add (value);
4758 public LabelsRange (long min, long max, ICollection<long> values)
4762 this.label_values = new List<long> (values);
4767 return max - min + 1;
4771 public bool AddValue (long value)
4773 var gap = value - min + 1;
4774 // Ensure the range has > 50% occupancy
4775 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4779 label_values.Add (value);
4783 public int CompareTo (LabelsRange other)
4785 int nLength = label_values.Count;
4786 int nLengthOther = other.label_values.Count;
4787 if (nLengthOther == nLength)
4788 return (int) (other.min - min);
4790 return nLength - nLengthOther;
4794 sealed class DispatchStatement : Statement
4796 readonly Switch body;
4798 public DispatchStatement (Switch body)
4803 protected override void CloneTo (CloneContext clonectx, Statement target)
4805 throw new NotImplementedException ();
4808 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4813 protected override void DoEmit (EmitContext ec)
4815 body.EmitDispatch (ec);
4819 class MissingBreak : Statement
4821 readonly SwitchLabel label;
4823 public MissingBreak (SwitchLabel sl)
4829 public bool FallOut { get; set; }
4831 protected override void DoEmit (EmitContext ec)
4835 protected override void CloneTo (CloneContext clonectx, Statement target)
4839 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4842 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4843 label.GetSignatureForError ());
4845 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4846 label.GetSignatureForError ());
4852 public Expression Expr;
4855 // Mapping of all labels to their SwitchLabels
4857 Dictionary<long, SwitchLabel> labels;
4858 Dictionary<string, SwitchLabel> string_labels;
4859 List<SwitchLabel> case_labels;
4861 List<Tuple<GotoCase, Constant>> goto_cases;
4862 List<DefiniteAssignmentBitSet> end_reachable_das;
4865 /// The governing switch type
4867 public TypeSpec SwitchType;
4869 Expression new_expr;
4871 SwitchLabel case_null;
4872 SwitchLabel case_default;
4874 Label defaultLabel, nullLabel;
4875 VariableReference value;
4876 ExpressionStatement string_dictionary;
4877 FieldExpr switch_cache_field;
4878 ExplicitBlock block;
4882 // Nullable Types support
4884 Nullable.Unwrap unwrap;
4886 public Switch (Expression e, ExplicitBlock block, Location l)
4894 public SwitchLabel ActiveLabel { get; set; }
4896 public ExplicitBlock Block {
4902 public SwitchLabel DefaultLabel {
4904 return case_default;
4908 public bool IsNullable {
4910 return unwrap != null;
4914 public bool IsPatternMatching {
4916 return new_expr == null && SwitchType != null;
4920 public List<SwitchLabel> RegisteredLabels {
4926 public VariableReference ExpressionValue {
4933 // Determines the governing type for a switch. The returned
4934 // expression might be the expression from the switch, or an
4935 // expression that includes any potential conversions to
4937 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4939 switch (expr.Type.BuiltinType) {
4940 case BuiltinTypeSpec.Type.Byte:
4941 case BuiltinTypeSpec.Type.SByte:
4942 case BuiltinTypeSpec.Type.UShort:
4943 case BuiltinTypeSpec.Type.Short:
4944 case BuiltinTypeSpec.Type.UInt:
4945 case BuiltinTypeSpec.Type.Int:
4946 case BuiltinTypeSpec.Type.ULong:
4947 case BuiltinTypeSpec.Type.Long:
4948 case BuiltinTypeSpec.Type.Char:
4949 case BuiltinTypeSpec.Type.String:
4950 case BuiltinTypeSpec.Type.Bool:
4954 if (expr.Type.IsEnum)
4958 // Try to find a *user* defined implicit conversion.
4960 // If there is no implicit conversion, or if there are multiple
4961 // conversions, we have to report an error
4963 Expression converted = null;
4964 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4966 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4969 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4970 Convert.UserConversionRestriction.ProbingOnly;
4973 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4975 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4980 // Ignore over-worked ImplicitUserConversions that do
4981 // an implicit conversion in addition to the user conversion.
4983 var uc = e as UserCast;
4987 if (converted != null){
4988 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4997 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4999 var types = module.Compiler.BuiltinTypes;
5001 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5002 TypeSpec[] stypes = new[] {
5015 if (nullable != null) {
5017 Array.Resize (ref stypes, stypes.Length + 9);
5019 for (int i = 0; i < 9; ++i) {
5020 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5027 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5029 case_labels.Add (sl);
5032 if (case_default != null) {
5033 sl.Error_AlreadyOccurs (rc, case_default);
5041 if (sl.Converted == null)
5045 if (string_labels != null) {
5046 string string_value = sl.Converted.GetValue () as string;
5047 if (string_value == null)
5050 string_labels.Add (string_value, sl);
5052 if (sl.Converted.IsNull) {
5055 labels.Add (sl.Converted.GetValueAsLong (), sl);
5058 } catch (ArgumentException) {
5059 if (string_labels != null)
5060 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5062 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5067 // This method emits code for a lookup-based switch statement (non-string)
5068 // Basically it groups the cases into blocks that are at least half full,
5069 // and then spits out individual lookup opcodes for each block.
5070 // It emits the longest blocks first, and short blocks are just
5071 // handled with direct compares.
5073 void EmitTableSwitch (EmitContext ec, Expression val)
5075 if (labels != null && labels.Count > 0) {
5076 List<LabelsRange> ranges;
5077 if (string_labels != null) {
5078 // We have done all hard work for string already
5079 // setup single range only
5080 ranges = new List<LabelsRange> (1);
5081 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5083 var element_keys = new long[labels.Count];
5084 labels.Keys.CopyTo (element_keys, 0);
5085 Array.Sort (element_keys);
5088 // Build possible ranges of switch labes to reduce number
5091 ranges = new List<LabelsRange> (element_keys.Length);
5092 var range = new LabelsRange (element_keys[0]);
5094 for (int i = 1; i < element_keys.Length; ++i) {
5095 var l = element_keys[i];
5096 if (range.AddValue (l))
5099 range = new LabelsRange (l);
5103 // sort the blocks so we can tackle the largest ones first
5107 Label lbl_default = defaultLabel;
5108 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5110 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5111 LabelsRange kb = ranges[range_index];
5112 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5114 // Optimize small ranges using simple equality check
5115 if (kb.Range <= 2) {
5116 foreach (var key in kb.label_values) {
5117 SwitchLabel sl = labels[key];
5118 if (sl == case_default || sl == case_null)
5121 if (sl.Converted.IsZeroInteger) {
5122 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5125 sl.Converted.Emit (ec);
5126 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5130 // TODO: if all the keys in the block are the same and there are
5131 // no gaps/defaults then just use a range-check.
5132 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5133 // TODO: optimize constant/I4 cases
5135 // check block range (could be > 2^31)
5137 ec.EmitLong (kb.min);
5138 ec.Emit (OpCodes.Blt, lbl_default);
5141 ec.EmitLong (kb.max);
5142 ec.Emit (OpCodes.Bgt, lbl_default);
5147 ec.EmitLong (kb.min);
5148 ec.Emit (OpCodes.Sub);
5151 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5155 int first = (int) kb.min;
5158 ec.Emit (OpCodes.Sub);
5159 } else if (first < 0) {
5160 ec.EmitInt (-first);
5161 ec.Emit (OpCodes.Add);
5165 // first, build the list of labels for the switch
5167 long cJumps = kb.Range;
5168 Label[] switch_labels = new Label[cJumps];
5169 for (int iJump = 0; iJump < cJumps; iJump++) {
5170 var key = kb.label_values[iKey];
5171 if (key == kb.min + iJump) {
5172 switch_labels[iJump] = labels[key].GetILLabel (ec);
5175 switch_labels[iJump] = lbl_default;
5179 // emit the switch opcode
5180 ec.Emit (OpCodes.Switch, switch_labels);
5183 // mark the default for this block
5184 if (range_index != 0)
5185 ec.MarkLabel (lbl_default);
5188 // the last default just goes to the end
5189 if (ranges.Count > 0)
5190 ec.Emit (OpCodes.Br, lbl_default);
5194 public SwitchLabel FindLabel (Constant value)
5196 SwitchLabel sl = null;
5198 if (string_labels != null) {
5199 string s = value.GetValue () as string;
5201 if (case_null != null)
5203 else if (case_default != null)
5206 string_labels.TryGetValue (s, out sl);
5209 if (value is NullLiteral) {
5212 labels.TryGetValue (value.GetValueAsLong (), out sl);
5216 if (sl == null || sl.SectionStart)
5220 // Always return section start, it simplifies handling of switch labels
5222 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5223 var cs = case_labels [idx];
5224 if (cs.SectionStart)
5229 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5231 Expr.FlowAnalysis (fc);
5233 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5234 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5235 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5237 block.FlowAnalysis (fc);
5239 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5241 if (end_reachable_das != null) {
5242 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5243 InitialDefinitiveAssignment |= sections_das;
5244 end_reachable_das = null;
5247 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5249 return case_default != null && !end_reachable;
5252 public override bool Resolve (BlockContext ec)
5254 Expr = Expr.Resolve (ec);
5259 // LAMESPEC: User conversion from non-nullable governing type has a priority
5261 new_expr = SwitchGoverningType (ec, Expr, false);
5263 if (new_expr == null) {
5264 if (Expr.Type.IsNullableType) {
5265 unwrap = Nullable.Unwrap.Create (Expr, false);
5270 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5271 // involving nullable Expr and nullable governing type is
5273 new_expr = SwitchGoverningType (ec, unwrap, true);
5277 Expression switch_expr;
5278 if (new_expr == null) {
5279 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5280 if (Expr.Type != InternalType.ErrorType) {
5281 ec.Report.Error (151, loc,
5282 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5283 Expr.Type.GetSignatureForError ());
5290 SwitchType = Expr.Type;
5292 switch_expr = new_expr;
5293 SwitchType = new_expr.Type;
5294 if (SwitchType.IsNullableType) {
5295 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5296 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5299 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5300 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5304 if (block.Statements.Count == 0)
5307 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5308 string_labels = new Dictionary<string, SwitchLabel> ();
5310 labels = new Dictionary<long, SwitchLabel> ();
5314 var constant = switch_expr as Constant;
5317 // Don't need extra variable for constant switch or switch with
5318 // only default case
5320 if (constant == null) {
5322 // Store switch expression for comparison purposes
5324 value = switch_expr as VariableReference;
5325 if (value == null && !HasOnlyDefaultSection ()) {
5326 var current_block = ec.CurrentBlock;
5327 ec.CurrentBlock = Block;
5328 // Create temporary variable inside switch scope
5329 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5331 ec.CurrentBlock = current_block;
5335 case_labels = new List<SwitchLabel> ();
5337 Switch old_switch = ec.Switch;
5339 var parent_los = ec.EnclosingLoopOrSwitch;
5340 ec.EnclosingLoopOrSwitch = this;
5342 var ok = Statement.Resolve (ec);
5344 ec.EnclosingLoopOrSwitch = parent_los;
5345 ec.Switch = old_switch;
5348 // Check if all goto cases are valid. Needs to be done after switch
5349 // is resolved because goto can jump forward in the scope.
5351 if (goto_cases != null) {
5352 foreach (var gc in goto_cases) {
5353 if (gc.Item1 == null) {
5354 if (DefaultLabel == null) {
5355 Goto.Error_UnknownLabel (ec, "default", loc);
5361 var sl = FindLabel (gc.Item2);
5363 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5365 gc.Item1.Label = sl;
5373 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5374 ResolveStringSwitchMap (ec);
5378 // Anonymous storey initialization has to happen before
5379 // any generated switch dispatch
5381 block.InsertStatement (0, new DispatchStatement (this));
5386 bool HasOnlyDefaultSection ()
5388 for (int i = 0; i < block.Statements.Count; ++i) {
5389 var s = block.Statements[i] as SwitchLabel;
5391 if (s == null || s.IsDefault)
5400 public override Reachability MarkReachable (Reachability rc)
5402 if (rc.IsUnreachable)
5405 base.MarkReachable (rc);
5407 block.MarkReachableScope (rc);
5409 if (block.Statements.Count == 0)
5412 SwitchLabel constant_label = null;
5413 var constant = new_expr as Constant;
5415 if (constant != null) {
5416 constant_label = FindLabel (constant) ?? case_default;
5417 if (constant_label == null) {
5418 block.Statements.RemoveAt (0);
5423 var section_rc = new Reachability ();
5424 SwitchLabel prev_label = null;
5426 for (int i = 0; i < block.Statements.Count; ++i) {
5427 var s = block.Statements[i];
5428 var sl = s as SwitchLabel;
5430 if (sl != null && sl.SectionStart) {
5432 // Section is marked already via goto case
5434 if (!sl.IsUnreachable) {
5435 section_rc = new Reachability ();
5439 if (section_rc.IsUnreachable) {
5441 // Common case. Previous label section end is unreachable as
5442 // it ends with break, return, etc. For next section revert
5443 // to reachable again unless we have constant switch block
5445 section_rc = constant_label != null && constant_label != sl ?
5446 Reachability.CreateUnreachable () :
5447 new Reachability ();
5448 } else if (prev_label != null) {
5450 // Error case as control cannot fall through from one case label
5452 sl.SectionStart = false;
5453 s = new MissingBreak (prev_label);
5454 s.MarkReachable (rc);
5455 block.Statements.Insert (i - 1, s);
5457 } else if (constant_label != null && constant_label != sl) {
5459 // Special case for the first unreachable label in constant
5462 section_rc = Reachability.CreateUnreachable ();
5468 section_rc = s.MarkReachable (section_rc);
5471 if (!section_rc.IsUnreachable && prev_label != null) {
5472 prev_label.SectionStart = false;
5473 var s = new MissingBreak (prev_label) {
5477 s.MarkReachable (rc);
5478 block.Statements.Add (s);
5482 // Reachability can affect parent only when all possible paths are handled but
5483 // we still need to run reachability check on switch body to check for fall-through
5485 if (case_default == null && constant_label == null)
5489 // We have at least one local exit from the switch
5494 return Reachability.CreateUnreachable ();
5497 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5499 if (goto_cases == null)
5500 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5502 goto_cases.Add (Tuple.Create (gotoCase, value));
5506 // Converts string switch into string hashtable
5508 void ResolveStringSwitchMap (ResolveContext ec)
5510 FullNamedExpression string_dictionary_type;
5511 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5512 string_dictionary_type = new TypeExpression (
5513 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5514 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5516 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5517 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5519 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5523 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5524 Field field = new Field (ctype, string_dictionary_type,
5525 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5526 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5527 if (!field.Define ())
5529 ctype.AddField (field);
5531 var init = new List<Expression> ();
5533 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5534 string value = null;
5536 foreach (SwitchLabel sl in case_labels) {
5538 if (sl.SectionStart)
5539 labels.Add (++counter, sl);
5541 if (sl == case_default || sl == case_null)
5544 value = (string) sl.Converted.GetValue ();
5545 var init_args = new List<Expression> (2);
5546 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5548 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5549 init_args.Add (sl.Converted);
5551 init.Add (new CollectionElementInitializer (init_args, loc));
5554 Arguments args = new Arguments (1);
5555 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5556 Expression initializer = new NewInitialize (string_dictionary_type, args,
5557 new CollectionOrObjectInitializers (init, loc), loc);
5559 switch_cache_field = new FieldExpr (field, loc);
5560 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5563 void DoEmitStringSwitch (EmitContext ec)
5565 Label l_initialized = ec.DefineLabel ();
5568 // Skip initialization when value is null
5570 value.EmitBranchable (ec, nullLabel, false);
5573 // Check if string dictionary is initialized and initialize
5575 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5576 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5577 string_dictionary.EmitStatement (ec);
5579 ec.MarkLabel (l_initialized);
5581 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5583 ResolveContext rc = new ResolveContext (ec.MemberContext);
5585 if (switch_cache_field.Type.IsGeneric) {
5586 Arguments get_value_args = new Arguments (2);
5587 get_value_args.Add (new Argument (value));
5588 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5589 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5590 if (get_item == null)
5594 // A value was not found, go to default case
5596 get_item.EmitBranchable (ec, defaultLabel, false);
5598 Arguments get_value_args = new Arguments (1);
5599 get_value_args.Add (new Argument (value));
5601 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5602 if (get_item == null)
5605 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5606 get_item_object.EmitAssign (ec, get_item, true, false);
5607 ec.Emit (OpCodes.Brfalse, defaultLabel);
5609 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5610 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5612 get_item_int.EmitStatement (ec);
5613 get_item_object.Release (ec);
5616 EmitTableSwitch (ec, string_switch_variable);
5617 string_switch_variable.Release (ec);
5621 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5623 void EmitShortSwitch (EmitContext ec)
5625 MethodSpec equal_method = null;
5626 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5627 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5630 if (equal_method != null) {
5631 value.EmitBranchable (ec, nullLabel, false);
5634 for (int i = 0; i < case_labels.Count; ++i) {
5635 var label = case_labels [i];
5636 if (label == case_default || label == case_null)
5639 var constant = label.Converted;
5641 if (constant == null) {
5642 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5646 if (equal_method != null) {
5650 var call = new CallEmitter ();
5651 call.EmitPredefined (ec, equal_method, new Arguments (0));
5652 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5656 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5657 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5663 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5666 ec.Emit (OpCodes.Br, defaultLabel);
5669 void EmitDispatch (EmitContext ec)
5671 if (IsPatternMatching) {
5672 EmitShortSwitch (ec);
5676 if (value == null) {
5678 // Constant switch, we've already done the work if there is only 1 label
5682 foreach (var sl in case_labels) {
5683 if (sl.IsUnreachable)
5686 if (reachable++ > 0) {
5687 var constant = (Constant) new_expr;
5688 var constant_label = FindLabel (constant) ?? case_default;
5690 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5698 if (string_dictionary != null) {
5699 DoEmitStringSwitch (ec);
5700 } else if (case_labels.Count < 4 || string_labels != null) {
5701 EmitShortSwitch (ec);
5703 EmitTableSwitch (ec, value);
5707 protected override void DoEmit (EmitContext ec)
5710 // Setup the codegen context
5712 Label old_end = ec.LoopEnd;
5713 Switch old_switch = ec.Switch;
5715 ec.LoopEnd = ec.DefineLabel ();
5718 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5719 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5721 if (value != null) {
5724 var switch_expr = new_expr ?? Expr;
5726 unwrap.EmitCheck (ec);
5727 ec.Emit (OpCodes.Brfalse, nullLabel);
5728 value.EmitAssign (ec, switch_expr, false, false);
5729 } else if (switch_expr != value) {
5730 value.EmitAssign (ec, switch_expr, false, false);
5735 // Next statement is compiler generated we don't need extra
5736 // nop when we can use the statement for sequence point
5738 ec.Mark (block.StartLocation);
5739 block.IsCompilerGenerated = true;
5741 new_expr.EmitSideEffect (ec);
5746 // Restore context state.
5747 ec.MarkLabel (ec.LoopEnd);
5750 // Restore the previous context
5752 ec.LoopEnd = old_end;
5753 ec.Switch = old_switch;
5756 protected override void CloneTo (CloneContext clonectx, Statement t)
5758 Switch target = (Switch) t;
5760 target.Expr = Expr.Clone (clonectx);
5761 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5764 public override object Accept (StructuralVisitor visitor)
5766 return visitor.Visit (this);
5769 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5771 if (case_default == null && !(new_expr is Constant))
5774 if (end_reachable_das == null)
5775 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5777 end_reachable_das.Add (fc.DefiniteAssignment);
5780 public override void SetEndReachable ()
5782 end_reachable = true;
5786 // A place where execution can restart in a state machine
5787 public abstract class ResumableStatement : Statement
5790 protected Label resume_point;
5792 public Label PrepareForEmit (EmitContext ec)
5796 resume_point = ec.DefineLabel ();
5798 return resume_point;
5801 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5806 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5811 public abstract class TryFinallyBlock : ExceptionStatement
5813 protected Statement stmt;
5814 Label dispose_try_block;
5815 bool prepared_for_dispose, emitted_dispose;
5816 Method finally_host;
5818 protected TryFinallyBlock (Statement stmt, Location loc)
5826 public Statement Statement {
5834 protected abstract void EmitTryBody (EmitContext ec);
5835 public abstract void EmitFinallyBody (EmitContext ec);
5837 public override Label PrepareForDispose (EmitContext ec, Label end)
5839 if (!prepared_for_dispose) {
5840 prepared_for_dispose = true;
5841 dispose_try_block = ec.DefineLabel ();
5843 return dispose_try_block;
5846 protected sealed override void DoEmit (EmitContext ec)
5848 EmitTryBodyPrepare (ec);
5851 bool beginFinally = EmitBeginFinallyBlock (ec);
5853 Label start_finally = ec.DefineLabel ();
5854 if (resume_points != null && beginFinally) {
5855 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5857 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5858 ec.Emit (OpCodes.Brfalse_S, start_finally);
5859 ec.Emit (OpCodes.Endfinally);
5862 ec.MarkLabel (start_finally);
5864 if (finally_host != null) {
5865 finally_host.Define ();
5866 finally_host.PrepareEmit ();
5867 finally_host.Emit ();
5869 // Now it's safe to add, to close it properly and emit sequence points
5870 finally_host.Parent.AddMember (finally_host);
5872 var ce = new CallEmitter ();
5873 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5874 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5876 EmitFinallyBody (ec);
5880 ec.EndExceptionBlock ();
5883 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5885 if (emitted_dispose)
5888 emitted_dispose = true;
5890 Label end_of_try = ec.DefineLabel ();
5892 // Ensure that the only way we can get into this code is through a dispatcher
5893 if (have_dispatcher)
5894 ec.Emit (OpCodes.Br, end);
5896 ec.BeginExceptionBlock ();
5898 ec.MarkLabel (dispose_try_block);
5900 Label[] labels = null;
5901 for (int i = 0; i < resume_points.Count; ++i) {
5902 ResumableStatement s = resume_points[i];
5903 Label ret = s.PrepareForDispose (ec, end_of_try);
5904 if (ret.Equals (end_of_try) && labels == null)
5906 if (labels == null) {
5907 labels = new Label[resume_points.Count];
5908 for (int j = 0; j < i; ++j)
5909 labels[j] = end_of_try;
5914 if (labels != null) {
5916 for (j = 1; j < labels.Length; ++j)
5917 if (!labels[0].Equals (labels[j]))
5919 bool emit_dispatcher = j < labels.Length;
5921 if (emit_dispatcher) {
5922 ec.Emit (OpCodes.Ldloc, pc);
5923 ec.EmitInt (first_resume_pc);
5924 ec.Emit (OpCodes.Sub);
5925 ec.Emit (OpCodes.Switch, labels);
5928 foreach (ResumableStatement s in resume_points)
5929 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5932 ec.MarkLabel (end_of_try);
5934 ec.BeginFinallyBlock ();
5936 if (finally_host != null) {
5937 var ce = new CallEmitter ();
5938 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5939 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5941 EmitFinallyBody (ec);
5944 ec.EndExceptionBlock ();
5947 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5949 var res = stmt.FlowAnalysis (fc);
5950 parent_try_block = null;
5954 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5956 ec.BeginFinallyBlock ();
5960 public override Reachability MarkReachable (Reachability rc)
5962 base.MarkReachable (rc);
5963 return Statement.MarkReachable (rc);
5966 public override bool Resolve (BlockContext bc)
5970 parent_try_block = bc.CurrentTryBlock;
5971 bc.CurrentTryBlock = this;
5973 if (stmt is TryCatch) {
5974 ok = stmt.Resolve (bc);
5976 using (bc.Set (ResolveContext.Options.TryScope)) {
5977 ok = stmt.Resolve (bc);
5981 bc.CurrentTryBlock = parent_try_block;
5984 // Finally block inside iterator is called from MoveNext and
5985 // Dispose methods that means we need to lift the block into
5986 // newly created host method to emit the body only once. The
5987 // original block then simply calls the newly generated method.
5989 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5990 var b = stmt as Block;
5991 if (b != null && b.Explicit.HasYield) {
5992 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5996 return base.Resolve (bc) && ok;
6001 // Base class for blocks using exception handling
6003 public abstract class ExceptionStatement : ResumableStatement
6005 protected List<ResumableStatement> resume_points;
6006 protected int first_resume_pc;
6007 protected ExceptionStatement parent_try_block;
6008 protected int first_catch_resume_pc = -1;
6010 protected ExceptionStatement (Location loc)
6015 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6017 StateMachineInitializer state_machine = null;
6018 if (resume_points != null) {
6019 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6021 ec.EmitInt ((int) IteratorStorey.State.Running);
6022 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6026 // The resume points in catch section when this is try-catch-finally
6028 if (IsRewrittenTryCatchFinally ()) {
6029 ec.BeginExceptionBlock ();
6031 if (first_catch_resume_pc >= 0) {
6033 ec.MarkLabel (resume_point);
6035 // For normal control flow, we want to fall-through the Switch
6036 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6037 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6038 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6039 ec.Emit (OpCodes.Sub);
6041 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6042 for (int i = 0; i < labels.Length; ++i)
6043 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6044 ec.Emit (OpCodes.Switch, labels);
6048 ec.BeginExceptionBlock ();
6051 // The resume points for try section
6053 if (resume_points != null && first_catch_resume_pc != 0) {
6054 if (first_catch_resume_pc < 0)
6055 ec.MarkLabel (resume_point);
6057 // For normal control flow, we want to fall-through the Switch
6058 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6059 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6060 ec.EmitInt (first_resume_pc);
6061 ec.Emit (OpCodes.Sub);
6063 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6064 for (int i = 0; i < labels.Length; ++i)
6065 labels[i] = resume_points[i].PrepareForEmit (ec);
6066 ec.Emit (OpCodes.Switch, labels);
6070 bool IsRewrittenTryCatchFinally ()
6072 var tf = this as TryFinally;
6076 var tc = tf.Statement as TryCatch;
6080 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6083 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6085 if (parent_try_block != null) {
6086 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6088 pc = stateMachine.AddResumePoint (this);
6091 if (resume_points == null) {
6092 resume_points = new List<ResumableStatement> ();
6093 first_resume_pc = pc;
6096 if (pc != first_resume_pc + resume_points.Count)
6097 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6099 var tf = this as TryFinally;
6100 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6101 first_catch_resume_pc = resume_points.Count;
6104 resume_points.Add (stmt);
6109 public class Lock : TryFinallyBlock
6112 TemporaryVariableReference expr_copy;
6113 TemporaryVariableReference lock_taken;
6115 public Lock (Expression expr, Statement stmt, Location loc)
6121 public Expression Expr {
6127 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6129 expr.FlowAnalysis (fc);
6130 return base.DoFlowAnalysis (fc);
6133 public override bool Resolve (BlockContext ec)
6135 expr = expr.Resolve (ec);
6139 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6140 ec.Report.Error (185, loc,
6141 "`{0}' is not a reference type as required by the lock statement",
6142 expr.Type.GetSignatureForError ());
6145 if (expr.Type.IsGenericParameter) {
6146 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6149 VariableReference lv = expr as VariableReference;
6152 locked = lv.IsLockedByStatement;
6153 lv.IsLockedByStatement = true;
6160 // Have to keep original lock value around to unlock same location
6161 // in the case of original value has changed or is null
6163 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6164 expr_copy.Resolve (ec);
6167 // Ensure Monitor methods are available
6169 if (ResolvePredefinedMethods (ec) > 1) {
6170 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6171 lock_taken.Resolve (ec);
6174 using (ec.Set (ResolveContext.Options.LockScope)) {
6179 lv.IsLockedByStatement = locked;
6185 protected override void EmitTryBodyPrepare (EmitContext ec)
6187 expr_copy.EmitAssign (ec, expr);
6189 if (lock_taken != null) {
6191 // Initialize ref variable
6193 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6196 // Monitor.Enter (expr_copy)
6198 expr_copy.Emit (ec);
6199 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6202 base.EmitTryBodyPrepare (ec);
6205 protected override void EmitTryBody (EmitContext ec)
6208 // Monitor.Enter (expr_copy, ref lock_taken)
6210 if (lock_taken != null) {
6211 expr_copy.Emit (ec);
6212 lock_taken.LocalInfo.CreateBuilder (ec);
6213 lock_taken.AddressOf (ec, AddressOp.Load);
6214 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6217 Statement.Emit (ec);
6220 public override void EmitFinallyBody (EmitContext ec)
6223 // if (lock_taken) Monitor.Exit (expr_copy)
6225 Label skip = ec.DefineLabel ();
6227 if (lock_taken != null) {
6228 lock_taken.Emit (ec);
6229 ec.Emit (OpCodes.Brfalse_S, skip);
6232 expr_copy.Emit (ec);
6233 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6235 ec.Emit (OpCodes.Call, m);
6237 ec.MarkLabel (skip);
6240 int ResolvePredefinedMethods (ResolveContext rc)
6242 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6243 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6247 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6251 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6255 protected override void CloneTo (CloneContext clonectx, Statement t)
6257 Lock target = (Lock) t;
6259 target.expr = expr.Clone (clonectx);
6260 target.stmt = Statement.Clone (clonectx);
6263 public override object Accept (StructuralVisitor visitor)
6265 return visitor.Visit (this);
6270 public class Unchecked : Statement {
6273 public Unchecked (Block b, Location loc)
6280 public override bool Resolve (BlockContext ec)
6282 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6283 return Block.Resolve (ec);
6286 protected override void DoEmit (EmitContext ec)
6288 using (ec.With (EmitContext.Options.CheckedScope, false))
6292 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6294 return Block.FlowAnalysis (fc);
6297 public override Reachability MarkReachable (Reachability rc)
6299 base.MarkReachable (rc);
6300 return Block.MarkReachable (rc);
6303 protected override void CloneTo (CloneContext clonectx, Statement t)
6305 Unchecked target = (Unchecked) t;
6307 target.Block = clonectx.LookupBlock (Block);
6310 public override object Accept (StructuralVisitor visitor)
6312 return visitor.Visit (this);
6316 public class Checked : Statement {
6319 public Checked (Block b, Location loc)
6322 b.Unchecked = false;
6326 public override bool Resolve (BlockContext ec)
6328 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6329 return Block.Resolve (ec);
6332 protected override void DoEmit (EmitContext ec)
6334 using (ec.With (EmitContext.Options.CheckedScope, true))
6338 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6340 return Block.FlowAnalysis (fc);
6343 public override Reachability MarkReachable (Reachability rc)
6345 base.MarkReachable (rc);
6346 return Block.MarkReachable (rc);
6349 protected override void CloneTo (CloneContext clonectx, Statement t)
6351 Checked target = (Checked) t;
6353 target.Block = clonectx.LookupBlock (Block);
6356 public override object Accept (StructuralVisitor visitor)
6358 return visitor.Visit (this);
6362 public class Unsafe : Statement {
6365 public Unsafe (Block b, Location loc)
6368 Block.Unsafe = true;
6372 public override bool Resolve (BlockContext ec)
6374 if (ec.CurrentIterator != null)
6375 Expression.UnsafeInsideIteratorError (ec, loc);
6377 using (ec.Set (ResolveContext.Options.UnsafeScope))
6378 return Block.Resolve (ec);
6381 protected override void DoEmit (EmitContext ec)
6386 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6388 return Block.FlowAnalysis (fc);
6391 public override Reachability MarkReachable (Reachability rc)
6393 base.MarkReachable (rc);
6394 return Block.MarkReachable (rc);
6397 protected override void CloneTo (CloneContext clonectx, Statement t)
6399 Unsafe target = (Unsafe) t;
6401 target.Block = clonectx.LookupBlock (Block);
6404 public override object Accept (StructuralVisitor visitor)
6406 return visitor.Visit (this);
6413 public class Fixed : Statement
6415 abstract class Emitter : ShimExpression
6417 protected LocalVariable vi;
6419 protected Emitter (Expression expr, LocalVariable li)
6425 public abstract void EmitExit (EmitContext ec);
6427 public override void FlowAnalysis (FlowAnalysisContext fc)
6429 expr.FlowAnalysis (fc);
6433 sealed class ExpressionEmitter : Emitter {
6434 public ExpressionEmitter (Expression converted, LocalVariable li)
6435 : base (converted, li)
6439 protected override Expression DoResolve (ResolveContext rc)
6441 throw new NotImplementedException ();
6444 public override void Emit (EmitContext ec) {
6446 // Store pointer in pinned location
6452 public override void EmitExit (EmitContext ec)
6455 ec.Emit (OpCodes.Conv_U);
6460 class StringEmitter : Emitter
6462 LocalVariable pinned_string;
6464 public StringEmitter (Expression expr, LocalVariable li)
6469 protected override Expression DoResolve (ResolveContext rc)
6471 pinned_string = new LocalVariable (vi.Block, "$pinned",
6472 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6474 pinned_string.Type = rc.BuiltinTypes.String;
6477 eclass = ExprClass.Variable;
6478 type = rc.BuiltinTypes.Int;
6482 public override void Emit (EmitContext ec)
6484 pinned_string.CreateBuilder (ec);
6487 pinned_string.EmitAssign (ec);
6489 // TODO: Should use Binary::Add
6490 pinned_string.Emit (ec);
6491 ec.Emit (OpCodes.Conv_I);
6493 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6497 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6498 //pe.InstanceExpression = pinned_string;
6499 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6501 ec.Emit (OpCodes.Add);
6505 public override void EmitExit (EmitContext ec)
6508 pinned_string.EmitAssign (ec);
6512 public class VariableDeclaration : BlockVariable
6514 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6519 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6521 if (!Variable.Type.IsPointer && li == Variable) {
6522 bc.Report.Error (209, TypeExpression.Location,
6523 "The type of locals declared in a fixed statement must be a pointer type");
6527 var res = initializer.Resolve (bc);
6534 var ac = res.Type as ArrayContainer;
6536 TypeSpec array_type = ac.Element;
6539 // Provided that array_type is unmanaged,
6541 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6544 Expression res_init;
6545 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6548 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6549 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6550 res = expr_variable.CreateReferenceExpression (bc, loc);
6554 // and T* is implicitly convertible to the
6555 // pointer type given in the fixed statement.
6557 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6559 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6560 if (converted == null)
6564 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6566 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6567 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6568 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6569 new NullLiteral (loc),
6572 converted = converted.Resolve (bc);
6574 return new ExpressionEmitter (converted, li);
6580 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6581 return new StringEmitter (res, li).Resolve (bc);
6584 // Case 3: fixed buffer
6585 if (res is FixedBufferPtr) {
6586 return new ExpressionEmitter (res, li);
6589 bool already_fixed = true;
6592 // Case 4: & object.
6594 Unary u = res as Unary;
6596 if (u.Oper == Unary.Operator.AddressOf) {
6597 IVariableReference vr = u.Expr as IVariableReference;
6598 if (vr == null || !vr.IsFixed) {
6599 already_fixed = false;
6602 } else if (initializer is Cast) {
6603 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6607 if (already_fixed) {
6608 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6611 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6612 return new ExpressionEmitter (res, li);
6617 VariableDeclaration decl;
6618 Statement statement;
6621 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6630 public Statement Statement {
6636 public BlockVariable Variables {
6644 public override bool Resolve (BlockContext bc)
6646 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6647 if (!decl.Resolve (bc))
6651 return statement.Resolve (bc);
6654 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6656 decl.FlowAnalysis (fc);
6657 return statement.FlowAnalysis (fc);
6660 protected override void DoEmit (EmitContext ec)
6662 decl.Variable.CreateBuilder (ec);
6663 decl.Initializer.Emit (ec);
6664 if (decl.Declarators != null) {
6665 foreach (var d in decl.Declarators) {
6666 d.Variable.CreateBuilder (ec);
6667 d.Initializer.Emit (ec);
6671 statement.Emit (ec);
6677 // Clear the pinned variable
6679 ((Emitter) decl.Initializer).EmitExit (ec);
6680 if (decl.Declarators != null) {
6681 foreach (var d in decl.Declarators) {
6682 ((Emitter)d.Initializer).EmitExit (ec);
6687 public override Reachability MarkReachable (Reachability rc)
6689 base.MarkReachable (rc);
6691 decl.MarkReachable (rc);
6693 rc = statement.MarkReachable (rc);
6695 // TODO: What if there is local exit?
6696 has_ret = rc.IsUnreachable;
6700 protected override void CloneTo (CloneContext clonectx, Statement t)
6702 Fixed target = (Fixed) t;
6704 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6705 target.statement = statement.Clone (clonectx);
6708 public override object Accept (StructuralVisitor visitor)
6710 return visitor.Visit (this);
6714 public class Catch : Statement
6716 class CatchVariableStore : Statement
6718 readonly Catch ctch;
6720 public CatchVariableStore (Catch ctch)
6725 protected override void CloneTo (CloneContext clonectx, Statement target)
6729 protected override void DoEmit (EmitContext ec)
6731 // Emits catch variable debug information inside correct block
6732 ctch.EmitCatchVariableStore (ec);
6735 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6741 class FilterStatement : Statement
6743 readonly Catch ctch;
6745 public FilterStatement (Catch ctch)
6750 protected override void CloneTo (CloneContext clonectx, Statement target)
6754 protected override void DoEmit (EmitContext ec)
6756 if (ctch.li != null) {
6757 if (ctch.hoisted_temp != null)
6758 ctch.hoisted_temp.Emit (ec);
6762 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6763 ec.Emit (OpCodes.Box, ctch.type);
6766 var expr_start = ec.DefineLabel ();
6767 var end = ec.DefineLabel ();
6769 ec.Emit (OpCodes.Brtrue_S, expr_start);
6771 ec.Emit (OpCodes.Br, end);
6772 ec.MarkLabel (expr_start);
6774 ctch.Filter.Emit (ec);
6777 ec.Emit (OpCodes.Endfilter);
6778 ec.BeginFilterHandler ();
6779 ec.Emit (OpCodes.Pop);
6782 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6784 ctch.Filter.FlowAnalysis (fc);
6788 public override bool Resolve (BlockContext bc)
6790 ctch.Filter = ctch.Filter.Resolve (bc);
6792 if (ctch.Filter != null) {
6793 if (ctch.Filter.ContainsEmitWithAwait ()) {
6794 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6797 var c = ctch.Filter as Constant;
6798 if (c != null && !c.IsDefaultValue) {
6799 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6807 ExplicitBlock block;
6809 FullNamedExpression type_expr;
6810 CompilerAssign assign;
6812 LocalTemporary hoisted_temp;
6814 public Catch (ExplicitBlock block, Location loc)
6822 public ExplicitBlock Block {
6828 public TypeSpec CatchType {
6834 public Expression Filter {
6838 public bool IsGeneral {
6840 return type_expr == null;
6844 public FullNamedExpression TypeExpression {
6853 public LocalVariable Variable {
6864 protected override void DoEmit (EmitContext ec)
6866 if (Filter != null) {
6867 ec.BeginExceptionFilterBlock ();
6868 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6870 if (Block.HasAwait) {
6871 Block.EmitScopeInitialization (ec);
6880 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6882 ec.BeginCatchBlock (CatchType);
6885 ec.Emit (OpCodes.Pop);
6887 if (Block.HasAwait) {
6889 EmitCatchVariableStore (ec);
6895 void EmitCatchVariableStore (EmitContext ec)
6897 li.CreateBuilder (ec);
6900 // For hoisted catch variable we have to use a temporary local variable
6901 // for captured variable initialization during storey setup because variable
6902 // needs to be on the stack after storey instance for stfld operation
6904 if (li.HoistedVariant != null) {
6905 hoisted_temp = new LocalTemporary (li.Type);
6906 hoisted_temp.Store (ec);
6908 // switch to assignment from temporary variable and not from top of the stack
6909 assign.UpdateSource (hoisted_temp);
6913 public override bool Resolve (BlockContext bc)
6915 using (bc.Set (ResolveContext.Options.CatchScope)) {
6916 if (type_expr == null) {
6917 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6918 if (!block.HasAwait || Filter != null)
6919 block.AddScopeStatement (new CatchVariableStore (this));
6921 Expression source = new EmptyExpression (li.Type);
6922 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6923 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6926 type = type_expr.ResolveAsType (bc);
6931 CreateExceptionVariable (type);
6933 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6934 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6935 } else if (li != null) {
6937 li.PrepareAssignmentAnalysis (bc);
6939 // source variable is at the top of the stack
6940 Expression source = new EmptyExpression (li.Type);
6941 if (li.Type.IsGenericParameter)
6942 source = new UnboxCast (source, li.Type);
6944 if (!block.HasAwait || Filter != null)
6945 block.AddScopeStatement (new CatchVariableStore (this));
6948 // Uses Location.Null to hide from symbol file
6950 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6951 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6955 if (Filter != null) {
6956 Block.AddScopeStatement (new FilterStatement (this));
6959 Block.SetCatchBlock ();
6960 return Block.Resolve (bc);
6964 bool CreateExceptionVariable (TypeSpec type)
6966 if (!Block.HasAwait)
6969 // TODO: Scan the block for rethrow expression
6970 //if (!Block.HasRethrow)
6973 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6977 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6979 if (li != null && !li.IsCompilerGenerated) {
6980 fc.SetVariableAssigned (li.VariableInfo, true);
6983 return block.FlowAnalysis (fc);
6986 public override Reachability MarkReachable (Reachability rc)
6988 base.MarkReachable (rc);
6990 var c = Filter as Constant;
6991 if (c != null && c.IsDefaultValue)
6992 return Reachability.CreateUnreachable ();
6994 return block.MarkReachable (rc);
6997 protected override void CloneTo (CloneContext clonectx, Statement t)
6999 Catch target = (Catch) t;
7001 if (type_expr != null)
7002 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7005 target.Filter = Filter.Clone (clonectx);
7007 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7011 public class TryFinally : TryFinallyBlock
7014 List<DefiniteAssignmentBitSet> try_exit_dat;
7015 List<Tuple<Label, bool>> redirected_jumps;
7016 Label? start_fin_label;
7018 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7024 public ExplicitBlock FinallyBlock {
7030 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7032 if (try_exit_dat == null)
7033 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7035 try_exit_dat.Add (vector);
7038 public override bool Resolve (BlockContext bc)
7040 bool ok = base.Resolve (bc);
7042 fini.SetFinallyBlock ();
7043 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7044 ok &= fini.Resolve (bc);
7050 protected override void EmitTryBody (EmitContext ec)
7052 if (fini.HasAwait) {
7053 if (ec.TryFinallyUnwind == null)
7054 ec.TryFinallyUnwind = new List<TryFinally> ();
7056 ec.TryFinallyUnwind.Add (this);
7059 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7060 ec.EndExceptionBlock ();
7062 ec.TryFinallyUnwind.Remove (this);
7064 if (start_fin_label != null)
7065 ec.MarkLabel (start_fin_label.Value);
7073 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7078 return base.EmitBeginFinallyBlock (ec);
7081 public override void EmitFinallyBody (EmitContext ec)
7083 if (!fini.HasAwait) {
7089 // Emits catch block like
7091 // catch (object temp) {
7092 // this.exception_field = temp;
7095 var type = ec.BuiltinTypes.Object;
7096 ec.BeginCatchBlock (type);
7098 var temp = ec.GetTemporaryLocal (type);
7099 ec.Emit (OpCodes.Stloc, temp);
7101 var exception_field = ec.GetTemporaryField (type);
7102 exception_field.AutomaticallyReuse = false;
7104 ec.Emit (OpCodes.Ldloc, temp);
7105 exception_field.EmitAssignFromStack (ec);
7107 ec.EndExceptionBlock ();
7109 ec.FreeTemporaryLocal (temp, type);
7114 // Emits exception rethrow
7116 // if (this.exception_field != null)
7117 // throw this.exception_field;
7119 exception_field.Emit (ec);
7120 var skip_throw = ec.DefineLabel ();
7121 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7122 exception_field.Emit (ec);
7123 ec.Emit (OpCodes.Throw);
7124 ec.MarkLabel (skip_throw);
7126 exception_field.PrepareCleanup (ec);
7128 EmitUnwindFinallyTable (ec);
7131 bool IsParentBlock (Block block)
7133 for (Block b = fini; b != null; b = b.Parent) {
7141 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7144 if (labelBlock != null) {
7145 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7146 var fin = ec.TryFinallyUnwind [idx - 1];
7147 if (!fin.IsParentBlock (labelBlock))
7154 bool set_return_state = true;
7156 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7157 var fin = ec.TryFinallyUnwind [idx];
7158 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7161 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7162 set_return_state = false;
7164 if (fin.start_fin_label == null) {
7165 fin.start_fin_label = ec.DefineLabel ();
7168 label = fin.start_fin_label.Value;
7174 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7176 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7179 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7181 if (redirected_jumps == null) {
7182 redirected_jumps = new List<Tuple<Label, bool>> ();
7184 // Add fallthrough label
7185 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7188 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7191 int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7193 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7194 index = redirected_jumps.Count - 1;
7198 // Indicates we have captured exit jump
7200 if (setReturnState) {
7201 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7202 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7207 // Emits state table of jumps outside of try block and reload of return
7208 // value when try block returns value
7210 void EmitUnwindFinallyTable (EmitContext ec)
7212 if (redirected_jumps == null)
7215 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7216 initializer.HoistedReturnState.EmitLoad (ec);
7218 var jumps_table = new Label [redirected_jumps.Count];
7219 List<Tuple<Label, Label>> leave_redirect = null;
7220 for (int i = 0; i < jumps_table.Length; ++i) {
7221 var val = redirected_jumps [i];
7224 if (leave_redirect == null)
7225 leave_redirect = new List<Tuple<Label, Label>> ();
7226 var label = ec.DefineLabel ();
7227 leave_redirect.Add (Tuple.Create (label, val.Item1));
7228 jumps_table [i] = label;
7230 jumps_table [i] = val.Item1;
7234 ec.Emit (OpCodes.Switch, jumps_table);
7236 if (leave_redirect != null) {
7237 foreach (var entry in leave_redirect) {
7238 ec.MarkLabel (entry.Item1);
7239 ec.Emit (OpCodes.Leave, entry.Item2);
7243 // Mark fallthrough label
7244 ec.MarkLabel (jumps_table [0]);
7247 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7249 var da = fc.BranchDefiniteAssignment ();
7251 var tf = fc.TryFinally;
7252 fc.TryFinally = this;
7254 var res_stmt = Statement.FlowAnalysis (fc);
7258 var try_da = fc.DefiniteAssignment;
7259 fc.DefiniteAssignment = da;
7261 var res_fin = fini.FlowAnalysis (fc);
7263 if (try_exit_dat != null) {
7265 // try block has global exit but we need to run definite assignment check
7266 // for parameter block out parameter after finally block because it's always
7267 // executed before exit
7269 foreach (var try_da_part in try_exit_dat)
7270 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7272 try_exit_dat = null;
7275 fc.DefiniteAssignment |= try_da;
7276 return res_stmt | res_fin;
7279 public override Reachability MarkReachable (Reachability rc)
7282 // Mark finally block first for any exit statement in try block
7283 // to know whether the code which follows finally is reachable
7285 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7288 protected override void CloneTo (CloneContext clonectx, Statement t)
7290 TryFinally target = (TryFinally) t;
7292 target.stmt = stmt.Clone (clonectx);
7294 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7297 public override object Accept (StructuralVisitor visitor)
7299 return visitor.Visit (this);
7303 public class TryCatch : ExceptionStatement
7306 List<Catch> clauses;
7307 readonly bool inside_try_finally;
7308 List<Catch> catch_sm;
7310 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7314 this.clauses = catch_clauses;
7315 this.inside_try_finally = inside_try_finally;
7318 public List<Catch> Clauses {
7324 public bool HasClauseWithAwait {
7326 return catch_sm != null;
7330 public bool IsTryCatchFinally {
7332 return inside_try_finally;
7336 public override bool Resolve (BlockContext bc)
7340 using (bc.Set (ResolveContext.Options.TryScope)) {
7342 parent_try_block = bc.CurrentTryBlock;
7344 if (IsTryCatchFinally) {
7345 ok = Block.Resolve (bc);
7347 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7348 bc.CurrentTryBlock = this;
7349 ok = Block.Resolve (bc);
7350 bc.CurrentTryBlock = parent_try_block;
7355 var prev_catch = bc.CurrentTryCatch;
7356 bc.CurrentTryCatch = this;
7358 for (int i = 0; i < clauses.Count; ++i) {
7361 ok &= c.Resolve (bc);
7363 if (c.Block.HasAwait) {
7364 if (catch_sm == null)
7365 catch_sm = new List<Catch> ();
7370 if (c.Filter != null)
7373 TypeSpec resolved_type = c.CatchType;
7374 if (resolved_type == null)
7377 for (int ii = 0; ii < clauses.Count; ++ii) {
7381 if (clauses[ii].Filter != null)
7384 if (clauses[ii].IsGeneral) {
7385 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7388 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7391 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7394 bc.Report.Warning (1058, 1, c.loc,
7395 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7403 var ct = clauses[ii].CatchType;
7407 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7408 bc.Report.Error (160, c.loc,
7409 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7410 ct.GetSignatureForError ());
7416 bc.CurrentTryCatch = prev_catch;
7418 return base.Resolve (bc) && ok;
7421 protected sealed override void DoEmit (EmitContext ec)
7423 if (!inside_try_finally)
7424 EmitTryBodyPrepare (ec);
7428 LocalBuilder state_variable = null;
7429 foreach (Catch c in clauses) {
7432 if (catch_sm != null) {
7433 if (state_variable == null) {
7435 // Cannot reuse temp variable because non-catch path assumes the value is 0
7436 // which may not be true for reused local variable
7438 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7441 var index = catch_sm.IndexOf (c);
7445 ec.EmitInt (index + 1);
7446 ec.Emit (OpCodes.Stloc, state_variable);
7450 if (state_variable == null) {
7451 if (!inside_try_finally)
7452 ec.EndExceptionBlock ();
7454 ec.EndExceptionBlock ();
7456 ec.Emit (OpCodes.Ldloc, state_variable);
7458 var labels = new Label [catch_sm.Count + 1];
7459 for (int i = 0; i < labels.Length; ++i) {
7460 labels [i] = ec.DefineLabel ();
7463 var end = ec.DefineLabel ();
7464 ec.Emit (OpCodes.Switch, labels);
7466 // 0 value is default label
7467 ec.MarkLabel (labels [0]);
7468 ec.Emit (OpCodes.Br, end);
7470 var atv = ec.AsyncThrowVariable;
7472 for (int i = 0; i < catch_sm.Count; ++i) {
7473 if (c != null && c.Block.HasReachableClosingBrace)
7474 ec.Emit (OpCodes.Br, end);
7476 ec.MarkLabel (labels [i + 1]);
7479 ec.Emit (OpCodes.Stloc, state_variable);
7482 ec.AsyncThrowVariable = c.Variable;
7485 ec.AsyncThrowVariable = atv;
7491 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7493 var start_fc = fc.BranchDefiniteAssignment ();
7494 var res = Block.FlowAnalysis (fc);
7496 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7498 foreach (var c in clauses) {
7499 fc.BranchDefiniteAssignment (start_fc);
7500 if (!c.FlowAnalysis (fc)) {
7502 try_fc = fc.DefiniteAssignment;
7504 try_fc &= fc.DefiniteAssignment;
7510 fc.DefiniteAssignment = try_fc ?? start_fc;
7511 parent_try_block = null;
7515 public override Reachability MarkReachable (Reachability rc)
7517 if (rc.IsUnreachable)
7520 base.MarkReachable (rc);
7522 var tc_rc = Block.MarkReachable (rc);
7524 foreach (var c in clauses)
7525 tc_rc &= c.MarkReachable (rc);
7530 protected override void CloneTo (CloneContext clonectx, Statement t)
7532 TryCatch target = (TryCatch) t;
7534 target.Block = clonectx.LookupBlock (Block);
7535 if (clauses != null){
7536 target.clauses = new List<Catch> ();
7537 foreach (Catch c in clauses)
7538 target.clauses.Add ((Catch) c.Clone (clonectx));
7542 public override object Accept (StructuralVisitor visitor)
7544 return visitor.Visit (this);
7548 public class Using : TryFinallyBlock
7550 public class VariableDeclaration : BlockVariable
7552 Statement dispose_call;
7554 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7559 public VariableDeclaration (LocalVariable li, Location loc)
7566 public VariableDeclaration (Expression expr)
7569 loc = expr.Location;
7575 public bool IsNested { get; private set; }
7579 public void EmitDispose (EmitContext ec)
7581 dispose_call.Emit (ec);
7584 public override bool Resolve (BlockContext bc)
7589 return base.Resolve (bc, false);
7592 public Expression ResolveExpression (BlockContext bc)
7594 var e = Initializer.Resolve (bc);
7598 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7599 Initializer = ResolveInitializer (bc, Variable, e);
7603 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7605 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7606 initializer = initializer.Resolve (bc);
7607 if (initializer == null)
7610 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7611 Arguments args = new Arguments (1);
7612 args.Add (new Argument (initializer));
7613 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7614 if (initializer == null)
7617 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7618 dispose_call = CreateDisposeCall (bc, var);
7619 dispose_call.Resolve (bc);
7621 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7624 if (li == Variable) {
7625 CheckIDiposableConversion (bc, li, initializer);
7626 dispose_call = CreateDisposeCall (bc, li);
7627 dispose_call.Resolve (bc);
7630 return base.ResolveInitializer (bc, li, initializer);
7633 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7637 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7638 if (type.IsNullableType) {
7639 // it's handled in CreateDisposeCall
7643 if (type != InternalType.ErrorType) {
7644 bc.Report.SymbolRelatedToPreviousError (type);
7645 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7646 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7647 type.GetSignatureForError ());
7654 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7656 var target = bc.BuiltinTypes.IDisposable;
7657 var tp = type as TypeParameterSpec;
7659 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7661 return type.ImplementsInterface (target, false);
7664 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7666 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7668 var loc = lv.Location;
7670 var idt = bc.BuiltinTypes.IDisposable;
7671 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7673 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7674 dispose_mg.InstanceExpression = type.IsNullableType ?
7675 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7679 // Hide it from symbol file via null location
7681 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7683 // Add conditional call when disposing possible null variable
7684 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7685 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7690 public void ResolveDeclaratorInitializer (BlockContext bc)
7692 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7695 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7697 for (int i = declarators.Count - 1; i >= 0; --i) {
7698 var d = declarators [i];
7699 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7700 vd.Initializer = d.Initializer;
7702 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7703 vd.dispose_call.Resolve (bc);
7705 stmt = new Using (vd, stmt, d.Variable.Location);
7712 public override object Accept (StructuralVisitor visitor)
7714 return visitor.Visit (this);
7718 VariableDeclaration decl;
7720 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7726 public Using (Expression expr, Statement stmt, Location loc)
7729 this.decl = new VariableDeclaration (expr);
7734 public Expression Expr {
7736 return decl.Variable == null ? decl.Initializer : null;
7740 public BlockVariable Variables {
7748 public override void Emit (EmitContext ec)
7751 // Don't emit sequence point it will be set on variable declaration
7756 protected override void EmitTryBodyPrepare (EmitContext ec)
7759 base.EmitTryBodyPrepare (ec);
7762 protected override void EmitTryBody (EmitContext ec)
7767 public override void EmitFinallyBody (EmitContext ec)
7769 decl.EmitDispose (ec);
7772 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7774 decl.FlowAnalysis (fc);
7775 return stmt.FlowAnalysis (fc);
7778 public override Reachability MarkReachable (Reachability rc)
7780 decl.MarkReachable (rc);
7781 return base.MarkReachable (rc);
7784 public override bool Resolve (BlockContext ec)
7786 VariableReference vr;
7787 bool vr_locked = false;
7789 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7790 if (decl.Variable == null) {
7791 vr = decl.ResolveExpression (ec) as VariableReference;
7793 vr_locked = vr.IsLockedByStatement;
7794 vr.IsLockedByStatement = true;
7797 if (decl.IsNested) {
7798 decl.ResolveDeclaratorInitializer (ec);
7800 if (!decl.Resolve (ec))
7803 if (decl.Declarators != null) {
7804 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7812 var ok = base.Resolve (ec);
7815 vr.IsLockedByStatement = vr_locked;
7820 protected override void CloneTo (CloneContext clonectx, Statement t)
7822 Using target = (Using) t;
7824 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7825 target.stmt = stmt.Clone (clonectx);
7828 public override object Accept (StructuralVisitor visitor)
7830 return visitor.Visit (this);
7835 /// Implementation of the foreach C# statement
7837 public class Foreach : LoopStatement
7839 abstract class IteratorStatement : Statement
7841 protected readonly Foreach for_each;
7843 protected IteratorStatement (Foreach @foreach)
7845 this.for_each = @foreach;
7846 this.loc = @foreach.expr.Location;
7849 protected override void CloneTo (CloneContext clonectx, Statement target)
7851 throw new NotImplementedException ();
7854 public override void Emit (EmitContext ec)
7856 if (ec.EmitAccurateDebugInfo) {
7857 ec.Emit (OpCodes.Nop);
7863 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7865 throw new NotImplementedException ();
7869 sealed class ArrayForeach : IteratorStatement
7871 TemporaryVariableReference[] lengths;
7872 Expression [] length_exprs;
7873 StatementExpression[] counter;
7874 TemporaryVariableReference[] variables;
7876 TemporaryVariableReference copy;
7878 public ArrayForeach (Foreach @foreach, int rank)
7881 counter = new StatementExpression[rank];
7882 variables = new TemporaryVariableReference[rank];
7883 length_exprs = new Expression [rank];
7886 // Only use temporary length variables when dealing with
7887 // multi-dimensional arrays
7890 lengths = new TemporaryVariableReference [rank];
7893 public override bool Resolve (BlockContext ec)
7895 Block variables_block = for_each.variable.Block;
7896 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7899 int rank = length_exprs.Length;
7900 Arguments list = new Arguments (rank);
7901 for (int i = 0; i < rank; i++) {
7902 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7904 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7905 counter[i].Resolve (ec);
7908 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7910 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7911 lengths[i].Resolve (ec);
7913 Arguments args = new Arguments (1);
7914 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7915 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7918 list.Add (new Argument (v));
7921 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7926 if (for_each.type is VarExpr) {
7927 // Infer implicitly typed local variable from foreach array type
7928 var_type = access.Type;
7930 var_type = for_each.type.ResolveAsType (ec);
7932 if (var_type == null)
7935 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7940 for_each.variable.Type = var_type;
7942 var prev_block = ec.CurrentBlock;
7943 ec.CurrentBlock = variables_block;
7944 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7945 ec.CurrentBlock = prev_block;
7947 if (variable_ref == null)
7950 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7952 return for_each.body.Resolve (ec);
7955 protected override void DoEmit (EmitContext ec)
7957 copy.EmitAssign (ec, for_each.expr);
7959 int rank = length_exprs.Length;
7960 Label[] test = new Label [rank];
7961 Label[] loop = new Label [rank];
7963 for (int i = 0; i < rank; i++) {
7964 test [i] = ec.DefineLabel ();
7965 loop [i] = ec.DefineLabel ();
7967 if (lengths != null)
7968 lengths [i].EmitAssign (ec, length_exprs [i]);
7971 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7972 for (int i = 0; i < rank; i++) {
7973 variables [i].EmitAssign (ec, zero);
7975 ec.Emit (OpCodes.Br, test [i]);
7976 ec.MarkLabel (loop [i]);
7979 for_each.body.Emit (ec);
7981 ec.MarkLabel (ec.LoopBegin);
7982 ec.Mark (for_each.expr.Location);
7984 for (int i = rank - 1; i >= 0; i--){
7985 counter [i].Emit (ec);
7987 ec.MarkLabel (test [i]);
7988 variables [i].Emit (ec);
7990 if (lengths != null)
7991 lengths [i].Emit (ec);
7993 length_exprs [i].Emit (ec);
7995 ec.Emit (OpCodes.Blt, loop [i]);
7998 ec.MarkLabel (ec.LoopEnd);
8002 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8004 class RuntimeDispose : Using.VariableDeclaration
8006 public RuntimeDispose (LocalVariable lv, Location loc)
8012 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8014 // Defered to runtime check
8017 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8019 var idt = bc.BuiltinTypes.IDisposable;
8022 // Fabricates code like
8024 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8027 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8029 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8030 dispose_variable.CreateReferenceExpression (bc, loc),
8031 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8032 loc), new NullLiteral (loc));
8034 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8036 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8037 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8039 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8040 return new If (idisaposable_test, dispose, loc);
8044 LocalVariable variable;
8046 Statement statement;
8047 ExpressionStatement init;
8048 TemporaryVariableReference enumerator_variable;
8049 bool ambiguous_getenumerator_name;
8051 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8054 this.variable = var;
8058 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8060 rc.Report.SymbolRelatedToPreviousError (enumerator);
8061 rc.Report.Error (202, loc,
8062 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8063 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8066 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8069 // Option 1: Try to match by name GetEnumerator first
8071 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8072 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8074 var mg = mexpr as MethodGroupExpr;
8076 mg.InstanceExpression = expr;
8077 Arguments args = new Arguments (0);
8078 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8080 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8081 if (ambiguous_getenumerator_name)
8084 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8090 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8093 PredefinedMember<MethodSpec> iface_candidate = null;
8094 var ptypes = rc.Module.PredefinedTypes;
8095 var gen_ienumerable = ptypes.IEnumerableGeneric;
8096 if (!gen_ienumerable.Define ())
8097 gen_ienumerable = null;
8099 var ifaces = t.Interfaces;
8100 if (ifaces != null) {
8101 foreach (var iface in ifaces) {
8102 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8103 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8104 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8105 rc.Report.Error (1640, loc,
8106 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8107 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8112 // TODO: Cache this somehow
8113 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8114 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8119 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8120 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8125 if (iface_candidate == null) {
8126 if (expr.Type != InternalType.ErrorType) {
8127 rc.Report.Error (1579, loc,
8128 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8129 expr.Type.GetSignatureForError (), "GetEnumerator");
8135 var method = iface_candidate.Resolve (loc);
8139 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8140 mg.InstanceExpression = expr;
8144 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8146 var ms = MemberCache.FindMember (enumerator.ReturnType,
8147 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8148 BindingRestriction.InstanceOnly) as MethodSpec;
8150 if (ms == null || !ms.IsPublic) {
8151 Error_WrongEnumerator (rc, enumerator);
8155 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8158 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8160 var ps = MemberCache.FindMember (enumerator.ReturnType,
8161 MemberFilter.Property ("Current", null),
8162 BindingRestriction.InstanceOnly) as PropertySpec;
8164 if (ps == null || !ps.IsPublic) {
8165 Error_WrongEnumerator (rc, enumerator);
8172 public override bool Resolve (BlockContext ec)
8174 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8177 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8178 } else if (expr.Type.IsNullableType) {
8179 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8182 var get_enumerator_mg = ResolveGetEnumerator (ec);
8183 if (get_enumerator_mg == null) {
8187 var get_enumerator = get_enumerator_mg.BestCandidate;
8188 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8189 enumerator_variable.Resolve (ec);
8191 // Prepare bool MoveNext ()
8192 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8193 if (move_next_mg == null) {
8197 move_next_mg.InstanceExpression = enumerator_variable;
8199 // Prepare ~T~ Current { get; }
8200 var current_prop = ResolveCurrent (ec, get_enumerator);
8201 if (current_prop == null) {
8205 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8206 if (current_pe == null)
8209 VarExpr ve = for_each.type as VarExpr;
8213 // Source type is dynamic, set element type to dynamic too
8214 variable.Type = ec.BuiltinTypes.Dynamic;
8216 // Infer implicitly typed local variable from foreach enumerable type
8217 variable.Type = current_pe.Type;
8221 // Explicit cast of dynamic collection elements has to be done at runtime
8222 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8225 variable.Type = for_each.type.ResolveAsType (ec);
8227 if (variable.Type == null)
8230 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8231 if (current_pe == null)
8235 var prev_block = ec.CurrentBlock;
8236 ec.CurrentBlock = for_each.variable.Block;
8237 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8238 ec.CurrentBlock = prev_block;
8239 if (variable_ref == null)
8242 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8244 var init = new Invocation.Predefined (get_enumerator_mg, null);
8246 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8247 for_each.body, Location.Null);
8249 var enum_type = enumerator_variable.Type;
8252 // Add Dispose method call when enumerator can be IDisposable
8254 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8255 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8257 // Runtime Dispose check
8259 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8260 vd.Initializer = init;
8261 statement = new Using (vd, statement, Location.Null);
8264 // No Dispose call needed
8266 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8267 this.init.Resolve (ec);
8271 // Static Dispose check
8273 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8274 vd.Initializer = init;
8275 statement = new Using (vd, statement, Location.Null);
8278 return statement.Resolve (ec);
8281 protected override void DoEmit (EmitContext ec)
8283 enumerator_variable.LocalInfo.CreateBuilder (ec);
8286 init.EmitStatement (ec);
8288 statement.Emit (ec);
8291 #region IErrorHandler Members
8293 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8295 ec.Report.SymbolRelatedToPreviousError (best);
8296 ec.Report.Warning (278, 2, expr.Location,
8297 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8298 expr.Type.GetSignatureForError (), "enumerable",
8299 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8301 ambiguous_getenumerator_name = true;
8305 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8310 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8315 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8324 LocalVariable variable;
8328 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8332 this.variable = var;
8338 public Expression Expr {
8339 get { return expr; }
8342 public Expression TypeExpression {
8343 get { return type; }
8346 public LocalVariable Variable {
8347 get { return variable; }
8350 public override Reachability MarkReachable (Reachability rc)
8352 base.MarkReachable (rc);
8354 body.MarkReachable (rc);
8359 public override bool Resolve (BlockContext ec)
8361 expr = expr.Resolve (ec);
8366 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8370 body.AddStatement (Statement);
8372 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8373 Statement = new ArrayForeach (this, 1);
8374 } else if (expr.Type is ArrayContainer) {
8375 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8377 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8378 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8379 expr.ExprClassName);
8383 Statement = new CollectionForeach (this, variable, expr);
8386 return base.Resolve (ec);
8389 protected override void DoEmit (EmitContext ec)
8391 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8392 ec.LoopBegin = ec.DefineLabel ();
8393 ec.LoopEnd = ec.DefineLabel ();
8395 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8396 body.Explicit.DisableDebugScopeIndex ();
8398 variable.CreateBuilder (ec);
8400 Statement.Emit (ec);
8404 ec.LoopBegin = old_begin;
8405 ec.LoopEnd = old_end;
8408 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8410 expr.FlowAnalysis (fc);
8412 var da = fc.BranchDefiniteAssignment ();
8413 body.FlowAnalysis (fc);
8414 fc.DefiniteAssignment = da;
8418 protected override void CloneTo (CloneContext clonectx, Statement t)
8420 Foreach target = (Foreach) t;
8422 target.type = type.Clone (clonectx);
8423 target.expr = expr.Clone (clonectx);
8424 target.body = (Block) body.Clone (clonectx);
8425 target.Statement = Statement.Clone (clonectx);
8428 public override object Accept (StructuralVisitor visitor)
8430 return visitor.Visit (this);
8434 class SentinelStatement: Statement
8436 protected override void CloneTo (CloneContext clonectx, Statement target)
8440 protected override void DoEmit (EmitContext ec)
8442 var l = ec.DefineLabel ();
8444 ec.Emit (OpCodes.Br_S, l);
8447 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8449 throw new NotImplementedException ();