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);
561 expr.EmitPrepare (ec);
565 ec.MarkLabel (ec.LoopBegin);
568 expr.EmitBranchable (ec, while_loop, true);
570 ec.MarkLabel (ec.LoopEnd);
573 ec.LoopBegin = old_begin;
574 ec.LoopEnd = old_end;
577 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
579 expr.FlowAnalysisConditional (fc);
581 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
582 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
584 Statement.FlowAnalysis (fc);
587 // Special case infinite while with breaks
589 if (end_reachable_das != null) {
590 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
591 end_reachable_das = null;
594 fc.DefiniteAssignment = da_false;
596 if (infinite && !end_reachable)
602 public override Reachability MarkReachable (Reachability rc)
604 if (rc.IsUnreachable)
607 base.MarkReachable (rc);
610 // Special case unreachable while body
613 Statement.MarkReachable (Reachability.CreateUnreachable ());
617 Statement.MarkReachable (rc);
620 // When infinite while end is unreachable via break anything what follows is unreachable too
622 if (infinite && !end_reachable)
623 return Reachability.CreateUnreachable ();
628 protected override void CloneTo (CloneContext clonectx, Statement t)
630 While target = (While) t;
632 target.expr = expr.Clone (clonectx);
633 target.Statement = Statement.Clone (clonectx);
636 public override object Accept (StructuralVisitor visitor)
638 return visitor.Visit (this);
641 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
646 if (end_reachable_das == null)
647 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
649 end_reachable_das.Add (fc.DefiniteAssignment);
652 public override void SetEndReachable ()
654 end_reachable = true;
658 public class For : LoopStatement
660 bool infinite, empty, iterator_reachable, end_reachable;
661 List<DefiniteAssignmentBitSet> end_reachable_das;
663 public For (Location l)
669 public Statement Initializer {
673 public Expression Condition {
677 public Statement Iterator {
681 public override bool Resolve (BlockContext bc)
683 Initializer.Resolve (bc);
685 if (Condition != null) {
686 Condition = Condition.Resolve (bc);
687 var condition_constant = Condition as Constant;
688 if (condition_constant != null) {
689 if (condition_constant.IsDefaultValue) {
699 return base.Resolve (bc) && Iterator.Resolve (bc);
702 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
704 Initializer.FlowAnalysis (fc);
706 DefiniteAssignmentBitSet da_false;
707 if (Condition != null) {
708 Condition.FlowAnalysisConditional (fc);
709 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
710 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
712 da_false = fc.BranchDefiniteAssignment ();
715 Statement.FlowAnalysis (fc);
717 Iterator.FlowAnalysis (fc);
720 // Special case infinite for with breaks
722 if (end_reachable_das != null) {
723 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
724 end_reachable_das = null;
727 fc.DefiniteAssignment = da_false;
729 if (infinite && !end_reachable)
735 public override Reachability MarkReachable (Reachability rc)
737 base.MarkReachable (rc);
739 Initializer.MarkReachable (rc);
741 var body_rc = Statement.MarkReachable (rc);
742 if (!body_rc.IsUnreachable || iterator_reachable) {
743 Iterator.MarkReachable (rc);
747 // When infinite for end is unreachable via break anything what follows is unreachable too
749 if (infinite && !end_reachable) {
750 return Reachability.CreateUnreachable ();
756 protected override void DoEmit (EmitContext ec)
758 if (Initializer != null)
759 Initializer.Emit (ec);
762 Condition.EmitSideEffect (ec);
766 Label old_begin = ec.LoopBegin;
767 Label old_end = ec.LoopEnd;
768 Label loop = ec.DefineLabel ();
769 Label test = ec.DefineLabel ();
771 ec.LoopBegin = ec.DefineLabel ();
772 ec.LoopEnd = ec.DefineLabel ();
774 ec.Emit (OpCodes.Br, test);
778 ec.MarkLabel (ec.LoopBegin);
783 // If test is null, there is no test, and we are just
786 if (Condition != null) {
787 ec.Mark (Condition.Location);
790 // The Resolve code already catches the case for
791 // Test == Constant (false) so we know that
794 if (Condition is Constant) {
795 Condition.EmitSideEffect (ec);
796 ec.Emit (OpCodes.Br, loop);
798 Condition.EmitBranchable (ec, loop, true);
802 ec.Emit (OpCodes.Br, loop);
803 ec.MarkLabel (ec.LoopEnd);
805 ec.LoopBegin = old_begin;
806 ec.LoopEnd = old_end;
809 protected override void CloneTo (CloneContext clonectx, Statement t)
811 For target = (For) t;
813 if (Initializer != null)
814 target.Initializer = Initializer.Clone (clonectx);
815 if (Condition != null)
816 target.Condition = Condition.Clone (clonectx);
817 if (Iterator != null)
818 target.Iterator = Iterator.Clone (clonectx);
819 target.Statement = Statement.Clone (clonectx);
822 public override object Accept (StructuralVisitor visitor)
824 return visitor.Visit (this);
827 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
832 if (end_reachable_das == null)
833 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
835 end_reachable_das.Add (fc.DefiniteAssignment);
838 public override void SetEndReachable ()
840 end_reachable = true;
843 public override void SetIteratorReachable ()
845 iterator_reachable = true;
849 public abstract class LoopStatement : Statement
851 protected LoopStatement (Statement statement)
853 Statement = statement;
856 public Statement Statement { get; set; }
858 public override bool Resolve (BlockContext bc)
860 var prev_loop = bc.EnclosingLoop;
861 var prev_los = bc.EnclosingLoopOrSwitch;
862 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
863 var ok = Statement.Resolve (bc);
864 bc.EnclosingLoopOrSwitch = prev_los;
865 bc.EnclosingLoop = prev_loop;
871 // Needed by possibly infinite loops statements (for, while) and switch statment
873 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
877 public virtual void SetEndReachable ()
881 public virtual void SetIteratorReachable ()
886 public class StatementExpression : Statement
888 ExpressionStatement expr;
890 public StatementExpression (ExpressionStatement expr)
893 loc = expr.StartLocation;
896 public StatementExpression (ExpressionStatement expr, Location loc)
902 public ExpressionStatement Expr {
908 protected override void CloneTo (CloneContext clonectx, Statement t)
910 StatementExpression target = (StatementExpression) t;
911 target.expr = (ExpressionStatement) expr.Clone (clonectx);
914 protected override void DoEmit (EmitContext ec)
916 expr.EmitStatement (ec);
919 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
921 expr.FlowAnalysis (fc);
925 public override Reachability MarkReachable (Reachability rc)
927 base.MarkReachable (rc);
928 expr.MarkReachable (rc);
932 public override bool Resolve (BlockContext ec)
934 expr = expr.ResolveStatement (ec);
938 public override object Accept (StructuralVisitor visitor)
940 return visitor.Visit (this);
944 public class StatementErrorExpression : Statement
948 public StatementErrorExpression (Expression expr)
951 this.loc = expr.StartLocation;
954 public Expression Expr {
960 public override bool Resolve (BlockContext bc)
962 expr.Error_InvalidExpressionStatement (bc);
966 protected override void DoEmit (EmitContext ec)
968 throw new NotSupportedException ();
971 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
976 protected override void CloneTo (CloneContext clonectx, Statement target)
978 var t = (StatementErrorExpression) target;
980 t.expr = expr.Clone (clonectx);
983 public override object Accept (StructuralVisitor visitor)
985 return visitor.Visit (this);
990 // Simple version of statement list not requiring a block
992 public class StatementList : Statement
994 List<Statement> statements;
996 public StatementList (Statement first, Statement second)
998 statements = new List<Statement> { first, second };
1002 public IList<Statement> Statements {
1009 public void Add (Statement statement)
1011 statements.Add (statement);
1014 public override bool Resolve (BlockContext ec)
1016 foreach (var s in statements)
1022 protected override void DoEmit (EmitContext ec)
1024 foreach (var s in statements)
1028 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1030 foreach (var s in statements)
1031 s.FlowAnalysis (fc);
1036 public override Reachability MarkReachable (Reachability rc)
1038 base.MarkReachable (rc);
1040 Reachability res = rc;
1041 foreach (var s in statements)
1042 res = s.MarkReachable (rc);
1047 protected override void CloneTo (CloneContext clonectx, Statement target)
1049 StatementList t = (StatementList) target;
1051 t.statements = new List<Statement> (statements.Count);
1052 foreach (Statement s in statements)
1053 t.statements.Add (s.Clone (clonectx));
1056 public override object Accept (StructuralVisitor visitor)
1058 return visitor.Visit (this);
1063 // For statements which require special handling when inside try or catch block
1065 public abstract class ExitStatement : Statement
1067 protected bool unwind_protect;
1069 protected abstract bool DoResolve (BlockContext bc);
1070 protected abstract bool IsLocalExit { get; }
1072 public override bool Resolve (BlockContext bc)
1074 var res = DoResolve (bc);
1078 // We are inside finally scope but is it the scope we are exiting
1080 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1082 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1083 if (b.IsFinallyBlock) {
1084 Error_FinallyClauseExit (bc);
1088 if (b is ParametersBlock)
1094 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1098 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1103 if (fc.TryFinally != null) {
1104 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1106 fc.ParametersBlock.CheckControlExit (fc);
1114 /// Implements the return statement
1116 public class Return : ExitStatement
1121 public Return (Expression expr, Location l)
1129 public Expression Expr {
1138 protected override bool IsLocalExit {
1146 protected override bool DoResolve (BlockContext ec)
1148 var block_return_type = ec.ReturnType;
1151 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1155 // Return must not be followed by an expression when
1156 // the method return type is Task
1158 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1159 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1160 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1162 // Extra trick not to emit ret/leave inside awaiter body
1164 expr = EmptyExpression.Null;
1168 if (storey.ReturnType.IsGenericTask)
1169 block_return_type = storey.ReturnType.TypeArguments[0];
1172 if (ec.CurrentIterator != null) {
1173 Error_ReturnFromIterator (ec);
1174 } else if (block_return_type != InternalType.ErrorType) {
1175 ec.Report.Error (126, loc,
1176 "An object of a type convertible to `{0}' is required for the return statement",
1177 block_return_type.GetSignatureForError ());
1183 expr = expr.Resolve (ec);
1185 AnonymousExpression am = ec.CurrentAnonymousMethod;
1187 if (block_return_type.Kind == MemberKind.Void) {
1188 ec.Report.Error (127, loc,
1189 "`{0}': A return keyword must not be followed by any expression when method returns void",
1190 ec.GetSignatureForError ());
1195 if (am.IsIterator) {
1196 Error_ReturnFromIterator (ec);
1200 var async_block = am as AsyncInitializer;
1201 if (async_block != null) {
1203 var storey = (AsyncTaskStorey) am.Storey;
1204 var async_type = storey.ReturnType;
1206 if (async_type == null && async_block.ReturnTypeInference != null) {
1207 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1208 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1210 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1214 if (async_type.Kind == MemberKind.Void) {
1215 ec.Report.Error (8030, loc,
1216 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1220 if (!async_type.IsGenericTask) {
1221 if (this is ContextualReturn)
1224 if (async_block.DelegateType != null) {
1225 ec.Report.Error (8031, loc,
1226 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1228 ec.Report.Error (1997, loc,
1229 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1230 ec.GetSignatureForError ());
1236 // The return type is actually Task<T> type argument
1238 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1239 ec.Report.Error (4016, loc,
1240 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1241 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1243 block_return_type = async_type.TypeArguments[0];
1247 if (block_return_type.Kind == MemberKind.Void) {
1248 ec.Report.Error (8030, loc,
1249 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1253 var l = am as AnonymousMethodBody;
1254 if (l != null && expr != null) {
1255 if (l.ReturnTypeInference != null) {
1256 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1261 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1262 // unexpected debugging experience
1264 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1265 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1274 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1275 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1278 if (am != null && block_return_type == ec.ReturnType) {
1279 ec.Report.Error (1662, loc,
1280 "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",
1281 am.ContainerType, am.GetSignatureForError ());
1290 protected override void DoEmit (EmitContext ec)
1294 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1295 if (async_body != null) {
1296 var storey = (AsyncTaskStorey)async_body.Storey;
1297 Label exit_label = async_body.BodyEnd;
1300 // It's null for await without async
1302 if (storey.HoistedReturnValue != null) {
1304 // Special case hoisted return value (happens in try/finally scenario)
1306 if (ec.TryFinallyUnwind != null) {
1307 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1310 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1311 async_return.EmitAssign (ec, expr, false, false);
1316 if (ec.TryFinallyUnwind != null)
1317 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1320 ec.Emit (OpCodes.Leave, exit_label);
1327 if (unwind_protect || ec.EmitAccurateDebugInfo)
1328 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1331 if (unwind_protect) {
1332 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1333 } else if (ec.EmitAccurateDebugInfo) {
1334 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1336 ec.Emit (OpCodes.Ret);
1340 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1343 expr.FlowAnalysis (fc);
1346 base.DoFlowAnalysis (fc);
1351 void Error_ReturnFromIterator (ResolveContext rc)
1353 rc.Report.Error (1622, loc,
1354 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1357 public override Reachability MarkReachable (Reachability rc)
1359 base.MarkReachable (rc);
1362 rc = Expr.MarkReachable (rc);
1363 expr_returns = rc.IsUnreachable;
1366 return Reachability.CreateUnreachable ();
1369 protected override void CloneTo (CloneContext clonectx, Statement t)
1371 Return target = (Return) t;
1372 // It's null for simple return;
1374 target.expr = expr.Clone (clonectx);
1377 public override object Accept (StructuralVisitor visitor)
1379 return visitor.Visit (this);
1383 public class Goto : ExitStatement
1386 LabeledStatement label;
1387 TryFinally try_finally;
1389 public Goto (string label, Location l)
1395 public string Target {
1396 get { return target; }
1399 protected override bool IsLocalExit {
1405 protected override bool DoResolve (BlockContext bc)
1407 label = bc.CurrentBlock.LookupLabel (target);
1408 if (label == null) {
1409 Error_UnknownLabel (bc, target, loc);
1413 try_finally = bc.CurrentTryBlock as TryFinally;
1415 CheckExitBoundaries (bc, label.Block);
1420 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1422 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1426 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1428 // Goto to unreachable label
1432 if (fc.AddReachedLabel (label))
1435 label.Block.ScanGotoJump (label, fc);
1439 public override Reachability MarkReachable (Reachability rc)
1441 if (rc.IsUnreachable)
1444 base.MarkReachable (rc);
1446 if (try_finally != null) {
1447 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1448 label.AddGotoReference (rc);
1453 label.AddGotoReference (rc);
1456 return Reachability.CreateUnreachable ();
1459 protected override void CloneTo (CloneContext clonectx, Statement target)
1464 protected override void DoEmit (EmitContext ec)
1466 // This should only happen for goto from try block to unrechable label
1470 Label l = label.LabelTarget (ec);
1472 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1473 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1474 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1477 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1480 bool IsLeavingFinally (Block labelBlock)
1482 var b = try_finally.Statement as Block;
1484 if (b == labelBlock)
1493 public override object Accept (StructuralVisitor visitor)
1495 return visitor.Visit (this);
1499 public class LabeledStatement : Statement {
1506 public LabeledStatement (string name, Block block, Location l)
1513 public Label LabelTarget (EmitContext ec)
1518 label = ec.DefineLabel ();
1523 public Block Block {
1529 public string Name {
1530 get { return name; }
1533 protected override void CloneTo (CloneContext clonectx, Statement target)
1535 var t = (LabeledStatement) target;
1537 t.block = clonectx.RemapBlockCopy (block);
1540 public override bool Resolve (BlockContext bc)
1545 protected override void DoEmit (EmitContext ec)
1548 ec.MarkLabel (label);
1551 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1554 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1560 public override Reachability MarkReachable (Reachability rc)
1562 base.MarkReachable (rc);
1565 rc = new Reachability ();
1570 public void AddGotoReference (Reachability rc)
1578 block.ScanGotoJump (this);
1581 public override object Accept (StructuralVisitor visitor)
1583 return visitor.Visit (this);
1589 /// `goto default' statement
1591 public class GotoDefault : SwitchGoto
1593 public GotoDefault (Location l)
1598 public override bool Resolve (BlockContext bc)
1600 if (bc.Switch == null) {
1601 Error_GotoCaseRequiresSwitchBlock (bc);
1605 bc.Switch.RegisterGotoCase (null, null);
1611 protected override void DoEmit (EmitContext ec)
1613 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1616 public override Reachability MarkReachable (Reachability rc)
1618 if (!rc.IsUnreachable) {
1619 var label = switch_statement.DefaultLabel;
1620 if (label.IsUnreachable) {
1621 label.MarkReachable (rc);
1622 switch_statement.Block.ScanGotoJump (label);
1626 return base.MarkReachable (rc);
1629 public override object Accept (StructuralVisitor visitor)
1631 return visitor.Visit (this);
1636 /// `goto case' statement
1638 public class GotoCase : SwitchGoto
1642 public GotoCase (Expression e, Location l)
1648 public Expression Expr {
1654 public SwitchLabel Label { get; set; }
1656 public override bool Resolve (BlockContext ec)
1658 if (ec.Switch == null) {
1659 Error_GotoCaseRequiresSwitchBlock (ec);
1663 Constant c = expr.ResolveLabelConstant (ec);
1669 if (ec.Switch.IsNullable && c is NullLiteral) {
1672 TypeSpec type = ec.Switch.SwitchType;
1673 res = c.Reduce (ec, type);
1675 c.Error_ValueCannotBeConverted (ec, type, true);
1679 if (!Convert.ImplicitStandardConversionExists (c, type))
1680 ec.Report.Warning (469, 2, loc,
1681 "The `goto case' value is not implicitly convertible to type `{0}'",
1682 type.GetSignatureForError ());
1686 ec.Switch.RegisterGotoCase (this, res);
1693 protected override void DoEmit (EmitContext ec)
1695 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1698 protected override void CloneTo (CloneContext clonectx, Statement t)
1700 GotoCase target = (GotoCase) t;
1702 target.expr = expr.Clone (clonectx);
1705 public override Reachability MarkReachable (Reachability rc)
1707 if (!rc.IsUnreachable) {
1708 var label = switch_statement.FindLabel ((Constant) expr);
1709 if (label.IsUnreachable) {
1710 label.MarkReachable (rc);
1711 switch_statement.Block.ScanGotoJump (label);
1715 return base.MarkReachable (rc);
1718 public override object Accept (StructuralVisitor visitor)
1720 return visitor.Visit (this);
1724 public abstract class SwitchGoto : Statement
1726 protected bool unwind_protect;
1727 protected Switch switch_statement;
1729 protected SwitchGoto (Location loc)
1734 protected override void CloneTo (CloneContext clonectx, Statement target)
1739 public override bool Resolve (BlockContext bc)
1741 CheckExitBoundaries (bc, bc.Switch.Block);
1743 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1744 switch_statement = bc.Switch;
1749 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1754 public override Reachability MarkReachable (Reachability rc)
1756 base.MarkReachable (rc);
1757 return Reachability.CreateUnreachable ();
1760 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1762 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1766 public class Throw : Statement {
1769 public Throw (Expression expr, Location l)
1775 public Expression Expr {
1781 public static Expression ConvertType (ResolveContext rc, Expression expr)
1783 var et = rc.BuiltinTypes.Exception;
1784 if (Convert.ImplicitConversionExists (rc, expr, et))
1785 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1787 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1788 expr = EmptyCast.Create (expr, et);
1794 public override bool Resolve (BlockContext ec)
1797 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1798 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1799 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1800 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1801 if (b.IsFinallyBlock) {
1802 ec.Report.Error (724, loc,
1803 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1812 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1817 expr = ConvertType (ec, expr);
1822 protected override void DoEmit (EmitContext ec)
1825 var atv = ec.AsyncThrowVariable;
1827 if (atv.HoistedVariant != null) {
1828 atv.HoistedVariant.Emit (ec);
1833 ec.Emit (OpCodes.Throw);
1835 ec.Emit (OpCodes.Rethrow);
1840 ec.Emit (OpCodes.Throw);
1844 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1847 expr.FlowAnalysis (fc);
1852 public override Reachability MarkReachable (Reachability rc)
1854 base.MarkReachable (rc);
1855 return Reachability.CreateUnreachable ();
1858 protected override void CloneTo (CloneContext clonectx, Statement t)
1860 Throw target = (Throw) t;
1863 target.expr = expr.Clone (clonectx);
1866 public override object Accept (StructuralVisitor visitor)
1868 return visitor.Visit (this);
1872 public class Break : LocalExitStatement
1874 public Break (Location l)
1879 public override object Accept (StructuralVisitor visitor)
1881 return visitor.Visit (this);
1884 protected override void DoEmit (EmitContext ec)
1888 if (ec.TryFinallyUnwind != null) {
1889 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1890 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1893 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1896 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1898 enclosing_loop.AddEndDefiniteAssignment (fc);
1902 protected override bool DoResolve (BlockContext bc)
1904 enclosing_loop = bc.EnclosingLoopOrSwitch;
1905 return base.DoResolve (bc);
1908 public override Reachability MarkReachable (Reachability rc)
1910 base.MarkReachable (rc);
1912 if (!rc.IsUnreachable)
1913 enclosing_loop.SetEndReachable ();
1915 return Reachability.CreateUnreachable ();
1919 public class Continue : LocalExitStatement
1921 public Continue (Location l)
1926 public override object Accept (StructuralVisitor visitor)
1928 return visitor.Visit (this);
1932 protected override void DoEmit (EmitContext ec)
1934 var l = ec.LoopBegin;
1936 if (ec.TryFinallyUnwind != null) {
1937 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1938 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1941 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1944 protected override bool DoResolve (BlockContext bc)
1946 enclosing_loop = bc.EnclosingLoop;
1947 return base.DoResolve (bc);
1950 public override Reachability MarkReachable (Reachability rc)
1952 base.MarkReachable (rc);
1954 if (!rc.IsUnreachable)
1955 enclosing_loop.SetIteratorReachable ();
1957 return Reachability.CreateUnreachable ();
1961 public abstract class LocalExitStatement : ExitStatement
1963 protected LoopStatement enclosing_loop;
1965 protected LocalExitStatement (Location loc)
1970 protected override bool IsLocalExit {
1976 protected override void CloneTo (CloneContext clonectx, Statement t)
1981 protected override bool DoResolve (BlockContext bc)
1983 if (enclosing_loop == null) {
1984 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1988 var block = enclosing_loop.Statement as Block;
1990 // Don't need to do extra checks for simple statements loops
1991 if (block != null) {
1992 CheckExitBoundaries (bc, block);
1999 public interface ILocalVariable
2001 void Emit (EmitContext ec);
2002 void EmitAssign (EmitContext ec);
2003 void EmitAddressOf (EmitContext ec);
2006 public interface INamedBlockVariable
2008 Block Block { get; }
2009 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2010 bool IsDeclared { get; }
2011 bool IsParameter { get; }
2012 Location Location { get; }
2015 public class BlockVariableDeclarator
2018 Expression initializer;
2020 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2022 if (li.Type != null)
2023 throw new ArgumentException ("Expected null variable type");
2026 this.initializer = initializer;
2031 public LocalVariable Variable {
2037 public Expression Initializer {
2042 initializer = value;
2048 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2050 var t = (BlockVariableDeclarator) MemberwiseClone ();
2051 if (initializer != null)
2052 t.initializer = initializer.Clone (cloneCtx);
2058 public class BlockVariable : Statement
2060 Expression initializer;
2061 protected FullNamedExpression type_expr;
2062 protected LocalVariable li;
2063 protected List<BlockVariableDeclarator> declarators;
2066 public BlockVariable (FullNamedExpression type, LocalVariable li)
2068 this.type_expr = type;
2070 this.loc = type_expr.Location;
2073 protected BlockVariable (LocalVariable li)
2080 public List<BlockVariableDeclarator> Declarators {
2086 public Expression Initializer {
2091 initializer = value;
2095 public FullNamedExpression TypeExpression {
2101 public LocalVariable Variable {
2109 public void AddDeclarator (BlockVariableDeclarator decl)
2111 if (declarators == null)
2112 declarators = new List<BlockVariableDeclarator> ();
2114 declarators.Add (decl);
2117 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2119 if (bc.Report.Errors != 0)
2122 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2124 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2125 new MemberName (li.Name, li.Location), null);
2127 container.AddField (f);
2130 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2134 public override bool Resolve (BlockContext bc)
2136 return Resolve (bc, true);
2139 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2141 if (type == null && !li.IsCompilerGenerated) {
2142 var vexpr = type_expr as VarExpr;
2145 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2146 // same name exists or as a keyword when no type was found
2148 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2149 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2150 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2153 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2157 if (li.IsConstant) {
2158 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2162 if (Initializer == null) {
2163 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2167 if (declarators != null) {
2168 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2172 Initializer = Initializer.Resolve (bc);
2173 if (Initializer != null) {
2174 ((VarExpr) type_expr).InferType (bc, Initializer);
2175 type = type_expr.Type;
2177 // Set error type to indicate the var was placed correctly but could
2180 // var a = missing ();
2182 type = InternalType.ErrorType;
2187 type = type_expr.ResolveAsType (bc);
2191 if (li.IsConstant && !type.IsConstantCompatible) {
2192 Const.Error_InvalidConstantType (type, loc, bc.Report);
2197 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2202 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2204 CreateEvaluatorVariable (bc, li);
2205 } else if (type != InternalType.ErrorType) {
2206 li.PrepareAssignmentAnalysis (bc);
2209 if (initializer != null) {
2210 initializer = ResolveInitializer (bc, li, initializer);
2211 // li.Variable.DefinitelyAssigned
2214 if (declarators != null) {
2215 foreach (var d in declarators) {
2216 d.Variable.Type = li.Type;
2218 CreateEvaluatorVariable (bc, d.Variable);
2219 } else if (type != InternalType.ErrorType) {
2220 d.Variable.PrepareAssignmentAnalysis (bc);
2223 if (d.Initializer != null && resolveDeclaratorInitializers) {
2224 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2225 // d.Variable.DefinitelyAssigned
2233 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2235 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2236 return a.ResolveStatement (bc);
2239 protected override void DoEmit (EmitContext ec)
2241 li.CreateBuilder (ec);
2243 if (Initializer != null && !IsUnreachable)
2244 ((ExpressionStatement) Initializer).EmitStatement (ec);
2246 if (declarators != null) {
2247 foreach (var d in declarators) {
2248 d.Variable.CreateBuilder (ec);
2249 if (d.Initializer != null && !IsUnreachable) {
2250 ec.Mark (d.Variable.Location);
2251 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2257 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2259 if (Initializer != null)
2260 Initializer.FlowAnalysis (fc);
2262 if (declarators != null) {
2263 foreach (var d in declarators) {
2264 if (d.Initializer != null)
2265 d.Initializer.FlowAnalysis (fc);
2272 public override Reachability MarkReachable (Reachability rc)
2274 base.MarkReachable (rc);
2275 return initializer == null ? rc : initializer.MarkReachable (rc);
2278 protected override void CloneTo (CloneContext clonectx, Statement target)
2280 BlockVariable t = (BlockVariable) target;
2282 if (type_expr != null)
2283 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2285 if (initializer != null)
2286 t.initializer = initializer.Clone (clonectx);
2288 if (declarators != null) {
2289 t.declarators = null;
2290 foreach (var d in declarators)
2291 t.AddDeclarator (d.Clone (clonectx));
2295 public override object Accept (StructuralVisitor visitor)
2297 return visitor.Visit (this);
2301 public class BlockConstant : BlockVariable
2303 public BlockConstant (FullNamedExpression type, LocalVariable li)
2308 public override void Emit (EmitContext ec)
2310 if (!Variable.IsUsed)
2311 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2313 // Nothing to emit, not even sequence point
2316 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2318 initializer = initializer.Resolve (bc);
2319 if (initializer == null)
2322 var c = initializer as Constant;
2324 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2328 c = c.ConvertImplicitly (li.Type);
2330 if (TypeSpec.IsReferenceType (li.Type))
2331 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2333 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2338 li.ConstantValue = c;
2342 public override object Accept (StructuralVisitor visitor)
2344 return visitor.Visit (this);
2349 // The information about a user-perceived local variable
2351 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2358 AddressTaken = 1 << 2,
2359 CompilerGenerated = 1 << 3,
2361 ForeachVariable = 1 << 5,
2362 FixedVariable = 1 << 6,
2363 UsingVariable = 1 << 7,
2365 SymbolFileHidden = 1 << 9,
2367 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2371 readonly string name;
2372 readonly Location loc;
2373 readonly Block block;
2375 Constant const_value;
2377 public VariableInfo VariableInfo;
2378 HoistedVariable hoisted_variant;
2380 LocalBuilder builder;
2382 public LocalVariable (Block block, string name, Location loc)
2389 public LocalVariable (Block block, string name, Flags flags, Location loc)
2390 : this (block, name, loc)
2396 // Used by variable declarators
2398 public LocalVariable (LocalVariable li, string name, Location loc)
2399 : this (li.block, name, li.flags, loc)
2405 public bool AddressTaken {
2407 return (flags & Flags.AddressTaken) != 0;
2411 public Block Block {
2417 public Constant ConstantValue {
2422 const_value = value;
2427 // Hoisted local variable variant
2429 public HoistedVariable HoistedVariant {
2431 return hoisted_variant;
2434 hoisted_variant = value;
2438 public bool Created {
2440 return builder != null;
2444 public bool IsDeclared {
2446 return type != null;
2450 public bool IsCompilerGenerated {
2452 return (flags & Flags.CompilerGenerated) != 0;
2456 public bool IsConstant {
2458 return (flags & Flags.Constant) != 0;
2462 public bool IsLocked {
2464 return (flags & Flags.IsLocked) != 0;
2467 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2471 public bool IsThis {
2473 return (flags & Flags.IsThis) != 0;
2477 public bool IsUsed {
2479 return (flags & Flags.Used) != 0;
2483 public bool IsFixed {
2485 return (flags & Flags.FixedVariable) != 0;
2488 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2492 bool INamedBlockVariable.IsParameter {
2498 public bool IsReadonly {
2500 return (flags & Flags.ReadonlyMask) != 0;
2504 public Location Location {
2510 public string Name {
2516 public TypeSpec Type {
2527 public void CreateBuilder (EmitContext ec)
2529 if ((flags & Flags.Used) == 0) {
2530 if (VariableInfo == null) {
2531 // Missing flow analysis or wrong variable flags
2532 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2535 if (VariableInfo.IsEverAssigned)
2536 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2538 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2541 if (HoistedVariant != null)
2544 if (builder != null) {
2545 if ((flags & Flags.CompilerGenerated) != 0)
2548 // To avoid Used warning duplicates
2549 throw new InternalErrorException ("Already created variable `{0}'", name);
2553 // All fixed variabled are pinned, a slot has to be alocated
2555 builder = ec.DeclareLocal (Type, IsFixed);
2556 if ((flags & Flags.SymbolFileHidden) == 0)
2557 ec.DefineLocalVariable (name, builder);
2560 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2562 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2563 if (!writeToSymbolFile)
2564 li.flags |= Flags.SymbolFileHidden;
2570 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2572 if (IsConstant && const_value != null) {
2574 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2577 return new LocalVariableReference (this, loc);
2580 public void Emit (EmitContext ec)
2582 // TODO: Need something better for temporary variables
2583 if ((flags & Flags.CompilerGenerated) != 0)
2586 ec.Emit (OpCodes.Ldloc, builder);
2589 public void EmitAssign (EmitContext ec)
2591 // TODO: Need something better for temporary variables
2592 if ((flags & Flags.CompilerGenerated) != 0)
2595 ec.Emit (OpCodes.Stloc, builder);
2598 public void EmitAddressOf (EmitContext ec)
2600 // TODO: Need something better for temporary variables
2601 if ((flags & Flags.CompilerGenerated) != 0)
2604 ec.Emit (OpCodes.Ldloca, builder);
2607 public static string GetCompilerGeneratedName (Block block)
2609 // HACK: Debugger depends on the name semantics
2610 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2613 public string GetReadOnlyContext ()
2615 switch (flags & Flags.ReadonlyMask) {
2616 case Flags.FixedVariable:
2617 return "fixed variable";
2618 case Flags.ForeachVariable:
2619 return "foreach iteration variable";
2620 case Flags.UsingVariable:
2621 return "using variable";
2624 throw new InternalErrorException ("Variable is not readonly");
2627 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2629 if (VariableInfo == null)
2630 throw new Exception ();
2632 if (IsAssigned (fc))
2635 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2638 public bool IsAssigned (FlowAnalysisContext fc)
2640 return fc.IsDefinitelyAssigned (VariableInfo);
2643 public void PrepareAssignmentAnalysis (BlockContext bc)
2646 // No need to run assignment analysis for these guys
2648 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2651 VariableInfo = VariableInfo.Create (bc, this);
2655 // Mark the variables as referenced in the user code
2657 public void SetIsUsed ()
2659 flags |= Flags.Used;
2662 public void SetHasAddressTaken ()
2664 flags |= (Flags.AddressTaken | Flags.Used);
2667 public override string ToString ()
2669 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2674 /// Block represents a C# block.
2678 /// This class is used in a number of places: either to represent
2679 /// explicit blocks that the programmer places or implicit blocks.
2681 /// Implicit blocks are used as labels or to introduce variable
2684 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2685 /// they contain extra information that is not necessary on normal blocks.
2687 public class Block : Statement {
2694 HasCapturedVariable = 64,
2695 HasCapturedThis = 1 << 7,
2696 IsExpressionTree = 1 << 8,
2697 CompilerGenerated = 1 << 9,
2698 HasAsyncModifier = 1 << 10,
2700 YieldBlock = 1 << 12,
2701 AwaitBlock = 1 << 13,
2702 FinallyBlock = 1 << 14,
2703 CatchBlock = 1 << 15,
2704 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2706 NoFlowAnalysis = 1 << 21,
2707 InitializationEmitted = 1 << 22
2710 public Block Parent;
2711 public Location StartLocation;
2712 public Location EndLocation;
2714 public ExplicitBlock Explicit;
2715 public ParametersBlock ParametersBlock;
2717 protected Flags flags;
2720 // The statements in this block
2722 protected List<Statement> statements;
2724 protected List<Statement> scope_initializers;
2726 int? resolving_init_idx;
2732 public int ID = id++;
2734 static int clone_id_counter;
2738 // int assignable_slots;
2740 public Block (Block parent, Location start, Location end)
2741 : this (parent, 0, start, end)
2745 public Block (Block parent, Flags flags, Location start, Location end)
2747 if (parent != null) {
2748 // the appropriate constructors will fixup these fields
2749 ParametersBlock = parent.ParametersBlock;
2750 Explicit = parent.Explicit;
2753 this.Parent = parent;
2755 this.StartLocation = start;
2756 this.EndLocation = end;
2758 statements = new List<Statement> (4);
2760 this.original = this;
2765 public Block Original {
2774 public bool IsCompilerGenerated {
2775 get { return (flags & Flags.CompilerGenerated) != 0; }
2776 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2780 public bool IsCatchBlock {
2782 return (flags & Flags.CatchBlock) != 0;
2786 public bool IsFinallyBlock {
2788 return (flags & Flags.FinallyBlock) != 0;
2792 public bool Unchecked {
2793 get { return (flags & Flags.Unchecked) != 0; }
2794 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2797 public bool Unsafe {
2798 get { return (flags & Flags.Unsafe) != 0; }
2799 set { flags |= Flags.Unsafe; }
2802 public List<Statement> Statements {
2803 get { return statements; }
2808 public void SetEndLocation (Location loc)
2813 public void AddLabel (LabeledStatement target)
2815 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2818 public void AddLocalName (LocalVariable li)
2820 AddLocalName (li.Name, li);
2823 public void AddLocalName (string name, INamedBlockVariable li)
2825 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2828 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2830 if (reason == null) {
2831 Error_AlreadyDeclared (name, variable);
2835 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2836 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2837 "to `{0}', which is already used in a `{1}' scope to denote something else",
2841 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2843 var pi = variable as ParametersBlock.ParameterInfo;
2845 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2847 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2848 "A local variable named `{0}' is already defined in this scope", name);
2852 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2854 ParametersBlock.TopBlock.Report.Error (412, loc,
2855 "The type parameter name `{0}' is the same as local variable or parameter name",
2860 // It should be used by expressions which require to
2861 // register a statement during resolve process.
2863 public void AddScopeStatement (Statement s)
2865 if (scope_initializers == null)
2866 scope_initializers = new List<Statement> ();
2869 // Simple recursive helper, when resolve scope initializer another
2870 // new scope initializer can be added, this ensures it's initialized
2871 // before existing one. For now this can happen with expression trees
2872 // in base ctor initializer only
2874 if (resolving_init_idx.HasValue) {
2875 scope_initializers.Insert (resolving_init_idx.Value, s);
2876 ++resolving_init_idx;
2878 scope_initializers.Add (s);
2882 public void InsertStatement (int index, Statement s)
2884 statements.Insert (index, s);
2887 public void AddStatement (Statement s)
2892 public LabeledStatement LookupLabel (string name)
2894 return ParametersBlock.GetLabel (name, this);
2897 public override Reachability MarkReachable (Reachability rc)
2899 if (rc.IsUnreachable)
2902 MarkReachableScope (rc);
2904 foreach (var s in statements) {
2905 rc = s.MarkReachable (rc);
2906 if (rc.IsUnreachable) {
2907 if ((flags & Flags.ReachableEnd) != 0)
2908 return new Reachability ();
2914 flags |= Flags.ReachableEnd;
2919 public void MarkReachableScope (Reachability rc)
2921 base.MarkReachable (rc);
2923 if (scope_initializers != null) {
2924 foreach (var si in scope_initializers)
2925 si.MarkReachable (rc);
2929 public override bool Resolve (BlockContext bc)
2931 if ((flags & Flags.Resolved) != 0)
2934 Block prev_block = bc.CurrentBlock;
2935 bc.CurrentBlock = this;
2938 // Compiler generated scope statements
2940 if (scope_initializers != null) {
2941 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2942 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2945 resolving_init_idx = null;
2949 int statement_count = statements.Count;
2950 for (int ix = 0; ix < statement_count; ix++){
2951 Statement s = statements [ix];
2953 if (!s.Resolve (bc)) {
2955 statements [ix] = new EmptyStatement (s.loc);
2960 bc.CurrentBlock = prev_block;
2962 flags |= Flags.Resolved;
2966 protected override void DoEmit (EmitContext ec)
2968 for (int ix = 0; ix < statements.Count; ix++){
2969 statements [ix].Emit (ec);
2973 public override void Emit (EmitContext ec)
2975 if (scope_initializers != null)
2976 EmitScopeInitializers (ec);
2981 protected void EmitScopeInitializers (EmitContext ec)
2983 foreach (Statement s in scope_initializers)
2987 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2989 if (scope_initializers != null) {
2990 foreach (var si in scope_initializers)
2991 si.FlowAnalysis (fc);
2994 return DoFlowAnalysis (fc, 0);
2997 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2999 bool end_unreachable = !reachable;
3000 bool goto_flow_analysis = startIndex != 0;
3001 for (; startIndex < statements.Count; ++startIndex) {
3002 var s = statements[startIndex];
3004 end_unreachable = s.FlowAnalysis (fc);
3005 if (s.IsUnreachable) {
3006 statements [startIndex] = RewriteUnreachableStatement (s);
3011 // Statement end reachability is needed mostly due to goto support. Consider
3020 // X label is reachable only via goto not as another statement after if. We need
3021 // this for flow-analysis only to carry variable info correctly.
3023 if (end_unreachable) {
3024 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3026 var f = s as TryFinally;
3027 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3029 // Special case for try-finally with unreachable code after
3030 // finally block. Try block has to include leave opcode but there is
3031 // no label to leave to after unreachable finally block closing
3032 // brace. This sentinel ensures there is always IL instruction to
3033 // leave to even if we know it'll never be reached.
3035 statements.Insert (startIndex + 1, new SentinelStatement ());
3037 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3038 s = statements [startIndex];
3039 if (s is SwitchLabel) {
3040 if (!after_goto_case)
3041 s.FlowAnalysis (fc);
3046 if (s.IsUnreachable) {
3047 s.FlowAnalysis (fc);
3048 statements [startIndex] = RewriteUnreachableStatement (s);
3054 // Idea is to stop after goto case because goto case will always have at least same
3055 // variable assigned as switch case label. This saves a lot for complex goto case tests
3057 if (after_goto_case)
3063 var lb = s as LabeledStatement;
3064 if (lb != null && fc.AddReachedLabel (lb))
3069 // The condition should be true unless there is forward jumping goto
3071 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3074 return !Explicit.HasReachableClosingBrace;
3077 static Statement RewriteUnreachableStatement (Statement s)
3079 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3080 // analysis. Even csc report unreachable warning for it but it's actually used hence
3081 // we try to emulate this behaviour
3089 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3092 return new EmptyStatement (s.loc);
3095 public void ScanGotoJump (Statement label)
3098 for (i = 0; i < statements.Count; ++i) {
3099 if (statements[i] == label)
3103 var rc = new Reachability ();
3104 for (++i; i < statements.Count; ++i) {
3105 var s = statements[i];
3106 rc = s.MarkReachable (rc);
3107 if (rc.IsUnreachable)
3111 flags |= Flags.ReachableEnd;
3114 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3117 for (i = 0; i < statements.Count; ++i) {
3118 if (statements[i] == label)
3122 DoFlowAnalysis (fc, ++i);
3126 public override string ToString ()
3128 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3132 protected override void CloneTo (CloneContext clonectx, Statement t)
3134 Block target = (Block) t;
3136 target.clone_id = ++clone_id_counter;
3139 clonectx.AddBlockMap (this, target);
3140 if (original != this)
3141 clonectx.AddBlockMap (original, target);
3143 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3144 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3147 target.Parent = clonectx.RemapBlockCopy (Parent);
3149 target.statements = new List<Statement> (statements.Count);
3150 foreach (Statement s in statements)
3151 target.statements.Add (s.Clone (clonectx));
3154 public override object Accept (StructuralVisitor visitor)
3156 return visitor.Visit (this);
3160 public class ExplicitBlock : Block
3162 protected AnonymousMethodStorey am_storey;
3163 int debug_scope_index;
3165 public ExplicitBlock (Block parent, Location start, Location end)
3166 : this (parent, (Flags) 0, start, end)
3170 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3171 : base (parent, flags, start, end)
3173 this.Explicit = this;
3178 public AnonymousMethodStorey AnonymousMethodStorey {
3184 public bool HasAwait {
3186 return (flags & Flags.AwaitBlock) != 0;
3190 public bool HasCapturedThis {
3192 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3195 return (flags & Flags.HasCapturedThis) != 0;
3200 // Used to indicate that the block has reference to parent
3201 // block and cannot be made static when defining anonymous method
3203 public bool HasCapturedVariable {
3205 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3208 return (flags & Flags.HasCapturedVariable) != 0;
3212 public bool HasReachableClosingBrace {
3214 return (flags & Flags.ReachableEnd) != 0;
3217 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3221 public bool HasYield {
3223 return (flags & Flags.YieldBlock) != 0;
3230 // Creates anonymous method storey in current block
3232 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3235 // Return same story for iterator and async blocks unless we are
3236 // in nested anonymous method
3238 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3239 return ec.CurrentAnonymousMethod.Storey;
3241 if (am_storey == null) {
3242 MemberBase mc = ec.MemberContext as MemberBase;
3245 // Creates anonymous method storey for this block
3247 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3253 public void EmitScopeInitialization (EmitContext ec)
3255 if ((flags & Flags.InitializationEmitted) != 0)
3258 if (am_storey != null) {
3259 DefineStoreyContainer (ec, am_storey);
3260 am_storey.EmitStoreyInstantiation (ec, this);
3263 if (scope_initializers != null)
3264 EmitScopeInitializers (ec);
3266 flags |= Flags.InitializationEmitted;
3269 public override void Emit (EmitContext ec)
3271 // TODO: It's needed only when scope has variable (normal or lifted)
3272 var scopeIndex = GetDebugSymbolScopeIndex ();
3273 if (scopeIndex > 0) {
3274 ec.BeginScope (scopeIndex);
3277 EmitScopeInitialization (ec);
3279 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3280 ec.Emit (OpCodes.Nop);
3288 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3289 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3290 ec.Emit (OpCodes.Nop);
3294 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3296 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3297 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3298 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3302 // Creates anonymous method storey
3304 storey.CreateContainer ();
3305 storey.DefineContainer ();
3306 storey.ExpandBaseInterfaces ();
3308 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3311 // Only first storey in path will hold this reference. All children blocks will
3312 // reference it indirectly using $ref field
3314 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3315 if (b.Parent != null) {
3316 var s = b.Parent.Explicit.AnonymousMethodStorey;
3318 storey.HoistedThis = s.HoistedThis;
3323 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3324 if (storey.HoistedThis == null)
3325 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3327 if (storey.HoistedThis != null)
3333 // We are the first storey on path and 'this' has to be hoisted
3335 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3336 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3338 // ThisReferencesFromChildrenBlock holds all reference even if they
3339 // are not on this path. It saves some memory otherwise it'd have to
3340 // be in every explicit block. We run this check to see if the reference
3341 // is valid for this storey
3343 Block block_on_path = ref_block;
3344 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3346 if (block_on_path == null)
3349 if (storey.HoistedThis == null) {
3350 storey.AddCapturedThisField (ec, null);
3353 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3355 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3357 if (b_storey != null) {
3359 // Don't add storey cross reference for `this' when the storey ends up not
3360 // beeing attached to any parent
3362 if (b.ParametersBlock.StateMachine == null) {
3363 AnonymousMethodStorey s = null;
3364 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3365 s = ab.Explicit.AnonymousMethodStorey;
3370 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3372 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3373 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3380 // Stop propagation inside same top block
3382 if (b.ParametersBlock == ParametersBlock.Original) {
3383 b_storey.AddParentStoreyReference (ec, storey);
3384 // b_storey.HoistedThis = storey.HoistedThis;
3388 b = pb = b.ParametersBlock;
3390 pb = b as ParametersBlock;
3393 if (pb != null && pb.StateMachine != null) {
3394 if (pb.StateMachine == storey)
3398 // If we are state machine with no parent. We can hook into parent without additional
3399 // reference and capture this directly
3401 ExplicitBlock parent_storey_block = pb;
3402 while (parent_storey_block.Parent != null) {
3403 parent_storey_block = parent_storey_block.Parent.Explicit;
3404 if (parent_storey_block.AnonymousMethodStorey != null) {
3409 if (parent_storey_block.AnonymousMethodStorey == null) {
3410 if (pb.StateMachine.HoistedThis == null) {
3411 pb.StateMachine.AddCapturedThisField (ec, null);
3412 b.HasCapturedThis = true;
3418 var parent_this_block = pb;
3419 while (parent_this_block.Parent != null) {
3420 parent_this_block = parent_this_block.Parent.ParametersBlock;
3421 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3427 // Add reference to closest storey which holds captured this
3429 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3433 // Add parent storey reference only when this is not captured directly
3435 if (b_storey != null) {
3436 b_storey.AddParentStoreyReference (ec, storey);
3437 b_storey.HoistedThis = storey.HoistedThis;
3444 var ref_blocks = storey.ReferencesFromChildrenBlock;
3445 if (ref_blocks != null) {
3446 foreach (ExplicitBlock ref_block in ref_blocks) {
3447 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3448 if (b.AnonymousMethodStorey != null) {
3449 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3452 // Stop propagation inside same top block
3454 if (b.ParametersBlock == ParametersBlock.Original)
3457 b = b.ParametersBlock;
3460 var pb = b as ParametersBlock;
3461 if (pb != null && pb.StateMachine != null) {
3462 if (pb.StateMachine == storey)
3465 pb.StateMachine.AddParentStoreyReference (ec, storey);
3468 b.HasCapturedVariable = true;
3474 storey.PrepareEmit ();
3475 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3478 public void DisableDebugScopeIndex ()
3480 debug_scope_index = -1;
3483 public virtual int GetDebugSymbolScopeIndex ()
3485 if (debug_scope_index == 0)
3486 debug_scope_index = ++ParametersBlock.debug_scope_index;
3488 return debug_scope_index;
3491 public void RegisterAsyncAwait ()
3494 while ((block.flags & Flags.AwaitBlock) == 0) {
3495 block.flags |= Flags.AwaitBlock;
3497 if (block is ParametersBlock)
3500 block = block.Parent.Explicit;
3504 public void RegisterIteratorYield ()
3506 ParametersBlock.TopBlock.IsIterator = true;
3509 while ((block.flags & Flags.YieldBlock) == 0) {
3510 block.flags |= Flags.YieldBlock;
3512 if (block.Parent == null)
3515 block = block.Parent.Explicit;
3519 public void SetCatchBlock ()
3521 flags |= Flags.CatchBlock;
3524 public void SetFinallyBlock ()
3526 flags |= Flags.FinallyBlock;
3529 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3531 tryBlock.statements = statements;
3532 statements = new List<Statement> (1);
3533 statements.Add (tf);
3538 // ParametersBlock was introduced to support anonymous methods
3539 // and lambda expressions
3541 public class ParametersBlock : ExplicitBlock
3543 public class ParameterInfo : INamedBlockVariable
3545 readonly ParametersBlock block;
3547 public VariableInfo VariableInfo;
3550 public ParameterInfo (ParametersBlock block, int index)
3558 public ParametersBlock Block {
3564 Block INamedBlockVariable.Block {
3570 public bool IsDeclared {
3576 public bool IsParameter {
3582 public bool IsLocked {
3591 public Location Location {
3593 return Parameter.Location;
3597 public Parameter Parameter {
3599 return block.Parameters [index];
3603 public TypeSpec ParameterType {
3605 return Parameter.Type;
3611 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3613 return new ParameterReference (this, loc);
3618 // Block is converted into an expression
3620 sealed class BlockScopeExpression : Expression
3623 readonly ParametersBlock block;
3625 public BlockScopeExpression (Expression child, ParametersBlock block)
3631 public override bool ContainsEmitWithAwait ()
3633 return child.ContainsEmitWithAwait ();
3636 public override Expression CreateExpressionTree (ResolveContext ec)
3638 throw new NotSupportedException ();
3641 protected override Expression DoResolve (ResolveContext ec)
3646 child = child.Resolve (ec);
3650 eclass = child.eclass;
3655 public override void Emit (EmitContext ec)
3657 block.EmitScopeInitializers (ec);
3662 protected ParametersCompiled parameters;
3663 protected ParameterInfo[] parameter_info;
3664 protected bool resolved;
3665 protected ToplevelBlock top_block;
3666 protected StateMachine state_machine;
3667 protected Dictionary<string, object> labels;
3669 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3670 : base (parent, 0, start, start)
3672 if (parameters == null)
3673 throw new ArgumentNullException ("parameters");
3675 this.parameters = parameters;
3676 ParametersBlock = this;
3678 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3680 this.top_block = parent.ParametersBlock.top_block;
3681 ProcessParameters ();
3684 protected ParametersBlock (ParametersCompiled parameters, Location start)
3685 : base (null, 0, start, start)
3687 if (parameters == null)
3688 throw new ArgumentNullException ("parameters");
3690 this.parameters = parameters;
3691 ParametersBlock = this;
3695 // It's supposed to be used by method body implementation of anonymous methods
3697 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3698 : base (null, 0, source.StartLocation, source.EndLocation)
3700 this.parameters = parameters;
3701 this.statements = source.statements;
3702 this.scope_initializers = source.scope_initializers;
3704 this.resolved = true;
3705 this.reachable = source.reachable;
3706 this.am_storey = source.am_storey;
3707 this.state_machine = source.state_machine;
3708 this.flags = source.flags & Flags.ReachableEnd;
3710 ParametersBlock = this;
3713 // Overwrite original for comparison purposes when linking cross references
3714 // between anonymous methods
3716 Original = source.Original;
3721 public bool HasReferenceToStoreyForInstanceLambdas {
3723 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3726 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3730 public bool IsAsync {
3732 return (flags & Flags.HasAsyncModifier) != 0;
3735 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3740 // Block has been converted to expression tree
3742 public bool IsExpressionTree {
3744 return (flags & Flags.IsExpressionTree) != 0;
3749 // The parameters for the block.
3751 public ParametersCompiled Parameters {
3757 public StateMachine StateMachine {
3759 return state_machine;
3763 public ToplevelBlock TopBlock {
3772 public bool Resolved {
3774 return (flags & Flags.Resolved) != 0;
3778 public int TemporaryLocalsCount { get; set; }
3783 // Checks whether all `out' parameters have been assigned.
3785 public void CheckControlExit (FlowAnalysisContext fc)
3787 CheckControlExit (fc, fc.DefiniteAssignment);
3790 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3792 if (parameter_info == null)
3795 foreach (var p in parameter_info) {
3796 if (p.VariableInfo == null)
3799 if (p.VariableInfo.IsAssigned (dat))
3802 fc.Report.Error (177, p.Location,
3803 "The out parameter `{0}' must be assigned to before control leaves the current method",
3808 protected override void CloneTo (CloneContext clonectx, Statement t)
3810 base.CloneTo (clonectx, t);
3812 var target = (ParametersBlock) t;
3815 // Clone label statements as well as they contain block reference
3819 if (pb.labels != null) {
3820 target.labels = new Dictionary<string, object> ();
3822 foreach (var entry in pb.labels) {
3823 var list = entry.Value as List<LabeledStatement>;
3826 var list_clone = new List<LabeledStatement> ();
3827 foreach (var lentry in list) {
3828 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3831 target.labels.Add (entry.Key, list_clone);
3833 var labeled = (LabeledStatement) entry.Value;
3834 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3841 if (pb.Parent == null)
3844 pb = pb.Parent.ParametersBlock;
3848 public override Expression CreateExpressionTree (ResolveContext ec)
3850 if (statements.Count == 1) {
3851 Expression expr = statements[0].CreateExpressionTree (ec);
3852 if (scope_initializers != null)
3853 expr = new BlockScopeExpression (expr, this);
3858 return base.CreateExpressionTree (ec);
3861 public override void Emit (EmitContext ec)
3863 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3864 DefineStoreyContainer (ec, state_machine);
3865 state_machine.EmitStoreyInstantiation (ec, this);
3871 public void EmitEmbedded (EmitContext ec)
3873 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3874 DefineStoreyContainer (ec, state_machine);
3875 state_machine.EmitStoreyInstantiation (ec, this);
3881 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3883 var res = base.DoFlowAnalysis (fc);
3885 if (HasReachableClosingBrace)
3886 CheckControlExit (fc);
3891 public override int GetDebugSymbolScopeIndex ()
3896 public LabeledStatement GetLabel (string name, Block block)
3899 // Cloned parameters blocks can have their own cloned version of top-level labels
3901 if (labels == null) {
3903 return Parent.ParametersBlock.GetLabel (name, block);
3909 if (!labels.TryGetValue (name, out value)) {
3913 var label = value as LabeledStatement;
3915 if (label != null) {
3916 if (IsLabelVisible (label, b))
3920 List<LabeledStatement> list = (List<LabeledStatement>) value;
3921 for (int i = 0; i < list.Count; ++i) {
3923 if (IsLabelVisible (label, b))
3931 static bool IsLabelVisible (LabeledStatement label, Block b)
3934 if (label.Block == b)
3937 } while (b != null);
3942 public ParameterInfo GetParameterInfo (Parameter p)
3944 for (int i = 0; i < parameters.Count; ++i) {
3945 if (parameters[i] == p)
3946 return parameter_info[i];
3949 throw new ArgumentException ("Invalid parameter");
3952 public ParameterReference GetParameterReference (int index, Location loc)
3954 return new ParameterReference (parameter_info[index], loc);
3957 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3959 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3961 CloneContext clonectx = new CloneContext ();
3962 return Clone (clonectx);
3965 protected void ProcessParameters ()
3967 if (parameters.Count == 0)
3970 parameter_info = new ParameterInfo[parameters.Count];
3971 for (int i = 0; i < parameter_info.Length; ++i) {
3972 var p = parameters.FixedParameters[i];
3976 // TODO: Should use Parameter only and more block there
3977 parameter_info[i] = new ParameterInfo (this, i);
3979 AddLocalName (p.Name, parameter_info[i]);
3983 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3985 var src = stmt.Block;
3988 // Cannot remap label block if the label was not yet cloned which
3989 // can happen in case of anonymous method inside anoynymous method
3990 // with a label. But in this case we don't care because goto cannot
3991 // jump of out anonymous method
3993 if (src.ParametersBlock != this)
3996 var src_stmts = src.Statements;
3997 for (int i = 0; i < src_stmts.Count; ++i) {
3998 if (src_stmts[i] == stmt)
3999 return (LabeledStatement) dst.Statements[i];
4002 throw new InternalErrorException ("Should never be reached");
4005 public override bool Resolve (BlockContext bc)
4007 // TODO: if ((flags & Flags.Resolved) != 0)
4014 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4015 flags |= Flags.IsExpressionTree;
4018 PrepareAssignmentAnalysis (bc);
4020 if (!base.Resolve (bc))
4023 } catch (Exception e) {
4024 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4027 if (bc.CurrentBlock != null) {
4028 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4030 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4035 // If an asynchronous body of F is either an expression classified as nothing, or a
4036 // statement block where no return statements have expressions, the inferred return type is Task
4039 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4040 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4041 am.ReturnTypeInference = null;
4042 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4050 void PrepareAssignmentAnalysis (BlockContext bc)
4052 for (int i = 0; i < parameters.Count; ++i) {
4053 var par = parameters.FixedParameters[i];
4055 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4058 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4062 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4064 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4065 var stateMachine = new IteratorStorey (iterator);
4067 state_machine = stateMachine;
4068 iterator.SetStateMachine (stateMachine);
4070 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4071 tlb.Original = this;
4072 tlb.state_machine = stateMachine;
4073 tlb.AddStatement (new Return (iterator, iterator.Location));
4077 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4079 for (int i = 0; i < parameters.Count; i++) {
4080 Parameter p = parameters[i];
4081 Parameter.Modifier mod = p.ModFlags;
4082 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4083 host.Compiler.Report.Error (1988, p.Location,
4084 "Async methods cannot have ref or out parameters");
4088 if (p is ArglistParameter) {
4089 host.Compiler.Report.Error (4006, p.Location,
4090 "__arglist is not allowed in parameter list of async methods");
4094 if (parameters.Types[i].IsPointer) {
4095 host.Compiler.Report.Error (4005, p.Location,
4096 "Async methods cannot have unsafe parameters");
4102 host.Compiler.Report.Warning (1998, 1, loc,
4103 "Async block lacks `await' operator and will run synchronously");
4106 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4107 var initializer = new AsyncInitializer (this, host, block_type);
4108 initializer.Type = block_type;
4109 initializer.DelegateType = delegateType;
4111 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4113 state_machine = stateMachine;
4114 initializer.SetStateMachine (stateMachine);
4116 const Flags flags = Flags.CompilerGenerated;
4118 var b = this is ToplevelBlock ?
4119 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4120 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4123 b.state_machine = stateMachine;
4124 b.AddStatement (new AsyncInitializerStatement (initializer));
4132 public class ToplevelBlock : ParametersBlock
4134 LocalVariable this_variable;
4135 CompilerContext compiler;
4136 Dictionary<string, object> names;
4138 List<ExplicitBlock> this_references;
4140 public ToplevelBlock (CompilerContext ctx, Location loc)
4141 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4145 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4146 : base (parameters, start)
4148 this.compiler = ctx;
4152 ProcessParameters ();
4156 // Recreates a top level block from parameters block. Used for
4157 // compiler generated methods where the original block comes from
4158 // explicit child block. This works for already resolved blocks
4159 // only to ensure we resolve them in the correct flow order
4161 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4162 : base (source, parameters)
4164 this.compiler = source.TopBlock.compiler;
4168 public bool IsIterator {
4170 return (flags & Flags.Iterator) != 0;
4173 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4177 public Report Report {
4179 return compiler.Report;
4184 // Used by anonymous blocks to track references of `this' variable
4186 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4188 return this_references;
4193 // Returns the "this" instance variable of this block.
4194 // See AddThisVariable() for more information.
4196 public LocalVariable ThisVariable {
4198 return this_variable;
4202 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4205 names = new Dictionary<string, object> ();
4208 if (!names.TryGetValue (name, out value)) {
4209 names.Add (name, li);
4213 INamedBlockVariable existing = value as INamedBlockVariable;
4214 List<INamedBlockVariable> existing_list;
4215 if (existing != null) {
4216 existing_list = new List<INamedBlockVariable> ();
4217 existing_list.Add (existing);
4218 names[name] = existing_list;
4220 existing_list = (List<INamedBlockVariable>) value;
4224 // A collision checking between local names
4226 var variable_block = li.Block.Explicit;
4227 for (int i = 0; i < existing_list.Count; ++i) {
4228 existing = existing_list[i];
4229 Block b = existing.Block.Explicit;
4231 // Collision at same level
4232 if (variable_block == b) {
4233 li.Block.Error_AlreadyDeclared (name, li);
4237 // Collision with parent
4238 Block parent = variable_block;
4239 while ((parent = parent.Parent) != null) {
4241 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4242 i = existing_list.Count;
4247 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4248 // Collision with children
4249 while ((b = b.Parent) != null) {
4250 if (variable_block == b) {
4251 li.Block.Error_AlreadyDeclared (name, li, "child");
4252 i = existing_list.Count;
4259 existing_list.Add (li);
4262 public void AddLabel (string name, LabeledStatement label)
4265 labels = new Dictionary<string, object> ();
4268 if (!labels.TryGetValue (name, out value)) {
4269 labels.Add (name, label);
4273 LabeledStatement existing = value as LabeledStatement;
4274 List<LabeledStatement> existing_list;
4275 if (existing != null) {
4276 existing_list = new List<LabeledStatement> ();
4277 existing_list.Add (existing);
4278 labels[name] = existing_list;
4280 existing_list = (List<LabeledStatement>) value;
4284 // A collision checking between labels
4286 for (int i = 0; i < existing_list.Count; ++i) {
4287 existing = existing_list[i];
4288 Block b = existing.Block;
4290 // Collision at same level
4291 if (label.Block == b) {
4292 Report.SymbolRelatedToPreviousError (existing.loc, name);
4293 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4297 // Collision with parent
4299 while ((b = b.Parent) != null) {
4300 if (existing.Block == b) {
4301 Report.Error (158, label.loc,
4302 "The label `{0}' shadows another label by the same name in a contained scope", name);
4303 i = existing_list.Count;
4308 // Collision with with children
4310 while ((b = b.Parent) != null) {
4311 if (label.Block == b) {
4312 Report.Error (158, label.loc,
4313 "The label `{0}' shadows another label by the same name in a contained scope", name);
4314 i = existing_list.Count;
4320 existing_list.Add (label);
4323 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4325 if (this_references == null)
4326 this_references = new List<ExplicitBlock> ();
4328 if (!this_references.Contains (block))
4329 this_references.Add (block);
4332 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4334 this_references.Remove (block);
4338 // Creates an arguments set from all parameters, useful for method proxy calls
4340 public Arguments GetAllParametersArguments ()
4342 int count = parameters.Count;
4343 Arguments args = new Arguments (count);
4344 for (int i = 0; i < count; ++i) {
4345 var pi = parameter_info[i];
4346 var arg_expr = GetParameterReference (i, pi.Location);
4348 Argument.AType atype_modifier;
4349 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4350 case Parameter.Modifier.REF:
4351 atype_modifier = Argument.AType.Ref;
4353 case Parameter.Modifier.OUT:
4354 atype_modifier = Argument.AType.Out;
4361 args.Add (new Argument (arg_expr, atype_modifier));
4368 // Lookup inside a block, the returned value can represent 3 states
4370 // true+variable: A local name was found and it's valid
4371 // false+variable: A local name was found in a child block only
4372 // false+null: No local name was found
4374 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4380 if (!names.TryGetValue (name, out value))
4383 variable = value as INamedBlockVariable;
4385 if (variable != null) {
4387 if (variable.Block == b.Original)
4391 } while (b != null);
4399 } while (b != null);
4401 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4402 for (int i = 0; i < list.Count; ++i) {
4405 if (variable.Block == b.Original)
4409 } while (b != null);
4417 } while (b != null);
4427 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4429 if (block.names != null) {
4430 foreach (var n in block.names) {
4431 var variable = n.Value as INamedBlockVariable;
4432 if (variable != null) {
4433 if (variable.Block.ParametersBlock == pb)
4434 AddLocalName (n.Key, variable, false);
4438 foreach (var v in (List<INamedBlockVariable>) n.Value)
4439 if (v.Block.ParametersBlock == pb)
4440 AddLocalName (n.Key, v, false);
4446 // This is used by non-static `struct' constructors which do not have an
4447 // initializer - in this case, the constructor must initialize all of the
4448 // struct's fields. To do this, we add a "this" variable and use the flow
4449 // analysis code to ensure that it's been fully initialized before control
4450 // leaves the constructor.
4452 public void AddThisVariable (BlockContext bc)
4454 if (this_variable != null)
4455 throw new InternalErrorException (StartLocation.ToString ());
4457 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4458 this_variable.Type = bc.CurrentType;
4459 this_variable.PrepareAssignmentAnalysis (bc);
4462 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4465 // If we're a non-static struct constructor which doesn't have an
4466 // initializer, then we must initialize all of the struct's fields.
4468 if (this_variable != null)
4469 this_variable.IsThisAssigned (fc, this);
4471 base.CheckControlExit (fc, dat);
4474 public HashSet<LocalVariable> GetUndeclaredVariables ()
4479 HashSet<LocalVariable> variables = null;
4481 foreach (var entry in names) {
4482 var complex = entry.Value as List<INamedBlockVariable>;
4483 if (complex != null) {
4484 foreach (var centry in complex) {
4485 if (IsUndeclaredVariable (centry)) {
4486 if (variables == null)
4487 variables = new HashSet<LocalVariable> ();
4489 variables.Add ((LocalVariable) centry);
4492 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4493 if (variables == null)
4494 variables = new HashSet<LocalVariable> ();
4496 variables.Add ((LocalVariable)entry.Value);
4503 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4505 var lv = namedBlockVariable as LocalVariable;
4506 return lv != null && !lv.IsDeclared;
4509 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4514 foreach (var entry in names) {
4515 var complex = entry.Value as List<INamedBlockVariable>;
4516 if (complex != null) {
4517 foreach (var centry in complex) {
4518 var lv = centry as LocalVariable;
4519 if (lv != null && undeclaredVariables.Contains (lv)) {
4524 var lv = entry.Value as LocalVariable;
4525 if (lv != null && undeclaredVariables.Contains (lv))
4531 public override void Emit (EmitContext ec)
4533 if (Report.Errors > 0)
4537 if (IsCompilerGenerated) {
4538 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4546 // If `HasReturnLabel' is set, then we already emitted a
4547 // jump to the end of the method, so we must emit a `ret'
4550 // Unfortunately, System.Reflection.Emit automatically emits
4551 // a leave to the end of a finally block. This is a problem
4552 // if no code is following the try/finally block since we may
4553 // jump to a point after the end of the method.
4554 // As a workaround, we're always creating a return label in
4557 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4558 if (ec.HasReturnLabel)
4559 ec.MarkLabel (ec.ReturnLabel);
4561 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4562 ec.Mark (EndLocation);
4564 if (ec.ReturnType.Kind != MemberKind.Void)
4565 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4567 ec.Emit (OpCodes.Ret);
4570 } catch (Exception e) {
4571 throw new InternalErrorException (e, StartLocation);
4575 public bool Resolve (BlockContext bc, IMethodData md)
4580 var errors = bc.Report.Errors;
4584 if (bc.Report.Errors > errors)
4587 MarkReachable (new Reachability ());
4589 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4590 // TODO: var md = bc.CurrentMemberDefinition;
4591 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4594 if ((flags & Flags.NoFlowAnalysis) != 0)
4597 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4600 } catch (Exception e) {
4601 throw new InternalErrorException (e, StartLocation);
4608 public class SwitchLabel : Statement
4616 // if expr == null, then it is the default case.
4618 public SwitchLabel (Expression expr, Location l)
4624 public bool IsDefault {
4626 return label == null;
4630 public Expression Label {
4636 public Location Location {
4642 public Constant Converted {
4651 public bool PatternMatching { get; set; }
4653 public bool SectionStart { get; set; }
4655 public Label GetILLabel (EmitContext ec)
4657 if (il_label == null){
4658 il_label = ec.DefineLabel ();
4661 return il_label.Value;
4664 protected override void DoEmit (EmitContext ec)
4666 ec.MarkLabel (GetILLabel (ec));
4669 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4674 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4678 public override bool Resolve (BlockContext bc)
4680 if (ResolveAndReduce (bc))
4681 bc.Switch.RegisterLabel (bc, this);
4687 // Resolves the expression, reduces it to a literal if possible
4688 // and then converts it to the requested type.
4690 bool ResolveAndReduce (BlockContext bc)
4695 var switch_statement = bc.Switch;
4697 if (PatternMatching) {
4698 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4699 return label != null;
4702 var c = label.ResolveLabelConstant (bc);
4706 if (switch_statement.IsNullable && c is NullLiteral) {
4711 if (switch_statement.IsPatternMatching) {
4712 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4716 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4717 return converted != null;
4720 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4722 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4723 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4726 protected override void CloneTo (CloneContext clonectx, Statement target)
4728 var t = (SwitchLabel) target;
4730 t.label = label.Clone (clonectx);
4733 public override object Accept (StructuralVisitor visitor)
4735 return visitor.Visit (this);
4738 public string GetSignatureForError ()
4741 if (converted == null)
4744 label = converted.GetValueAsLiteral ();
4746 return string.Format ("case {0}:", label);
4750 public class Switch : LoopStatement
4752 // structure used to hold blocks of keys while calculating table switch
4753 sealed class LabelsRange : IComparable<LabelsRange>
4755 public readonly long min;
4757 public readonly List<long> label_values;
4759 public LabelsRange (long value)
4762 label_values = new List<long> ();
4763 label_values.Add (value);
4766 public LabelsRange (long min, long max, ICollection<long> values)
4770 this.label_values = new List<long> (values);
4775 return max - min + 1;
4779 public bool AddValue (long value)
4781 var gap = value - min + 1;
4782 // Ensure the range has > 50% occupancy
4783 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4787 label_values.Add (value);
4791 public int CompareTo (LabelsRange other)
4793 int nLength = label_values.Count;
4794 int nLengthOther = other.label_values.Count;
4795 if (nLengthOther == nLength)
4796 return (int) (other.min - min);
4798 return nLength - nLengthOther;
4802 sealed class DispatchStatement : Statement
4804 readonly Switch body;
4806 public DispatchStatement (Switch body)
4811 protected override void CloneTo (CloneContext clonectx, Statement target)
4813 throw new NotImplementedException ();
4816 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4821 protected override void DoEmit (EmitContext ec)
4823 body.EmitDispatch (ec);
4827 class MissingBreak : Statement
4829 readonly SwitchLabel label;
4831 public MissingBreak (SwitchLabel sl)
4837 public bool FallOut { get; set; }
4839 protected override void DoEmit (EmitContext ec)
4843 protected override void CloneTo (CloneContext clonectx, Statement target)
4847 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4850 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4851 label.GetSignatureForError ());
4853 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4854 label.GetSignatureForError ());
4860 public Expression Expr;
4863 // Mapping of all labels to their SwitchLabels
4865 Dictionary<long, SwitchLabel> labels;
4866 Dictionary<string, SwitchLabel> string_labels;
4867 List<SwitchLabel> case_labels;
4869 List<Tuple<GotoCase, Constant>> goto_cases;
4870 List<DefiniteAssignmentBitSet> end_reachable_das;
4873 /// The governing switch type
4875 public TypeSpec SwitchType;
4877 Expression new_expr;
4879 SwitchLabel case_null;
4880 SwitchLabel case_default;
4882 Label defaultLabel, nullLabel;
4883 VariableReference value;
4884 ExpressionStatement string_dictionary;
4885 FieldExpr switch_cache_field;
4886 ExplicitBlock block;
4890 // Nullable Types support
4892 Nullable.Unwrap unwrap;
4894 public Switch (Expression e, ExplicitBlock block, Location l)
4902 public SwitchLabel ActiveLabel { get; set; }
4904 public ExplicitBlock Block {
4910 public SwitchLabel DefaultLabel {
4912 return case_default;
4916 public bool IsNullable {
4918 return unwrap != null;
4922 public bool IsPatternMatching {
4924 return new_expr == null && SwitchType != null;
4928 public List<SwitchLabel> RegisteredLabels {
4934 public VariableReference ExpressionValue {
4941 // Determines the governing type for a switch. The returned
4942 // expression might be the expression from the switch, or an
4943 // expression that includes any potential conversions to
4945 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4947 switch (expr.Type.BuiltinType) {
4948 case BuiltinTypeSpec.Type.Byte:
4949 case BuiltinTypeSpec.Type.SByte:
4950 case BuiltinTypeSpec.Type.UShort:
4951 case BuiltinTypeSpec.Type.Short:
4952 case BuiltinTypeSpec.Type.UInt:
4953 case BuiltinTypeSpec.Type.Int:
4954 case BuiltinTypeSpec.Type.ULong:
4955 case BuiltinTypeSpec.Type.Long:
4956 case BuiltinTypeSpec.Type.Char:
4957 case BuiltinTypeSpec.Type.String:
4958 case BuiltinTypeSpec.Type.Bool:
4962 if (expr.Type.IsEnum)
4966 // Try to find a *user* defined implicit conversion.
4968 // If there is no implicit conversion, or if there are multiple
4969 // conversions, we have to report an error
4971 Expression converted = null;
4972 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4974 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4977 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4978 Convert.UserConversionRestriction.ProbingOnly;
4981 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4983 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4988 // Ignore over-worked ImplicitUserConversions that do
4989 // an implicit conversion in addition to the user conversion.
4991 var uc = e as UserCast;
4995 if (converted != null){
4996 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
5005 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
5007 var types = module.Compiler.BuiltinTypes;
5009 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5010 TypeSpec[] stypes = new[] {
5023 if (nullable != null) {
5025 Array.Resize (ref stypes, stypes.Length + 9);
5027 for (int i = 0; i < 9; ++i) {
5028 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5035 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5037 case_labels.Add (sl);
5040 if (case_default != null) {
5041 sl.Error_AlreadyOccurs (rc, case_default);
5049 if (sl.Converted == null)
5053 if (string_labels != null) {
5054 string string_value = sl.Converted.GetValue () as string;
5055 if (string_value == null)
5058 string_labels.Add (string_value, sl);
5060 if (sl.Converted.IsNull) {
5063 labels.Add (sl.Converted.GetValueAsLong (), sl);
5066 } catch (ArgumentException) {
5067 if (string_labels != null)
5068 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5070 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5075 // This method emits code for a lookup-based switch statement (non-string)
5076 // Basically it groups the cases into blocks that are at least half full,
5077 // and then spits out individual lookup opcodes for each block.
5078 // It emits the longest blocks first, and short blocks are just
5079 // handled with direct compares.
5081 void EmitTableSwitch (EmitContext ec, Expression val)
5083 if (labels != null && labels.Count > 0) {
5084 List<LabelsRange> ranges;
5085 if (string_labels != null) {
5086 // We have done all hard work for string already
5087 // setup single range only
5088 ranges = new List<LabelsRange> (1);
5089 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5091 var element_keys = new long[labels.Count];
5092 labels.Keys.CopyTo (element_keys, 0);
5093 Array.Sort (element_keys);
5096 // Build possible ranges of switch labes to reduce number
5099 ranges = new List<LabelsRange> (element_keys.Length);
5100 var range = new LabelsRange (element_keys[0]);
5102 for (int i = 1; i < element_keys.Length; ++i) {
5103 var l = element_keys[i];
5104 if (range.AddValue (l))
5107 range = new LabelsRange (l);
5111 // sort the blocks so we can tackle the largest ones first
5115 Label lbl_default = defaultLabel;
5116 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5118 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5119 LabelsRange kb = ranges[range_index];
5120 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5122 // Optimize small ranges using simple equality check
5123 if (kb.Range <= 2) {
5124 foreach (var key in kb.label_values) {
5125 SwitchLabel sl = labels[key];
5126 if (sl == case_default || sl == case_null)
5129 if (sl.Converted.IsZeroInteger) {
5130 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5133 sl.Converted.Emit (ec);
5134 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5138 // TODO: if all the keys in the block are the same and there are
5139 // no gaps/defaults then just use a range-check.
5140 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5141 // TODO: optimize constant/I4 cases
5143 // check block range (could be > 2^31)
5145 ec.EmitLong (kb.min);
5146 ec.Emit (OpCodes.Blt, lbl_default);
5149 ec.EmitLong (kb.max);
5150 ec.Emit (OpCodes.Bgt, lbl_default);
5155 ec.EmitLong (kb.min);
5156 ec.Emit (OpCodes.Sub);
5159 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5163 int first = (int) kb.min;
5166 ec.Emit (OpCodes.Sub);
5167 } else if (first < 0) {
5168 ec.EmitInt (-first);
5169 ec.Emit (OpCodes.Add);
5173 // first, build the list of labels for the switch
5175 long cJumps = kb.Range;
5176 Label[] switch_labels = new Label[cJumps];
5177 for (int iJump = 0; iJump < cJumps; iJump++) {
5178 var key = kb.label_values[iKey];
5179 if (key == kb.min + iJump) {
5180 switch_labels[iJump] = labels[key].GetILLabel (ec);
5183 switch_labels[iJump] = lbl_default;
5187 // emit the switch opcode
5188 ec.Emit (OpCodes.Switch, switch_labels);
5191 // mark the default for this block
5192 if (range_index != 0)
5193 ec.MarkLabel (lbl_default);
5196 // the last default just goes to the end
5197 if (ranges.Count > 0)
5198 ec.Emit (OpCodes.Br, lbl_default);
5202 public SwitchLabel FindLabel (Constant value)
5204 SwitchLabel sl = null;
5206 if (string_labels != null) {
5207 string s = value.GetValue () as string;
5209 if (case_null != null)
5211 else if (case_default != null)
5214 string_labels.TryGetValue (s, out sl);
5217 if (value is NullLiteral) {
5220 labels.TryGetValue (value.GetValueAsLong (), out sl);
5224 if (sl == null || sl.SectionStart)
5228 // Always return section start, it simplifies handling of switch labels
5230 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5231 var cs = case_labels [idx];
5232 if (cs.SectionStart)
5237 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5239 Expr.FlowAnalysis (fc);
5241 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5242 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5243 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5245 block.FlowAnalysis (fc);
5247 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5249 if (end_reachable_das != null) {
5250 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5251 InitialDefinitiveAssignment |= sections_das;
5252 end_reachable_das = null;
5255 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5257 return case_default != null && !end_reachable;
5260 public override bool Resolve (BlockContext ec)
5262 Expr = Expr.Resolve (ec);
5267 // LAMESPEC: User conversion from non-nullable governing type has a priority
5269 new_expr = SwitchGoverningType (ec, Expr, false);
5271 if (new_expr == null) {
5272 if (Expr.Type.IsNullableType) {
5273 unwrap = Nullable.Unwrap.Create (Expr, false);
5278 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5279 // involving nullable Expr and nullable governing type is
5281 new_expr = SwitchGoverningType (ec, unwrap, true);
5285 Expression switch_expr;
5286 if (new_expr == null) {
5287 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5288 if (Expr.Type != InternalType.ErrorType) {
5289 ec.Report.Error (151, loc,
5290 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5291 Expr.Type.GetSignatureForError ());
5298 SwitchType = Expr.Type;
5300 switch_expr = new_expr;
5301 SwitchType = new_expr.Type;
5302 if (SwitchType.IsNullableType) {
5303 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5304 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5307 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5308 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5312 if (block.Statements.Count == 0)
5315 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5316 string_labels = new Dictionary<string, SwitchLabel> ();
5318 labels = new Dictionary<long, SwitchLabel> ();
5322 var constant = switch_expr as Constant;
5325 // Don't need extra variable for constant switch or switch with
5326 // only default case
5328 if (constant == null) {
5330 // Store switch expression for comparison purposes
5332 value = switch_expr as VariableReference;
5333 if (value == null && !HasOnlyDefaultSection ()) {
5334 var current_block = ec.CurrentBlock;
5335 ec.CurrentBlock = Block;
5336 // Create temporary variable inside switch scope
5337 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5339 ec.CurrentBlock = current_block;
5343 case_labels = new List<SwitchLabel> ();
5345 Switch old_switch = ec.Switch;
5347 var parent_los = ec.EnclosingLoopOrSwitch;
5348 ec.EnclosingLoopOrSwitch = this;
5350 var ok = Statement.Resolve (ec);
5352 ec.EnclosingLoopOrSwitch = parent_los;
5353 ec.Switch = old_switch;
5356 // Check if all goto cases are valid. Needs to be done after switch
5357 // is resolved because goto can jump forward in the scope.
5359 if (goto_cases != null) {
5360 foreach (var gc in goto_cases) {
5361 if (gc.Item1 == null) {
5362 if (DefaultLabel == null) {
5363 Goto.Error_UnknownLabel (ec, "default", loc);
5369 var sl = FindLabel (gc.Item2);
5371 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5373 gc.Item1.Label = sl;
5381 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5382 ResolveStringSwitchMap (ec);
5386 // Anonymous storey initialization has to happen before
5387 // any generated switch dispatch
5389 block.InsertStatement (0, new DispatchStatement (this));
5394 bool HasOnlyDefaultSection ()
5396 for (int i = 0; i < block.Statements.Count; ++i) {
5397 var s = block.Statements[i] as SwitchLabel;
5399 if (s == null || s.IsDefault)
5408 public override Reachability MarkReachable (Reachability rc)
5410 if (rc.IsUnreachable)
5413 base.MarkReachable (rc);
5415 block.MarkReachableScope (rc);
5417 if (block.Statements.Count == 0)
5420 SwitchLabel constant_label = null;
5421 var constant = new_expr as Constant;
5423 if (constant != null) {
5424 constant_label = FindLabel (constant) ?? case_default;
5425 if (constant_label == null) {
5426 block.Statements.RemoveAt (0);
5431 var section_rc = new Reachability ();
5432 SwitchLabel prev_label = null;
5434 for (int i = 0; i < block.Statements.Count; ++i) {
5435 var s = block.Statements[i];
5436 var sl = s as SwitchLabel;
5438 if (sl != null && sl.SectionStart) {
5440 // Section is marked already via goto case
5442 if (!sl.IsUnreachable) {
5443 section_rc = new Reachability ();
5447 if (section_rc.IsUnreachable) {
5449 // Common case. Previous label section end is unreachable as
5450 // it ends with break, return, etc. For next section revert
5451 // to reachable again unless we have constant switch block
5453 section_rc = constant_label != null && constant_label != sl ?
5454 Reachability.CreateUnreachable () :
5455 new Reachability ();
5456 } else if (prev_label != null) {
5458 // Error case as control cannot fall through from one case label
5460 sl.SectionStart = false;
5461 s = new MissingBreak (prev_label);
5462 s.MarkReachable (rc);
5463 block.Statements.Insert (i - 1, s);
5465 } else if (constant_label != null && constant_label != sl) {
5467 // Special case for the first unreachable label in constant
5470 section_rc = Reachability.CreateUnreachable ();
5476 section_rc = s.MarkReachable (section_rc);
5479 if (!section_rc.IsUnreachable && prev_label != null) {
5480 prev_label.SectionStart = false;
5481 var s = new MissingBreak (prev_label) {
5485 s.MarkReachable (rc);
5486 block.Statements.Add (s);
5490 // Reachability can affect parent only when all possible paths are handled but
5491 // we still need to run reachability check on switch body to check for fall-through
5493 if (case_default == null && constant_label == null)
5497 // We have at least one local exit from the switch
5502 return Reachability.CreateUnreachable ();
5505 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5507 if (goto_cases == null)
5508 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5510 goto_cases.Add (Tuple.Create (gotoCase, value));
5514 // Converts string switch into string hashtable
5516 void ResolveStringSwitchMap (ResolveContext ec)
5518 FullNamedExpression string_dictionary_type;
5519 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5520 string_dictionary_type = new TypeExpression (
5521 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5522 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5524 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5525 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5527 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5531 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5532 Field field = new Field (ctype, string_dictionary_type,
5533 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5534 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5535 if (!field.Define ())
5537 ctype.AddField (field);
5539 var init = new List<Expression> ();
5541 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5542 string value = null;
5544 foreach (SwitchLabel sl in case_labels) {
5546 if (sl.SectionStart)
5547 labels.Add (++counter, sl);
5549 if (sl == case_default || sl == case_null)
5552 value = (string) sl.Converted.GetValue ();
5553 var init_args = new List<Expression> (2);
5554 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5556 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5557 init_args.Add (sl.Converted);
5559 init.Add (new CollectionElementInitializer (init_args, loc));
5562 Arguments args = new Arguments (1);
5563 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5564 Expression initializer = new NewInitialize (string_dictionary_type, args,
5565 new CollectionOrObjectInitializers (init, loc), loc);
5567 switch_cache_field = new FieldExpr (field, loc);
5568 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5571 void DoEmitStringSwitch (EmitContext ec)
5573 Label l_initialized = ec.DefineLabel ();
5576 // Skip initialization when value is null
5578 value.EmitBranchable (ec, nullLabel, false);
5581 // Check if string dictionary is initialized and initialize
5583 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5584 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5585 string_dictionary.EmitStatement (ec);
5587 ec.MarkLabel (l_initialized);
5589 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5591 ResolveContext rc = new ResolveContext (ec.MemberContext);
5593 if (switch_cache_field.Type.IsGeneric) {
5594 Arguments get_value_args = new Arguments (2);
5595 get_value_args.Add (new Argument (value));
5596 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5597 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5598 if (get_item == null)
5602 // A value was not found, go to default case
5604 get_item.EmitBranchable (ec, defaultLabel, false);
5606 Arguments get_value_args = new Arguments (1);
5607 get_value_args.Add (new Argument (value));
5609 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5610 if (get_item == null)
5613 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5614 get_item_object.EmitAssign (ec, get_item, true, false);
5615 ec.Emit (OpCodes.Brfalse, defaultLabel);
5617 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5618 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5620 get_item_int.EmitStatement (ec);
5621 get_item_object.Release (ec);
5624 EmitTableSwitch (ec, string_switch_variable);
5625 string_switch_variable.Release (ec);
5629 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5631 void EmitShortSwitch (EmitContext ec)
5633 MethodSpec equal_method = null;
5634 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5635 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5638 if (equal_method != null) {
5639 value.EmitBranchable (ec, nullLabel, false);
5642 for (int i = 0; i < case_labels.Count; ++i) {
5643 var label = case_labels [i];
5644 if (label == case_default || label == case_null)
5647 var constant = label.Converted;
5649 if (constant == null) {
5650 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5654 if (equal_method != null) {
5658 var call = new CallEmitter ();
5659 call.EmitPredefined (ec, equal_method, new Arguments (0));
5660 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5664 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5665 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5671 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5674 ec.Emit (OpCodes.Br, defaultLabel);
5677 void EmitDispatch (EmitContext ec)
5679 if (IsPatternMatching) {
5680 EmitShortSwitch (ec);
5684 if (value == null) {
5686 // Constant switch, we've already done the work if there is only 1 label
5690 foreach (var sl in case_labels) {
5691 if (sl.IsUnreachable)
5694 if (reachable++ > 0) {
5695 var constant = (Constant) new_expr;
5696 var constant_label = FindLabel (constant) ?? case_default;
5698 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5706 if (string_dictionary != null) {
5707 DoEmitStringSwitch (ec);
5708 } else if (case_labels.Count < 4 || string_labels != null) {
5709 EmitShortSwitch (ec);
5711 EmitTableSwitch (ec, value);
5715 protected override void DoEmit (EmitContext ec)
5718 // Setup the codegen context
5720 Label old_end = ec.LoopEnd;
5721 Switch old_switch = ec.Switch;
5723 ec.LoopEnd = ec.DefineLabel ();
5726 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5727 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5729 if (value != null) {
5732 var switch_expr = new_expr ?? Expr;
5734 unwrap.EmitCheck (ec);
5735 ec.Emit (OpCodes.Brfalse, nullLabel);
5736 value.EmitAssign (ec, switch_expr, false, false);
5737 } else if (switch_expr != value) {
5738 value.EmitAssign (ec, switch_expr, false, false);
5743 // Next statement is compiler generated we don't need extra
5744 // nop when we can use the statement for sequence point
5746 ec.Mark (block.StartLocation);
5747 block.IsCompilerGenerated = true;
5749 new_expr.EmitSideEffect (ec);
5754 // Restore context state.
5755 ec.MarkLabel (ec.LoopEnd);
5758 // Restore the previous context
5760 ec.LoopEnd = old_end;
5761 ec.Switch = old_switch;
5764 protected override void CloneTo (CloneContext clonectx, Statement t)
5766 Switch target = (Switch) t;
5768 target.Expr = Expr.Clone (clonectx);
5769 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5772 public override object Accept (StructuralVisitor visitor)
5774 return visitor.Visit (this);
5777 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5779 if (case_default == null && !(new_expr is Constant))
5782 if (end_reachable_das == null)
5783 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5785 end_reachable_das.Add (fc.DefiniteAssignment);
5788 public override void SetEndReachable ()
5790 end_reachable = true;
5794 // A place where execution can restart in a state machine
5795 public abstract class ResumableStatement : Statement
5798 protected Label resume_point;
5800 public Label PrepareForEmit (EmitContext ec)
5804 resume_point = ec.DefineLabel ();
5806 return resume_point;
5809 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5814 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5819 public abstract class TryFinallyBlock : ExceptionStatement
5821 protected Statement stmt;
5822 Label dispose_try_block;
5823 bool prepared_for_dispose, emitted_dispose;
5824 Method finally_host;
5826 protected TryFinallyBlock (Statement stmt, Location loc)
5834 public Statement Statement {
5842 protected abstract void EmitTryBody (EmitContext ec);
5843 public abstract void EmitFinallyBody (EmitContext ec);
5845 public override Label PrepareForDispose (EmitContext ec, Label end)
5847 if (!prepared_for_dispose) {
5848 prepared_for_dispose = true;
5849 dispose_try_block = ec.DefineLabel ();
5851 return dispose_try_block;
5854 protected sealed override void DoEmit (EmitContext ec)
5856 EmitTryBodyPrepare (ec);
5859 bool beginFinally = EmitBeginFinallyBlock (ec);
5861 Label start_finally = ec.DefineLabel ();
5862 if (resume_points != null && beginFinally) {
5863 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5865 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5866 ec.Emit (OpCodes.Brfalse_S, start_finally);
5867 ec.Emit (OpCodes.Endfinally);
5870 ec.MarkLabel (start_finally);
5872 if (finally_host != null) {
5873 finally_host.Define ();
5874 finally_host.PrepareEmit ();
5875 finally_host.Emit ();
5877 // Now it's safe to add, to close it properly and emit sequence points
5878 finally_host.Parent.AddMember (finally_host);
5880 var ce = new CallEmitter ();
5881 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5882 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5884 EmitFinallyBody (ec);
5888 ec.EndExceptionBlock ();
5891 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5893 if (emitted_dispose)
5896 emitted_dispose = true;
5898 Label end_of_try = ec.DefineLabel ();
5900 // Ensure that the only way we can get into this code is through a dispatcher
5901 if (have_dispatcher)
5902 ec.Emit (OpCodes.Br, end);
5904 ec.BeginExceptionBlock ();
5906 ec.MarkLabel (dispose_try_block);
5908 Label[] labels = null;
5909 for (int i = 0; i < resume_points.Count; ++i) {
5910 ResumableStatement s = resume_points[i];
5911 Label ret = s.PrepareForDispose (ec, end_of_try);
5912 if (ret.Equals (end_of_try) && labels == null)
5914 if (labels == null) {
5915 labels = new Label[resume_points.Count];
5916 for (int j = 0; j < i; ++j)
5917 labels[j] = end_of_try;
5922 if (labels != null) {
5924 for (j = 1; j < labels.Length; ++j)
5925 if (!labels[0].Equals (labels[j]))
5927 bool emit_dispatcher = j < labels.Length;
5929 if (emit_dispatcher) {
5930 ec.Emit (OpCodes.Ldloc, pc);
5931 ec.EmitInt (first_resume_pc);
5932 ec.Emit (OpCodes.Sub);
5933 ec.Emit (OpCodes.Switch, labels);
5936 foreach (ResumableStatement s in resume_points)
5937 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5940 ec.MarkLabel (end_of_try);
5942 ec.BeginFinallyBlock ();
5944 if (finally_host != null) {
5945 var ce = new CallEmitter ();
5946 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5947 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5949 EmitFinallyBody (ec);
5952 ec.EndExceptionBlock ();
5955 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5957 var res = stmt.FlowAnalysis (fc);
5958 parent_try_block = null;
5962 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5964 ec.BeginFinallyBlock ();
5968 public override Reachability MarkReachable (Reachability rc)
5970 base.MarkReachable (rc);
5971 return Statement.MarkReachable (rc);
5974 public override bool Resolve (BlockContext bc)
5978 parent_try_block = bc.CurrentTryBlock;
5979 bc.CurrentTryBlock = this;
5981 if (stmt is TryCatch) {
5982 ok = stmt.Resolve (bc);
5984 using (bc.Set (ResolveContext.Options.TryScope)) {
5985 ok = stmt.Resolve (bc);
5989 bc.CurrentTryBlock = parent_try_block;
5992 // Finally block inside iterator is called from MoveNext and
5993 // Dispose methods that means we need to lift the block into
5994 // newly created host method to emit the body only once. The
5995 // original block then simply calls the newly generated method.
5997 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5998 var b = stmt as Block;
5999 if (b != null && b.Explicit.HasYield) {
6000 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
6004 return base.Resolve (bc) && ok;
6009 // Base class for blocks using exception handling
6011 public abstract class ExceptionStatement : ResumableStatement
6013 protected List<ResumableStatement> resume_points;
6014 protected int first_resume_pc;
6015 protected ExceptionStatement parent_try_block;
6016 protected int first_catch_resume_pc = -1;
6018 protected ExceptionStatement (Location loc)
6023 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6025 StateMachineInitializer state_machine = null;
6026 if (resume_points != null) {
6027 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6029 ec.EmitInt ((int) IteratorStorey.State.Running);
6030 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6034 // The resume points in catch section when this is try-catch-finally
6036 if (IsRewrittenTryCatchFinally ()) {
6037 ec.BeginExceptionBlock ();
6039 if (first_catch_resume_pc >= 0) {
6041 ec.MarkLabel (resume_point);
6043 // For normal control flow, we want to fall-through the Switch
6044 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6045 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6046 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6047 ec.Emit (OpCodes.Sub);
6049 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6050 for (int i = 0; i < labels.Length; ++i)
6051 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6052 ec.Emit (OpCodes.Switch, labels);
6056 ec.BeginExceptionBlock ();
6059 // The resume points for try section
6061 if (resume_points != null && first_catch_resume_pc != 0) {
6062 if (first_catch_resume_pc < 0)
6063 ec.MarkLabel (resume_point);
6065 // For normal control flow, we want to fall-through the Switch
6066 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6067 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6068 ec.EmitInt (first_resume_pc);
6069 ec.Emit (OpCodes.Sub);
6071 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6072 for (int i = 0; i < labels.Length; ++i)
6073 labels[i] = resume_points[i].PrepareForEmit (ec);
6074 ec.Emit (OpCodes.Switch, labels);
6078 bool IsRewrittenTryCatchFinally ()
6080 var tf = this as TryFinally;
6084 var tc = tf.Statement as TryCatch;
6088 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6091 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6093 if (parent_try_block != null) {
6094 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6096 pc = stateMachine.AddResumePoint (this);
6099 if (resume_points == null) {
6100 resume_points = new List<ResumableStatement> ();
6101 first_resume_pc = pc;
6104 if (pc != first_resume_pc + resume_points.Count)
6105 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6107 var tf = this as TryFinally;
6108 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6109 first_catch_resume_pc = resume_points.Count;
6112 resume_points.Add (stmt);
6117 public class Lock : TryFinallyBlock
6120 TemporaryVariableReference expr_copy;
6121 TemporaryVariableReference lock_taken;
6123 public Lock (Expression expr, Statement stmt, Location loc)
6129 public Expression Expr {
6135 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6137 expr.FlowAnalysis (fc);
6138 return base.DoFlowAnalysis (fc);
6141 public override bool Resolve (BlockContext ec)
6143 expr = expr.Resolve (ec);
6147 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6148 ec.Report.Error (185, loc,
6149 "`{0}' is not a reference type as required by the lock statement",
6150 expr.Type.GetSignatureForError ());
6153 if (expr.Type.IsGenericParameter) {
6154 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6157 VariableReference lv = expr as VariableReference;
6160 locked = lv.IsLockedByStatement;
6161 lv.IsLockedByStatement = true;
6168 // Have to keep original lock value around to unlock same location
6169 // in the case of original value has changed or is null
6171 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6172 expr_copy.Resolve (ec);
6175 // Ensure Monitor methods are available
6177 if (ResolvePredefinedMethods (ec) > 1) {
6178 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6179 lock_taken.Resolve (ec);
6182 using (ec.Set (ResolveContext.Options.LockScope)) {
6187 lv.IsLockedByStatement = locked;
6193 protected override void EmitTryBodyPrepare (EmitContext ec)
6195 expr_copy.EmitAssign (ec, expr);
6197 if (lock_taken != null) {
6199 // Initialize ref variable
6201 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6204 // Monitor.Enter (expr_copy)
6206 expr_copy.Emit (ec);
6207 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6210 base.EmitTryBodyPrepare (ec);
6213 protected override void EmitTryBody (EmitContext ec)
6216 // Monitor.Enter (expr_copy, ref lock_taken)
6218 if (lock_taken != null) {
6219 expr_copy.Emit (ec);
6220 lock_taken.LocalInfo.CreateBuilder (ec);
6221 lock_taken.AddressOf (ec, AddressOp.Load);
6222 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6225 Statement.Emit (ec);
6228 public override void EmitFinallyBody (EmitContext ec)
6231 // if (lock_taken) Monitor.Exit (expr_copy)
6233 Label skip = ec.DefineLabel ();
6235 if (lock_taken != null) {
6236 lock_taken.Emit (ec);
6237 ec.Emit (OpCodes.Brfalse_S, skip);
6240 expr_copy.Emit (ec);
6241 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6243 ec.Emit (OpCodes.Call, m);
6245 ec.MarkLabel (skip);
6248 int ResolvePredefinedMethods (ResolveContext rc)
6250 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6251 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6255 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6259 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6263 protected override void CloneTo (CloneContext clonectx, Statement t)
6265 Lock target = (Lock) t;
6267 target.expr = expr.Clone (clonectx);
6268 target.stmt = Statement.Clone (clonectx);
6271 public override object Accept (StructuralVisitor visitor)
6273 return visitor.Visit (this);
6278 public class Unchecked : Statement {
6281 public Unchecked (Block b, Location loc)
6288 public override bool Resolve (BlockContext ec)
6290 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6291 return Block.Resolve (ec);
6294 protected override void DoEmit (EmitContext ec)
6296 using (ec.With (EmitContext.Options.CheckedScope, false))
6300 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6302 return Block.FlowAnalysis (fc);
6305 public override Reachability MarkReachable (Reachability rc)
6307 base.MarkReachable (rc);
6308 return Block.MarkReachable (rc);
6311 protected override void CloneTo (CloneContext clonectx, Statement t)
6313 Unchecked target = (Unchecked) t;
6315 target.Block = clonectx.LookupBlock (Block);
6318 public override object Accept (StructuralVisitor visitor)
6320 return visitor.Visit (this);
6324 public class Checked : Statement {
6327 public Checked (Block b, Location loc)
6330 b.Unchecked = false;
6334 public override bool Resolve (BlockContext ec)
6336 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6337 return Block.Resolve (ec);
6340 protected override void DoEmit (EmitContext ec)
6342 using (ec.With (EmitContext.Options.CheckedScope, true))
6346 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6348 return Block.FlowAnalysis (fc);
6351 public override Reachability MarkReachable (Reachability rc)
6353 base.MarkReachable (rc);
6354 return Block.MarkReachable (rc);
6357 protected override void CloneTo (CloneContext clonectx, Statement t)
6359 Checked target = (Checked) t;
6361 target.Block = clonectx.LookupBlock (Block);
6364 public override object Accept (StructuralVisitor visitor)
6366 return visitor.Visit (this);
6370 public class Unsafe : Statement {
6373 public Unsafe (Block b, Location loc)
6376 Block.Unsafe = true;
6380 public override bool Resolve (BlockContext ec)
6382 if (ec.CurrentIterator != null)
6383 Expression.UnsafeInsideIteratorError (ec, loc);
6385 using (ec.Set (ResolveContext.Options.UnsafeScope))
6386 return Block.Resolve (ec);
6389 protected override void DoEmit (EmitContext ec)
6394 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6396 return Block.FlowAnalysis (fc);
6399 public override Reachability MarkReachable (Reachability rc)
6401 base.MarkReachable (rc);
6402 return Block.MarkReachable (rc);
6405 protected override void CloneTo (CloneContext clonectx, Statement t)
6407 Unsafe target = (Unsafe) t;
6409 target.Block = clonectx.LookupBlock (Block);
6412 public override object Accept (StructuralVisitor visitor)
6414 return visitor.Visit (this);
6421 public class Fixed : Statement
6423 abstract class Emitter : ShimExpression
6425 protected LocalVariable vi;
6427 protected Emitter (Expression expr, LocalVariable li)
6433 public abstract void EmitExit (EmitContext ec);
6435 public override void FlowAnalysis (FlowAnalysisContext fc)
6437 expr.FlowAnalysis (fc);
6441 sealed class ExpressionEmitter : Emitter {
6442 public ExpressionEmitter (Expression converted, LocalVariable li)
6443 : base (converted, li)
6447 protected override Expression DoResolve (ResolveContext rc)
6449 throw new NotImplementedException ();
6452 public override void Emit (EmitContext ec) {
6454 // Store pointer in pinned location
6460 public override void EmitExit (EmitContext ec)
6463 ec.Emit (OpCodes.Conv_U);
6468 class StringEmitter : Emitter
6470 LocalVariable pinned_string;
6472 public StringEmitter (Expression expr, LocalVariable li)
6477 protected override Expression DoResolve (ResolveContext rc)
6479 pinned_string = new LocalVariable (vi.Block, "$pinned",
6480 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6482 pinned_string.Type = rc.BuiltinTypes.String;
6485 eclass = ExprClass.Variable;
6486 type = rc.BuiltinTypes.Int;
6490 public override void Emit (EmitContext ec)
6492 pinned_string.CreateBuilder (ec);
6495 pinned_string.EmitAssign (ec);
6497 // TODO: Should use Binary::Add
6498 pinned_string.Emit (ec);
6499 ec.Emit (OpCodes.Conv_I);
6501 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6505 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6506 //pe.InstanceExpression = pinned_string;
6507 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6509 ec.Emit (OpCodes.Add);
6513 public override void EmitExit (EmitContext ec)
6516 pinned_string.EmitAssign (ec);
6520 public class VariableDeclaration : BlockVariable
6522 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6527 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6529 if (!Variable.Type.IsPointer && li == Variable) {
6530 bc.Report.Error (209, TypeExpression.Location,
6531 "The type of locals declared in a fixed statement must be a pointer type");
6535 var res = initializer.Resolve (bc);
6542 var ac = res.Type as ArrayContainer;
6544 TypeSpec array_type = ac.Element;
6547 // Provided that array_type is unmanaged,
6549 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6552 Expression res_init;
6553 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6556 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6557 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6558 res = expr_variable.CreateReferenceExpression (bc, loc);
6562 // and T* is implicitly convertible to the
6563 // pointer type given in the fixed statement.
6565 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6567 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6568 if (converted == null)
6572 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6574 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6575 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6576 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6577 new NullLiteral (loc),
6580 converted = converted.Resolve (bc);
6582 return new ExpressionEmitter (converted, li);
6588 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6589 return new StringEmitter (res, li).Resolve (bc);
6592 // Case 3: fixed buffer
6593 if (res is FixedBufferPtr) {
6594 return new ExpressionEmitter (res, li);
6597 bool already_fixed = true;
6600 // Case 4: & object.
6602 Unary u = res as Unary;
6604 if (u.Oper == Unary.Operator.AddressOf) {
6605 IVariableReference vr = u.Expr as IVariableReference;
6606 if (vr == null || !vr.IsFixed) {
6607 already_fixed = false;
6610 } else if (initializer is Cast) {
6611 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6615 if (already_fixed) {
6616 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6619 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6620 return new ExpressionEmitter (res, li);
6625 VariableDeclaration decl;
6626 Statement statement;
6629 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6638 public Statement Statement {
6644 public BlockVariable Variables {
6652 public override bool Resolve (BlockContext bc)
6654 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6655 if (!decl.Resolve (bc))
6659 return statement.Resolve (bc);
6662 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6664 decl.FlowAnalysis (fc);
6665 return statement.FlowAnalysis (fc);
6668 protected override void DoEmit (EmitContext ec)
6670 decl.Variable.CreateBuilder (ec);
6671 decl.Initializer.Emit (ec);
6672 if (decl.Declarators != null) {
6673 foreach (var d in decl.Declarators) {
6674 d.Variable.CreateBuilder (ec);
6675 d.Initializer.Emit (ec);
6679 statement.Emit (ec);
6685 // Clear the pinned variable
6687 ((Emitter) decl.Initializer).EmitExit (ec);
6688 if (decl.Declarators != null) {
6689 foreach (var d in decl.Declarators) {
6690 ((Emitter)d.Initializer).EmitExit (ec);
6695 public override Reachability MarkReachable (Reachability rc)
6697 base.MarkReachable (rc);
6699 decl.MarkReachable (rc);
6701 rc = statement.MarkReachable (rc);
6703 // TODO: What if there is local exit?
6704 has_ret = rc.IsUnreachable;
6708 protected override void CloneTo (CloneContext clonectx, Statement t)
6710 Fixed target = (Fixed) t;
6712 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6713 target.statement = statement.Clone (clonectx);
6716 public override object Accept (StructuralVisitor visitor)
6718 return visitor.Visit (this);
6722 public class Catch : Statement
6724 class CatchVariableStore : Statement
6726 readonly Catch ctch;
6728 public CatchVariableStore (Catch ctch)
6733 protected override void CloneTo (CloneContext clonectx, Statement target)
6737 protected override void DoEmit (EmitContext ec)
6739 // Emits catch variable debug information inside correct block
6740 ctch.EmitCatchVariableStore (ec);
6743 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6749 class FilterStatement : Statement
6751 readonly Catch ctch;
6753 public FilterStatement (Catch ctch)
6758 protected override void CloneTo (CloneContext clonectx, Statement target)
6762 protected override void DoEmit (EmitContext ec)
6764 if (ctch.li != null) {
6765 if (ctch.hoisted_temp != null)
6766 ctch.hoisted_temp.Emit (ec);
6770 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6771 ec.Emit (OpCodes.Box, ctch.type);
6774 var expr_start = ec.DefineLabel ();
6775 var end = ec.DefineLabel ();
6777 ec.Emit (OpCodes.Brtrue_S, expr_start);
6779 ec.Emit (OpCodes.Br, end);
6780 ec.MarkLabel (expr_start);
6782 ctch.Filter.Emit (ec);
6785 ec.Emit (OpCodes.Endfilter);
6786 ec.BeginFilterHandler ();
6787 ec.Emit (OpCodes.Pop);
6790 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6792 ctch.Filter.FlowAnalysis (fc);
6796 public override bool Resolve (BlockContext bc)
6798 ctch.Filter = ctch.Filter.Resolve (bc);
6800 if (ctch.Filter != null) {
6801 if (ctch.Filter.ContainsEmitWithAwait ()) {
6802 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6805 var c = ctch.Filter as Constant;
6806 if (c != null && !c.IsDefaultValue) {
6807 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6815 ExplicitBlock block;
6817 FullNamedExpression type_expr;
6818 CompilerAssign assign;
6820 LocalTemporary hoisted_temp;
6822 public Catch (ExplicitBlock block, Location loc)
6830 public ExplicitBlock Block {
6836 public TypeSpec CatchType {
6842 public Expression Filter {
6846 public bool IsGeneral {
6848 return type_expr == null;
6852 public FullNamedExpression TypeExpression {
6861 public LocalVariable Variable {
6872 protected override void DoEmit (EmitContext ec)
6874 if (Filter != null) {
6875 ec.BeginExceptionFilterBlock ();
6876 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6878 if (Block.HasAwait) {
6879 Block.EmitScopeInitialization (ec);
6888 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6890 ec.BeginCatchBlock (CatchType);
6893 ec.Emit (OpCodes.Pop);
6895 if (Block.HasAwait) {
6897 EmitCatchVariableStore (ec);
6903 void EmitCatchVariableStore (EmitContext ec)
6905 li.CreateBuilder (ec);
6908 // For hoisted catch variable we have to use a temporary local variable
6909 // for captured variable initialization during storey setup because variable
6910 // needs to be on the stack after storey instance for stfld operation
6912 if (li.HoistedVariant != null) {
6913 hoisted_temp = new LocalTemporary (li.Type);
6914 hoisted_temp.Store (ec);
6916 // switch to assignment from temporary variable and not from top of the stack
6917 assign.UpdateSource (hoisted_temp);
6921 public override bool Resolve (BlockContext bc)
6923 using (bc.Set (ResolveContext.Options.CatchScope)) {
6924 if (type_expr == null) {
6925 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6926 if (!block.HasAwait || Filter != null)
6927 block.AddScopeStatement (new CatchVariableStore (this));
6929 Expression source = new EmptyExpression (li.Type);
6930 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6931 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6934 type = type_expr.ResolveAsType (bc);
6939 CreateExceptionVariable (type);
6941 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6942 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6943 } else if (li != null) {
6945 li.PrepareAssignmentAnalysis (bc);
6947 // source variable is at the top of the stack
6948 Expression source = new EmptyExpression (li.Type);
6949 if (li.Type.IsGenericParameter)
6950 source = new UnboxCast (source, li.Type);
6952 if (!block.HasAwait || Filter != null)
6953 block.AddScopeStatement (new CatchVariableStore (this));
6956 // Uses Location.Null to hide from symbol file
6958 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6959 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6963 if (Filter != null) {
6964 Block.AddScopeStatement (new FilterStatement (this));
6967 Block.SetCatchBlock ();
6968 return Block.Resolve (bc);
6972 bool CreateExceptionVariable (TypeSpec type)
6974 if (!Block.HasAwait)
6977 // TODO: Scan the block for rethrow expression
6978 //if (!Block.HasRethrow)
6981 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6985 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6987 if (li != null && !li.IsCompilerGenerated) {
6988 fc.SetVariableAssigned (li.VariableInfo, true);
6991 return block.FlowAnalysis (fc);
6994 public override Reachability MarkReachable (Reachability rc)
6996 base.MarkReachable (rc);
6998 var c = Filter as Constant;
6999 if (c != null && c.IsDefaultValue)
7000 return Reachability.CreateUnreachable ();
7002 return block.MarkReachable (rc);
7005 protected override void CloneTo (CloneContext clonectx, Statement t)
7007 Catch target = (Catch) t;
7009 if (type_expr != null)
7010 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7013 target.Filter = Filter.Clone (clonectx);
7015 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7019 public class TryFinally : TryFinallyBlock
7022 List<DefiniteAssignmentBitSet> try_exit_dat;
7023 List<Tuple<Label, bool>> redirected_jumps;
7024 Label? start_fin_label;
7026 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7032 public ExplicitBlock FinallyBlock {
7038 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7040 if (try_exit_dat == null)
7041 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7043 try_exit_dat.Add (vector);
7046 public override bool Resolve (BlockContext bc)
7048 bool ok = base.Resolve (bc);
7050 fini.SetFinallyBlock ();
7051 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7052 ok &= fini.Resolve (bc);
7058 protected override void EmitTryBody (EmitContext ec)
7060 if (fini.HasAwait) {
7061 if (ec.TryFinallyUnwind == null)
7062 ec.TryFinallyUnwind = new List<TryFinally> ();
7064 ec.TryFinallyUnwind.Add (this);
7067 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7068 ec.EndExceptionBlock ();
7070 ec.TryFinallyUnwind.Remove (this);
7072 if (start_fin_label != null)
7073 ec.MarkLabel (start_fin_label.Value);
7081 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7086 return base.EmitBeginFinallyBlock (ec);
7089 public override void EmitFinallyBody (EmitContext ec)
7091 if (!fini.HasAwait) {
7097 // Emits catch block like
7099 // catch (object temp) {
7100 // this.exception_field = temp;
7103 var type = ec.BuiltinTypes.Object;
7104 ec.BeginCatchBlock (type);
7106 var temp = ec.GetTemporaryLocal (type);
7107 ec.Emit (OpCodes.Stloc, temp);
7109 var exception_field = ec.GetTemporaryField (type);
7110 exception_field.AutomaticallyReuse = false;
7112 ec.Emit (OpCodes.Ldloc, temp);
7113 exception_field.EmitAssignFromStack (ec);
7115 ec.EndExceptionBlock ();
7117 ec.FreeTemporaryLocal (temp, type);
7122 // Emits exception rethrow
7124 // if (this.exception_field != null)
7125 // throw this.exception_field;
7127 exception_field.Emit (ec);
7128 var skip_throw = ec.DefineLabel ();
7129 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7130 exception_field.Emit (ec);
7131 ec.Emit (OpCodes.Throw);
7132 ec.MarkLabel (skip_throw);
7134 exception_field.PrepareCleanup (ec);
7136 EmitUnwindFinallyTable (ec);
7139 bool IsParentBlock (Block block)
7141 for (Block b = fini; b != null; b = b.Parent) {
7149 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7152 if (labelBlock != null) {
7153 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7154 var fin = ec.TryFinallyUnwind [idx - 1];
7155 if (!fin.IsParentBlock (labelBlock))
7162 bool set_return_state = true;
7164 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7165 var fin = ec.TryFinallyUnwind [idx];
7166 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7169 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7170 set_return_state = false;
7172 if (fin.start_fin_label == null) {
7173 fin.start_fin_label = ec.DefineLabel ();
7176 label = fin.start_fin_label.Value;
7182 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7184 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7187 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7189 if (redirected_jumps == null) {
7190 redirected_jumps = new List<Tuple<Label, bool>> ();
7192 // Add fallthrough label
7193 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7196 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7199 int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7201 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7202 index = redirected_jumps.Count - 1;
7206 // Indicates we have captured exit jump
7208 if (setReturnState) {
7209 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7210 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7215 // Emits state table of jumps outside of try block and reload of return
7216 // value when try block returns value
7218 void EmitUnwindFinallyTable (EmitContext ec)
7220 if (redirected_jumps == null)
7223 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7224 initializer.HoistedReturnState.EmitLoad (ec);
7226 var jumps_table = new Label [redirected_jumps.Count];
7227 List<Tuple<Label, Label>> leave_redirect = null;
7228 for (int i = 0; i < jumps_table.Length; ++i) {
7229 var val = redirected_jumps [i];
7232 if (leave_redirect == null)
7233 leave_redirect = new List<Tuple<Label, Label>> ();
7234 var label = ec.DefineLabel ();
7235 leave_redirect.Add (Tuple.Create (label, val.Item1));
7236 jumps_table [i] = label;
7238 jumps_table [i] = val.Item1;
7242 ec.Emit (OpCodes.Switch, jumps_table);
7244 if (leave_redirect != null) {
7245 foreach (var entry in leave_redirect) {
7246 ec.MarkLabel (entry.Item1);
7247 ec.Emit (OpCodes.Leave, entry.Item2);
7251 // Mark fallthrough label
7252 ec.MarkLabel (jumps_table [0]);
7255 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7257 var da = fc.BranchDefiniteAssignment ();
7259 var tf = fc.TryFinally;
7260 fc.TryFinally = this;
7262 var res_stmt = Statement.FlowAnalysis (fc);
7266 var try_da = fc.DefiniteAssignment;
7267 fc.DefiniteAssignment = da;
7269 var res_fin = fini.FlowAnalysis (fc);
7271 if (try_exit_dat != null) {
7273 // try block has global exit but we need to run definite assignment check
7274 // for parameter block out parameter after finally block because it's always
7275 // executed before exit
7277 foreach (var try_da_part in try_exit_dat)
7278 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7280 try_exit_dat = null;
7283 fc.DefiniteAssignment |= try_da;
7284 return res_stmt | res_fin;
7287 public override Reachability MarkReachable (Reachability rc)
7290 // Mark finally block first for any exit statement in try block
7291 // to know whether the code which follows finally is reachable
7293 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7296 protected override void CloneTo (CloneContext clonectx, Statement t)
7298 TryFinally target = (TryFinally) t;
7300 target.stmt = stmt.Clone (clonectx);
7302 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7305 public override object Accept (StructuralVisitor visitor)
7307 return visitor.Visit (this);
7311 public class TryCatch : ExceptionStatement
7314 List<Catch> clauses;
7315 readonly bool inside_try_finally;
7316 List<Catch> catch_sm;
7318 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7322 this.clauses = catch_clauses;
7323 this.inside_try_finally = inside_try_finally;
7326 public List<Catch> Clauses {
7332 public bool HasClauseWithAwait {
7334 return catch_sm != null;
7338 public bool IsTryCatchFinally {
7340 return inside_try_finally;
7344 public override bool Resolve (BlockContext bc)
7348 using (bc.Set (ResolveContext.Options.TryScope)) {
7350 parent_try_block = bc.CurrentTryBlock;
7352 if (IsTryCatchFinally) {
7353 ok = Block.Resolve (bc);
7355 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7356 bc.CurrentTryBlock = this;
7357 ok = Block.Resolve (bc);
7358 bc.CurrentTryBlock = parent_try_block;
7363 var prev_catch = bc.CurrentTryCatch;
7364 bc.CurrentTryCatch = this;
7366 for (int i = 0; i < clauses.Count; ++i) {
7369 ok &= c.Resolve (bc);
7371 if (c.Block.HasAwait) {
7372 if (catch_sm == null)
7373 catch_sm = new List<Catch> ();
7378 if (c.Filter != null)
7381 TypeSpec resolved_type = c.CatchType;
7382 if (resolved_type == null)
7385 for (int ii = 0; ii < clauses.Count; ++ii) {
7389 if (clauses[ii].Filter != null)
7392 if (clauses[ii].IsGeneral) {
7393 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7396 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7399 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7402 bc.Report.Warning (1058, 1, c.loc,
7403 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7411 var ct = clauses[ii].CatchType;
7415 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7416 bc.Report.Error (160, c.loc,
7417 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7418 ct.GetSignatureForError ());
7424 bc.CurrentTryCatch = prev_catch;
7426 return base.Resolve (bc) && ok;
7429 protected sealed override void DoEmit (EmitContext ec)
7431 if (!inside_try_finally)
7432 EmitTryBodyPrepare (ec);
7436 LocalBuilder state_variable = null;
7437 foreach (Catch c in clauses) {
7440 if (catch_sm != null) {
7441 if (state_variable == null) {
7443 // Cannot reuse temp variable because non-catch path assumes the value is 0
7444 // which may not be true for reused local variable
7446 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7449 var index = catch_sm.IndexOf (c);
7453 ec.EmitInt (index + 1);
7454 ec.Emit (OpCodes.Stloc, state_variable);
7458 if (state_variable == null) {
7459 if (!inside_try_finally)
7460 ec.EndExceptionBlock ();
7462 ec.EndExceptionBlock ();
7464 ec.Emit (OpCodes.Ldloc, state_variable);
7466 var labels = new Label [catch_sm.Count + 1];
7467 for (int i = 0; i < labels.Length; ++i) {
7468 labels [i] = ec.DefineLabel ();
7471 var end = ec.DefineLabel ();
7472 ec.Emit (OpCodes.Switch, labels);
7474 // 0 value is default label
7475 ec.MarkLabel (labels [0]);
7476 ec.Emit (OpCodes.Br, end);
7478 var atv = ec.AsyncThrowVariable;
7480 for (int i = 0; i < catch_sm.Count; ++i) {
7481 if (c != null && c.Block.HasReachableClosingBrace)
7482 ec.Emit (OpCodes.Br, end);
7484 ec.MarkLabel (labels [i + 1]);
7487 ec.Emit (OpCodes.Stloc, state_variable);
7490 ec.AsyncThrowVariable = c.Variable;
7493 ec.AsyncThrowVariable = atv;
7499 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7501 var start_fc = fc.BranchDefiniteAssignment ();
7502 var res = Block.FlowAnalysis (fc);
7504 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7506 foreach (var c in clauses) {
7507 fc.BranchDefiniteAssignment (start_fc);
7508 if (!c.FlowAnalysis (fc)) {
7510 try_fc = fc.DefiniteAssignment;
7512 try_fc &= fc.DefiniteAssignment;
7518 fc.DefiniteAssignment = try_fc ?? start_fc;
7519 parent_try_block = null;
7523 public override Reachability MarkReachable (Reachability rc)
7525 if (rc.IsUnreachable)
7528 base.MarkReachable (rc);
7530 var tc_rc = Block.MarkReachable (rc);
7532 foreach (var c in clauses)
7533 tc_rc &= c.MarkReachable (rc);
7538 protected override void CloneTo (CloneContext clonectx, Statement t)
7540 TryCatch target = (TryCatch) t;
7542 target.Block = clonectx.LookupBlock (Block);
7543 if (clauses != null){
7544 target.clauses = new List<Catch> ();
7545 foreach (Catch c in clauses)
7546 target.clauses.Add ((Catch) c.Clone (clonectx));
7550 public override object Accept (StructuralVisitor visitor)
7552 return visitor.Visit (this);
7556 public class Using : TryFinallyBlock
7558 public class VariableDeclaration : BlockVariable
7560 Statement dispose_call;
7562 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7567 public VariableDeclaration (LocalVariable li, Location loc)
7574 public VariableDeclaration (Expression expr)
7577 loc = expr.Location;
7583 public bool IsNested { get; private set; }
7587 public void EmitDispose (EmitContext ec)
7589 dispose_call.Emit (ec);
7592 public override bool Resolve (BlockContext bc)
7597 return base.Resolve (bc, false);
7600 public Expression ResolveExpression (BlockContext bc)
7602 var e = Initializer.Resolve (bc);
7606 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7607 Initializer = ResolveInitializer (bc, Variable, e);
7611 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7613 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7614 initializer = initializer.Resolve (bc);
7615 if (initializer == null)
7618 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7619 Arguments args = new Arguments (1);
7620 args.Add (new Argument (initializer));
7621 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7622 if (initializer == null)
7625 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7626 dispose_call = CreateDisposeCall (bc, var);
7627 dispose_call.Resolve (bc);
7629 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7632 if (li == Variable) {
7633 CheckIDiposableConversion (bc, li, initializer);
7634 dispose_call = CreateDisposeCall (bc, li);
7635 dispose_call.Resolve (bc);
7638 return base.ResolveInitializer (bc, li, initializer);
7641 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7645 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7646 if (type.IsNullableType) {
7647 // it's handled in CreateDisposeCall
7651 if (type != InternalType.ErrorType) {
7652 bc.Report.SymbolRelatedToPreviousError (type);
7653 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7654 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7655 type.GetSignatureForError ());
7662 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7664 var target = bc.BuiltinTypes.IDisposable;
7665 var tp = type as TypeParameterSpec;
7667 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7669 return type.ImplementsInterface (target, false);
7672 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7674 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7676 var loc = lv.Location;
7678 var idt = bc.BuiltinTypes.IDisposable;
7679 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7681 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7682 dispose_mg.InstanceExpression = type.IsNullableType ?
7683 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7687 // Hide it from symbol file via null location
7689 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7691 // Add conditional call when disposing possible null variable
7692 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7693 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7698 public void ResolveDeclaratorInitializer (BlockContext bc)
7700 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7703 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7705 for (int i = declarators.Count - 1; i >= 0; --i) {
7706 var d = declarators [i];
7707 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7708 vd.Initializer = d.Initializer;
7710 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7711 vd.dispose_call.Resolve (bc);
7713 stmt = new Using (vd, stmt, d.Variable.Location);
7720 public override object Accept (StructuralVisitor visitor)
7722 return visitor.Visit (this);
7726 VariableDeclaration decl;
7728 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7734 public Using (Expression expr, Statement stmt, Location loc)
7737 this.decl = new VariableDeclaration (expr);
7742 public Expression Expr {
7744 return decl.Variable == null ? decl.Initializer : null;
7748 public BlockVariable Variables {
7756 public override void Emit (EmitContext ec)
7759 // Don't emit sequence point it will be set on variable declaration
7764 protected override void EmitTryBodyPrepare (EmitContext ec)
7767 base.EmitTryBodyPrepare (ec);
7770 protected override void EmitTryBody (EmitContext ec)
7775 public override void EmitFinallyBody (EmitContext ec)
7777 decl.EmitDispose (ec);
7780 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7782 decl.FlowAnalysis (fc);
7783 return stmt.FlowAnalysis (fc);
7786 public override Reachability MarkReachable (Reachability rc)
7788 decl.MarkReachable (rc);
7789 return base.MarkReachable (rc);
7792 public override bool Resolve (BlockContext ec)
7794 VariableReference vr;
7795 bool vr_locked = false;
7797 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7798 if (decl.Variable == null) {
7799 vr = decl.ResolveExpression (ec) as VariableReference;
7801 vr_locked = vr.IsLockedByStatement;
7802 vr.IsLockedByStatement = true;
7805 if (decl.IsNested) {
7806 decl.ResolveDeclaratorInitializer (ec);
7808 if (!decl.Resolve (ec))
7811 if (decl.Declarators != null) {
7812 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7820 var ok = base.Resolve (ec);
7823 vr.IsLockedByStatement = vr_locked;
7828 protected override void CloneTo (CloneContext clonectx, Statement t)
7830 Using target = (Using) t;
7832 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7833 target.stmt = stmt.Clone (clonectx);
7836 public override object Accept (StructuralVisitor visitor)
7838 return visitor.Visit (this);
7843 /// Implementation of the foreach C# statement
7845 public class Foreach : LoopStatement
7847 abstract class IteratorStatement : Statement
7849 protected readonly Foreach for_each;
7851 protected IteratorStatement (Foreach @foreach)
7853 this.for_each = @foreach;
7854 this.loc = @foreach.expr.Location;
7857 protected override void CloneTo (CloneContext clonectx, Statement target)
7859 throw new NotImplementedException ();
7862 public override void Emit (EmitContext ec)
7864 if (ec.EmitAccurateDebugInfo) {
7865 ec.Emit (OpCodes.Nop);
7871 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7873 throw new NotImplementedException ();
7877 sealed class ArrayForeach : IteratorStatement
7879 TemporaryVariableReference[] lengths;
7880 Expression [] length_exprs;
7881 StatementExpression[] counter;
7882 TemporaryVariableReference[] variables;
7884 TemporaryVariableReference copy;
7886 public ArrayForeach (Foreach @foreach, int rank)
7889 counter = new StatementExpression[rank];
7890 variables = new TemporaryVariableReference[rank];
7891 length_exprs = new Expression [rank];
7894 // Only use temporary length variables when dealing with
7895 // multi-dimensional arrays
7898 lengths = new TemporaryVariableReference [rank];
7901 public override bool Resolve (BlockContext ec)
7903 Block variables_block = for_each.variable.Block;
7904 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7907 int rank = length_exprs.Length;
7908 Arguments list = new Arguments (rank);
7909 for (int i = 0; i < rank; i++) {
7910 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7912 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7913 counter[i].Resolve (ec);
7916 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7918 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7919 lengths[i].Resolve (ec);
7921 Arguments args = new Arguments (1);
7922 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7923 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7926 list.Add (new Argument (v));
7929 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7934 if (for_each.type is VarExpr) {
7935 // Infer implicitly typed local variable from foreach array type
7936 var_type = access.Type;
7938 var_type = for_each.type.ResolveAsType (ec);
7940 if (var_type == null)
7943 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7948 for_each.variable.Type = var_type;
7950 var prev_block = ec.CurrentBlock;
7951 ec.CurrentBlock = variables_block;
7952 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7953 ec.CurrentBlock = prev_block;
7955 if (variable_ref == null)
7958 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7960 return for_each.body.Resolve (ec);
7963 protected override void DoEmit (EmitContext ec)
7965 copy.EmitAssign (ec, for_each.expr);
7967 int rank = length_exprs.Length;
7968 Label[] test = new Label [rank];
7969 Label[] loop = new Label [rank];
7971 for (int i = 0; i < rank; i++) {
7972 test [i] = ec.DefineLabel ();
7973 loop [i] = ec.DefineLabel ();
7975 if (lengths != null)
7976 lengths [i].EmitAssign (ec, length_exprs [i]);
7979 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7980 for (int i = 0; i < rank; i++) {
7981 variables [i].EmitAssign (ec, zero);
7983 ec.Emit (OpCodes.Br, test [i]);
7984 ec.MarkLabel (loop [i]);
7987 for_each.body.Emit (ec);
7989 ec.MarkLabel (ec.LoopBegin);
7990 ec.Mark (for_each.expr.Location);
7992 for (int i = rank - 1; i >= 0; i--){
7993 counter [i].Emit (ec);
7995 ec.MarkLabel (test [i]);
7996 variables [i].Emit (ec);
7998 if (lengths != null)
7999 lengths [i].Emit (ec);
8001 length_exprs [i].Emit (ec);
8003 ec.Emit (OpCodes.Blt, loop [i]);
8006 ec.MarkLabel (ec.LoopEnd);
8010 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8012 class RuntimeDispose : Using.VariableDeclaration
8014 public RuntimeDispose (LocalVariable lv, Location loc)
8020 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8022 // Defered to runtime check
8025 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8027 var idt = bc.BuiltinTypes.IDisposable;
8030 // Fabricates code like
8032 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8035 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8037 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8038 dispose_variable.CreateReferenceExpression (bc, loc),
8039 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8040 loc), new NullLiteral (loc));
8042 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8044 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8045 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8047 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8048 return new If (idisaposable_test, dispose, loc);
8052 LocalVariable variable;
8054 Statement statement;
8055 ExpressionStatement init;
8056 TemporaryVariableReference enumerator_variable;
8057 bool ambiguous_getenumerator_name;
8059 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8062 this.variable = var;
8066 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8068 rc.Report.SymbolRelatedToPreviousError (enumerator);
8069 rc.Report.Error (202, loc,
8070 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8071 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8074 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8077 // Option 1: Try to match by name GetEnumerator first
8079 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8080 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8082 var mg = mexpr as MethodGroupExpr;
8084 mg.InstanceExpression = expr;
8085 Arguments args = new Arguments (0);
8086 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8088 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8089 if (ambiguous_getenumerator_name)
8092 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8098 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8101 PredefinedMember<MethodSpec> iface_candidate = null;
8102 var ptypes = rc.Module.PredefinedTypes;
8103 var gen_ienumerable = ptypes.IEnumerableGeneric;
8104 if (!gen_ienumerable.Define ())
8105 gen_ienumerable = null;
8107 var ifaces = t.Interfaces;
8108 if (ifaces != null) {
8109 foreach (var iface in ifaces) {
8110 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8111 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8112 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8113 rc.Report.Error (1640, loc,
8114 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8115 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8120 // TODO: Cache this somehow
8121 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8122 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8127 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8128 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8133 if (iface_candidate == null) {
8134 if (expr.Type != InternalType.ErrorType) {
8135 rc.Report.Error (1579, loc,
8136 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8137 expr.Type.GetSignatureForError (), "GetEnumerator");
8143 var method = iface_candidate.Resolve (loc);
8147 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8148 mg.InstanceExpression = expr;
8152 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8154 var ms = MemberCache.FindMember (enumerator.ReturnType,
8155 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8156 BindingRestriction.InstanceOnly) as MethodSpec;
8158 if (ms == null || !ms.IsPublic) {
8159 Error_WrongEnumerator (rc, enumerator);
8163 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8166 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8168 var ps = MemberCache.FindMember (enumerator.ReturnType,
8169 MemberFilter.Property ("Current", null),
8170 BindingRestriction.InstanceOnly) as PropertySpec;
8172 if (ps == null || !ps.IsPublic) {
8173 Error_WrongEnumerator (rc, enumerator);
8180 public override bool Resolve (BlockContext ec)
8182 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8185 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8186 } else if (expr.Type.IsNullableType) {
8187 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8190 var get_enumerator_mg = ResolveGetEnumerator (ec);
8191 if (get_enumerator_mg == null) {
8195 var get_enumerator = get_enumerator_mg.BestCandidate;
8196 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8197 enumerator_variable.Resolve (ec);
8199 // Prepare bool MoveNext ()
8200 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8201 if (move_next_mg == null) {
8205 move_next_mg.InstanceExpression = enumerator_variable;
8207 // Prepare ~T~ Current { get; }
8208 var current_prop = ResolveCurrent (ec, get_enumerator);
8209 if (current_prop == null) {
8213 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8214 if (current_pe == null)
8217 VarExpr ve = for_each.type as VarExpr;
8221 // Source type is dynamic, set element type to dynamic too
8222 variable.Type = ec.BuiltinTypes.Dynamic;
8224 // Infer implicitly typed local variable from foreach enumerable type
8225 variable.Type = current_pe.Type;
8229 // Explicit cast of dynamic collection elements has to be done at runtime
8230 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8233 variable.Type = for_each.type.ResolveAsType (ec);
8235 if (variable.Type == null)
8238 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8239 if (current_pe == null)
8243 var prev_block = ec.CurrentBlock;
8244 ec.CurrentBlock = for_each.variable.Block;
8245 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8246 ec.CurrentBlock = prev_block;
8247 if (variable_ref == null)
8250 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8252 var init = new Invocation.Predefined (get_enumerator_mg, null);
8254 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8255 for_each.body, Location.Null);
8257 var enum_type = enumerator_variable.Type;
8260 // Add Dispose method call when enumerator can be IDisposable
8262 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8263 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8265 // Runtime Dispose check
8267 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8268 vd.Initializer = init;
8269 statement = new Using (vd, statement, Location.Null);
8272 // No Dispose call needed
8274 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8275 this.init.Resolve (ec);
8279 // Static Dispose check
8281 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8282 vd.Initializer = init;
8283 statement = new Using (vd, statement, Location.Null);
8286 return statement.Resolve (ec);
8289 protected override void DoEmit (EmitContext ec)
8291 enumerator_variable.LocalInfo.CreateBuilder (ec);
8294 init.EmitStatement (ec);
8296 statement.Emit (ec);
8299 #region IErrorHandler Members
8301 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8303 ec.Report.SymbolRelatedToPreviousError (best);
8304 ec.Report.Warning (278, 2, expr.Location,
8305 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8306 expr.Type.GetSignatureForError (), "enumerable",
8307 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8309 ambiguous_getenumerator_name = true;
8313 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8318 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8323 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8332 LocalVariable variable;
8336 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8340 this.variable = var;
8346 public Expression Expr {
8347 get { return expr; }
8350 public Expression TypeExpression {
8351 get { return type; }
8354 public LocalVariable Variable {
8355 get { return variable; }
8358 public override Reachability MarkReachable (Reachability rc)
8360 base.MarkReachable (rc);
8362 body.MarkReachable (rc);
8367 public override bool Resolve (BlockContext ec)
8369 expr = expr.Resolve (ec);
8374 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8378 body.AddStatement (Statement);
8380 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8381 Statement = new ArrayForeach (this, 1);
8382 } else if (expr.Type is ArrayContainer) {
8383 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8385 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8386 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8387 expr.ExprClassName);
8391 Statement = new CollectionForeach (this, variable, expr);
8394 return base.Resolve (ec);
8397 protected override void DoEmit (EmitContext ec)
8399 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8400 ec.LoopBegin = ec.DefineLabel ();
8401 ec.LoopEnd = ec.DefineLabel ();
8403 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8404 body.Explicit.DisableDebugScopeIndex ();
8406 variable.CreateBuilder (ec);
8408 Statement.Emit (ec);
8412 ec.LoopBegin = old_begin;
8413 ec.LoopEnd = old_end;
8416 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8418 expr.FlowAnalysis (fc);
8420 var da = fc.BranchDefiniteAssignment ();
8421 body.FlowAnalysis (fc);
8422 fc.DefiniteAssignment = da;
8426 protected override void CloneTo (CloneContext clonectx, Statement t)
8428 Foreach target = (Foreach) t;
8430 target.type = type.Clone (clonectx);
8431 target.expr = expr.Clone (clonectx);
8432 target.body = (Block) body.Clone (clonectx);
8433 target.Statement = Statement.Clone (clonectx);
8436 public override object Accept (StructuralVisitor visitor)
8438 return visitor.Visit (this);
8442 class SentinelStatement: Statement
8444 protected override void CloneTo (CloneContext clonectx, Statement target)
8448 protected override void DoEmit (EmitContext ec)
8450 var l = ec.DefineLabel ();
8452 ec.Emit (OpCodes.Br_S, l);
8455 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8457 throw new NotImplementedException ();