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.BranchDefiniteAssignment (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 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
583 fc.BranchDefiniteAssignment (fc.DefiniteAssignmentOnTrue);
585 Statement.FlowAnalysis (fc);
588 // Special case infinite while with breaks
590 if (end_reachable_das != null) {
591 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
592 end_reachable_das = null;
595 fc.DefiniteAssignment = da_false;
597 if (infinite && !end_reachable)
603 public override Reachability MarkReachable (Reachability rc)
605 if (rc.IsUnreachable)
608 base.MarkReachable (rc);
611 // Special case unreachable while body
614 Statement.MarkReachable (Reachability.CreateUnreachable ());
618 Statement.MarkReachable (rc);
621 // When infinite while end is unreachable via break anything what follows is unreachable too
623 if (infinite && !end_reachable)
624 return Reachability.CreateUnreachable ();
629 protected override void CloneTo (CloneContext clonectx, Statement t)
631 While target = (While) t;
633 target.expr = expr.Clone (clonectx);
634 target.Statement = Statement.Clone (clonectx);
637 public override object Accept (StructuralVisitor visitor)
639 return visitor.Visit (this);
642 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
647 if (end_reachable_das == null)
648 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
650 end_reachable_das.Add (fc.DefiniteAssignment);
653 public override void SetEndReachable ()
655 end_reachable = true;
659 public class For : LoopStatement
661 bool infinite, empty, iterator_reachable, end_reachable;
662 List<DefiniteAssignmentBitSet> end_reachable_das;
664 public For (Location l)
670 public Statement Initializer {
674 public Expression Condition {
678 public Statement Iterator {
682 public override bool Resolve (BlockContext bc)
684 Initializer.Resolve (bc);
686 if (Condition != null) {
687 Condition = Condition.Resolve (bc);
688 var condition_constant = Condition as Constant;
689 if (condition_constant != null) {
690 if (condition_constant.IsDefaultValue) {
700 return base.Resolve (bc) && Iterator.Resolve (bc);
703 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
705 Initializer.FlowAnalysis (fc);
707 DefiniteAssignmentBitSet da_false;
708 if (Condition != null) {
709 Condition.FlowAnalysisConditional (fc);
710 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
711 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
713 da_false = fc.BranchDefiniteAssignment ();
716 Statement.FlowAnalysis (fc);
718 Iterator.FlowAnalysis (fc);
721 // Special case infinite for with breaks
723 if (end_reachable_das != null) {
724 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
725 end_reachable_das = null;
728 fc.DefiniteAssignment = da_false;
730 if (infinite && !end_reachable)
736 public override Reachability MarkReachable (Reachability rc)
738 base.MarkReachable (rc);
740 Initializer.MarkReachable (rc);
742 var body_rc = Statement.MarkReachable (rc);
743 if (!body_rc.IsUnreachable || iterator_reachable) {
744 Iterator.MarkReachable (rc);
748 // When infinite for end is unreachable via break anything what follows is unreachable too
750 if (infinite && !end_reachable) {
751 return Reachability.CreateUnreachable ();
757 protected override void DoEmit (EmitContext ec)
759 if (Initializer != null)
760 Initializer.Emit (ec);
763 Condition.EmitSideEffect (ec);
767 Label old_begin = ec.LoopBegin;
768 Label old_end = ec.LoopEnd;
769 Label loop = ec.DefineLabel ();
770 Label test = ec.DefineLabel ();
772 ec.LoopBegin = ec.DefineLabel ();
773 ec.LoopEnd = ec.DefineLabel ();
775 ec.Emit (OpCodes.Br, test);
779 ec.MarkLabel (ec.LoopBegin);
784 // If test is null, there is no test, and we are just
787 if (Condition != null) {
788 ec.Mark (Condition.Location);
791 // The Resolve code already catches the case for
792 // Test == Constant (false) so we know that
795 if (Condition is Constant) {
796 Condition.EmitSideEffect (ec);
797 ec.Emit (OpCodes.Br, loop);
799 Condition.EmitBranchable (ec, loop, true);
803 ec.Emit (OpCodes.Br, loop);
804 ec.MarkLabel (ec.LoopEnd);
806 ec.LoopBegin = old_begin;
807 ec.LoopEnd = old_end;
810 protected override void CloneTo (CloneContext clonectx, Statement t)
812 For target = (For) t;
814 if (Initializer != null)
815 target.Initializer = Initializer.Clone (clonectx);
816 if (Condition != null)
817 target.Condition = Condition.Clone (clonectx);
818 if (Iterator != null)
819 target.Iterator = Iterator.Clone (clonectx);
820 target.Statement = Statement.Clone (clonectx);
823 public override object Accept (StructuralVisitor visitor)
825 return visitor.Visit (this);
828 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
833 if (end_reachable_das == null)
834 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
836 end_reachable_das.Add (fc.DefiniteAssignment);
839 public override void SetEndReachable ()
841 end_reachable = true;
844 public override void SetIteratorReachable ()
846 iterator_reachable = true;
850 public abstract class LoopStatement : Statement
852 protected LoopStatement (Statement statement)
854 Statement = statement;
857 public Statement Statement { get; set; }
859 public override bool Resolve (BlockContext bc)
861 var prev_loop = bc.EnclosingLoop;
862 var prev_los = bc.EnclosingLoopOrSwitch;
863 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
864 var ok = Statement.Resolve (bc);
865 bc.EnclosingLoopOrSwitch = prev_los;
866 bc.EnclosingLoop = prev_loop;
872 // Needed by possibly infinite loops statements (for, while) and switch statment
874 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
878 public virtual void SetEndReachable ()
882 public virtual void SetIteratorReachable ()
887 public class StatementExpression : Statement
889 ExpressionStatement expr;
891 public StatementExpression (ExpressionStatement expr)
894 loc = expr.StartLocation;
897 public StatementExpression (ExpressionStatement expr, Location loc)
903 public ExpressionStatement Expr {
909 protected override void CloneTo (CloneContext clonectx, Statement t)
911 StatementExpression target = (StatementExpression) t;
912 target.expr = (ExpressionStatement) expr.Clone (clonectx);
915 protected override void DoEmit (EmitContext ec)
917 expr.EmitStatement (ec);
920 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
922 expr.FlowAnalysis (fc);
926 public override Reachability MarkReachable (Reachability rc)
928 base.MarkReachable (rc);
929 expr.MarkReachable (rc);
933 public override bool Resolve (BlockContext ec)
935 expr = expr.ResolveStatement (ec);
939 public override object Accept (StructuralVisitor visitor)
941 return visitor.Visit (this);
945 public class StatementErrorExpression : Statement
949 public StatementErrorExpression (Expression expr)
952 this.loc = expr.StartLocation;
955 public Expression Expr {
961 public override bool Resolve (BlockContext bc)
963 expr.Error_InvalidExpressionStatement (bc);
967 protected override void DoEmit (EmitContext ec)
969 throw new NotSupportedException ();
972 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
977 protected override void CloneTo (CloneContext clonectx, Statement target)
979 var t = (StatementErrorExpression) target;
981 t.expr = expr.Clone (clonectx);
984 public override object Accept (StructuralVisitor visitor)
986 return visitor.Visit (this);
991 // Simple version of statement list not requiring a block
993 public class StatementList : Statement
995 List<Statement> statements;
997 public StatementList (Statement first, Statement second)
999 statements = new List<Statement> { first, second };
1003 public IList<Statement> Statements {
1010 public void Add (Statement statement)
1012 statements.Add (statement);
1015 public override bool Resolve (BlockContext ec)
1017 foreach (var s in statements)
1023 protected override void DoEmit (EmitContext ec)
1025 foreach (var s in statements)
1029 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1031 foreach (var s in statements)
1032 s.FlowAnalysis (fc);
1037 public override Reachability MarkReachable (Reachability rc)
1039 base.MarkReachable (rc);
1041 Reachability res = rc;
1042 foreach (var s in statements)
1043 res = s.MarkReachable (rc);
1048 protected override void CloneTo (CloneContext clonectx, Statement target)
1050 StatementList t = (StatementList) target;
1052 t.statements = new List<Statement> (statements.Count);
1053 foreach (Statement s in statements)
1054 t.statements.Add (s.Clone (clonectx));
1057 public override object Accept (StructuralVisitor visitor)
1059 return visitor.Visit (this);
1064 // For statements which require special handling when inside try or catch block
1066 public abstract class ExitStatement : Statement
1068 protected bool unwind_protect;
1070 protected abstract bool DoResolve (BlockContext bc);
1071 protected abstract bool IsLocalExit { get; }
1073 public override bool Resolve (BlockContext bc)
1075 var res = DoResolve (bc);
1079 // We are inside finally scope but is it the scope we are exiting
1081 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1083 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1084 if (b.IsFinallyBlock) {
1085 Error_FinallyClauseExit (bc);
1089 if (b is ParametersBlock)
1095 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1099 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1104 if (fc.TryFinally != null) {
1105 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1107 fc.ParametersBlock.CheckControlExit (fc);
1115 /// Implements the return statement
1117 public class Return : ExitStatement
1122 public Return (Expression expr, Location l)
1130 public Expression Expr {
1139 protected override bool IsLocalExit {
1147 protected override bool DoResolve (BlockContext ec)
1149 var block_return_type = ec.ReturnType;
1152 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1156 // Return must not be followed by an expression when
1157 // the method return type is Task
1159 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1160 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1161 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1163 // Extra trick not to emit ret/leave inside awaiter body
1165 expr = EmptyExpression.Null;
1169 if (storey.ReturnType.IsGenericTask)
1170 block_return_type = storey.ReturnType.TypeArguments[0];
1173 if (ec.CurrentIterator != null) {
1174 Error_ReturnFromIterator (ec);
1175 } else if (block_return_type != InternalType.ErrorType) {
1176 ec.Report.Error (126, loc,
1177 "An object of a type convertible to `{0}' is required for the return statement",
1178 block_return_type.GetSignatureForError ());
1184 expr = expr.Resolve (ec);
1186 AnonymousExpression am = ec.CurrentAnonymousMethod;
1188 if (block_return_type.Kind == MemberKind.Void) {
1189 ec.Report.Error (127, loc,
1190 "`{0}': A return keyword must not be followed by any expression when method returns void",
1191 ec.GetSignatureForError ());
1196 if (am.IsIterator) {
1197 Error_ReturnFromIterator (ec);
1201 var async_block = am as AsyncInitializer;
1202 if (async_block != null) {
1204 var storey = (AsyncTaskStorey) am.Storey;
1205 var async_type = storey.ReturnType;
1207 if (async_type == null && async_block.ReturnTypeInference != null) {
1208 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1209 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1211 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1215 if (async_type.Kind == MemberKind.Void) {
1216 ec.Report.Error (8030, loc,
1217 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1221 if (!async_type.IsGenericTask) {
1222 if (this is ContextualReturn)
1225 if (async_block.DelegateType != null) {
1226 ec.Report.Error (8031, loc,
1227 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1229 ec.Report.Error (1997, loc,
1230 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1231 ec.GetSignatureForError ());
1237 // The return type is actually Task<T> type argument
1239 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1240 ec.Report.Error (4016, loc,
1241 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1242 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1244 block_return_type = async_type.TypeArguments[0];
1248 if (block_return_type.Kind == MemberKind.Void) {
1249 ec.Report.Error (8030, loc,
1250 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1254 var l = am as AnonymousMethodBody;
1255 if (l != null && expr != null) {
1256 if (l.ReturnTypeInference != null) {
1257 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1262 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1263 // unexpected debugging experience
1265 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1266 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1275 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1276 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1279 if (am != null && block_return_type == ec.ReturnType) {
1280 ec.Report.Error (1662, loc,
1281 "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",
1282 am.ContainerType, am.GetSignatureForError ());
1291 protected override void DoEmit (EmitContext ec)
1295 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1296 if (async_body != null) {
1297 var storey = (AsyncTaskStorey)async_body.Storey;
1298 Label exit_label = async_body.BodyEnd;
1301 // It's null for await without async
1303 if (storey.HoistedReturnValue != null) {
1305 // Special case hoisted return value (happens in try/finally scenario)
1307 if (ec.TryFinallyUnwind != null) {
1308 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1311 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1312 async_return.EmitAssign (ec, expr, false, false);
1317 if (ec.TryFinallyUnwind != null)
1318 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1321 ec.Emit (OpCodes.Leave, exit_label);
1328 if (unwind_protect || ec.EmitAccurateDebugInfo)
1329 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1332 if (unwind_protect) {
1333 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1334 } else if (ec.EmitAccurateDebugInfo) {
1335 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1337 ec.Emit (OpCodes.Ret);
1341 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1344 expr.FlowAnalysis (fc);
1347 base.DoFlowAnalysis (fc);
1352 void Error_ReturnFromIterator (ResolveContext rc)
1354 rc.Report.Error (1622, loc,
1355 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1358 public override Reachability MarkReachable (Reachability rc)
1360 base.MarkReachable (rc);
1363 rc = Expr.MarkReachable (rc);
1364 expr_returns = rc.IsUnreachable;
1367 return Reachability.CreateUnreachable ();
1370 protected override void CloneTo (CloneContext clonectx, Statement t)
1372 Return target = (Return) t;
1373 // It's null for simple return;
1375 target.expr = expr.Clone (clonectx);
1378 public override object Accept (StructuralVisitor visitor)
1380 return visitor.Visit (this);
1384 public class Goto : ExitStatement
1387 LabeledStatement label;
1388 TryFinally try_finally;
1390 public Goto (string label, Location l)
1396 public string Target {
1397 get { return target; }
1400 protected override bool IsLocalExit {
1406 protected override bool DoResolve (BlockContext bc)
1408 label = bc.CurrentBlock.LookupLabel (target);
1409 if (label == null) {
1410 Error_UnknownLabel (bc, target, loc);
1414 try_finally = bc.CurrentTryBlock as TryFinally;
1416 CheckExitBoundaries (bc, label.Block);
1421 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1423 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1427 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1429 // Goto to unreachable label
1433 if (fc.AddReachedLabel (label))
1436 label.Block.ScanGotoJump (label, fc);
1440 public override Reachability MarkReachable (Reachability rc)
1442 if (rc.IsUnreachable)
1445 base.MarkReachable (rc);
1447 if (try_finally != null) {
1448 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1449 label.AddGotoReference (rc);
1454 label.AddGotoReference (rc);
1457 return Reachability.CreateUnreachable ();
1460 protected override void CloneTo (CloneContext clonectx, Statement target)
1465 protected override void DoEmit (EmitContext ec)
1467 // This should only happen for goto from try block to unrechable label
1471 Label l = label.LabelTarget (ec);
1473 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1474 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1475 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1478 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1481 bool IsLeavingFinally (Block labelBlock)
1483 var b = try_finally.Statement as Block;
1485 if (b == labelBlock)
1494 public override object Accept (StructuralVisitor visitor)
1496 return visitor.Visit (this);
1500 public class LabeledStatement : Statement {
1507 public LabeledStatement (string name, Block block, Location l)
1514 public Label LabelTarget (EmitContext ec)
1519 label = ec.DefineLabel ();
1524 public Block Block {
1530 public string Name {
1531 get { return name; }
1534 protected override void CloneTo (CloneContext clonectx, Statement target)
1536 var t = (LabeledStatement) target;
1538 t.block = clonectx.RemapBlockCopy (block);
1541 public override bool Resolve (BlockContext bc)
1546 protected override void DoEmit (EmitContext ec)
1549 ec.MarkLabel (label);
1552 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1555 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1561 public override Reachability MarkReachable (Reachability rc)
1563 base.MarkReachable (rc);
1566 rc = new Reachability ();
1571 public void AddGotoReference (Reachability rc)
1579 block.ScanGotoJump (this);
1582 public override object Accept (StructuralVisitor visitor)
1584 return visitor.Visit (this);
1590 /// `goto default' statement
1592 public class GotoDefault : SwitchGoto
1594 public GotoDefault (Location l)
1599 public override bool Resolve (BlockContext bc)
1601 if (bc.Switch == null) {
1602 Error_GotoCaseRequiresSwitchBlock (bc);
1606 bc.Switch.RegisterGotoCase (null, null);
1612 protected override void DoEmit (EmitContext ec)
1614 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1617 public override Reachability MarkReachable (Reachability rc)
1619 if (!rc.IsUnreachable) {
1620 var label = switch_statement.DefaultLabel;
1621 if (label.IsUnreachable) {
1622 label.MarkReachable (rc);
1623 switch_statement.Block.ScanGotoJump (label);
1627 return base.MarkReachable (rc);
1630 public override object Accept (StructuralVisitor visitor)
1632 return visitor.Visit (this);
1637 /// `goto case' statement
1639 public class GotoCase : SwitchGoto
1643 public GotoCase (Expression e, Location l)
1649 public Expression Expr {
1655 public SwitchLabel Label { get; set; }
1657 public override bool Resolve (BlockContext ec)
1659 if (ec.Switch == null) {
1660 Error_GotoCaseRequiresSwitchBlock (ec);
1664 Constant c = expr.ResolveLabelConstant (ec);
1670 if (ec.Switch.IsNullable && c is NullLiteral) {
1673 TypeSpec type = ec.Switch.SwitchType;
1674 res = c.Reduce (ec, type);
1676 c.Error_ValueCannotBeConverted (ec, type, true);
1680 if (!Convert.ImplicitStandardConversionExists (c, type))
1681 ec.Report.Warning (469, 2, loc,
1682 "The `goto case' value is not implicitly convertible to type `{0}'",
1683 type.GetSignatureForError ());
1687 ec.Switch.RegisterGotoCase (this, res);
1694 protected override void DoEmit (EmitContext ec)
1696 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1699 protected override void CloneTo (CloneContext clonectx, Statement t)
1701 GotoCase target = (GotoCase) t;
1703 target.expr = expr.Clone (clonectx);
1706 public override Reachability MarkReachable (Reachability rc)
1708 if (!rc.IsUnreachable) {
1709 var label = switch_statement.FindLabel ((Constant) expr);
1710 if (label.IsUnreachable) {
1711 label.MarkReachable (rc);
1712 switch_statement.Block.ScanGotoJump (label);
1716 return base.MarkReachable (rc);
1719 public override object Accept (StructuralVisitor visitor)
1721 return visitor.Visit (this);
1725 public abstract class SwitchGoto : Statement
1727 protected bool unwind_protect;
1728 protected Switch switch_statement;
1730 protected SwitchGoto (Location loc)
1735 protected override void CloneTo (CloneContext clonectx, Statement target)
1740 public override bool Resolve (BlockContext bc)
1742 CheckExitBoundaries (bc, bc.Switch.Block);
1744 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1745 switch_statement = bc.Switch;
1750 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1755 public override Reachability MarkReachable (Reachability rc)
1757 base.MarkReachable (rc);
1758 return Reachability.CreateUnreachable ();
1761 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1763 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1767 public class Throw : Statement {
1770 public Throw (Expression expr, Location l)
1776 public Expression Expr {
1782 public static Expression ConvertType (ResolveContext rc, Expression expr)
1784 var et = rc.BuiltinTypes.Exception;
1785 if (Convert.ImplicitConversionExists (rc, expr, et))
1786 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1788 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1789 expr = EmptyCast.Create (expr, et);
1795 public override bool Resolve (BlockContext ec)
1798 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1799 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1800 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1801 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1802 if (b.IsFinallyBlock) {
1803 ec.Report.Error (724, loc,
1804 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1813 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1818 expr = ConvertType (ec, expr);
1823 protected override void DoEmit (EmitContext ec)
1826 var atv = ec.AsyncThrowVariable;
1828 if (atv.HoistedVariant != null) {
1829 atv.HoistedVariant.Emit (ec);
1834 ec.Emit (OpCodes.Throw);
1836 ec.Emit (OpCodes.Rethrow);
1841 ec.Emit (OpCodes.Throw);
1845 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1848 expr.FlowAnalysis (fc);
1853 public override Reachability MarkReachable (Reachability rc)
1855 base.MarkReachable (rc);
1856 return Reachability.CreateUnreachable ();
1859 protected override void CloneTo (CloneContext clonectx, Statement t)
1861 Throw target = (Throw) t;
1864 target.expr = expr.Clone (clonectx);
1867 public override object Accept (StructuralVisitor visitor)
1869 return visitor.Visit (this);
1873 public class Break : LocalExitStatement
1875 public Break (Location l)
1880 public override object Accept (StructuralVisitor visitor)
1882 return visitor.Visit (this);
1885 protected override void DoEmit (EmitContext ec)
1889 if (ec.TryFinallyUnwind != null) {
1890 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1891 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1894 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1897 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1899 enclosing_loop.AddEndDefiniteAssignment (fc);
1903 protected override bool DoResolve (BlockContext bc)
1905 enclosing_loop = bc.EnclosingLoopOrSwitch;
1906 return base.DoResolve (bc);
1909 public override Reachability MarkReachable (Reachability rc)
1911 base.MarkReachable (rc);
1913 if (!rc.IsUnreachable)
1914 enclosing_loop.SetEndReachable ();
1916 return Reachability.CreateUnreachable ();
1920 public class Continue : LocalExitStatement
1922 public Continue (Location l)
1927 public override object Accept (StructuralVisitor visitor)
1929 return visitor.Visit (this);
1933 protected override void DoEmit (EmitContext ec)
1935 var l = ec.LoopBegin;
1937 if (ec.TryFinallyUnwind != null) {
1938 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1939 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1942 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1945 protected override bool DoResolve (BlockContext bc)
1947 enclosing_loop = bc.EnclosingLoop;
1948 return base.DoResolve (bc);
1951 public override Reachability MarkReachable (Reachability rc)
1953 base.MarkReachable (rc);
1955 if (!rc.IsUnreachable)
1956 enclosing_loop.SetIteratorReachable ();
1958 return Reachability.CreateUnreachable ();
1962 public abstract class LocalExitStatement : ExitStatement
1964 protected LoopStatement enclosing_loop;
1966 protected LocalExitStatement (Location loc)
1971 protected override bool IsLocalExit {
1977 protected override void CloneTo (CloneContext clonectx, Statement t)
1982 protected override bool DoResolve (BlockContext bc)
1984 if (enclosing_loop == null) {
1985 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1989 var block = enclosing_loop.Statement as Block;
1991 // Don't need to do extra checks for simple statements loops
1992 if (block != null) {
1993 CheckExitBoundaries (bc, block);
2000 public interface ILocalVariable
2002 void Emit (EmitContext ec);
2003 void EmitAssign (EmitContext ec);
2004 void EmitAddressOf (EmitContext ec);
2007 public interface INamedBlockVariable
2009 Block Block { get; }
2010 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2011 bool IsDeclared { get; }
2012 bool IsParameter { get; }
2013 Location Location { get; }
2016 public class BlockVariableDeclarator
2019 Expression initializer;
2021 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2023 if (li.Type != null)
2024 throw new ArgumentException ("Expected null variable type");
2027 this.initializer = initializer;
2032 public LocalVariable Variable {
2038 public Expression Initializer {
2043 initializer = value;
2049 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2051 var t = (BlockVariableDeclarator) MemberwiseClone ();
2052 if (initializer != null)
2053 t.initializer = initializer.Clone (cloneCtx);
2059 public class BlockVariable : Statement
2061 Expression initializer;
2062 protected FullNamedExpression type_expr;
2063 protected LocalVariable li;
2064 protected List<BlockVariableDeclarator> declarators;
2067 public BlockVariable (FullNamedExpression type, LocalVariable li)
2069 this.type_expr = type;
2071 this.loc = type_expr.Location;
2074 protected BlockVariable (LocalVariable li)
2081 public List<BlockVariableDeclarator> Declarators {
2087 public Expression Initializer {
2092 initializer = value;
2096 public FullNamedExpression TypeExpression {
2102 public LocalVariable Variable {
2110 public void AddDeclarator (BlockVariableDeclarator decl)
2112 if (declarators == null)
2113 declarators = new List<BlockVariableDeclarator> ();
2115 declarators.Add (decl);
2118 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2120 if (bc.Report.Errors != 0)
2123 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2125 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2126 new MemberName (li.Name, li.Location), null);
2128 container.AddField (f);
2131 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2135 public override bool Resolve (BlockContext bc)
2137 return Resolve (bc, true);
2140 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2142 if (type == null && !li.IsCompilerGenerated) {
2143 var vexpr = type_expr as VarExpr;
2146 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2147 // same name exists or as a keyword when no type was found
2149 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2150 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2151 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2154 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2158 if (li.IsConstant) {
2159 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2163 if (Initializer == null) {
2164 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2168 if (declarators != null) {
2169 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2173 Initializer = Initializer.Resolve (bc);
2174 if (Initializer != null) {
2175 ((VarExpr) type_expr).InferType (bc, Initializer);
2176 type = type_expr.Type;
2178 // Set error type to indicate the var was placed correctly but could
2181 // var a = missing ();
2183 type = InternalType.ErrorType;
2188 type = type_expr.ResolveAsType (bc);
2192 if (li.IsConstant && !type.IsConstantCompatible) {
2193 Const.Error_InvalidConstantType (type, loc, bc.Report);
2198 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2203 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2205 CreateEvaluatorVariable (bc, li);
2206 } else if (type != InternalType.ErrorType) {
2207 li.PrepareAssignmentAnalysis (bc);
2210 if (initializer != null) {
2211 initializer = ResolveInitializer (bc, li, initializer);
2212 // li.Variable.DefinitelyAssigned
2215 if (declarators != null) {
2216 foreach (var d in declarators) {
2217 d.Variable.Type = li.Type;
2219 CreateEvaluatorVariable (bc, d.Variable);
2220 } else if (type != InternalType.ErrorType) {
2221 d.Variable.PrepareAssignmentAnalysis (bc);
2224 if (d.Initializer != null && resolveDeclaratorInitializers) {
2225 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2226 // d.Variable.DefinitelyAssigned
2234 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2236 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2237 return a.ResolveStatement (bc);
2240 protected override void DoEmit (EmitContext ec)
2242 li.CreateBuilder (ec);
2244 if (Initializer != null && !IsUnreachable)
2245 ((ExpressionStatement) Initializer).EmitStatement (ec);
2247 if (declarators != null) {
2248 foreach (var d in declarators) {
2249 d.Variable.CreateBuilder (ec);
2250 if (d.Initializer != null && !IsUnreachable) {
2251 ec.Mark (d.Variable.Location);
2252 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2258 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2260 if (Initializer != null)
2261 Initializer.FlowAnalysis (fc);
2263 if (declarators != null) {
2264 foreach (var d in declarators) {
2265 if (d.Initializer != null)
2266 d.Initializer.FlowAnalysis (fc);
2273 public override Reachability MarkReachable (Reachability rc)
2275 base.MarkReachable (rc);
2276 return initializer == null ? rc : initializer.MarkReachable (rc);
2279 protected override void CloneTo (CloneContext clonectx, Statement target)
2281 BlockVariable t = (BlockVariable) target;
2283 if (type_expr != null)
2284 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2286 if (initializer != null)
2287 t.initializer = initializer.Clone (clonectx);
2289 if (declarators != null) {
2290 t.declarators = null;
2291 foreach (var d in declarators)
2292 t.AddDeclarator (d.Clone (clonectx));
2296 public override object Accept (StructuralVisitor visitor)
2298 return visitor.Visit (this);
2302 public class BlockConstant : BlockVariable
2304 public BlockConstant (FullNamedExpression type, LocalVariable li)
2309 public override void Emit (EmitContext ec)
2311 if (!Variable.IsUsed)
2312 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2314 // Nothing to emit, not even sequence point
2317 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2319 initializer = initializer.Resolve (bc);
2320 if (initializer == null)
2323 var c = initializer as Constant;
2325 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2329 c = c.ConvertImplicitly (li.Type);
2331 if (TypeSpec.IsReferenceType (li.Type))
2332 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2334 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2339 li.ConstantValue = c;
2343 public override object Accept (StructuralVisitor visitor)
2345 return visitor.Visit (this);
2350 // The information about a user-perceived local variable
2352 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2359 AddressTaken = 1 << 2,
2360 CompilerGenerated = 1 << 3,
2362 ForeachVariable = 1 << 5,
2363 FixedVariable = 1 << 6,
2364 UsingVariable = 1 << 7,
2366 SymbolFileHidden = 1 << 9,
2368 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2372 readonly string name;
2373 readonly Location loc;
2374 readonly Block block;
2376 Constant const_value;
2378 public VariableInfo VariableInfo;
2379 HoistedVariable hoisted_variant;
2381 LocalBuilder builder;
2383 public LocalVariable (Block block, string name, Location loc)
2390 public LocalVariable (Block block, string name, Flags flags, Location loc)
2391 : this (block, name, loc)
2397 // Used by variable declarators
2399 public LocalVariable (LocalVariable li, string name, Location loc)
2400 : this (li.block, name, li.flags, loc)
2406 public bool AddressTaken {
2408 return (flags & Flags.AddressTaken) != 0;
2412 public Block Block {
2418 public Constant ConstantValue {
2423 const_value = value;
2428 // Hoisted local variable variant
2430 public HoistedVariable HoistedVariant {
2432 return hoisted_variant;
2435 hoisted_variant = value;
2439 public bool Created {
2441 return builder != null;
2445 public bool IsDeclared {
2447 return type != null;
2451 public bool IsCompilerGenerated {
2453 return (flags & Flags.CompilerGenerated) != 0;
2457 public bool IsConstant {
2459 return (flags & Flags.Constant) != 0;
2463 public bool IsLocked {
2465 return (flags & Flags.IsLocked) != 0;
2468 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2472 public bool IsThis {
2474 return (flags & Flags.IsThis) != 0;
2478 public bool IsUsed {
2480 return (flags & Flags.Used) != 0;
2484 public bool IsFixed {
2486 return (flags & Flags.FixedVariable) != 0;
2489 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2493 bool INamedBlockVariable.IsParameter {
2499 public bool IsReadonly {
2501 return (flags & Flags.ReadonlyMask) != 0;
2505 public Location Location {
2511 public string Name {
2517 public TypeSpec Type {
2528 public void CreateBuilder (EmitContext ec)
2530 if ((flags & Flags.Used) == 0) {
2531 if (VariableInfo == null) {
2532 // Missing flow analysis or wrong variable flags
2533 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2536 if (VariableInfo.IsEverAssigned)
2537 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2539 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2542 if (HoistedVariant != null)
2545 if (builder != null) {
2546 if ((flags & Flags.CompilerGenerated) != 0)
2549 // To avoid Used warning duplicates
2550 throw new InternalErrorException ("Already created variable `{0}'", name);
2554 // All fixed variabled are pinned, a slot has to be alocated
2556 builder = ec.DeclareLocal (Type, IsFixed);
2557 if ((flags & Flags.SymbolFileHidden) == 0)
2558 ec.DefineLocalVariable (name, builder);
2561 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2563 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2564 if (!writeToSymbolFile)
2565 li.flags |= Flags.SymbolFileHidden;
2571 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2573 if (IsConstant && const_value != null) {
2575 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2578 return new LocalVariableReference (this, loc);
2581 public void Emit (EmitContext ec)
2583 // TODO: Need something better for temporary variables
2584 if ((flags & Flags.CompilerGenerated) != 0)
2587 ec.Emit (OpCodes.Ldloc, builder);
2590 public void EmitAssign (EmitContext ec)
2592 // TODO: Need something better for temporary variables
2593 if ((flags & Flags.CompilerGenerated) != 0)
2596 ec.Emit (OpCodes.Stloc, builder);
2599 public void EmitAddressOf (EmitContext ec)
2601 // TODO: Need something better for temporary variables
2602 if ((flags & Flags.CompilerGenerated) != 0)
2605 ec.Emit (OpCodes.Ldloca, builder);
2608 public static string GetCompilerGeneratedName (Block block)
2610 // HACK: Debugger depends on the name semantics
2611 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2614 public string GetReadOnlyContext ()
2616 switch (flags & Flags.ReadonlyMask) {
2617 case Flags.FixedVariable:
2618 return "fixed variable";
2619 case Flags.ForeachVariable:
2620 return "foreach iteration variable";
2621 case Flags.UsingVariable:
2622 return "using variable";
2625 throw new InternalErrorException ("Variable is not readonly");
2628 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2630 if (VariableInfo == null)
2631 throw new Exception ();
2633 if (IsAssigned (fc))
2636 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2639 public bool IsAssigned (FlowAnalysisContext fc)
2641 return fc.IsDefinitelyAssigned (VariableInfo);
2644 public void PrepareAssignmentAnalysis (BlockContext bc)
2647 // No need to run assignment analysis for these guys
2649 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2652 VariableInfo = VariableInfo.Create (bc, this);
2656 // Mark the variables as referenced in the user code
2658 public void SetIsUsed ()
2660 flags |= Flags.Used;
2663 public void SetHasAddressTaken ()
2665 flags |= (Flags.AddressTaken | Flags.Used);
2668 public override string ToString ()
2670 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2675 /// Block represents a C# block.
2679 /// This class is used in a number of places: either to represent
2680 /// explicit blocks that the programmer places or implicit blocks.
2682 /// Implicit blocks are used as labels or to introduce variable
2685 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2686 /// they contain extra information that is not necessary on normal blocks.
2688 public class Block : Statement {
2695 HasCapturedVariable = 64,
2696 HasCapturedThis = 1 << 7,
2697 IsExpressionTree = 1 << 8,
2698 CompilerGenerated = 1 << 9,
2699 HasAsyncModifier = 1 << 10,
2701 YieldBlock = 1 << 12,
2702 AwaitBlock = 1 << 13,
2703 FinallyBlock = 1 << 14,
2704 CatchBlock = 1 << 15,
2705 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2707 NoFlowAnalysis = 1 << 21,
2708 InitializationEmitted = 1 << 22
2711 public Block Parent;
2712 public Location StartLocation;
2713 public Location EndLocation;
2715 public ExplicitBlock Explicit;
2716 public ParametersBlock ParametersBlock;
2718 protected Flags flags;
2721 // The statements in this block
2723 protected List<Statement> statements;
2725 protected List<Statement> scope_initializers;
2727 int? resolving_init_idx;
2733 public int ID = id++;
2735 static int clone_id_counter;
2739 // int assignable_slots;
2741 public Block (Block parent, Location start, Location end)
2742 : this (parent, 0, start, end)
2746 public Block (Block parent, Flags flags, Location start, Location end)
2748 if (parent != null) {
2749 // the appropriate constructors will fixup these fields
2750 ParametersBlock = parent.ParametersBlock;
2751 Explicit = parent.Explicit;
2754 this.Parent = parent;
2756 this.StartLocation = start;
2757 this.EndLocation = end;
2759 statements = new List<Statement> (4);
2761 this.original = this;
2766 public Block Original {
2775 public bool IsCompilerGenerated {
2776 get { return (flags & Flags.CompilerGenerated) != 0; }
2777 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2781 public bool IsCatchBlock {
2783 return (flags & Flags.CatchBlock) != 0;
2787 public bool IsFinallyBlock {
2789 return (flags & Flags.FinallyBlock) != 0;
2793 public bool Unchecked {
2794 get { return (flags & Flags.Unchecked) != 0; }
2795 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2798 public bool Unsafe {
2799 get { return (flags & Flags.Unsafe) != 0; }
2800 set { flags |= Flags.Unsafe; }
2803 public List<Statement> Statements {
2804 get { return statements; }
2809 public void SetEndLocation (Location loc)
2814 public void AddLabel (LabeledStatement target)
2816 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2819 public void AddLocalName (LocalVariable li)
2821 AddLocalName (li.Name, li);
2824 public void AddLocalName (string name, INamedBlockVariable li)
2826 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2829 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2831 if (reason == null) {
2832 Error_AlreadyDeclared (name, variable);
2836 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2837 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2838 "to `{0}', which is already used in a `{1}' scope to denote something else",
2842 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2844 var pi = variable as ParametersBlock.ParameterInfo;
2846 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2848 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2849 "A local variable named `{0}' is already defined in this scope", name);
2853 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2855 ParametersBlock.TopBlock.Report.Error (412, loc,
2856 "The type parameter name `{0}' is the same as local variable or parameter name",
2861 // It should be used by expressions which require to
2862 // register a statement during resolve process.
2864 public void AddScopeStatement (Statement s)
2866 if (scope_initializers == null)
2867 scope_initializers = new List<Statement> ();
2870 // Simple recursive helper, when resolve scope initializer another
2871 // new scope initializer can be added, this ensures it's initialized
2872 // before existing one. For now this can happen with expression trees
2873 // in base ctor initializer only
2875 if (resolving_init_idx.HasValue) {
2876 scope_initializers.Insert (resolving_init_idx.Value, s);
2877 ++resolving_init_idx;
2879 scope_initializers.Add (s);
2883 public void InsertStatement (int index, Statement s)
2885 statements.Insert (index, s);
2888 public void AddStatement (Statement s)
2893 public LabeledStatement LookupLabel (string name)
2895 return ParametersBlock.GetLabel (name, this);
2898 public override Reachability MarkReachable (Reachability rc)
2900 if (rc.IsUnreachable)
2903 MarkReachableScope (rc);
2905 foreach (var s in statements) {
2906 rc = s.MarkReachable (rc);
2907 if (rc.IsUnreachable) {
2908 if ((flags & Flags.ReachableEnd) != 0)
2909 return new Reachability ();
2915 flags |= Flags.ReachableEnd;
2920 public void MarkReachableScope (Reachability rc)
2922 base.MarkReachable (rc);
2924 if (scope_initializers != null) {
2925 foreach (var si in scope_initializers)
2926 si.MarkReachable (rc);
2930 public override bool Resolve (BlockContext bc)
2932 if ((flags & Flags.Resolved) != 0)
2935 Block prev_block = bc.CurrentBlock;
2936 bc.CurrentBlock = this;
2939 // Compiler generated scope statements
2941 if (scope_initializers != null) {
2942 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2943 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2946 resolving_init_idx = null;
2950 int statement_count = statements.Count;
2951 for (int ix = 0; ix < statement_count; ix++){
2952 Statement s = statements [ix];
2954 if (!s.Resolve (bc)) {
2956 statements [ix] = new EmptyStatement (s.loc);
2961 bc.CurrentBlock = prev_block;
2963 flags |= Flags.Resolved;
2967 protected override void DoEmit (EmitContext ec)
2969 for (int ix = 0; ix < statements.Count; ix++){
2970 statements [ix].Emit (ec);
2974 public override void Emit (EmitContext ec)
2976 if (scope_initializers != null)
2977 EmitScopeInitializers (ec);
2982 protected void EmitScopeInitializers (EmitContext ec)
2984 foreach (Statement s in scope_initializers)
2988 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2990 if (scope_initializers != null) {
2991 foreach (var si in scope_initializers)
2992 si.FlowAnalysis (fc);
2995 return DoFlowAnalysis (fc, 0);
2998 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
3000 bool end_unreachable = !reachable;
3001 bool goto_flow_analysis = startIndex != 0;
3002 for (; startIndex < statements.Count; ++startIndex) {
3003 var s = statements[startIndex];
3005 end_unreachable = s.FlowAnalysis (fc);
3006 if (s.IsUnreachable) {
3007 statements [startIndex] = RewriteUnreachableStatement (s);
3012 // Statement end reachability is needed mostly due to goto support. Consider
3021 // X label is reachable only via goto not as another statement after if. We need
3022 // this for flow-analysis only to carry variable info correctly.
3024 if (end_unreachable) {
3025 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3027 var f = s as TryFinally;
3028 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3030 // Special case for try-finally with unreachable code after
3031 // finally block. Try block has to include leave opcode but there is
3032 // no label to leave to after unreachable finally block closing
3033 // brace. This sentinel ensures there is always IL instruction to
3034 // leave to even if we know it'll never be reached.
3036 statements.Insert (startIndex + 1, new SentinelStatement ());
3038 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3039 s = statements [startIndex];
3040 if (s is SwitchLabel) {
3041 if (!after_goto_case)
3042 s.FlowAnalysis (fc);
3047 if (s.IsUnreachable) {
3048 s.FlowAnalysis (fc);
3049 statements [startIndex] = RewriteUnreachableStatement (s);
3055 // Idea is to stop after goto case because goto case will always have at least same
3056 // variable assigned as switch case label. This saves a lot for complex goto case tests
3058 if (after_goto_case)
3064 var lb = s as LabeledStatement;
3065 if (lb != null && fc.AddReachedLabel (lb))
3070 // The condition should be true unless there is forward jumping goto
3072 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3075 return !Explicit.HasReachableClosingBrace;
3078 static Statement RewriteUnreachableStatement (Statement s)
3080 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3081 // analysis. Even csc report unreachable warning for it but it's actually used hence
3082 // we try to emulate this behaviour
3090 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3093 return new EmptyStatement (s.loc);
3096 public void ScanGotoJump (Statement label)
3099 for (i = 0; i < statements.Count; ++i) {
3100 if (statements[i] == label)
3104 var rc = new Reachability ();
3105 for (++i; i < statements.Count; ++i) {
3106 var s = statements[i];
3107 rc = s.MarkReachable (rc);
3108 if (rc.IsUnreachable)
3112 flags |= Flags.ReachableEnd;
3115 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3118 for (i = 0; i < statements.Count; ++i) {
3119 if (statements[i] == label)
3123 DoFlowAnalysis (fc, ++i);
3127 public override string ToString ()
3129 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3133 protected override void CloneTo (CloneContext clonectx, Statement t)
3135 Block target = (Block) t;
3137 target.clone_id = ++clone_id_counter;
3140 clonectx.AddBlockMap (this, target);
3141 if (original != this)
3142 clonectx.AddBlockMap (original, target);
3144 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3145 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3148 target.Parent = clonectx.RemapBlockCopy (Parent);
3150 target.statements = new List<Statement> (statements.Count);
3151 foreach (Statement s in statements)
3152 target.statements.Add (s.Clone (clonectx));
3155 public override object Accept (StructuralVisitor visitor)
3157 return visitor.Visit (this);
3161 public class ExplicitBlock : Block
3163 protected AnonymousMethodStorey am_storey;
3164 int debug_scope_index;
3166 public ExplicitBlock (Block parent, Location start, Location end)
3167 : this (parent, (Flags) 0, start, end)
3171 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3172 : base (parent, flags, start, end)
3174 this.Explicit = this;
3179 public AnonymousMethodStorey AnonymousMethodStorey {
3185 public bool HasAwait {
3187 return (flags & Flags.AwaitBlock) != 0;
3191 public bool HasCapturedThis {
3193 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3196 return (flags & Flags.HasCapturedThis) != 0;
3201 // Used to indicate that the block has reference to parent
3202 // block and cannot be made static when defining anonymous method
3204 public bool HasCapturedVariable {
3206 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3209 return (flags & Flags.HasCapturedVariable) != 0;
3213 public bool HasReachableClosingBrace {
3215 return (flags & Flags.ReachableEnd) != 0;
3218 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3222 public bool HasYield {
3224 return (flags & Flags.YieldBlock) != 0;
3231 // Creates anonymous method storey in current block
3233 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3236 // Return same story for iterator and async blocks unless we are
3237 // in nested anonymous method
3239 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3240 return ec.CurrentAnonymousMethod.Storey;
3242 if (am_storey == null) {
3243 MemberBase mc = ec.MemberContext as MemberBase;
3246 // Creates anonymous method storey for this block
3248 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3254 public void EmitScopeInitialization (EmitContext ec)
3256 if ((flags & Flags.InitializationEmitted) != 0)
3259 if (am_storey != null) {
3260 DefineStoreyContainer (ec, am_storey);
3261 am_storey.EmitStoreyInstantiation (ec, this);
3264 if (scope_initializers != null)
3265 EmitScopeInitializers (ec);
3267 flags |= Flags.InitializationEmitted;
3270 public override void Emit (EmitContext ec)
3272 // TODO: It's needed only when scope has variable (normal or lifted)
3273 var scopeIndex = GetDebugSymbolScopeIndex ();
3274 if (scopeIndex > 0) {
3275 ec.BeginScope (scopeIndex);
3278 EmitScopeInitialization (ec);
3280 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3281 ec.Emit (OpCodes.Nop);
3289 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3290 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3291 ec.Emit (OpCodes.Nop);
3295 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3297 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3298 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3299 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3303 // Creates anonymous method storey
3305 storey.CreateContainer ();
3306 storey.DefineContainer ();
3307 storey.ExpandBaseInterfaces ();
3309 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3312 // Only first storey in path will hold this reference. All children blocks will
3313 // reference it indirectly using $ref field
3315 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3316 if (b.Parent != null) {
3317 var s = b.Parent.Explicit.AnonymousMethodStorey;
3319 storey.HoistedThis = s.HoistedThis;
3324 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3325 if (storey.HoistedThis == null)
3326 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3328 if (storey.HoistedThis != null)
3334 // We are the first storey on path and 'this' has to be hoisted
3336 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3337 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3339 // ThisReferencesFromChildrenBlock holds all reference even if they
3340 // are not on this path. It saves some memory otherwise it'd have to
3341 // be in every explicit block. We run this check to see if the reference
3342 // is valid for this storey
3344 Block block_on_path = ref_block;
3345 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3347 if (block_on_path == null)
3350 if (storey.HoistedThis == null) {
3351 storey.AddCapturedThisField (ec, null);
3354 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3356 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3358 if (b_storey != null) {
3360 // Don't add storey cross reference for `this' when the storey ends up not
3361 // beeing attached to any parent
3363 if (b.ParametersBlock.StateMachine == null) {
3364 AnonymousMethodStorey s = null;
3365 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3366 s = ab.Explicit.AnonymousMethodStorey;
3371 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3373 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3374 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3381 // Stop propagation inside same top block
3383 if (b.ParametersBlock == ParametersBlock.Original) {
3384 b_storey.AddParentStoreyReference (ec, storey);
3385 // b_storey.HoistedThis = storey.HoistedThis;
3389 b = pb = b.ParametersBlock;
3391 pb = b as ParametersBlock;
3394 if (pb != null && pb.StateMachine != null) {
3395 if (pb.StateMachine == storey)
3399 // If we are state machine with no parent. We can hook into parent without additional
3400 // reference and capture this directly
3402 ExplicitBlock parent_storey_block = pb;
3403 while (parent_storey_block.Parent != null) {
3404 parent_storey_block = parent_storey_block.Parent.Explicit;
3405 if (parent_storey_block.AnonymousMethodStorey != null) {
3410 if (parent_storey_block.AnonymousMethodStorey == null) {
3411 if (pb.StateMachine.HoistedThis == null) {
3412 pb.StateMachine.AddCapturedThisField (ec, null);
3413 b.HasCapturedThis = true;
3419 var parent_this_block = pb;
3420 while (parent_this_block.Parent != null) {
3421 parent_this_block = parent_this_block.Parent.ParametersBlock;
3422 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3428 // Add reference to closest storey which holds captured this
3430 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3434 // Add parent storey reference only when this is not captured directly
3436 if (b_storey != null) {
3437 b_storey.AddParentStoreyReference (ec, storey);
3438 b_storey.HoistedThis = storey.HoistedThis;
3445 var ref_blocks = storey.ReferencesFromChildrenBlock;
3446 if (ref_blocks != null) {
3447 foreach (ExplicitBlock ref_block in ref_blocks) {
3448 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3449 if (b.AnonymousMethodStorey != null) {
3450 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3453 // Stop propagation inside same top block
3455 if (b.ParametersBlock == ParametersBlock.Original)
3458 b = b.ParametersBlock;
3461 var pb = b as ParametersBlock;
3462 if (pb != null && pb.StateMachine != null) {
3463 if (pb.StateMachine == storey)
3466 pb.StateMachine.AddParentStoreyReference (ec, storey);
3469 b.HasCapturedVariable = true;
3475 storey.PrepareEmit ();
3476 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3479 public void DisableDebugScopeIndex ()
3481 debug_scope_index = -1;
3484 public virtual int GetDebugSymbolScopeIndex ()
3486 if (debug_scope_index == 0)
3487 debug_scope_index = ++ParametersBlock.debug_scope_index;
3489 return debug_scope_index;
3492 public void RegisterAsyncAwait ()
3495 while ((block.flags & Flags.AwaitBlock) == 0) {
3496 block.flags |= Flags.AwaitBlock;
3498 if (block is ParametersBlock)
3501 block = block.Parent.Explicit;
3505 public void RegisterIteratorYield ()
3507 ParametersBlock.TopBlock.IsIterator = true;
3510 while ((block.flags & Flags.YieldBlock) == 0) {
3511 block.flags |= Flags.YieldBlock;
3513 if (block.Parent == null)
3516 block = block.Parent.Explicit;
3520 public void SetCatchBlock ()
3522 flags |= Flags.CatchBlock;
3525 public void SetFinallyBlock ()
3527 flags |= Flags.FinallyBlock;
3530 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3532 tryBlock.statements = statements;
3533 statements = new List<Statement> (1);
3534 statements.Add (tf);
3539 // ParametersBlock was introduced to support anonymous methods
3540 // and lambda expressions
3542 public class ParametersBlock : ExplicitBlock
3544 public class ParameterInfo : INamedBlockVariable
3546 readonly ParametersBlock block;
3548 public VariableInfo VariableInfo;
3551 public ParameterInfo (ParametersBlock block, int index)
3559 public ParametersBlock Block {
3565 Block INamedBlockVariable.Block {
3571 public bool IsDeclared {
3577 public bool IsParameter {
3583 public bool IsLocked {
3592 public Location Location {
3594 return Parameter.Location;
3598 public Parameter Parameter {
3600 return block.Parameters [index];
3604 public TypeSpec ParameterType {
3606 return Parameter.Type;
3612 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3614 return new ParameterReference (this, loc);
3619 // Block is converted into an expression
3621 sealed class BlockScopeExpression : Expression
3624 readonly ParametersBlock block;
3626 public BlockScopeExpression (Expression child, ParametersBlock block)
3632 public override bool ContainsEmitWithAwait ()
3634 return child.ContainsEmitWithAwait ();
3637 public override Expression CreateExpressionTree (ResolveContext ec)
3639 throw new NotSupportedException ();
3642 protected override Expression DoResolve (ResolveContext ec)
3647 child = child.Resolve (ec);
3651 eclass = child.eclass;
3656 public override void Emit (EmitContext ec)
3658 block.EmitScopeInitializers (ec);
3663 protected ParametersCompiled parameters;
3664 protected ParameterInfo[] parameter_info;
3665 protected bool resolved;
3666 protected ToplevelBlock top_block;
3667 protected StateMachine state_machine;
3668 protected Dictionary<string, object> labels;
3670 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3671 : base (parent, 0, start, start)
3673 if (parameters == null)
3674 throw new ArgumentNullException ("parameters");
3676 this.parameters = parameters;
3677 ParametersBlock = this;
3679 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3681 this.top_block = parent.ParametersBlock.top_block;
3682 ProcessParameters ();
3685 protected ParametersBlock (ParametersCompiled parameters, Location start)
3686 : base (null, 0, start, start)
3688 if (parameters == null)
3689 throw new ArgumentNullException ("parameters");
3691 this.parameters = parameters;
3692 ParametersBlock = this;
3696 // It's supposed to be used by method body implementation of anonymous methods
3698 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3699 : base (null, 0, source.StartLocation, source.EndLocation)
3701 this.parameters = parameters;
3702 this.statements = source.statements;
3703 this.scope_initializers = source.scope_initializers;
3705 this.resolved = true;
3706 this.reachable = source.reachable;
3707 this.am_storey = source.am_storey;
3708 this.state_machine = source.state_machine;
3709 this.flags = source.flags & Flags.ReachableEnd;
3711 ParametersBlock = this;
3714 // Overwrite original for comparison purposes when linking cross references
3715 // between anonymous methods
3717 Original = source.Original;
3722 public bool HasReferenceToStoreyForInstanceLambdas {
3724 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3727 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3731 public bool IsAsync {
3733 return (flags & Flags.HasAsyncModifier) != 0;
3736 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3741 // Block has been converted to expression tree
3743 public bool IsExpressionTree {
3745 return (flags & Flags.IsExpressionTree) != 0;
3750 // The parameters for the block.
3752 public ParametersCompiled Parameters {
3758 public StateMachine StateMachine {
3760 return state_machine;
3764 public ToplevelBlock TopBlock {
3773 public bool Resolved {
3775 return (flags & Flags.Resolved) != 0;
3779 public int TemporaryLocalsCount { get; set; }
3784 // Checks whether all `out' parameters have been assigned.
3786 public void CheckControlExit (FlowAnalysisContext fc)
3788 CheckControlExit (fc, fc.DefiniteAssignment);
3791 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3793 if (parameter_info == null)
3796 foreach (var p in parameter_info) {
3797 if (p.VariableInfo == null)
3800 if (p.VariableInfo.IsAssigned (dat))
3803 fc.Report.Error (177, p.Location,
3804 "The out parameter `{0}' must be assigned to before control leaves the current method",
3809 protected override void CloneTo (CloneContext clonectx, Statement t)
3811 base.CloneTo (clonectx, t);
3813 var target = (ParametersBlock) t;
3816 // Clone label statements as well as they contain block reference
3820 if (pb.labels != null) {
3821 target.labels = new Dictionary<string, object> ();
3823 foreach (var entry in pb.labels) {
3824 var list = entry.Value as List<LabeledStatement>;
3827 var list_clone = new List<LabeledStatement> ();
3828 foreach (var lentry in list) {
3829 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3832 target.labels.Add (entry.Key, list_clone);
3834 var labeled = (LabeledStatement) entry.Value;
3835 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3842 if (pb.Parent == null)
3845 pb = pb.Parent.ParametersBlock;
3849 public override Expression CreateExpressionTree (ResolveContext ec)
3851 if (statements.Count == 1) {
3852 Expression expr = statements[0].CreateExpressionTree (ec);
3853 if (scope_initializers != null)
3854 expr = new BlockScopeExpression (expr, this);
3859 return base.CreateExpressionTree (ec);
3862 public override void Emit (EmitContext ec)
3864 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3865 DefineStoreyContainer (ec, state_machine);
3866 state_machine.EmitStoreyInstantiation (ec, this);
3872 public void EmitEmbedded (EmitContext ec)
3874 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3875 DefineStoreyContainer (ec, state_machine);
3876 state_machine.EmitStoreyInstantiation (ec, this);
3882 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3884 var res = base.DoFlowAnalysis (fc);
3886 if (HasReachableClosingBrace)
3887 CheckControlExit (fc);
3892 public override int GetDebugSymbolScopeIndex ()
3897 public LabeledStatement GetLabel (string name, Block block)
3900 // Cloned parameters blocks can have their own cloned version of top-level labels
3902 if (labels == null) {
3904 return Parent.ParametersBlock.GetLabel (name, block);
3910 if (!labels.TryGetValue (name, out value)) {
3914 var label = value as LabeledStatement;
3916 if (label != null) {
3917 if (IsLabelVisible (label, b))
3921 List<LabeledStatement> list = (List<LabeledStatement>) value;
3922 for (int i = 0; i < list.Count; ++i) {
3924 if (IsLabelVisible (label, b))
3932 static bool IsLabelVisible (LabeledStatement label, Block b)
3935 if (label.Block == b)
3938 } while (b != null);
3943 public ParameterInfo GetParameterInfo (Parameter p)
3945 for (int i = 0; i < parameters.Count; ++i) {
3946 if (parameters[i] == p)
3947 return parameter_info[i];
3950 throw new ArgumentException ("Invalid parameter");
3953 public ParameterReference GetParameterReference (int index, Location loc)
3955 return new ParameterReference (parameter_info[index], loc);
3958 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3960 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3962 CloneContext clonectx = new CloneContext ();
3963 return Clone (clonectx);
3966 protected void ProcessParameters ()
3968 if (parameters.Count == 0)
3971 parameter_info = new ParameterInfo[parameters.Count];
3972 for (int i = 0; i < parameter_info.Length; ++i) {
3973 var p = parameters.FixedParameters[i];
3977 // TODO: Should use Parameter only and more block there
3978 parameter_info[i] = new ParameterInfo (this, i);
3980 AddLocalName (p.Name, parameter_info[i]);
3984 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3986 var src = stmt.Block;
3989 // Cannot remap label block if the label was not yet cloned which
3990 // can happen in case of anonymous method inside anoynymous method
3991 // with a label. But in this case we don't care because goto cannot
3992 // jump of out anonymous method
3994 if (src.ParametersBlock != this)
3997 var src_stmts = src.Statements;
3998 for (int i = 0; i < src_stmts.Count; ++i) {
3999 if (src_stmts[i] == stmt)
4000 return (LabeledStatement) dst.Statements[i];
4003 throw new InternalErrorException ("Should never be reached");
4006 public override bool Resolve (BlockContext bc)
4008 // TODO: if ((flags & Flags.Resolved) != 0)
4015 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4016 flags |= Flags.IsExpressionTree;
4019 PrepareAssignmentAnalysis (bc);
4021 if (!base.Resolve (bc))
4024 } catch (Exception e) {
4025 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4028 if (bc.CurrentBlock != null) {
4029 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4031 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4036 // If an asynchronous body of F is either an expression classified as nothing, or a
4037 // statement block where no return statements have expressions, the inferred return type is Task
4040 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4041 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4042 am.ReturnTypeInference = null;
4043 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4051 void PrepareAssignmentAnalysis (BlockContext bc)
4053 for (int i = 0; i < parameters.Count; ++i) {
4054 var par = parameters.FixedParameters[i];
4056 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4059 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4063 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4065 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4066 var stateMachine = new IteratorStorey (iterator);
4068 state_machine = stateMachine;
4069 iterator.SetStateMachine (stateMachine);
4071 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4072 tlb.Original = this;
4073 tlb.state_machine = stateMachine;
4074 tlb.AddStatement (new Return (iterator, iterator.Location));
4078 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4080 for (int i = 0; i < parameters.Count; i++) {
4081 Parameter p = parameters[i];
4082 Parameter.Modifier mod = p.ModFlags;
4083 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4084 host.Compiler.Report.Error (1988, p.Location,
4085 "Async methods cannot have ref or out parameters");
4089 if (p is ArglistParameter) {
4090 host.Compiler.Report.Error (4006, p.Location,
4091 "__arglist is not allowed in parameter list of async methods");
4095 if (parameters.Types[i].IsPointer) {
4096 host.Compiler.Report.Error (4005, p.Location,
4097 "Async methods cannot have unsafe parameters");
4103 host.Compiler.Report.Warning (1998, 1, loc,
4104 "Async block lacks `await' operator and will run synchronously");
4107 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4108 var initializer = new AsyncInitializer (this, host, block_type);
4109 initializer.Type = block_type;
4110 initializer.DelegateType = delegateType;
4112 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4114 state_machine = stateMachine;
4115 initializer.SetStateMachine (stateMachine);
4117 const Flags flags = Flags.CompilerGenerated;
4119 var b = this is ToplevelBlock ?
4120 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4121 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4124 b.state_machine = stateMachine;
4125 b.AddStatement (new AsyncInitializerStatement (initializer));
4133 public class ToplevelBlock : ParametersBlock
4135 LocalVariable this_variable;
4136 CompilerContext compiler;
4137 Dictionary<string, object> names;
4139 List<ExplicitBlock> this_references;
4141 public ToplevelBlock (CompilerContext ctx, Location loc)
4142 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4146 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4147 : base (parameters, start)
4149 this.compiler = ctx;
4153 ProcessParameters ();
4157 // Recreates a top level block from parameters block. Used for
4158 // compiler generated methods where the original block comes from
4159 // explicit child block. This works for already resolved blocks
4160 // only to ensure we resolve them in the correct flow order
4162 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4163 : base (source, parameters)
4165 this.compiler = source.TopBlock.compiler;
4169 public bool IsIterator {
4171 return (flags & Flags.Iterator) != 0;
4174 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4178 public Report Report {
4180 return compiler.Report;
4185 // Used by anonymous blocks to track references of `this' variable
4187 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4189 return this_references;
4194 // Returns the "this" instance variable of this block.
4195 // See AddThisVariable() for more information.
4197 public LocalVariable ThisVariable {
4199 return this_variable;
4203 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4206 names = new Dictionary<string, object> ();
4209 if (!names.TryGetValue (name, out value)) {
4210 names.Add (name, li);
4214 INamedBlockVariable existing = value as INamedBlockVariable;
4215 List<INamedBlockVariable> existing_list;
4216 if (existing != null) {
4217 existing_list = new List<INamedBlockVariable> ();
4218 existing_list.Add (existing);
4219 names[name] = existing_list;
4221 existing_list = (List<INamedBlockVariable>) value;
4225 // A collision checking between local names
4227 var variable_block = li.Block.Explicit;
4228 for (int i = 0; i < existing_list.Count; ++i) {
4229 existing = existing_list[i];
4230 Block b = existing.Block.Explicit;
4232 // Collision at same level
4233 if (variable_block == b) {
4234 li.Block.Error_AlreadyDeclared (name, li);
4238 // Collision with parent
4239 Block parent = variable_block;
4240 while ((parent = parent.Parent) != null) {
4242 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4243 i = existing_list.Count;
4248 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4249 // Collision with children
4250 while ((b = b.Parent) != null) {
4251 if (variable_block == b) {
4252 li.Block.Error_AlreadyDeclared (name, li, "child");
4253 i = existing_list.Count;
4260 existing_list.Add (li);
4263 public void AddLabel (string name, LabeledStatement label)
4266 labels = new Dictionary<string, object> ();
4269 if (!labels.TryGetValue (name, out value)) {
4270 labels.Add (name, label);
4274 LabeledStatement existing = value as LabeledStatement;
4275 List<LabeledStatement> existing_list;
4276 if (existing != null) {
4277 existing_list = new List<LabeledStatement> ();
4278 existing_list.Add (existing);
4279 labels[name] = existing_list;
4281 existing_list = (List<LabeledStatement>) value;
4285 // A collision checking between labels
4287 for (int i = 0; i < existing_list.Count; ++i) {
4288 existing = existing_list[i];
4289 Block b = existing.Block;
4291 // Collision at same level
4292 if (label.Block == b) {
4293 Report.SymbolRelatedToPreviousError (existing.loc, name);
4294 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4298 // Collision with parent
4300 while ((b = b.Parent) != null) {
4301 if (existing.Block == b) {
4302 Report.Error (158, label.loc,
4303 "The label `{0}' shadows another label by the same name in a contained scope", name);
4304 i = existing_list.Count;
4309 // Collision with with children
4311 while ((b = b.Parent) != null) {
4312 if (label.Block == b) {
4313 Report.Error (158, label.loc,
4314 "The label `{0}' shadows another label by the same name in a contained scope", name);
4315 i = existing_list.Count;
4321 existing_list.Add (label);
4324 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4326 if (this_references == null)
4327 this_references = new List<ExplicitBlock> ();
4329 if (!this_references.Contains (block))
4330 this_references.Add (block);
4333 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4335 this_references.Remove (block);
4339 // Creates an arguments set from all parameters, useful for method proxy calls
4341 public Arguments GetAllParametersArguments ()
4343 int count = parameters.Count;
4344 Arguments args = new Arguments (count);
4345 for (int i = 0; i < count; ++i) {
4346 var pi = parameter_info[i];
4347 var arg_expr = GetParameterReference (i, pi.Location);
4349 Argument.AType atype_modifier;
4350 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4351 case Parameter.Modifier.REF:
4352 atype_modifier = Argument.AType.Ref;
4354 case Parameter.Modifier.OUT:
4355 atype_modifier = Argument.AType.Out;
4362 args.Add (new Argument (arg_expr, atype_modifier));
4369 // Lookup inside a block, the returned value can represent 3 states
4371 // true+variable: A local name was found and it's valid
4372 // false+variable: A local name was found in a child block only
4373 // false+null: No local name was found
4375 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4381 if (!names.TryGetValue (name, out value))
4384 variable = value as INamedBlockVariable;
4386 if (variable != null) {
4388 if (variable.Block == b.Original)
4392 } while (b != null);
4400 } while (b != null);
4402 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4403 for (int i = 0; i < list.Count; ++i) {
4406 if (variable.Block == b.Original)
4410 } while (b != null);
4418 } while (b != null);
4428 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4430 if (block.names != null) {
4431 foreach (var n in block.names) {
4432 var variable = n.Value as INamedBlockVariable;
4433 if (variable != null) {
4434 if (variable.Block.ParametersBlock == pb)
4435 AddLocalName (n.Key, variable, false);
4439 foreach (var v in (List<INamedBlockVariable>) n.Value)
4440 if (v.Block.ParametersBlock == pb)
4441 AddLocalName (n.Key, v, false);
4447 // This is used by non-static `struct' constructors which do not have an
4448 // initializer - in this case, the constructor must initialize all of the
4449 // struct's fields. To do this, we add a "this" variable and use the flow
4450 // analysis code to ensure that it's been fully initialized before control
4451 // leaves the constructor.
4453 public void AddThisVariable (BlockContext bc)
4455 if (this_variable != null)
4456 throw new InternalErrorException (StartLocation.ToString ());
4458 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4459 this_variable.Type = bc.CurrentType;
4460 this_variable.PrepareAssignmentAnalysis (bc);
4463 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4466 // If we're a non-static struct constructor which doesn't have an
4467 // initializer, then we must initialize all of the struct's fields.
4469 if (this_variable != null)
4470 this_variable.IsThisAssigned (fc, this);
4472 base.CheckControlExit (fc, dat);
4475 public HashSet<LocalVariable> GetUndeclaredVariables ()
4480 HashSet<LocalVariable> variables = null;
4482 foreach (var entry in names) {
4483 var complex = entry.Value as List<INamedBlockVariable>;
4484 if (complex != null) {
4485 foreach (var centry in complex) {
4486 if (IsUndeclaredVariable (centry)) {
4487 if (variables == null)
4488 variables = new HashSet<LocalVariable> ();
4490 variables.Add ((LocalVariable) centry);
4493 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4494 if (variables == null)
4495 variables = new HashSet<LocalVariable> ();
4497 variables.Add ((LocalVariable)entry.Value);
4504 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4506 var lv = namedBlockVariable as LocalVariable;
4507 return lv != null && !lv.IsDeclared;
4510 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4515 foreach (var entry in names) {
4516 var complex = entry.Value as List<INamedBlockVariable>;
4517 if (complex != null) {
4518 foreach (var centry in complex) {
4519 var lv = centry as LocalVariable;
4520 if (lv != null && undeclaredVariables.Contains (lv)) {
4525 var lv = entry.Value as LocalVariable;
4526 if (lv != null && undeclaredVariables.Contains (lv))
4532 public override void Emit (EmitContext ec)
4534 if (Report.Errors > 0)
4538 if (IsCompilerGenerated) {
4539 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4547 // If `HasReturnLabel' is set, then we already emitted a
4548 // jump to the end of the method, so we must emit a `ret'
4551 // Unfortunately, System.Reflection.Emit automatically emits
4552 // a leave to the end of a finally block. This is a problem
4553 // if no code is following the try/finally block since we may
4554 // jump to a point after the end of the method.
4555 // As a workaround, we're always creating a return label in
4558 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4559 if (ec.HasReturnLabel)
4560 ec.MarkLabel (ec.ReturnLabel);
4562 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4563 ec.Mark (EndLocation);
4565 if (ec.ReturnType.Kind != MemberKind.Void)
4566 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4568 ec.Emit (OpCodes.Ret);
4571 } catch (Exception e) {
4572 throw new InternalErrorException (e, StartLocation);
4576 public bool Resolve (BlockContext bc, IMethodData md)
4581 var errors = bc.Report.Errors;
4585 if (bc.Report.Errors > errors)
4588 MarkReachable (new Reachability ());
4590 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4591 // TODO: var md = bc.CurrentMemberDefinition;
4592 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4595 if ((flags & Flags.NoFlowAnalysis) != 0)
4598 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4601 } catch (Exception e) {
4602 throw new InternalErrorException (e, StartLocation);
4609 public class SwitchLabel : Statement
4617 // if expr == null, then it is the default case.
4619 public SwitchLabel (Expression expr, Location l)
4625 public bool IsDefault {
4627 return label == null;
4631 public Expression Label {
4637 public Location Location {
4643 public Constant Converted {
4652 public bool PatternMatching { get; set; }
4654 public bool SectionStart { get; set; }
4656 public Label GetILLabel (EmitContext ec)
4658 if (il_label == null){
4659 il_label = ec.DefineLabel ();
4662 return il_label.Value;
4665 protected override void DoEmit (EmitContext ec)
4667 ec.MarkLabel (GetILLabel (ec));
4670 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4675 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4679 public override bool Resolve (BlockContext bc)
4681 if (ResolveAndReduce (bc))
4682 bc.Switch.RegisterLabel (bc, this);
4688 // Resolves the expression, reduces it to a literal if possible
4689 // and then converts it to the requested type.
4691 bool ResolveAndReduce (BlockContext bc)
4696 var switch_statement = bc.Switch;
4698 if (PatternMatching) {
4699 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4700 return label != null;
4703 var c = label.ResolveLabelConstant (bc);
4707 if (switch_statement.IsNullable && c is NullLiteral) {
4712 if (switch_statement.IsPatternMatching) {
4713 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4717 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4718 return converted != null;
4721 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4723 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4724 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4727 protected override void CloneTo (CloneContext clonectx, Statement target)
4729 var t = (SwitchLabel) target;
4731 t.label = label.Clone (clonectx);
4734 public override object Accept (StructuralVisitor visitor)
4736 return visitor.Visit (this);
4739 public string GetSignatureForError ()
4742 if (converted == null)
4745 label = converted.GetValueAsLiteral ();
4747 return string.Format ("case {0}:", label);
4751 public class Switch : LoopStatement
4753 // structure used to hold blocks of keys while calculating table switch
4754 sealed class LabelsRange : IComparable<LabelsRange>
4756 public readonly long min;
4758 public readonly List<long> label_values;
4760 public LabelsRange (long value)
4763 label_values = new List<long> ();
4764 label_values.Add (value);
4767 public LabelsRange (long min, long max, ICollection<long> values)
4771 this.label_values = new List<long> (values);
4776 return max - min + 1;
4780 public bool AddValue (long value)
4782 var gap = value - min + 1;
4783 // Ensure the range has > 50% occupancy
4784 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4788 label_values.Add (value);
4792 public int CompareTo (LabelsRange other)
4794 int nLength = label_values.Count;
4795 int nLengthOther = other.label_values.Count;
4796 if (nLengthOther == nLength)
4797 return (int) (other.min - min);
4799 return nLength - nLengthOther;
4803 sealed class DispatchStatement : Statement
4805 readonly Switch body;
4807 public DispatchStatement (Switch body)
4812 protected override void CloneTo (CloneContext clonectx, Statement target)
4814 throw new NotImplementedException ();
4817 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4822 protected override void DoEmit (EmitContext ec)
4824 body.EmitDispatch (ec);
4828 class MissingBreak : Statement
4830 readonly SwitchLabel label;
4832 public MissingBreak (SwitchLabel sl)
4838 public bool FallOut { get; set; }
4840 protected override void DoEmit (EmitContext ec)
4844 protected override void CloneTo (CloneContext clonectx, Statement target)
4848 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4851 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4852 label.GetSignatureForError ());
4854 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4855 label.GetSignatureForError ());
4861 public Expression Expr;
4864 // Mapping of all labels to their SwitchLabels
4866 Dictionary<long, SwitchLabel> labels;
4867 Dictionary<string, SwitchLabel> string_labels;
4868 List<SwitchLabel> case_labels;
4870 List<Tuple<GotoCase, Constant>> goto_cases;
4871 List<DefiniteAssignmentBitSet> end_reachable_das;
4874 /// The governing switch type
4876 public TypeSpec SwitchType;
4878 Expression new_expr;
4880 SwitchLabel case_null;
4881 SwitchLabel case_default;
4883 Label defaultLabel, nullLabel;
4884 VariableReference value;
4885 ExpressionStatement string_dictionary;
4886 FieldExpr switch_cache_field;
4887 ExplicitBlock block;
4891 // Nullable Types support
4893 Nullable.Unwrap unwrap;
4895 public Switch (Expression e, ExplicitBlock block, Location l)
4903 public SwitchLabel ActiveLabel { get; set; }
4905 public ExplicitBlock Block {
4911 public SwitchLabel DefaultLabel {
4913 return case_default;
4917 public bool IsNullable {
4919 return unwrap != null;
4923 public bool IsPatternMatching {
4925 return new_expr == null && SwitchType != null;
4929 public List<SwitchLabel> RegisteredLabels {
4935 public VariableReference ExpressionValue {
4942 // Determines the governing type for a switch. The returned
4943 // expression might be the expression from the switch, or an
4944 // expression that includes any potential conversions to
4946 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4948 switch (expr.Type.BuiltinType) {
4949 case BuiltinTypeSpec.Type.Byte:
4950 case BuiltinTypeSpec.Type.SByte:
4951 case BuiltinTypeSpec.Type.UShort:
4952 case BuiltinTypeSpec.Type.Short:
4953 case BuiltinTypeSpec.Type.UInt:
4954 case BuiltinTypeSpec.Type.Int:
4955 case BuiltinTypeSpec.Type.ULong:
4956 case BuiltinTypeSpec.Type.Long:
4957 case BuiltinTypeSpec.Type.Char:
4958 case BuiltinTypeSpec.Type.String:
4959 case BuiltinTypeSpec.Type.Bool:
4963 if (expr.Type.IsEnum)
4967 // Try to find a *user* defined implicit conversion.
4969 // If there is no implicit conversion, or if there are multiple
4970 // conversions, we have to report an error
4972 Expression converted = null;
4973 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4975 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4978 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4979 Convert.UserConversionRestriction.ProbingOnly;
4982 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4984 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4989 // Ignore over-worked ImplicitUserConversions that do
4990 // an implicit conversion in addition to the user conversion.
4992 var uc = e as UserCast;
4996 if (converted != null){
4997 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
5006 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
5008 var types = module.Compiler.BuiltinTypes;
5010 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5011 TypeSpec[] stypes = new[] {
5024 if (nullable != null) {
5026 Array.Resize (ref stypes, stypes.Length + 9);
5028 for (int i = 0; i < 9; ++i) {
5029 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5036 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5038 case_labels.Add (sl);
5041 if (case_default != null) {
5042 sl.Error_AlreadyOccurs (rc, case_default);
5050 if (sl.Converted == null)
5054 if (string_labels != null) {
5055 string string_value = sl.Converted.GetValue () as string;
5056 if (string_value == null)
5059 string_labels.Add (string_value, sl);
5061 if (sl.Converted.IsNull) {
5064 labels.Add (sl.Converted.GetValueAsLong (), sl);
5067 } catch (ArgumentException) {
5068 if (string_labels != null)
5069 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5071 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5076 // This method emits code for a lookup-based switch statement (non-string)
5077 // Basically it groups the cases into blocks that are at least half full,
5078 // and then spits out individual lookup opcodes for each block.
5079 // It emits the longest blocks first, and short blocks are just
5080 // handled with direct compares.
5082 void EmitTableSwitch (EmitContext ec, Expression val)
5084 if (labels != null && labels.Count > 0) {
5085 List<LabelsRange> ranges;
5086 if (string_labels != null) {
5087 // We have done all hard work for string already
5088 // setup single range only
5089 ranges = new List<LabelsRange> (1);
5090 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5092 var element_keys = new long[labels.Count];
5093 labels.Keys.CopyTo (element_keys, 0);
5094 Array.Sort (element_keys);
5097 // Build possible ranges of switch labes to reduce number
5100 ranges = new List<LabelsRange> (element_keys.Length);
5101 var range = new LabelsRange (element_keys[0]);
5103 for (int i = 1; i < element_keys.Length; ++i) {
5104 var l = element_keys[i];
5105 if (range.AddValue (l))
5108 range = new LabelsRange (l);
5112 // sort the blocks so we can tackle the largest ones first
5116 Label lbl_default = defaultLabel;
5117 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5119 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5120 LabelsRange kb = ranges[range_index];
5121 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5123 // Optimize small ranges using simple equality check
5124 if (kb.Range <= 2) {
5125 foreach (var key in kb.label_values) {
5126 SwitchLabel sl = labels[key];
5127 if (sl == case_default || sl == case_null)
5130 if (sl.Converted.IsZeroInteger) {
5131 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5134 sl.Converted.Emit (ec);
5135 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5139 // TODO: if all the keys in the block are the same and there are
5140 // no gaps/defaults then just use a range-check.
5141 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5142 // TODO: optimize constant/I4 cases
5144 // check block range (could be > 2^31)
5146 ec.EmitLong (kb.min);
5147 ec.Emit (OpCodes.Blt, lbl_default);
5150 ec.EmitLong (kb.max);
5151 ec.Emit (OpCodes.Bgt, lbl_default);
5156 ec.EmitLong (kb.min);
5157 ec.Emit (OpCodes.Sub);
5160 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5164 int first = (int) kb.min;
5167 ec.Emit (OpCodes.Sub);
5168 } else if (first < 0) {
5169 ec.EmitInt (-first);
5170 ec.Emit (OpCodes.Add);
5174 // first, build the list of labels for the switch
5176 long cJumps = kb.Range;
5177 Label[] switch_labels = new Label[cJumps];
5178 for (int iJump = 0; iJump < cJumps; iJump++) {
5179 var key = kb.label_values[iKey];
5180 if (key == kb.min + iJump) {
5181 switch_labels[iJump] = labels[key].GetILLabel (ec);
5184 switch_labels[iJump] = lbl_default;
5188 // emit the switch opcode
5189 ec.Emit (OpCodes.Switch, switch_labels);
5192 // mark the default for this block
5193 if (range_index != 0)
5194 ec.MarkLabel (lbl_default);
5197 // the last default just goes to the end
5198 if (ranges.Count > 0)
5199 ec.Emit (OpCodes.Br, lbl_default);
5203 public SwitchLabel FindLabel (Constant value)
5205 SwitchLabel sl = null;
5207 if (string_labels != null) {
5208 string s = value.GetValue () as string;
5210 if (case_null != null)
5212 else if (case_default != null)
5215 string_labels.TryGetValue (s, out sl);
5218 if (value is NullLiteral) {
5221 labels.TryGetValue (value.GetValueAsLong (), out sl);
5225 if (sl == null || sl.SectionStart)
5229 // Always return section start, it simplifies handling of switch labels
5231 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5232 var cs = case_labels [idx];
5233 if (cs.SectionStart)
5238 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5240 Expr.FlowAnalysis (fc);
5242 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5243 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5244 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5246 block.FlowAnalysis (fc);
5248 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5250 if (end_reachable_das != null) {
5251 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5252 InitialDefinitiveAssignment |= sections_das;
5253 end_reachable_das = null;
5256 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5258 return case_default != null && !end_reachable;
5261 public override bool Resolve (BlockContext ec)
5263 Expr = Expr.Resolve (ec);
5268 // LAMESPEC: User conversion from non-nullable governing type has a priority
5270 new_expr = SwitchGoverningType (ec, Expr, false);
5272 if (new_expr == null) {
5273 if (Expr.Type.IsNullableType) {
5274 unwrap = Nullable.Unwrap.Create (Expr, false);
5279 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5280 // involving nullable Expr and nullable governing type is
5282 new_expr = SwitchGoverningType (ec, unwrap, true);
5286 Expression switch_expr;
5287 if (new_expr == null) {
5288 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5289 if (Expr.Type != InternalType.ErrorType) {
5290 ec.Report.Error (151, loc,
5291 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5292 Expr.Type.GetSignatureForError ());
5299 SwitchType = Expr.Type;
5301 switch_expr = new_expr;
5302 SwitchType = new_expr.Type;
5303 if (SwitchType.IsNullableType) {
5304 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5305 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5308 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5309 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5313 if (block.Statements.Count == 0)
5316 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5317 string_labels = new Dictionary<string, SwitchLabel> ();
5319 labels = new Dictionary<long, SwitchLabel> ();
5323 var constant = switch_expr as Constant;
5326 // Don't need extra variable for constant switch or switch with
5327 // only default case
5329 if (constant == null) {
5331 // Store switch expression for comparison purposes
5333 value = switch_expr as VariableReference;
5334 if (value == null && !HasOnlyDefaultSection ()) {
5335 var current_block = ec.CurrentBlock;
5336 ec.CurrentBlock = Block;
5337 // Create temporary variable inside switch scope
5338 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5340 ec.CurrentBlock = current_block;
5344 case_labels = new List<SwitchLabel> ();
5346 Switch old_switch = ec.Switch;
5348 var parent_los = ec.EnclosingLoopOrSwitch;
5349 ec.EnclosingLoopOrSwitch = this;
5351 var ok = Statement.Resolve (ec);
5353 ec.EnclosingLoopOrSwitch = parent_los;
5354 ec.Switch = old_switch;
5357 // Check if all goto cases are valid. Needs to be done after switch
5358 // is resolved because goto can jump forward in the scope.
5360 if (goto_cases != null) {
5361 foreach (var gc in goto_cases) {
5362 if (gc.Item1 == null) {
5363 if (DefaultLabel == null) {
5364 Goto.Error_UnknownLabel (ec, "default", loc);
5370 var sl = FindLabel (gc.Item2);
5372 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5374 gc.Item1.Label = sl;
5382 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5383 ResolveStringSwitchMap (ec);
5387 // Anonymous storey initialization has to happen before
5388 // any generated switch dispatch
5390 block.InsertStatement (0, new DispatchStatement (this));
5395 bool HasOnlyDefaultSection ()
5397 for (int i = 0; i < block.Statements.Count; ++i) {
5398 var s = block.Statements[i] as SwitchLabel;
5400 if (s == null || s.IsDefault)
5409 public override Reachability MarkReachable (Reachability rc)
5411 if (rc.IsUnreachable)
5414 base.MarkReachable (rc);
5416 block.MarkReachableScope (rc);
5418 if (block.Statements.Count == 0)
5421 SwitchLabel constant_label = null;
5422 var constant = new_expr as Constant;
5424 if (constant != null) {
5425 constant_label = FindLabel (constant) ?? case_default;
5426 if (constant_label == null) {
5427 block.Statements.RemoveAt (0);
5432 var section_rc = new Reachability ();
5433 SwitchLabel prev_label = null;
5435 for (int i = 0; i < block.Statements.Count; ++i) {
5436 var s = block.Statements[i];
5437 var sl = s as SwitchLabel;
5439 if (sl != null && sl.SectionStart) {
5441 // Section is marked already via goto case
5443 if (!sl.IsUnreachable) {
5444 section_rc = new Reachability ();
5448 if (section_rc.IsUnreachable) {
5450 // Common case. Previous label section end is unreachable as
5451 // it ends with break, return, etc. For next section revert
5452 // to reachable again unless we have constant switch block
5454 section_rc = constant_label != null && constant_label != sl ?
5455 Reachability.CreateUnreachable () :
5456 new Reachability ();
5457 } else if (prev_label != null) {
5459 // Error case as control cannot fall through from one case label
5461 sl.SectionStart = false;
5462 s = new MissingBreak (prev_label);
5463 s.MarkReachable (rc);
5464 block.Statements.Insert (i - 1, s);
5466 } else if (constant_label != null && constant_label != sl) {
5468 // Special case for the first unreachable label in constant
5471 section_rc = Reachability.CreateUnreachable ();
5477 section_rc = s.MarkReachable (section_rc);
5480 if (!section_rc.IsUnreachable && prev_label != null) {
5481 prev_label.SectionStart = false;
5482 var s = new MissingBreak (prev_label) {
5486 s.MarkReachable (rc);
5487 block.Statements.Add (s);
5491 // Reachability can affect parent only when all possible paths are handled but
5492 // we still need to run reachability check on switch body to check for fall-through
5494 if (case_default == null && constant_label == null)
5498 // We have at least one local exit from the switch
5503 return Reachability.CreateUnreachable ();
5506 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5508 if (goto_cases == null)
5509 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5511 goto_cases.Add (Tuple.Create (gotoCase, value));
5515 // Converts string switch into string hashtable
5517 void ResolveStringSwitchMap (ResolveContext ec)
5519 FullNamedExpression string_dictionary_type;
5520 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5521 string_dictionary_type = new TypeExpression (
5522 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5523 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5525 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5526 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5528 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5532 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5533 Field field = new Field (ctype, string_dictionary_type,
5534 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5535 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5536 if (!field.Define ())
5538 ctype.AddField (field);
5540 var init = new List<Expression> ();
5542 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5543 string value = null;
5545 foreach (SwitchLabel sl in case_labels) {
5547 if (sl.SectionStart)
5548 labels.Add (++counter, sl);
5550 if (sl == case_default || sl == case_null)
5553 value = (string) sl.Converted.GetValue ();
5554 var init_args = new List<Expression> (2);
5555 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5557 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5558 init_args.Add (sl.Converted);
5560 init.Add (new CollectionElementInitializer (init_args, loc));
5563 Arguments args = new Arguments (1);
5564 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5565 Expression initializer = new NewInitialize (string_dictionary_type, args,
5566 new CollectionOrObjectInitializers (init, loc), loc);
5568 switch_cache_field = new FieldExpr (field, loc);
5569 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5572 void DoEmitStringSwitch (EmitContext ec)
5574 Label l_initialized = ec.DefineLabel ();
5577 // Skip initialization when value is null
5579 value.EmitBranchable (ec, nullLabel, false);
5582 // Check if string dictionary is initialized and initialize
5584 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5585 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5586 string_dictionary.EmitStatement (ec);
5588 ec.MarkLabel (l_initialized);
5590 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5592 ResolveContext rc = new ResolveContext (ec.MemberContext);
5594 if (switch_cache_field.Type.IsGeneric) {
5595 Arguments get_value_args = new Arguments (2);
5596 get_value_args.Add (new Argument (value));
5597 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5598 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5599 if (get_item == null)
5603 // A value was not found, go to default case
5605 get_item.EmitBranchable (ec, defaultLabel, false);
5607 Arguments get_value_args = new Arguments (1);
5608 get_value_args.Add (new Argument (value));
5610 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5611 if (get_item == null)
5614 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5615 get_item_object.EmitAssign (ec, get_item, true, false);
5616 ec.Emit (OpCodes.Brfalse, defaultLabel);
5618 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5619 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5621 get_item_int.EmitStatement (ec);
5622 get_item_object.Release (ec);
5625 EmitTableSwitch (ec, string_switch_variable);
5626 string_switch_variable.Release (ec);
5630 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5632 void EmitShortSwitch (EmitContext ec)
5634 MethodSpec equal_method = null;
5635 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5636 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5639 if (equal_method != null) {
5640 value.EmitBranchable (ec, nullLabel, false);
5643 for (int i = 0; i < case_labels.Count; ++i) {
5644 var label = case_labels [i];
5645 if (label == case_default || label == case_null)
5648 var constant = label.Converted;
5650 if (constant == null) {
5651 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5655 if (equal_method != null) {
5659 var call = new CallEmitter ();
5660 call.EmitPredefined (ec, equal_method, new Arguments (0));
5661 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5665 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5666 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5672 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5675 ec.Emit (OpCodes.Br, defaultLabel);
5678 void EmitDispatch (EmitContext ec)
5680 if (IsPatternMatching) {
5681 EmitShortSwitch (ec);
5685 if (value == null) {
5687 // Constant switch, we've already done the work if there is only 1 label
5691 foreach (var sl in case_labels) {
5692 if (sl.IsUnreachable)
5695 if (reachable++ > 0) {
5696 var constant = (Constant) new_expr;
5697 var constant_label = FindLabel (constant) ?? case_default;
5699 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5707 if (string_dictionary != null) {
5708 DoEmitStringSwitch (ec);
5709 } else if (case_labels.Count < 4 || string_labels != null) {
5710 EmitShortSwitch (ec);
5712 EmitTableSwitch (ec, value);
5716 protected override void DoEmit (EmitContext ec)
5719 // Setup the codegen context
5721 Label old_end = ec.LoopEnd;
5722 Switch old_switch = ec.Switch;
5724 ec.LoopEnd = ec.DefineLabel ();
5727 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5728 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5730 if (value != null) {
5733 var switch_expr = new_expr ?? Expr;
5735 unwrap.EmitCheck (ec);
5736 ec.Emit (OpCodes.Brfalse, nullLabel);
5737 value.EmitAssign (ec, switch_expr, false, false);
5738 } else if (switch_expr != value) {
5739 value.EmitAssign (ec, switch_expr, false, false);
5744 // Next statement is compiler generated we don't need extra
5745 // nop when we can use the statement for sequence point
5747 ec.Mark (block.StartLocation);
5748 block.IsCompilerGenerated = true;
5750 new_expr.EmitSideEffect (ec);
5755 // Restore context state.
5756 ec.MarkLabel (ec.LoopEnd);
5759 // Restore the previous context
5761 ec.LoopEnd = old_end;
5762 ec.Switch = old_switch;
5765 protected override void CloneTo (CloneContext clonectx, Statement t)
5767 Switch target = (Switch) t;
5769 target.Expr = Expr.Clone (clonectx);
5770 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5773 public override object Accept (StructuralVisitor visitor)
5775 return visitor.Visit (this);
5778 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5780 if (case_default == null && !(new_expr is Constant))
5783 if (end_reachable_das == null)
5784 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5786 end_reachable_das.Add (fc.DefiniteAssignment);
5789 public override void SetEndReachable ()
5791 end_reachable = true;
5795 // A place where execution can restart in a state machine
5796 public abstract class ResumableStatement : Statement
5799 protected Label resume_point;
5801 public Label PrepareForEmit (EmitContext ec)
5805 resume_point = ec.DefineLabel ();
5807 return resume_point;
5810 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5815 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5820 public abstract class TryFinallyBlock : ExceptionStatement
5822 protected Statement stmt;
5823 Label dispose_try_block;
5824 bool prepared_for_dispose, emitted_dispose;
5825 Method finally_host;
5827 protected TryFinallyBlock (Statement stmt, Location loc)
5835 public Statement Statement {
5843 protected abstract void EmitTryBody (EmitContext ec);
5844 public abstract void EmitFinallyBody (EmitContext ec);
5846 public override Label PrepareForDispose (EmitContext ec, Label end)
5848 if (!prepared_for_dispose) {
5849 prepared_for_dispose = true;
5850 dispose_try_block = ec.DefineLabel ();
5852 return dispose_try_block;
5855 protected sealed override void DoEmit (EmitContext ec)
5857 EmitTryBodyPrepare (ec);
5860 bool beginFinally = EmitBeginFinallyBlock (ec);
5862 Label start_finally = ec.DefineLabel ();
5863 if (resume_points != null && beginFinally) {
5864 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5866 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5867 ec.Emit (OpCodes.Brfalse_S, start_finally);
5868 ec.Emit (OpCodes.Endfinally);
5871 ec.MarkLabel (start_finally);
5873 if (finally_host != null) {
5874 finally_host.Define ();
5875 finally_host.PrepareEmit ();
5876 finally_host.Emit ();
5878 // Now it's safe to add, to close it properly and emit sequence points
5879 finally_host.Parent.AddMember (finally_host);
5881 var ce = new CallEmitter ();
5882 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5883 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5885 EmitFinallyBody (ec);
5889 ec.EndExceptionBlock ();
5892 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5894 if (emitted_dispose)
5897 emitted_dispose = true;
5899 Label end_of_try = ec.DefineLabel ();
5901 // Ensure that the only way we can get into this code is through a dispatcher
5902 if (have_dispatcher)
5903 ec.Emit (OpCodes.Br, end);
5905 ec.BeginExceptionBlock ();
5907 ec.MarkLabel (dispose_try_block);
5909 Label[] labels = null;
5910 for (int i = 0; i < resume_points.Count; ++i) {
5911 ResumableStatement s = resume_points[i];
5912 Label ret = s.PrepareForDispose (ec, end_of_try);
5913 if (ret.Equals (end_of_try) && labels == null)
5915 if (labels == null) {
5916 labels = new Label[resume_points.Count];
5917 for (int j = 0; j < i; ++j)
5918 labels[j] = end_of_try;
5923 if (labels != null) {
5925 for (j = 1; j < labels.Length; ++j)
5926 if (!labels[0].Equals (labels[j]))
5928 bool emit_dispatcher = j < labels.Length;
5930 if (emit_dispatcher) {
5931 ec.Emit (OpCodes.Ldloc, pc);
5932 ec.EmitInt (first_resume_pc);
5933 ec.Emit (OpCodes.Sub);
5934 ec.Emit (OpCodes.Switch, labels);
5937 foreach (ResumableStatement s in resume_points)
5938 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5941 ec.MarkLabel (end_of_try);
5943 ec.BeginFinallyBlock ();
5945 if (finally_host != null) {
5946 var ce = new CallEmitter ();
5947 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5948 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5950 EmitFinallyBody (ec);
5953 ec.EndExceptionBlock ();
5956 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5958 var res = stmt.FlowAnalysis (fc);
5959 parent_try_block = null;
5963 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5965 ec.BeginFinallyBlock ();
5969 public override Reachability MarkReachable (Reachability rc)
5971 base.MarkReachable (rc);
5972 return Statement.MarkReachable (rc);
5975 public override bool Resolve (BlockContext bc)
5979 parent_try_block = bc.CurrentTryBlock;
5980 bc.CurrentTryBlock = this;
5982 if (stmt is TryCatch) {
5983 ok = stmt.Resolve (bc);
5985 using (bc.Set (ResolveContext.Options.TryScope)) {
5986 ok = stmt.Resolve (bc);
5990 bc.CurrentTryBlock = parent_try_block;
5993 // Finally block inside iterator is called from MoveNext and
5994 // Dispose methods that means we need to lift the block into
5995 // newly created host method to emit the body only once. The
5996 // original block then simply calls the newly generated method.
5998 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5999 var b = stmt as Block;
6000 if (b != null && b.Explicit.HasYield) {
6001 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
6005 return base.Resolve (bc) && ok;
6010 // Base class for blocks using exception handling
6012 public abstract class ExceptionStatement : ResumableStatement
6014 protected List<ResumableStatement> resume_points;
6015 protected int first_resume_pc;
6016 protected ExceptionStatement parent_try_block;
6017 protected int first_catch_resume_pc = -1;
6019 protected ExceptionStatement (Location loc)
6024 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6026 StateMachineInitializer state_machine = null;
6027 if (resume_points != null) {
6028 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6030 ec.EmitInt ((int) IteratorStorey.State.Running);
6031 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6035 // The resume points in catch section when this is try-catch-finally
6037 if (IsRewrittenTryCatchFinally ()) {
6038 ec.BeginExceptionBlock ();
6040 if (first_catch_resume_pc >= 0) {
6042 ec.MarkLabel (resume_point);
6044 // For normal control flow, we want to fall-through the Switch
6045 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6046 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6047 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6048 ec.Emit (OpCodes.Sub);
6050 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6051 for (int i = 0; i < labels.Length; ++i)
6052 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6053 ec.Emit (OpCodes.Switch, labels);
6057 ec.BeginExceptionBlock ();
6060 // The resume points for try section
6062 if (resume_points != null && first_catch_resume_pc != 0) {
6063 if (first_catch_resume_pc < 0)
6064 ec.MarkLabel (resume_point);
6066 // For normal control flow, we want to fall-through the Switch
6067 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6068 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6069 ec.EmitInt (first_resume_pc);
6070 ec.Emit (OpCodes.Sub);
6072 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6073 for (int i = 0; i < labels.Length; ++i)
6074 labels[i] = resume_points[i].PrepareForEmit (ec);
6075 ec.Emit (OpCodes.Switch, labels);
6079 bool IsRewrittenTryCatchFinally ()
6081 var tf = this as TryFinally;
6085 var tc = tf.Statement as TryCatch;
6089 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6092 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6094 if (parent_try_block != null) {
6095 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6097 pc = stateMachine.AddResumePoint (this);
6100 if (resume_points == null) {
6101 resume_points = new List<ResumableStatement> ();
6102 first_resume_pc = pc;
6105 if (pc != first_resume_pc + resume_points.Count)
6106 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6108 var tf = this as TryFinally;
6109 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6110 first_catch_resume_pc = resume_points.Count;
6113 resume_points.Add (stmt);
6118 public class Lock : TryFinallyBlock
6121 TemporaryVariableReference expr_copy;
6122 TemporaryVariableReference lock_taken;
6124 public Lock (Expression expr, Statement stmt, Location loc)
6130 public Expression Expr {
6136 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6138 expr.FlowAnalysis (fc);
6139 return base.DoFlowAnalysis (fc);
6142 public override bool Resolve (BlockContext ec)
6144 expr = expr.Resolve (ec);
6148 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6149 ec.Report.Error (185, loc,
6150 "`{0}' is not a reference type as required by the lock statement",
6151 expr.Type.GetSignatureForError ());
6154 if (expr.Type.IsGenericParameter) {
6155 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6158 VariableReference lv = expr as VariableReference;
6161 locked = lv.IsLockedByStatement;
6162 lv.IsLockedByStatement = true;
6169 // Have to keep original lock value around to unlock same location
6170 // in the case of original value has changed or is null
6172 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6173 expr_copy.Resolve (ec);
6176 // Ensure Monitor methods are available
6178 if (ResolvePredefinedMethods (ec) > 1) {
6179 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6180 lock_taken.Resolve (ec);
6183 using (ec.Set (ResolveContext.Options.LockScope)) {
6188 lv.IsLockedByStatement = locked;
6194 protected override void EmitTryBodyPrepare (EmitContext ec)
6196 expr_copy.EmitAssign (ec, expr);
6198 if (lock_taken != null) {
6200 // Initialize ref variable
6202 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6205 // Monitor.Enter (expr_copy)
6207 expr_copy.Emit (ec);
6208 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6211 base.EmitTryBodyPrepare (ec);
6214 protected override void EmitTryBody (EmitContext ec)
6217 // Monitor.Enter (expr_copy, ref lock_taken)
6219 if (lock_taken != null) {
6220 expr_copy.Emit (ec);
6221 lock_taken.LocalInfo.CreateBuilder (ec);
6222 lock_taken.AddressOf (ec, AddressOp.Load);
6223 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6226 Statement.Emit (ec);
6229 public override void EmitFinallyBody (EmitContext ec)
6232 // if (lock_taken) Monitor.Exit (expr_copy)
6234 Label skip = ec.DefineLabel ();
6236 if (lock_taken != null) {
6237 lock_taken.Emit (ec);
6238 ec.Emit (OpCodes.Brfalse_S, skip);
6241 expr_copy.Emit (ec);
6242 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6244 ec.Emit (OpCodes.Call, m);
6246 ec.MarkLabel (skip);
6249 int ResolvePredefinedMethods (ResolveContext rc)
6251 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6252 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6256 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6260 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6264 protected override void CloneTo (CloneContext clonectx, Statement t)
6266 Lock target = (Lock) t;
6268 target.expr = expr.Clone (clonectx);
6269 target.stmt = Statement.Clone (clonectx);
6272 public override object Accept (StructuralVisitor visitor)
6274 return visitor.Visit (this);
6279 public class Unchecked : Statement {
6282 public Unchecked (Block b, Location loc)
6289 public override bool Resolve (BlockContext ec)
6291 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6292 return Block.Resolve (ec);
6295 protected override void DoEmit (EmitContext ec)
6297 using (ec.With (EmitContext.Options.CheckedScope, false))
6301 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6303 return Block.FlowAnalysis (fc);
6306 public override Reachability MarkReachable (Reachability rc)
6308 base.MarkReachable (rc);
6309 return Block.MarkReachable (rc);
6312 protected override void CloneTo (CloneContext clonectx, Statement t)
6314 Unchecked target = (Unchecked) t;
6316 target.Block = clonectx.LookupBlock (Block);
6319 public override object Accept (StructuralVisitor visitor)
6321 return visitor.Visit (this);
6325 public class Checked : Statement {
6328 public Checked (Block b, Location loc)
6331 b.Unchecked = false;
6335 public override bool Resolve (BlockContext ec)
6337 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6338 return Block.Resolve (ec);
6341 protected override void DoEmit (EmitContext ec)
6343 using (ec.With (EmitContext.Options.CheckedScope, true))
6347 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6349 return Block.FlowAnalysis (fc);
6352 public override Reachability MarkReachable (Reachability rc)
6354 base.MarkReachable (rc);
6355 return Block.MarkReachable (rc);
6358 protected override void CloneTo (CloneContext clonectx, Statement t)
6360 Checked target = (Checked) t;
6362 target.Block = clonectx.LookupBlock (Block);
6365 public override object Accept (StructuralVisitor visitor)
6367 return visitor.Visit (this);
6371 public class Unsafe : Statement {
6374 public Unsafe (Block b, Location loc)
6377 Block.Unsafe = true;
6381 public override bool Resolve (BlockContext ec)
6383 if (ec.CurrentIterator != null)
6384 Expression.UnsafeInsideIteratorError (ec, loc);
6386 using (ec.Set (ResolveContext.Options.UnsafeScope))
6387 return Block.Resolve (ec);
6390 protected override void DoEmit (EmitContext ec)
6395 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6397 return Block.FlowAnalysis (fc);
6400 public override Reachability MarkReachable (Reachability rc)
6402 base.MarkReachable (rc);
6403 return Block.MarkReachable (rc);
6406 protected override void CloneTo (CloneContext clonectx, Statement t)
6408 Unsafe target = (Unsafe) t;
6410 target.Block = clonectx.LookupBlock (Block);
6413 public override object Accept (StructuralVisitor visitor)
6415 return visitor.Visit (this);
6422 public class Fixed : Statement
6424 abstract class Emitter : ShimExpression
6426 protected LocalVariable vi;
6428 protected Emitter (Expression expr, LocalVariable li)
6434 public abstract void EmitExit (EmitContext ec);
6436 public override void FlowAnalysis (FlowAnalysisContext fc)
6438 expr.FlowAnalysis (fc);
6442 sealed class ExpressionEmitter : Emitter {
6443 public ExpressionEmitter (Expression converted, LocalVariable li)
6444 : base (converted, li)
6448 protected override Expression DoResolve (ResolveContext rc)
6450 throw new NotImplementedException ();
6453 public override void Emit (EmitContext ec) {
6455 // Store pointer in pinned location
6461 public override void EmitExit (EmitContext ec)
6464 ec.Emit (OpCodes.Conv_U);
6469 class StringEmitter : Emitter
6471 LocalVariable pinned_string;
6473 public StringEmitter (Expression expr, LocalVariable li)
6478 protected override Expression DoResolve (ResolveContext rc)
6480 pinned_string = new LocalVariable (vi.Block, "$pinned",
6481 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6483 pinned_string.Type = rc.BuiltinTypes.String;
6486 eclass = ExprClass.Variable;
6487 type = rc.BuiltinTypes.Int;
6491 public override void Emit (EmitContext ec)
6493 pinned_string.CreateBuilder (ec);
6496 pinned_string.EmitAssign (ec);
6498 // TODO: Should use Binary::Add
6499 pinned_string.Emit (ec);
6500 ec.Emit (OpCodes.Conv_I);
6502 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6506 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6507 //pe.InstanceExpression = pinned_string;
6508 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6510 ec.Emit (OpCodes.Add);
6514 public override void EmitExit (EmitContext ec)
6517 pinned_string.EmitAssign (ec);
6521 public class VariableDeclaration : BlockVariable
6523 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6528 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6530 if (!Variable.Type.IsPointer && li == Variable) {
6531 bc.Report.Error (209, TypeExpression.Location,
6532 "The type of locals declared in a fixed statement must be a pointer type");
6536 var res = initializer.Resolve (bc);
6543 var ac = res.Type as ArrayContainer;
6545 TypeSpec array_type = ac.Element;
6548 // Provided that array_type is unmanaged,
6550 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6553 Expression res_init;
6554 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6557 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6558 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6559 res = expr_variable.CreateReferenceExpression (bc, loc);
6563 // and T* is implicitly convertible to the
6564 // pointer type given in the fixed statement.
6566 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6568 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6569 if (converted == null)
6573 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6575 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6576 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6577 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6578 new NullLiteral (loc),
6581 converted = converted.Resolve (bc);
6583 return new ExpressionEmitter (converted, li);
6589 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6590 return new StringEmitter (res, li).Resolve (bc);
6593 // Case 3: fixed buffer
6594 if (res is FixedBufferPtr) {
6595 return new ExpressionEmitter (res, li);
6598 bool already_fixed = true;
6601 // Case 4: & object.
6603 Unary u = res as Unary;
6605 if (u.Oper == Unary.Operator.AddressOf) {
6606 IVariableReference vr = u.Expr as IVariableReference;
6607 if (vr == null || !vr.IsFixed) {
6608 already_fixed = false;
6611 } else if (initializer is Cast) {
6612 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6616 if (already_fixed) {
6617 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6620 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6621 return new ExpressionEmitter (res, li);
6626 VariableDeclaration decl;
6627 Statement statement;
6630 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6639 public Statement Statement {
6645 public BlockVariable Variables {
6653 public override bool Resolve (BlockContext bc)
6655 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6656 if (!decl.Resolve (bc))
6660 return statement.Resolve (bc);
6663 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6665 decl.FlowAnalysis (fc);
6666 return statement.FlowAnalysis (fc);
6669 protected override void DoEmit (EmitContext ec)
6671 decl.Variable.CreateBuilder (ec);
6672 decl.Initializer.Emit (ec);
6673 if (decl.Declarators != null) {
6674 foreach (var d in decl.Declarators) {
6675 d.Variable.CreateBuilder (ec);
6676 d.Initializer.Emit (ec);
6680 statement.Emit (ec);
6686 // Clear the pinned variable
6688 ((Emitter) decl.Initializer).EmitExit (ec);
6689 if (decl.Declarators != null) {
6690 foreach (var d in decl.Declarators) {
6691 ((Emitter)d.Initializer).EmitExit (ec);
6696 public override Reachability MarkReachable (Reachability rc)
6698 base.MarkReachable (rc);
6700 decl.MarkReachable (rc);
6702 rc = statement.MarkReachable (rc);
6704 // TODO: What if there is local exit?
6705 has_ret = rc.IsUnreachable;
6709 protected override void CloneTo (CloneContext clonectx, Statement t)
6711 Fixed target = (Fixed) t;
6713 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6714 target.statement = statement.Clone (clonectx);
6717 public override object Accept (StructuralVisitor visitor)
6719 return visitor.Visit (this);
6723 public class Catch : Statement
6725 class CatchVariableStore : Statement
6727 readonly Catch ctch;
6729 public CatchVariableStore (Catch ctch)
6734 protected override void CloneTo (CloneContext clonectx, Statement target)
6738 protected override void DoEmit (EmitContext ec)
6740 // Emits catch variable debug information inside correct block
6741 ctch.EmitCatchVariableStore (ec);
6744 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6750 class FilterStatement : Statement
6752 readonly Catch ctch;
6754 public FilterStatement (Catch ctch)
6759 protected override void CloneTo (CloneContext clonectx, Statement target)
6763 protected override void DoEmit (EmitContext ec)
6765 if (ctch.li != null) {
6766 if (ctch.hoisted_temp != null)
6767 ctch.hoisted_temp.Emit (ec);
6771 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6772 ec.Emit (OpCodes.Box, ctch.type);
6775 var expr_start = ec.DefineLabel ();
6776 var end = ec.DefineLabel ();
6778 ec.Emit (OpCodes.Brtrue_S, expr_start);
6780 ec.Emit (OpCodes.Br, end);
6781 ec.MarkLabel (expr_start);
6783 ctch.Filter.Emit (ec);
6786 ec.Emit (OpCodes.Endfilter);
6787 ec.BeginFilterHandler ();
6788 ec.Emit (OpCodes.Pop);
6791 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6793 ctch.Filter.FlowAnalysis (fc);
6797 public override bool Resolve (BlockContext bc)
6799 ctch.Filter = ctch.Filter.Resolve (bc);
6801 if (ctch.Filter != null) {
6802 if (ctch.Filter.ContainsEmitWithAwait ()) {
6803 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6806 var c = ctch.Filter as Constant;
6807 if (c != null && !c.IsDefaultValue) {
6808 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6816 ExplicitBlock block;
6818 FullNamedExpression type_expr;
6819 CompilerAssign assign;
6821 LocalTemporary hoisted_temp;
6823 public Catch (ExplicitBlock block, Location loc)
6831 public ExplicitBlock Block {
6837 public TypeSpec CatchType {
6843 public Expression Filter {
6847 public bool IsGeneral {
6849 return type_expr == null;
6853 public FullNamedExpression TypeExpression {
6862 public LocalVariable Variable {
6873 protected override void DoEmit (EmitContext ec)
6875 if (Filter != null) {
6876 ec.BeginExceptionFilterBlock ();
6877 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6879 if (Block.HasAwait) {
6880 Block.EmitScopeInitialization (ec);
6889 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6891 ec.BeginCatchBlock (CatchType);
6894 ec.Emit (OpCodes.Pop);
6896 if (Block.HasAwait) {
6898 EmitCatchVariableStore (ec);
6904 void EmitCatchVariableStore (EmitContext ec)
6906 li.CreateBuilder (ec);
6909 // For hoisted catch variable we have to use a temporary local variable
6910 // for captured variable initialization during storey setup because variable
6911 // needs to be on the stack after storey instance for stfld operation
6913 if (li.HoistedVariant != null) {
6914 hoisted_temp = new LocalTemporary (li.Type);
6915 hoisted_temp.Store (ec);
6917 // switch to assignment from temporary variable and not from top of the stack
6918 assign.UpdateSource (hoisted_temp);
6922 public override bool Resolve (BlockContext bc)
6924 using (bc.Set (ResolveContext.Options.CatchScope)) {
6925 if (type_expr == null) {
6926 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6927 if (!block.HasAwait || Filter != null)
6928 block.AddScopeStatement (new CatchVariableStore (this));
6930 Expression source = new EmptyExpression (li.Type);
6931 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6932 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6935 type = type_expr.ResolveAsType (bc);
6940 CreateExceptionVariable (type);
6942 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6943 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6944 } else if (li != null) {
6946 li.PrepareAssignmentAnalysis (bc);
6948 // source variable is at the top of the stack
6949 Expression source = new EmptyExpression (li.Type);
6950 if (li.Type.IsGenericParameter)
6951 source = new UnboxCast (source, li.Type);
6953 if (!block.HasAwait || Filter != null)
6954 block.AddScopeStatement (new CatchVariableStore (this));
6957 // Uses Location.Null to hide from symbol file
6959 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6960 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6964 if (Filter != null) {
6965 Block.AddScopeStatement (new FilterStatement (this));
6968 Block.SetCatchBlock ();
6969 return Block.Resolve (bc);
6973 bool CreateExceptionVariable (TypeSpec type)
6975 if (!Block.HasAwait)
6978 // TODO: Scan the block for rethrow expression
6979 //if (!Block.HasRethrow)
6982 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6986 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6988 if (li != null && !li.IsCompilerGenerated) {
6989 fc.SetVariableAssigned (li.VariableInfo, true);
6992 return block.FlowAnalysis (fc);
6995 public override Reachability MarkReachable (Reachability rc)
6997 base.MarkReachable (rc);
6999 var c = Filter as Constant;
7000 if (c != null && c.IsDefaultValue)
7001 return Reachability.CreateUnreachable ();
7003 return block.MarkReachable (rc);
7006 protected override void CloneTo (CloneContext clonectx, Statement t)
7008 Catch target = (Catch) t;
7010 if (type_expr != null)
7011 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7014 target.Filter = Filter.Clone (clonectx);
7016 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7020 public class TryFinally : TryFinallyBlock
7023 List<DefiniteAssignmentBitSet> try_exit_dat;
7024 List<Tuple<Label, bool>> redirected_jumps;
7025 Label? start_fin_label;
7027 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7033 public ExplicitBlock FinallyBlock {
7039 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7041 if (try_exit_dat == null)
7042 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7044 try_exit_dat.Add (vector);
7047 public override bool Resolve (BlockContext bc)
7049 bool ok = base.Resolve (bc);
7051 fini.SetFinallyBlock ();
7052 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7053 ok &= fini.Resolve (bc);
7059 protected override void EmitTryBody (EmitContext ec)
7061 if (fini.HasAwait) {
7062 if (ec.TryFinallyUnwind == null)
7063 ec.TryFinallyUnwind = new List<TryFinally> ();
7065 ec.TryFinallyUnwind.Add (this);
7068 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7069 ec.EndExceptionBlock ();
7071 ec.TryFinallyUnwind.Remove (this);
7073 if (start_fin_label != null)
7074 ec.MarkLabel (start_fin_label.Value);
7082 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7087 return base.EmitBeginFinallyBlock (ec);
7090 public override void EmitFinallyBody (EmitContext ec)
7092 if (!fini.HasAwait) {
7098 // Emits catch block like
7100 // catch (object temp) {
7101 // this.exception_field = temp;
7104 var type = ec.BuiltinTypes.Object;
7105 ec.BeginCatchBlock (type);
7107 var temp = ec.GetTemporaryLocal (type);
7108 ec.Emit (OpCodes.Stloc, temp);
7110 var exception_field = ec.GetTemporaryField (type);
7111 exception_field.AutomaticallyReuse = false;
7113 ec.Emit (OpCodes.Ldloc, temp);
7114 exception_field.EmitAssignFromStack (ec);
7116 ec.EndExceptionBlock ();
7118 ec.FreeTemporaryLocal (temp, type);
7123 // Emits exception rethrow
7125 // if (this.exception_field != null)
7126 // throw this.exception_field;
7128 exception_field.Emit (ec);
7129 var skip_throw = ec.DefineLabel ();
7130 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7131 exception_field.Emit (ec);
7132 ec.Emit (OpCodes.Throw);
7133 ec.MarkLabel (skip_throw);
7135 exception_field.PrepareCleanup (ec);
7137 EmitUnwindFinallyTable (ec);
7140 bool IsParentBlock (Block block)
7142 for (Block b = fini; b != null; b = b.Parent) {
7150 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7153 if (labelBlock != null) {
7154 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7155 var fin = ec.TryFinallyUnwind [idx - 1];
7156 if (!fin.IsParentBlock (labelBlock))
7163 bool set_return_state = true;
7165 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7166 var fin = ec.TryFinallyUnwind [idx];
7167 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7170 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7171 set_return_state = false;
7173 if (fin.start_fin_label == null) {
7174 fin.start_fin_label = ec.DefineLabel ();
7177 label = fin.start_fin_label.Value;
7183 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7185 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7188 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7190 if (redirected_jumps == null) {
7191 redirected_jumps = new List<Tuple<Label, bool>> ();
7193 // Add fallthrough label
7194 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7197 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7200 int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7202 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7203 index = redirected_jumps.Count - 1;
7207 // Indicates we have captured exit jump
7209 if (setReturnState) {
7210 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7211 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7216 // Emits state table of jumps outside of try block and reload of return
7217 // value when try block returns value
7219 void EmitUnwindFinallyTable (EmitContext ec)
7221 if (redirected_jumps == null)
7224 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7225 initializer.HoistedReturnState.EmitLoad (ec);
7227 var jumps_table = new Label [redirected_jumps.Count];
7228 List<Tuple<Label, Label>> leave_redirect = null;
7229 for (int i = 0; i < jumps_table.Length; ++i) {
7230 var val = redirected_jumps [i];
7233 if (leave_redirect == null)
7234 leave_redirect = new List<Tuple<Label, Label>> ();
7235 var label = ec.DefineLabel ();
7236 leave_redirect.Add (Tuple.Create (label, val.Item1));
7237 jumps_table [i] = label;
7239 jumps_table [i] = val.Item1;
7243 ec.Emit (OpCodes.Switch, jumps_table);
7245 if (leave_redirect != null) {
7246 foreach (var entry in leave_redirect) {
7247 ec.MarkLabel (entry.Item1);
7248 ec.Emit (OpCodes.Leave, entry.Item2);
7252 // Mark fallthrough label
7253 ec.MarkLabel (jumps_table [0]);
7256 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7258 var da = fc.BranchDefiniteAssignment ();
7260 var tf = fc.TryFinally;
7261 fc.TryFinally = this;
7263 var res_stmt = Statement.FlowAnalysis (fc);
7267 var try_da = fc.DefiniteAssignment;
7268 fc.DefiniteAssignment = da;
7270 var res_fin = fini.FlowAnalysis (fc);
7272 if (try_exit_dat != null) {
7274 // try block has global exit but we need to run definite assignment check
7275 // for parameter block out parameter after finally block because it's always
7276 // executed before exit
7278 foreach (var try_da_part in try_exit_dat)
7279 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7281 try_exit_dat = null;
7284 fc.DefiniteAssignment |= try_da;
7285 return res_stmt | res_fin;
7288 public override Reachability MarkReachable (Reachability rc)
7291 // Mark finally block first for any exit statement in try block
7292 // to know whether the code which follows finally is reachable
7294 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7297 protected override void CloneTo (CloneContext clonectx, Statement t)
7299 TryFinally target = (TryFinally) t;
7301 target.stmt = stmt.Clone (clonectx);
7303 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7306 public override object Accept (StructuralVisitor visitor)
7308 return visitor.Visit (this);
7312 public class TryCatch : ExceptionStatement
7315 List<Catch> clauses;
7316 readonly bool inside_try_finally;
7317 List<Catch> catch_sm;
7319 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7323 this.clauses = catch_clauses;
7324 this.inside_try_finally = inside_try_finally;
7327 public List<Catch> Clauses {
7333 public bool HasClauseWithAwait {
7335 return catch_sm != null;
7339 public bool IsTryCatchFinally {
7341 return inside_try_finally;
7345 public override bool Resolve (BlockContext bc)
7349 using (bc.Set (ResolveContext.Options.TryScope)) {
7351 parent_try_block = bc.CurrentTryBlock;
7353 if (IsTryCatchFinally) {
7354 ok = Block.Resolve (bc);
7356 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7357 bc.CurrentTryBlock = this;
7358 ok = Block.Resolve (bc);
7359 bc.CurrentTryBlock = parent_try_block;
7364 var prev_catch = bc.CurrentTryCatch;
7365 bc.CurrentTryCatch = this;
7367 for (int i = 0; i < clauses.Count; ++i) {
7370 ok &= c.Resolve (bc);
7372 if (c.Block.HasAwait) {
7373 if (catch_sm == null)
7374 catch_sm = new List<Catch> ();
7379 if (c.Filter != null)
7382 TypeSpec resolved_type = c.CatchType;
7383 if (resolved_type == null)
7386 for (int ii = 0; ii < clauses.Count; ++ii) {
7390 if (clauses[ii].Filter != null)
7393 if (clauses[ii].IsGeneral) {
7394 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7397 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7400 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7403 bc.Report.Warning (1058, 1, c.loc,
7404 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7412 var ct = clauses[ii].CatchType;
7416 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7417 bc.Report.Error (160, c.loc,
7418 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7419 ct.GetSignatureForError ());
7425 bc.CurrentTryCatch = prev_catch;
7427 return base.Resolve (bc) && ok;
7430 protected sealed override void DoEmit (EmitContext ec)
7432 if (!inside_try_finally)
7433 EmitTryBodyPrepare (ec);
7437 LocalBuilder state_variable = null;
7438 foreach (Catch c in clauses) {
7441 if (catch_sm != null) {
7442 if (state_variable == null) {
7444 // Cannot reuse temp variable because non-catch path assumes the value is 0
7445 // which may not be true for reused local variable
7447 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7450 var index = catch_sm.IndexOf (c);
7454 ec.EmitInt (index + 1);
7455 ec.Emit (OpCodes.Stloc, state_variable);
7459 if (state_variable == null) {
7460 if (!inside_try_finally)
7461 ec.EndExceptionBlock ();
7463 ec.EndExceptionBlock ();
7465 ec.Emit (OpCodes.Ldloc, state_variable);
7467 var labels = new Label [catch_sm.Count + 1];
7468 for (int i = 0; i < labels.Length; ++i) {
7469 labels [i] = ec.DefineLabel ();
7472 var end = ec.DefineLabel ();
7473 ec.Emit (OpCodes.Switch, labels);
7475 // 0 value is default label
7476 ec.MarkLabel (labels [0]);
7477 ec.Emit (OpCodes.Br, end);
7479 var atv = ec.AsyncThrowVariable;
7481 for (int i = 0; i < catch_sm.Count; ++i) {
7482 if (c != null && c.Block.HasReachableClosingBrace)
7483 ec.Emit (OpCodes.Br, end);
7485 ec.MarkLabel (labels [i + 1]);
7488 ec.Emit (OpCodes.Stloc, state_variable);
7491 ec.AsyncThrowVariable = c.Variable;
7494 ec.AsyncThrowVariable = atv;
7500 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7502 var start_fc = fc.BranchDefiniteAssignment ();
7503 var res = Block.FlowAnalysis (fc);
7505 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7507 foreach (var c in clauses) {
7508 fc.BranchDefiniteAssignment (start_fc);
7509 if (!c.FlowAnalysis (fc)) {
7511 try_fc = fc.DefiniteAssignment;
7513 try_fc &= fc.DefiniteAssignment;
7519 fc.DefiniteAssignment = try_fc ?? start_fc;
7520 parent_try_block = null;
7524 public override Reachability MarkReachable (Reachability rc)
7526 if (rc.IsUnreachable)
7529 base.MarkReachable (rc);
7531 var tc_rc = Block.MarkReachable (rc);
7533 foreach (var c in clauses)
7534 tc_rc &= c.MarkReachable (rc);
7539 protected override void CloneTo (CloneContext clonectx, Statement t)
7541 TryCatch target = (TryCatch) t;
7543 target.Block = clonectx.LookupBlock (Block);
7544 if (clauses != null){
7545 target.clauses = new List<Catch> ();
7546 foreach (Catch c in clauses)
7547 target.clauses.Add ((Catch) c.Clone (clonectx));
7551 public override object Accept (StructuralVisitor visitor)
7553 return visitor.Visit (this);
7557 public class Using : TryFinallyBlock
7559 public class VariableDeclaration : BlockVariable
7561 Statement dispose_call;
7563 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7568 public VariableDeclaration (LocalVariable li, Location loc)
7575 public VariableDeclaration (Expression expr)
7578 loc = expr.Location;
7584 public bool IsNested { get; private set; }
7588 public void EmitDispose (EmitContext ec)
7590 dispose_call.Emit (ec);
7593 public override bool Resolve (BlockContext bc)
7598 return base.Resolve (bc, false);
7601 public Expression ResolveExpression (BlockContext bc)
7603 var e = Initializer.Resolve (bc);
7607 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7608 Initializer = ResolveInitializer (bc, Variable, e);
7612 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7614 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7615 initializer = initializer.Resolve (bc);
7616 if (initializer == null)
7619 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7620 Arguments args = new Arguments (1);
7621 args.Add (new Argument (initializer));
7622 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7623 if (initializer == null)
7626 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7627 dispose_call = CreateDisposeCall (bc, var);
7628 dispose_call.Resolve (bc);
7630 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7633 if (li == Variable) {
7634 CheckIDiposableConversion (bc, li, initializer);
7635 dispose_call = CreateDisposeCall (bc, li);
7636 dispose_call.Resolve (bc);
7639 return base.ResolveInitializer (bc, li, initializer);
7642 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7646 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7647 if (type.IsNullableType) {
7648 // it's handled in CreateDisposeCall
7652 if (type != InternalType.ErrorType) {
7653 bc.Report.SymbolRelatedToPreviousError (type);
7654 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7655 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7656 type.GetSignatureForError ());
7663 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7665 var target = bc.BuiltinTypes.IDisposable;
7666 var tp = type as TypeParameterSpec;
7668 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7670 return type.ImplementsInterface (target, false);
7673 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7675 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7677 var loc = lv.Location;
7679 var idt = bc.BuiltinTypes.IDisposable;
7680 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7682 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7683 dispose_mg.InstanceExpression = type.IsNullableType ?
7684 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7688 // Hide it from symbol file via null location
7690 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7692 // Add conditional call when disposing possible null variable
7693 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7694 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7699 public void ResolveDeclaratorInitializer (BlockContext bc)
7701 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7704 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7706 for (int i = declarators.Count - 1; i >= 0; --i) {
7707 var d = declarators [i];
7708 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7709 vd.Initializer = d.Initializer;
7711 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7712 vd.dispose_call.Resolve (bc);
7714 stmt = new Using (vd, stmt, d.Variable.Location);
7721 public override object Accept (StructuralVisitor visitor)
7723 return visitor.Visit (this);
7727 VariableDeclaration decl;
7729 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7735 public Using (Expression expr, Statement stmt, Location loc)
7738 this.decl = new VariableDeclaration (expr);
7743 public Expression Expr {
7745 return decl.Variable == null ? decl.Initializer : null;
7749 public BlockVariable Variables {
7757 public override void Emit (EmitContext ec)
7760 // Don't emit sequence point it will be set on variable declaration
7765 protected override void EmitTryBodyPrepare (EmitContext ec)
7768 base.EmitTryBodyPrepare (ec);
7771 protected override void EmitTryBody (EmitContext ec)
7776 public override void EmitFinallyBody (EmitContext ec)
7778 decl.EmitDispose (ec);
7781 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7783 decl.FlowAnalysis (fc);
7784 return stmt.FlowAnalysis (fc);
7787 public override Reachability MarkReachable (Reachability rc)
7789 decl.MarkReachable (rc);
7790 return base.MarkReachable (rc);
7793 public override bool Resolve (BlockContext ec)
7795 VariableReference vr;
7796 bool vr_locked = false;
7798 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7799 if (decl.Variable == null) {
7800 vr = decl.ResolveExpression (ec) as VariableReference;
7802 vr_locked = vr.IsLockedByStatement;
7803 vr.IsLockedByStatement = true;
7806 if (decl.IsNested) {
7807 decl.ResolveDeclaratorInitializer (ec);
7809 if (!decl.Resolve (ec))
7812 if (decl.Declarators != null) {
7813 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7821 var ok = base.Resolve (ec);
7824 vr.IsLockedByStatement = vr_locked;
7829 protected override void CloneTo (CloneContext clonectx, Statement t)
7831 Using target = (Using) t;
7833 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7834 target.stmt = stmt.Clone (clonectx);
7837 public override object Accept (StructuralVisitor visitor)
7839 return visitor.Visit (this);
7844 /// Implementation of the foreach C# statement
7846 public class Foreach : LoopStatement
7848 abstract class IteratorStatement : Statement
7850 protected readonly Foreach for_each;
7852 protected IteratorStatement (Foreach @foreach)
7854 this.for_each = @foreach;
7855 this.loc = @foreach.expr.Location;
7858 protected override void CloneTo (CloneContext clonectx, Statement target)
7860 throw new NotImplementedException ();
7863 public override void Emit (EmitContext ec)
7865 if (ec.EmitAccurateDebugInfo) {
7866 ec.Emit (OpCodes.Nop);
7872 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7874 throw new NotImplementedException ();
7878 sealed class ArrayForeach : IteratorStatement
7880 TemporaryVariableReference[] lengths;
7881 Expression [] length_exprs;
7882 StatementExpression[] counter;
7883 TemporaryVariableReference[] variables;
7885 TemporaryVariableReference copy;
7887 public ArrayForeach (Foreach @foreach, int rank)
7890 counter = new StatementExpression[rank];
7891 variables = new TemporaryVariableReference[rank];
7892 length_exprs = new Expression [rank];
7895 // Only use temporary length variables when dealing with
7896 // multi-dimensional arrays
7899 lengths = new TemporaryVariableReference [rank];
7902 public override bool Resolve (BlockContext ec)
7904 Block variables_block = for_each.variable.Block;
7905 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7908 int rank = length_exprs.Length;
7909 Arguments list = new Arguments (rank);
7910 for (int i = 0; i < rank; i++) {
7911 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7913 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7914 counter[i].Resolve (ec);
7917 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7919 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7920 lengths[i].Resolve (ec);
7922 Arguments args = new Arguments (1);
7923 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7924 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7927 list.Add (new Argument (v));
7930 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7935 if (for_each.type is VarExpr) {
7936 // Infer implicitly typed local variable from foreach array type
7937 var_type = access.Type;
7939 var_type = for_each.type.ResolveAsType (ec);
7941 if (var_type == null)
7944 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7949 for_each.variable.Type = var_type;
7951 var prev_block = ec.CurrentBlock;
7952 ec.CurrentBlock = variables_block;
7953 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7954 ec.CurrentBlock = prev_block;
7956 if (variable_ref == null)
7959 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7961 return for_each.body.Resolve (ec);
7964 protected override void DoEmit (EmitContext ec)
7966 copy.EmitAssign (ec, for_each.expr);
7968 int rank = length_exprs.Length;
7969 Label[] test = new Label [rank];
7970 Label[] loop = new Label [rank];
7972 for (int i = 0; i < rank; i++) {
7973 test [i] = ec.DefineLabel ();
7974 loop [i] = ec.DefineLabel ();
7976 if (lengths != null)
7977 lengths [i].EmitAssign (ec, length_exprs [i]);
7980 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7981 for (int i = 0; i < rank; i++) {
7982 variables [i].EmitAssign (ec, zero);
7984 ec.Emit (OpCodes.Br, test [i]);
7985 ec.MarkLabel (loop [i]);
7988 for_each.body.Emit (ec);
7990 ec.MarkLabel (ec.LoopBegin);
7991 ec.Mark (for_each.expr.Location);
7993 for (int i = rank - 1; i >= 0; i--){
7994 counter [i].Emit (ec);
7996 ec.MarkLabel (test [i]);
7997 variables [i].Emit (ec);
7999 if (lengths != null)
8000 lengths [i].Emit (ec);
8002 length_exprs [i].Emit (ec);
8004 ec.Emit (OpCodes.Blt, loop [i]);
8007 ec.MarkLabel (ec.LoopEnd);
8011 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8013 class RuntimeDispose : Using.VariableDeclaration
8015 public RuntimeDispose (LocalVariable lv, Location loc)
8021 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8023 // Defered to runtime check
8026 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8028 var idt = bc.BuiltinTypes.IDisposable;
8031 // Fabricates code like
8033 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8036 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8038 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8039 dispose_variable.CreateReferenceExpression (bc, loc),
8040 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8041 loc), new NullLiteral (loc));
8043 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8045 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8046 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8048 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8049 return new If (idisaposable_test, dispose, loc);
8053 LocalVariable variable;
8055 Statement statement;
8056 ExpressionStatement init;
8057 TemporaryVariableReference enumerator_variable;
8058 bool ambiguous_getenumerator_name;
8060 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8063 this.variable = var;
8067 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8069 rc.Report.SymbolRelatedToPreviousError (enumerator);
8070 rc.Report.Error (202, loc,
8071 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8072 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8075 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8078 // Option 1: Try to match by name GetEnumerator first
8080 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8081 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8083 var mg = mexpr as MethodGroupExpr;
8085 mg.InstanceExpression = expr;
8086 Arguments args = new Arguments (0);
8087 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8089 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8090 if (ambiguous_getenumerator_name)
8093 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8099 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8102 PredefinedMember<MethodSpec> iface_candidate = null;
8103 var ptypes = rc.Module.PredefinedTypes;
8104 var gen_ienumerable = ptypes.IEnumerableGeneric;
8105 if (!gen_ienumerable.Define ())
8106 gen_ienumerable = null;
8108 var ifaces = t.Interfaces;
8109 if (ifaces != null) {
8110 foreach (var iface in ifaces) {
8111 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8112 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8113 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8114 rc.Report.Error (1640, loc,
8115 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8116 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8121 // TODO: Cache this somehow
8122 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8123 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8128 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8129 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8134 if (iface_candidate == null) {
8135 if (expr.Type != InternalType.ErrorType) {
8136 rc.Report.Error (1579, loc,
8137 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8138 expr.Type.GetSignatureForError (), "GetEnumerator");
8144 var method = iface_candidate.Resolve (loc);
8148 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8149 mg.InstanceExpression = expr;
8153 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8155 var ms = MemberCache.FindMember (enumerator.ReturnType,
8156 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8157 BindingRestriction.InstanceOnly) as MethodSpec;
8159 if (ms == null || !ms.IsPublic) {
8160 Error_WrongEnumerator (rc, enumerator);
8164 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8167 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8169 var ps = MemberCache.FindMember (enumerator.ReturnType,
8170 MemberFilter.Property ("Current", null),
8171 BindingRestriction.InstanceOnly) as PropertySpec;
8173 if (ps == null || !ps.IsPublic) {
8174 Error_WrongEnumerator (rc, enumerator);
8181 public override bool Resolve (BlockContext ec)
8183 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8186 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8187 } else if (expr.Type.IsNullableType) {
8188 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8191 var get_enumerator_mg = ResolveGetEnumerator (ec);
8192 if (get_enumerator_mg == null) {
8196 var get_enumerator = get_enumerator_mg.BestCandidate;
8197 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8198 enumerator_variable.Resolve (ec);
8200 // Prepare bool MoveNext ()
8201 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8202 if (move_next_mg == null) {
8206 move_next_mg.InstanceExpression = enumerator_variable;
8208 // Prepare ~T~ Current { get; }
8209 var current_prop = ResolveCurrent (ec, get_enumerator);
8210 if (current_prop == null) {
8214 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8215 if (current_pe == null)
8218 VarExpr ve = for_each.type as VarExpr;
8222 // Source type is dynamic, set element type to dynamic too
8223 variable.Type = ec.BuiltinTypes.Dynamic;
8225 // Infer implicitly typed local variable from foreach enumerable type
8226 variable.Type = current_pe.Type;
8230 // Explicit cast of dynamic collection elements has to be done at runtime
8231 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8234 variable.Type = for_each.type.ResolveAsType (ec);
8236 if (variable.Type == null)
8239 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8240 if (current_pe == null)
8244 var prev_block = ec.CurrentBlock;
8245 ec.CurrentBlock = for_each.variable.Block;
8246 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8247 ec.CurrentBlock = prev_block;
8248 if (variable_ref == null)
8251 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8253 var init = new Invocation.Predefined (get_enumerator_mg, null);
8255 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8256 for_each.body, Location.Null);
8258 var enum_type = enumerator_variable.Type;
8261 // Add Dispose method call when enumerator can be IDisposable
8263 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8264 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8266 // Runtime Dispose check
8268 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8269 vd.Initializer = init;
8270 statement = new Using (vd, statement, Location.Null);
8273 // No Dispose call needed
8275 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8276 this.init.Resolve (ec);
8280 // Static Dispose check
8282 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8283 vd.Initializer = init;
8284 statement = new Using (vd, statement, Location.Null);
8287 return statement.Resolve (ec);
8290 protected override void DoEmit (EmitContext ec)
8292 enumerator_variable.LocalInfo.CreateBuilder (ec);
8295 init.EmitStatement (ec);
8297 statement.Emit (ec);
8300 #region IErrorHandler Members
8302 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8304 ec.Report.SymbolRelatedToPreviousError (best);
8305 ec.Report.Warning (278, 2, expr.Location,
8306 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8307 expr.Type.GetSignatureForError (), "enumerable",
8308 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8310 ambiguous_getenumerator_name = true;
8314 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8319 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8324 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8333 LocalVariable variable;
8337 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8341 this.variable = var;
8347 public Expression Expr {
8348 get { return expr; }
8351 public Expression TypeExpression {
8352 get { return type; }
8355 public LocalVariable Variable {
8356 get { return variable; }
8359 public override Reachability MarkReachable (Reachability rc)
8361 base.MarkReachable (rc);
8363 body.MarkReachable (rc);
8368 public override bool Resolve (BlockContext ec)
8370 expr = expr.Resolve (ec);
8375 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8379 body.AddStatement (Statement);
8381 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8382 Statement = new ArrayForeach (this, 1);
8383 } else if (expr.Type is ArrayContainer) {
8384 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8386 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8387 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8388 expr.ExprClassName);
8392 Statement = new CollectionForeach (this, variable, expr);
8395 return base.Resolve (ec);
8398 protected override void DoEmit (EmitContext ec)
8400 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8401 ec.LoopBegin = ec.DefineLabel ();
8402 ec.LoopEnd = ec.DefineLabel ();
8404 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8405 body.Explicit.DisableDebugScopeIndex ();
8407 variable.CreateBuilder (ec);
8409 Statement.Emit (ec);
8413 ec.LoopBegin = old_begin;
8414 ec.LoopEnd = old_end;
8417 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8419 expr.FlowAnalysis (fc);
8421 var da = fc.BranchDefiniteAssignment ();
8422 body.FlowAnalysis (fc);
8423 fc.DefiniteAssignment = da;
8427 protected override void CloneTo (CloneContext clonectx, Statement t)
8429 Foreach target = (Foreach) t;
8431 target.type = type.Clone (clonectx);
8432 target.expr = expr.Clone (clonectx);
8433 target.body = (Block) body.Clone (clonectx);
8434 target.Statement = Statement.Clone (clonectx);
8437 public override object Accept (StructuralVisitor visitor)
8439 return visitor.Visit (this);
8443 class SentinelStatement: Statement
8445 protected override void CloneTo (CloneContext clonectx, Statement target)
8449 protected override void DoEmit (EmitContext ec)
8451 var l = ec.DefineLabel ();
8453 ec.Emit (OpCodes.Br_S, l);
8456 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8458 throw new NotImplementedException ();