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 is ReferenceExpression && block_return_type.Kind != MemberKind.ByRef) {
1276 ec.Report.Error (8149, loc, "By-reference returns can only be used in methods that return by reference");
1280 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1281 if (block_return_type.Kind == MemberKind.ByRef) {
1282 var ref_expr = Expr as ReferenceExpression;
1283 if (ref_expr == null) {
1284 ec.Report.Error (8150, loc, "By-reference return is required when method returns by reference");
1288 var byref_return = (ReferenceContainer)block_return_type;
1290 if (expr.Type != byref_return.Element) {
1291 ec.Report.Error (8151, loc, "The return by reference expression must be of type `{0}' because this method returns by reference",
1292 byref_return.GetSignatureForError ());
1297 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1300 if (am != null && block_return_type == ec.ReturnType) {
1301 ec.Report.Error (1662, loc,
1302 "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",
1303 am.ContainerType, am.GetSignatureForError ());
1313 protected override void DoEmit (EmitContext ec)
1317 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1318 if (async_body != null) {
1319 var storey = (AsyncTaskStorey)async_body.Storey;
1320 Label exit_label = async_body.BodyEnd;
1323 // It's null for await without async
1325 if (storey.HoistedReturnValue != null) {
1327 // Special case hoisted return value (happens in try/finally scenario)
1329 if (ec.TryFinallyUnwind != null) {
1330 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1333 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1334 async_return.EmitAssign (ec, expr, false, false);
1339 if (ec.TryFinallyUnwind != null)
1340 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1343 ec.Emit (OpCodes.Leave, exit_label);
1350 if (unwind_protect || ec.EmitAccurateDebugInfo)
1351 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1354 if (unwind_protect) {
1355 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1356 } else if (ec.EmitAccurateDebugInfo) {
1357 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1359 ec.Emit (OpCodes.Ret);
1363 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1366 expr.FlowAnalysis (fc);
1369 base.DoFlowAnalysis (fc);
1374 void Error_ReturnFromIterator (ResolveContext rc)
1376 rc.Report.Error (1622, loc,
1377 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1380 public override Reachability MarkReachable (Reachability rc)
1382 base.MarkReachable (rc);
1385 rc = Expr.MarkReachable (rc);
1386 expr_returns = rc.IsUnreachable;
1389 return Reachability.CreateUnreachable ();
1392 protected override void CloneTo (CloneContext clonectx, Statement t)
1394 Return target = (Return) t;
1395 // It's null for simple return;
1397 target.expr = expr.Clone (clonectx);
1400 public override object Accept (StructuralVisitor visitor)
1402 return visitor.Visit (this);
1406 public class Goto : ExitStatement
1409 LabeledStatement label;
1410 TryFinally try_finally;
1412 public Goto (string label, Location l)
1418 public string Target {
1419 get { return target; }
1422 protected override bool IsLocalExit {
1428 protected override bool DoResolve (BlockContext bc)
1430 label = bc.CurrentBlock.LookupLabel (target);
1431 if (label == null) {
1432 Error_UnknownLabel (bc, target, loc);
1436 try_finally = bc.CurrentTryBlock as TryFinally;
1438 CheckExitBoundaries (bc, label.Block);
1443 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1445 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1449 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1451 // Goto to unreachable label
1455 if (fc.AddReachedLabel (label))
1458 label.Block.ScanGotoJump (label, fc);
1462 public override Reachability MarkReachable (Reachability rc)
1464 if (rc.IsUnreachable)
1467 base.MarkReachable (rc);
1469 if (try_finally != null) {
1470 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1471 label.AddGotoReference (rc);
1476 label.AddGotoReference (rc);
1479 return Reachability.CreateUnreachable ();
1482 protected override void CloneTo (CloneContext clonectx, Statement target)
1487 protected override void DoEmit (EmitContext ec)
1489 // This should only happen for goto from try block to unrechable label
1493 Label l = label.LabelTarget (ec);
1495 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1496 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1497 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1500 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1503 bool IsLeavingFinally (Block labelBlock)
1505 var b = try_finally.Statement as Block;
1507 if (b == labelBlock)
1516 public override object Accept (StructuralVisitor visitor)
1518 return visitor.Visit (this);
1522 public class LabeledStatement : Statement {
1529 public LabeledStatement (string name, Block block, Location l)
1536 public Label LabelTarget (EmitContext ec)
1541 label = ec.DefineLabel ();
1546 public Block Block {
1552 public string Name {
1553 get { return name; }
1556 protected override void CloneTo (CloneContext clonectx, Statement target)
1558 var t = (LabeledStatement) target;
1560 t.block = clonectx.RemapBlockCopy (block);
1563 public override bool Resolve (BlockContext bc)
1568 protected override void DoEmit (EmitContext ec)
1571 ec.MarkLabel (label);
1574 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1577 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1583 public override Reachability MarkReachable (Reachability rc)
1585 base.MarkReachable (rc);
1588 rc = new Reachability ();
1593 public void AddGotoReference (Reachability rc)
1601 block.ScanGotoJump (this);
1604 public override object Accept (StructuralVisitor visitor)
1606 return visitor.Visit (this);
1612 /// `goto default' statement
1614 public class GotoDefault : SwitchGoto
1616 public GotoDefault (Location l)
1621 public override bool Resolve (BlockContext bc)
1623 if (bc.Switch == null) {
1624 Error_GotoCaseRequiresSwitchBlock (bc);
1628 bc.Switch.RegisterGotoCase (null, null);
1634 protected override void DoEmit (EmitContext ec)
1636 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1639 public override Reachability MarkReachable (Reachability rc)
1641 if (!rc.IsUnreachable) {
1642 var label = switch_statement.DefaultLabel;
1643 if (label.IsUnreachable) {
1644 label.MarkReachable (rc);
1645 switch_statement.Block.ScanGotoJump (label);
1649 return base.MarkReachable (rc);
1652 public override object Accept (StructuralVisitor visitor)
1654 return visitor.Visit (this);
1659 /// `goto case' statement
1661 public class GotoCase : SwitchGoto
1665 public GotoCase (Expression e, Location l)
1671 public Expression Expr {
1677 public SwitchLabel Label { get; set; }
1679 public override bool Resolve (BlockContext ec)
1681 if (ec.Switch == null) {
1682 Error_GotoCaseRequiresSwitchBlock (ec);
1686 Constant c = expr.ResolveLabelConstant (ec);
1692 if (ec.Switch.IsNullable && c is NullLiteral) {
1695 TypeSpec type = ec.Switch.SwitchType;
1696 res = c.Reduce (ec, type);
1698 c.Error_ValueCannotBeConverted (ec, type, true);
1702 if (!Convert.ImplicitStandardConversionExists (c, type))
1703 ec.Report.Warning (469, 2, loc,
1704 "The `goto case' value is not implicitly convertible to type `{0}'",
1705 type.GetSignatureForError ());
1709 ec.Switch.RegisterGotoCase (this, res);
1716 protected override void DoEmit (EmitContext ec)
1718 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1721 protected override void CloneTo (CloneContext clonectx, Statement t)
1723 GotoCase target = (GotoCase) t;
1725 target.expr = expr.Clone (clonectx);
1728 public override Reachability MarkReachable (Reachability rc)
1730 if (!rc.IsUnreachable) {
1731 var label = switch_statement.FindLabel ((Constant) expr);
1732 if (label.IsUnreachable) {
1733 label.MarkReachable (rc);
1734 switch_statement.Block.ScanGotoJump (label);
1738 return base.MarkReachable (rc);
1741 public override object Accept (StructuralVisitor visitor)
1743 return visitor.Visit (this);
1747 public abstract class SwitchGoto : Statement
1749 protected bool unwind_protect;
1750 protected Switch switch_statement;
1752 protected SwitchGoto (Location loc)
1757 protected override void CloneTo (CloneContext clonectx, Statement target)
1762 public override bool Resolve (BlockContext bc)
1764 CheckExitBoundaries (bc, bc.Switch.Block);
1766 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1767 switch_statement = bc.Switch;
1772 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1777 public override Reachability MarkReachable (Reachability rc)
1779 base.MarkReachable (rc);
1780 return Reachability.CreateUnreachable ();
1783 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1785 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1789 public class Throw : Statement {
1792 public Throw (Expression expr, Location l)
1798 public Expression Expr {
1804 public static Expression ConvertType (ResolveContext rc, Expression expr)
1806 var et = rc.BuiltinTypes.Exception;
1807 if (Convert.ImplicitConversionExists (rc, expr, et))
1808 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1810 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1811 expr = EmptyCast.Create (expr, et);
1817 public override bool Resolve (BlockContext ec)
1820 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1821 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1822 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1823 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1824 if (b.IsFinallyBlock) {
1825 ec.Report.Error (724, loc,
1826 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1835 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1840 expr = ConvertType (ec, expr);
1845 protected override void DoEmit (EmitContext ec)
1848 var atv = ec.AsyncThrowVariable;
1850 if (atv.HoistedVariant != null) {
1851 atv.HoistedVariant.Emit (ec);
1856 ec.Emit (OpCodes.Throw);
1858 ec.Emit (OpCodes.Rethrow);
1863 ec.Emit (OpCodes.Throw);
1867 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1870 expr.FlowAnalysis (fc);
1875 public override Reachability MarkReachable (Reachability rc)
1877 base.MarkReachable (rc);
1878 return Reachability.CreateUnreachable ();
1881 protected override void CloneTo (CloneContext clonectx, Statement t)
1883 Throw target = (Throw) t;
1886 target.expr = expr.Clone (clonectx);
1889 public override object Accept (StructuralVisitor visitor)
1891 return visitor.Visit (this);
1895 public class Break : LocalExitStatement
1897 public Break (Location l)
1902 public override object Accept (StructuralVisitor visitor)
1904 return visitor.Visit (this);
1907 protected override void DoEmit (EmitContext ec)
1911 if (ec.TryFinallyUnwind != null) {
1912 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1913 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1916 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1919 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1921 enclosing_loop.AddEndDefiniteAssignment (fc);
1925 protected override bool DoResolve (BlockContext bc)
1927 enclosing_loop = bc.EnclosingLoopOrSwitch;
1928 return base.DoResolve (bc);
1931 public override Reachability MarkReachable (Reachability rc)
1933 base.MarkReachable (rc);
1935 if (!rc.IsUnreachable)
1936 enclosing_loop.SetEndReachable ();
1938 return Reachability.CreateUnreachable ();
1942 public class Continue : LocalExitStatement
1944 public Continue (Location l)
1949 public override object Accept (StructuralVisitor visitor)
1951 return visitor.Visit (this);
1955 protected override void DoEmit (EmitContext ec)
1957 var l = ec.LoopBegin;
1959 if (ec.TryFinallyUnwind != null) {
1960 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1961 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1964 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1967 protected override bool DoResolve (BlockContext bc)
1969 enclosing_loop = bc.EnclosingLoop;
1970 return base.DoResolve (bc);
1973 public override Reachability MarkReachable (Reachability rc)
1975 base.MarkReachable (rc);
1977 if (!rc.IsUnreachable)
1978 enclosing_loop.SetIteratorReachable ();
1980 return Reachability.CreateUnreachable ();
1984 public abstract class LocalExitStatement : ExitStatement
1986 protected LoopStatement enclosing_loop;
1988 protected LocalExitStatement (Location loc)
1993 protected override bool IsLocalExit {
1999 protected override void CloneTo (CloneContext clonectx, Statement t)
2004 protected override bool DoResolve (BlockContext bc)
2006 if (enclosing_loop == null) {
2007 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
2011 var block = enclosing_loop.Statement as Block;
2013 // Don't need to do extra checks for simple statements loops
2014 if (block != null) {
2015 CheckExitBoundaries (bc, block);
2022 public interface ILocalVariable
2024 void Emit (EmitContext ec);
2025 void EmitAssign (EmitContext ec);
2026 void EmitAddressOf (EmitContext ec);
2029 public interface INamedBlockVariable
2031 Block Block { get; }
2032 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2033 bool IsDeclared { get; }
2034 bool IsParameter { get; }
2035 Location Location { get; }
2038 public class BlockVariableDeclarator
2041 Expression initializer;
2043 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2045 if (li.Type != null)
2046 throw new ArgumentException ("Expected null variable type");
2049 this.initializer = initializer;
2054 public LocalVariable Variable {
2060 public Expression Initializer {
2065 initializer = value;
2071 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2073 var t = (BlockVariableDeclarator) MemberwiseClone ();
2074 if (initializer != null)
2075 t.initializer = initializer.Clone (cloneCtx);
2081 public class BlockVariable : Statement
2083 Expression initializer;
2084 protected FullNamedExpression type_expr;
2085 protected LocalVariable li;
2086 protected List<BlockVariableDeclarator> declarators;
2089 public BlockVariable (FullNamedExpression type, LocalVariable li)
2091 this.type_expr = type;
2093 this.loc = type_expr.Location;
2096 protected BlockVariable (LocalVariable li)
2103 public List<BlockVariableDeclarator> Declarators {
2109 public Expression Initializer {
2114 initializer = value;
2118 public FullNamedExpression TypeExpression {
2124 public LocalVariable Variable {
2132 public void AddDeclarator (BlockVariableDeclarator decl)
2134 if (declarators == null)
2135 declarators = new List<BlockVariableDeclarator> ();
2137 declarators.Add (decl);
2140 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2142 if (bc.Report.Errors != 0)
2145 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2147 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2148 new MemberName (li.Name, li.Location), null);
2150 container.AddField (f);
2153 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2157 public override bool Resolve (BlockContext bc)
2159 return Resolve (bc, true);
2162 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2164 if (type == null && !li.IsCompilerGenerated) {
2165 var vexpr = type_expr as VarExpr;
2168 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2169 // same name exists or as a keyword when no type was found
2171 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2172 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2173 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2176 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2180 if (li.IsConstant) {
2181 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2185 if (Initializer == null) {
2186 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2190 if (declarators != null) {
2191 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2195 Initializer = Initializer.Resolve (bc);
2196 if (Initializer != null) {
2197 ((VarExpr) type_expr).InferType (bc, Initializer);
2198 type = type_expr.Type;
2200 // Set error type to indicate the var was placed correctly but could
2203 // var a = missing ();
2205 type = InternalType.ErrorType;
2210 type = ResolveTypeExpression (bc);
2214 if (li.IsConstant && !type.IsConstantCompatible) {
2215 Const.Error_InvalidConstantType (type, loc, bc.Report);
2220 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2225 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2227 CreateEvaluatorVariable (bc, li);
2228 } else if (type != InternalType.ErrorType) {
2229 li.PrepareAssignmentAnalysis (bc);
2232 if (initializer != null) {
2234 if (!(initializer is ReferenceExpression)) {
2235 bc.Report.Error (8172, loc, "Cannot initialize a by-reference variable `{0}' with a value", li.Name);
2239 if (bc.CurrentAnonymousMethod is AsyncInitializer) {
2240 bc.Report.Error (8177, loc, "Async methods cannot use by-reference variables");
2241 } else if (bc.CurrentIterator != null) {
2242 bc.Report.Error (8176, loc, "Iterators cannot use by-reference variables");
2246 if (initializer is ReferenceExpression) {
2247 bc.Report.Error (8171, loc, "Cannot initialize a by-value variable `{0}' with a reference expression", li.Name);
2252 initializer = ResolveInitializer (bc, li, initializer);
2253 // li.Variable.DefinitelyAssigned
2256 if (declarators != null) {
2257 foreach (var d in declarators) {
2258 d.Variable.Type = li.Type;
2260 CreateEvaluatorVariable (bc, d.Variable);
2261 } else if (type != InternalType.ErrorType) {
2262 d.Variable.PrepareAssignmentAnalysis (bc);
2265 if (d.Initializer != null && resolveDeclaratorInitializers) {
2266 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2267 // d.Variable.DefinitelyAssigned
2275 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2277 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2278 return a.ResolveStatement (bc);
2281 protected virtual TypeSpec ResolveTypeExpression (BlockContext bc)
2283 return type_expr.ResolveAsType (bc);
2286 protected override void DoEmit (EmitContext ec)
2288 li.CreateBuilder (ec);
2290 if (Initializer != null && !IsUnreachable)
2291 ((ExpressionStatement) Initializer).EmitStatement (ec);
2293 if (declarators != null) {
2294 foreach (var d in declarators) {
2295 d.Variable.CreateBuilder (ec);
2296 if (d.Initializer != null && !IsUnreachable) {
2297 ec.Mark (d.Variable.Location);
2298 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2304 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2306 if (Initializer != null)
2307 Initializer.FlowAnalysis (fc);
2309 if (declarators != null) {
2310 foreach (var d in declarators) {
2311 if (d.Initializer != null)
2312 d.Initializer.FlowAnalysis (fc);
2319 public override Reachability MarkReachable (Reachability rc)
2321 base.MarkReachable (rc);
2322 return initializer == null ? rc : initializer.MarkReachable (rc);
2325 protected override void CloneTo (CloneContext clonectx, Statement target)
2327 BlockVariable t = (BlockVariable) target;
2329 if (type_expr != null)
2330 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2332 if (initializer != null)
2333 t.initializer = initializer.Clone (clonectx);
2335 if (declarators != null) {
2336 t.declarators = null;
2337 foreach (var d in declarators)
2338 t.AddDeclarator (d.Clone (clonectx));
2342 public override object Accept (StructuralVisitor visitor)
2344 return visitor.Visit (this);
2348 public class BlockConstant : BlockVariable
2350 public BlockConstant (FullNamedExpression type, LocalVariable li)
2355 public override void Emit (EmitContext ec)
2357 if (!Variable.IsUsed)
2358 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2360 // Nothing to emit, not even sequence point
2363 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2365 initializer = initializer.Resolve (bc);
2366 if (initializer == null)
2369 var c = initializer as Constant;
2371 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2375 c = c.ConvertImplicitly (li.Type);
2377 if (TypeSpec.IsReferenceType (li.Type))
2378 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2380 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2385 li.ConstantValue = c;
2389 public override object Accept (StructuralVisitor visitor)
2391 return visitor.Visit (this);
2396 // The information about a user-perceived local variable
2398 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2405 AddressTaken = 1 << 2,
2406 CompilerGenerated = 1 << 3,
2408 ForeachVariable = 1 << 5,
2409 FixedVariable = 1 << 6,
2410 UsingVariable = 1 << 7,
2412 SymbolFileHidden = 1 << 9,
2415 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2419 readonly string name;
2420 readonly Location loc;
2421 readonly Block block;
2423 Constant const_value;
2425 public VariableInfo VariableInfo;
2426 HoistedVariable hoisted_variant;
2428 LocalBuilder builder;
2430 public LocalVariable (Block block, string name, Location loc)
2437 public LocalVariable (Block block, string name, Flags flags, Location loc)
2438 : this (block, name, loc)
2444 // Used by variable declarators
2446 public LocalVariable (LocalVariable li, string name, Location loc)
2447 : this (li.block, name, li.flags, loc)
2453 public bool AddressTaken {
2455 return (flags & Flags.AddressTaken) != 0;
2459 public Block Block {
2465 public Constant ConstantValue {
2470 const_value = value;
2475 // Hoisted local variable variant
2477 public HoistedVariable HoistedVariant {
2479 return hoisted_variant;
2482 hoisted_variant = value;
2486 public bool Created {
2488 return builder != null;
2492 public bool IsDeclared {
2494 return type != null;
2498 public bool IsByRef => (flags & Flags.ByRef) != 0;
2500 public bool IsCompilerGenerated {
2502 return (flags & Flags.CompilerGenerated) != 0;
2506 public bool IsConstant {
2508 return (flags & Flags.Constant) != 0;
2512 public bool IsLocked {
2514 return (flags & Flags.IsLocked) != 0;
2517 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2521 public bool IsThis {
2523 return (flags & Flags.IsThis) != 0;
2527 public bool IsUsed {
2529 return (flags & Flags.Used) != 0;
2533 public bool IsFixed {
2535 return (flags & Flags.FixedVariable) != 0;
2538 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2542 bool INamedBlockVariable.IsParameter {
2548 public bool IsReadonly {
2550 return (flags & Flags.ReadonlyMask) != 0;
2554 public Location Location {
2560 public string Name {
2566 public TypeSpec Type {
2577 public void CreateBuilder (EmitContext ec)
2579 if ((flags & Flags.Used) == 0) {
2580 if (VariableInfo == null) {
2581 // Missing flow analysis or wrong variable flags
2582 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2585 if (VariableInfo.IsEverAssigned)
2586 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2588 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2591 if (HoistedVariant != null)
2594 if (builder != null) {
2595 if ((flags & Flags.CompilerGenerated) != 0)
2598 // To avoid Used warning duplicates
2599 throw new InternalErrorException ("Already created variable `{0}'", name);
2603 builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, Type), IsFixed);
2606 // All fixed variabled are pinned, a slot has to be alocated
2608 builder = ec.DeclareLocal(Type, IsFixed);
2611 if ((flags & Flags.SymbolFileHidden) == 0)
2612 ec.DefineLocalVariable (name, builder);
2615 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2617 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2618 if (!writeToSymbolFile)
2619 li.flags |= Flags.SymbolFileHidden;
2625 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2627 if (IsConstant && const_value != null) {
2629 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2632 return new LocalVariableReference (this, loc);
2635 public void Emit (EmitContext ec)
2637 // TODO: Need something better for temporary variables
2638 if ((flags & Flags.CompilerGenerated) != 0)
2641 ec.Emit (OpCodes.Ldloc, builder);
2644 public void EmitAssign (EmitContext ec)
2646 // TODO: Need something better for temporary variables
2647 if ((flags & Flags.CompilerGenerated) != 0)
2650 ec.Emit (OpCodes.Stloc, builder);
2653 public void EmitAddressOf (EmitContext ec)
2655 // TODO: Need something better for temporary variables
2656 if ((flags & Flags.CompilerGenerated) != 0)
2660 ec.Emit (OpCodes.Ldloc, builder);
2662 ec.Emit (OpCodes.Ldloca, builder);
2665 public static string GetCompilerGeneratedName (Block block)
2667 // HACK: Debugger depends on the name semantics
2668 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2671 public string GetReadOnlyContext ()
2673 switch (flags & Flags.ReadonlyMask) {
2674 case Flags.FixedVariable:
2675 return "fixed variable";
2676 case Flags.ForeachVariable:
2677 return "foreach iteration variable";
2678 case Flags.UsingVariable:
2679 return "using variable";
2682 throw new InternalErrorException ("Variable is not readonly");
2685 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2687 if (VariableInfo == null)
2688 throw new Exception ();
2690 if (IsAssigned (fc))
2693 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2696 public bool IsAssigned (FlowAnalysisContext fc)
2698 return fc.IsDefinitelyAssigned (VariableInfo);
2701 public void PrepareAssignmentAnalysis (BlockContext bc)
2704 // No need to run assignment analysis for these guys
2706 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2709 VariableInfo = VariableInfo.Create (bc, this);
2713 // Mark the variables as referenced in the user code
2715 public void SetIsUsed ()
2717 flags |= Flags.Used;
2720 public void SetHasAddressTaken ()
2722 flags |= (Flags.AddressTaken | Flags.Used);
2725 public override string ToString ()
2727 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2732 /// Block represents a C# block.
2736 /// This class is used in a number of places: either to represent
2737 /// explicit blocks that the programmer places or implicit blocks.
2739 /// Implicit blocks are used as labels or to introduce variable
2742 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2743 /// they contain extra information that is not necessary on normal blocks.
2745 public class Block : Statement {
2752 HasCapturedVariable = 64,
2753 HasCapturedThis = 1 << 7,
2754 IsExpressionTree = 1 << 8,
2755 CompilerGenerated = 1 << 9,
2756 HasAsyncModifier = 1 << 10,
2758 YieldBlock = 1 << 12,
2759 AwaitBlock = 1 << 13,
2760 FinallyBlock = 1 << 14,
2761 CatchBlock = 1 << 15,
2762 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2764 NoFlowAnalysis = 1 << 21,
2765 InitializationEmitted = 1 << 22
2768 public Block Parent;
2769 public Location StartLocation;
2770 public Location EndLocation;
2772 public ExplicitBlock Explicit;
2773 public ParametersBlock ParametersBlock;
2775 protected Flags flags;
2778 // The statements in this block
2780 protected List<Statement> statements;
2782 protected List<Statement> scope_initializers;
2784 int? resolving_init_idx;
2790 public int ID = id++;
2792 static int clone_id_counter;
2796 // int assignable_slots;
2798 public Block (Block parent, Location start, Location end)
2799 : this (parent, 0, start, end)
2803 public Block (Block parent, Flags flags, Location start, Location end)
2805 if (parent != null) {
2806 // the appropriate constructors will fixup these fields
2807 ParametersBlock = parent.ParametersBlock;
2808 Explicit = parent.Explicit;
2811 this.Parent = parent;
2813 this.StartLocation = start;
2814 this.EndLocation = end;
2816 statements = new List<Statement> (4);
2818 this.original = this;
2823 public Block Original {
2832 public bool IsCompilerGenerated {
2833 get { return (flags & Flags.CompilerGenerated) != 0; }
2834 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2838 public bool IsCatchBlock {
2840 return (flags & Flags.CatchBlock) != 0;
2844 public bool IsFinallyBlock {
2846 return (flags & Flags.FinallyBlock) != 0;
2850 public bool Unchecked {
2851 get { return (flags & Flags.Unchecked) != 0; }
2852 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2855 public bool Unsafe {
2856 get { return (flags & Flags.Unsafe) != 0; }
2857 set { flags |= Flags.Unsafe; }
2860 public List<Statement> Statements {
2861 get { return statements; }
2866 public void SetEndLocation (Location loc)
2871 public void AddLabel (LabeledStatement target)
2873 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2876 public void AddLocalName (LocalVariable li)
2878 AddLocalName (li.Name, li);
2881 public void AddLocalName (string name, INamedBlockVariable li)
2883 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2886 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2888 if (reason == null) {
2889 Error_AlreadyDeclared (name, variable);
2893 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2894 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2895 "to `{0}', which is already used in a `{1}' scope to denote something else",
2899 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2901 var pi = variable as ParametersBlock.ParameterInfo;
2903 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2905 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2906 "A local variable named `{0}' is already defined in this scope", name);
2910 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2912 ParametersBlock.TopBlock.Report.Error (412, loc,
2913 "The type parameter name `{0}' is the same as local variable or parameter name",
2918 // It should be used by expressions which require to
2919 // register a statement during resolve process.
2921 public void AddScopeStatement (Statement s)
2923 if (scope_initializers == null)
2924 scope_initializers = new List<Statement> ();
2927 // Simple recursive helper, when resolve scope initializer another
2928 // new scope initializer can be added, this ensures it's initialized
2929 // before existing one. For now this can happen with expression trees
2930 // in base ctor initializer only
2932 if (resolving_init_idx.HasValue) {
2933 scope_initializers.Insert (resolving_init_idx.Value, s);
2934 ++resolving_init_idx;
2936 scope_initializers.Add (s);
2940 public void InsertStatement (int index, Statement s)
2942 statements.Insert (index, s);
2945 public void AddStatement (Statement s)
2950 public LabeledStatement LookupLabel (string name)
2952 return ParametersBlock.GetLabel (name, this);
2955 public override Reachability MarkReachable (Reachability rc)
2957 if (rc.IsUnreachable)
2960 MarkReachableScope (rc);
2962 foreach (var s in statements) {
2963 rc = s.MarkReachable (rc);
2964 if (rc.IsUnreachable) {
2965 if ((flags & Flags.ReachableEnd) != 0)
2966 return new Reachability ();
2972 flags |= Flags.ReachableEnd;
2977 public void MarkReachableScope (Reachability rc)
2979 base.MarkReachable (rc);
2981 if (scope_initializers != null) {
2982 foreach (var si in scope_initializers)
2983 si.MarkReachable (rc);
2987 public override bool Resolve (BlockContext bc)
2989 if ((flags & Flags.Resolved) != 0)
2992 Block prev_block = bc.CurrentBlock;
2993 bc.CurrentBlock = this;
2996 // Compiler generated scope statements
2998 if (scope_initializers != null) {
2999 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
3000 scope_initializers[resolving_init_idx.Value].Resolve (bc);
3003 resolving_init_idx = null;
3007 int statement_count = statements.Count;
3008 for (int ix = 0; ix < statement_count; ix++){
3009 Statement s = statements [ix];
3011 if (!s.Resolve (bc)) {
3013 statements [ix] = new EmptyStatement (s.loc);
3018 bc.CurrentBlock = prev_block;
3020 flags |= Flags.Resolved;
3024 protected override void DoEmit (EmitContext ec)
3026 for (int ix = 0; ix < statements.Count; ix++){
3027 statements [ix].Emit (ec);
3031 public override void Emit (EmitContext ec)
3033 if (scope_initializers != null)
3034 EmitScopeInitializers (ec);
3039 protected void EmitScopeInitializers (EmitContext ec)
3041 foreach (Statement s in scope_initializers)
3045 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3047 if (scope_initializers != null) {
3048 foreach (var si in scope_initializers)
3049 si.FlowAnalysis (fc);
3052 return DoFlowAnalysis (fc, 0);
3055 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
3057 bool end_unreachable = !reachable;
3058 bool goto_flow_analysis = startIndex != 0;
3059 for (; startIndex < statements.Count; ++startIndex) {
3060 var s = statements[startIndex];
3062 end_unreachable = s.FlowAnalysis (fc);
3063 if (s.IsUnreachable) {
3064 statements [startIndex] = RewriteUnreachableStatement (s);
3069 // Statement end reachability is needed mostly due to goto support. Consider
3078 // X label is reachable only via goto not as another statement after if. We need
3079 // this for flow-analysis only to carry variable info correctly.
3081 if (end_unreachable) {
3082 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3084 var f = s as TryFinally;
3085 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3087 // Special case for try-finally with unreachable code after
3088 // finally block. Try block has to include leave opcode but there is
3089 // no label to leave to after unreachable finally block closing
3090 // brace. This sentinel ensures there is always IL instruction to
3091 // leave to even if we know it'll never be reached.
3093 statements.Insert (startIndex + 1, new SentinelStatement ());
3095 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3096 s = statements [startIndex];
3097 if (s is SwitchLabel) {
3098 if (!after_goto_case)
3099 s.FlowAnalysis (fc);
3104 if (s.IsUnreachable) {
3105 s.FlowAnalysis (fc);
3106 statements [startIndex] = RewriteUnreachableStatement (s);
3112 // Idea is to stop after goto case because goto case will always have at least same
3113 // variable assigned as switch case label. This saves a lot for complex goto case tests
3115 if (after_goto_case)
3121 var lb = s as LabeledStatement;
3122 if (lb != null && fc.AddReachedLabel (lb))
3127 // The condition should be true unless there is forward jumping goto
3129 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3132 return !Explicit.HasReachableClosingBrace;
3135 static Statement RewriteUnreachableStatement (Statement s)
3137 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3138 // analysis. Even csc report unreachable warning for it but it's actually used hence
3139 // we try to emulate this behaviour
3147 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3150 return new EmptyStatement (s.loc);
3153 public void ScanGotoJump (Statement label)
3156 for (i = 0; i < statements.Count; ++i) {
3157 if (statements[i] == label)
3161 var rc = new Reachability ();
3162 for (++i; i < statements.Count; ++i) {
3163 var s = statements[i];
3164 rc = s.MarkReachable (rc);
3165 if (rc.IsUnreachable)
3169 flags |= Flags.ReachableEnd;
3172 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3175 for (i = 0; i < statements.Count; ++i) {
3176 if (statements[i] == label)
3180 DoFlowAnalysis (fc, ++i);
3184 public override string ToString ()
3186 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3190 protected override void CloneTo (CloneContext clonectx, Statement t)
3192 Block target = (Block) t;
3194 target.clone_id = ++clone_id_counter;
3197 clonectx.AddBlockMap (this, target);
3198 if (original != this)
3199 clonectx.AddBlockMap (original, target);
3201 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3202 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3205 target.Parent = clonectx.RemapBlockCopy (Parent);
3207 target.statements = new List<Statement> (statements.Count);
3208 foreach (Statement s in statements)
3209 target.statements.Add (s.Clone (clonectx));
3212 public override object Accept (StructuralVisitor visitor)
3214 return visitor.Visit (this);
3218 public class ExplicitBlock : Block
3220 protected AnonymousMethodStorey am_storey;
3221 int debug_scope_index;
3223 public ExplicitBlock (Block parent, Location start, Location end)
3224 : this (parent, (Flags) 0, start, end)
3228 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3229 : base (parent, flags, start, end)
3231 this.Explicit = this;
3236 public AnonymousMethodStorey AnonymousMethodStorey {
3242 public bool HasAwait {
3244 return (flags & Flags.AwaitBlock) != 0;
3248 public bool HasCapturedThis {
3250 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3253 return (flags & Flags.HasCapturedThis) != 0;
3258 // Used to indicate that the block has reference to parent
3259 // block and cannot be made static when defining anonymous method
3261 public bool HasCapturedVariable {
3263 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3266 return (flags & Flags.HasCapturedVariable) != 0;
3270 public bool HasReachableClosingBrace {
3272 return (flags & Flags.ReachableEnd) != 0;
3275 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3279 public bool HasYield {
3281 return (flags & Flags.YieldBlock) != 0;
3288 // Creates anonymous method storey in current block
3290 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3293 // Return same story for iterator and async blocks unless we are
3294 // in nested anonymous method
3296 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3297 return ec.CurrentAnonymousMethod.Storey;
3299 if (am_storey == null) {
3300 MemberBase mc = ec.MemberContext as MemberBase;
3303 // Creates anonymous method storey for this block
3305 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3311 public void EmitScopeInitialization (EmitContext ec)
3313 if ((flags & Flags.InitializationEmitted) != 0)
3316 if (am_storey != null) {
3317 DefineStoreyContainer (ec, am_storey);
3318 am_storey.EmitStoreyInstantiation (ec, this);
3321 if (scope_initializers != null)
3322 EmitScopeInitializers (ec);
3324 flags |= Flags.InitializationEmitted;
3327 public override void Emit (EmitContext ec)
3329 // TODO: It's needed only when scope has variable (normal or lifted)
3330 var scopeIndex = GetDebugSymbolScopeIndex ();
3331 if (scopeIndex > 0) {
3332 ec.BeginScope (scopeIndex);
3335 EmitScopeInitialization (ec);
3337 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3338 ec.Emit (OpCodes.Nop);
3346 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3347 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3348 ec.Emit (OpCodes.Nop);
3352 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3354 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3355 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3356 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3360 // Creates anonymous method storey
3362 storey.CreateContainer ();
3363 storey.DefineContainer ();
3364 storey.ExpandBaseInterfaces ();
3366 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3369 // Only first storey in path will hold this reference. All children blocks will
3370 // reference it indirectly using $ref field
3372 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3373 if (b.Parent != null) {
3374 var s = b.Parent.Explicit.AnonymousMethodStorey;
3376 storey.HoistedThis = s.HoistedThis;
3381 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3382 if (storey.HoistedThis == null)
3383 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3385 if (storey.HoistedThis != null)
3391 // We are the first storey on path and 'this' has to be hoisted
3393 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3394 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3396 // ThisReferencesFromChildrenBlock holds all reference even if they
3397 // are not on this path. It saves some memory otherwise it'd have to
3398 // be in every explicit block. We run this check to see if the reference
3399 // is valid for this storey
3401 Block block_on_path = ref_block;
3402 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3404 if (block_on_path == null)
3407 if (storey.HoistedThis == null) {
3408 storey.AddCapturedThisField (ec, null);
3411 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3413 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3415 if (b_storey != null) {
3417 // Don't add storey cross reference for `this' when the storey ends up not
3418 // beeing attached to any parent
3420 if (b.ParametersBlock.StateMachine == null) {
3421 AnonymousMethodStorey s = null;
3422 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3423 s = ab.Explicit.AnonymousMethodStorey;
3428 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3430 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3431 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3438 // Stop propagation inside same top block
3440 if (b.ParametersBlock == ParametersBlock.Original) {
3441 b_storey.AddParentStoreyReference (ec, storey);
3442 // b_storey.HoistedThis = storey.HoistedThis;
3446 b = pb = b.ParametersBlock;
3448 pb = b as ParametersBlock;
3451 if (pb != null && pb.StateMachine != null) {
3452 if (pb.StateMachine == storey)
3456 // If we are state machine with no parent. We can hook into parent without additional
3457 // reference and capture this directly
3459 ExplicitBlock parent_storey_block = pb;
3460 while (parent_storey_block.Parent != null) {
3461 parent_storey_block = parent_storey_block.Parent.Explicit;
3462 if (parent_storey_block.AnonymousMethodStorey != null) {
3467 if (parent_storey_block.AnonymousMethodStorey == null) {
3468 if (pb.StateMachine.HoistedThis == null) {
3469 pb.StateMachine.AddCapturedThisField (ec, null);
3470 b.HasCapturedThis = true;
3476 var parent_this_block = pb;
3477 while (parent_this_block.Parent != null) {
3478 parent_this_block = parent_this_block.Parent.ParametersBlock;
3479 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3485 // Add reference to closest storey which holds captured this
3487 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3491 // Add parent storey reference only when this is not captured directly
3493 if (b_storey != null) {
3494 b_storey.AddParentStoreyReference (ec, storey);
3495 b_storey.HoistedThis = storey.HoistedThis;
3502 var ref_blocks = storey.ReferencesFromChildrenBlock;
3503 if (ref_blocks != null) {
3504 foreach (ExplicitBlock ref_block in ref_blocks) {
3505 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3506 if (b.AnonymousMethodStorey != null) {
3507 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3510 // Stop propagation inside same top block
3512 if (b.ParametersBlock == ParametersBlock.Original)
3515 b = b.ParametersBlock;
3518 var pb = b as ParametersBlock;
3519 if (pb != null && pb.StateMachine != null) {
3520 if (pb.StateMachine == storey)
3523 pb.StateMachine.AddParentStoreyReference (ec, storey);
3526 b.HasCapturedVariable = true;
3532 storey.PrepareEmit ();
3533 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3536 public void DisableDebugScopeIndex ()
3538 debug_scope_index = -1;
3541 public virtual int GetDebugSymbolScopeIndex ()
3543 if (debug_scope_index == 0)
3544 debug_scope_index = ++ParametersBlock.debug_scope_index;
3546 return debug_scope_index;
3549 public void RegisterAsyncAwait ()
3552 while ((block.flags & Flags.AwaitBlock) == 0) {
3553 block.flags |= Flags.AwaitBlock;
3555 if (block is ParametersBlock)
3558 block = block.Parent.Explicit;
3562 public void RegisterIteratorYield ()
3564 ParametersBlock.TopBlock.IsIterator = true;
3567 while ((block.flags & Flags.YieldBlock) == 0) {
3568 block.flags |= Flags.YieldBlock;
3570 if (block.Parent == null)
3573 block = block.Parent.Explicit;
3577 public void SetCatchBlock ()
3579 flags |= Flags.CatchBlock;
3582 public void SetFinallyBlock ()
3584 flags |= Flags.FinallyBlock;
3587 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3589 tryBlock.statements = statements;
3590 statements = new List<Statement> (1);
3591 statements.Add (tf);
3596 // ParametersBlock was introduced to support anonymous methods
3597 // and lambda expressions
3599 public class ParametersBlock : ExplicitBlock
3601 public class ParameterInfo : INamedBlockVariable
3603 readonly ParametersBlock block;
3605 public VariableInfo VariableInfo;
3608 public ParameterInfo (ParametersBlock block, int index)
3616 public ParametersBlock Block {
3622 Block INamedBlockVariable.Block {
3628 public bool IsDeclared {
3634 public bool IsParameter {
3640 public bool IsLocked {
3649 public Location Location {
3651 return Parameter.Location;
3655 public Parameter Parameter {
3657 return block.Parameters [index];
3661 public TypeSpec ParameterType {
3663 return Parameter.Type;
3669 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3671 return new ParameterReference (this, loc);
3676 // Block is converted into an expression
3678 sealed class BlockScopeExpression : Expression
3681 readonly ParametersBlock block;
3683 public BlockScopeExpression (Expression child, ParametersBlock block)
3689 public override bool ContainsEmitWithAwait ()
3691 return child.ContainsEmitWithAwait ();
3694 public override Expression CreateExpressionTree (ResolveContext ec)
3696 throw new NotSupportedException ();
3699 protected override Expression DoResolve (ResolveContext ec)
3704 child = child.Resolve (ec);
3708 eclass = child.eclass;
3713 public override void Emit (EmitContext ec)
3715 block.EmitScopeInitializers (ec);
3720 protected ParametersCompiled parameters;
3721 protected ParameterInfo[] parameter_info;
3722 protected bool resolved;
3723 protected ToplevelBlock top_block;
3724 protected StateMachine state_machine;
3725 protected Dictionary<string, object> labels;
3727 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3728 : base (parent, 0, start, start)
3730 if (parameters == null)
3731 throw new ArgumentNullException ("parameters");
3733 this.parameters = parameters;
3734 ParametersBlock = this;
3736 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3738 this.top_block = parent.ParametersBlock.top_block;
3739 ProcessParameters ();
3742 protected ParametersBlock (ParametersCompiled parameters, Location start)
3743 : base (null, 0, start, start)
3745 if (parameters == null)
3746 throw new ArgumentNullException ("parameters");
3748 this.parameters = parameters;
3749 ParametersBlock = this;
3753 // It's supposed to be used by method body implementation of anonymous methods
3755 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3756 : base (null, 0, source.StartLocation, source.EndLocation)
3758 this.parameters = parameters;
3759 this.statements = source.statements;
3760 this.scope_initializers = source.scope_initializers;
3762 this.resolved = true;
3763 this.reachable = source.reachable;
3764 this.am_storey = source.am_storey;
3765 this.state_machine = source.state_machine;
3766 this.flags = source.flags & Flags.ReachableEnd;
3768 ParametersBlock = this;
3771 // Overwrite original for comparison purposes when linking cross references
3772 // between anonymous methods
3774 Original = source.Original;
3779 public bool HasReferenceToStoreyForInstanceLambdas {
3781 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3784 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3788 public bool IsAsync {
3790 return (flags & Flags.HasAsyncModifier) != 0;
3793 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3798 // Block has been converted to expression tree
3800 public bool IsExpressionTree {
3802 return (flags & Flags.IsExpressionTree) != 0;
3807 // The parameters for the block.
3809 public ParametersCompiled Parameters {
3815 public StateMachine StateMachine {
3817 return state_machine;
3821 public ToplevelBlock TopBlock {
3830 public bool Resolved {
3832 return (flags & Flags.Resolved) != 0;
3836 public int TemporaryLocalsCount { get; set; }
3841 // Checks whether all `out' parameters have been assigned.
3843 public void CheckControlExit (FlowAnalysisContext fc)
3845 CheckControlExit (fc, fc.DefiniteAssignment);
3848 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3850 if (parameter_info == null)
3853 foreach (var p in parameter_info) {
3854 if (p.VariableInfo == null)
3857 if (p.VariableInfo.IsAssigned (dat))
3860 fc.Report.Error (177, p.Location,
3861 "The out parameter `{0}' must be assigned to before control leaves the current method",
3866 protected override void CloneTo (CloneContext clonectx, Statement t)
3868 base.CloneTo (clonectx, t);
3870 var target = (ParametersBlock) t;
3873 // Clone label statements as well as they contain block reference
3877 if (pb.labels != null) {
3878 target.labels = new Dictionary<string, object> ();
3880 foreach (var entry in pb.labels) {
3881 var list = entry.Value as List<LabeledStatement>;
3884 var list_clone = new List<LabeledStatement> ();
3885 foreach (var lentry in list) {
3886 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3889 target.labels.Add (entry.Key, list_clone);
3891 var labeled = (LabeledStatement) entry.Value;
3892 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3899 if (pb.Parent == null)
3902 pb = pb.Parent.ParametersBlock;
3906 public override Expression CreateExpressionTree (ResolveContext ec)
3908 if (statements.Count == 1) {
3909 Expression expr = statements[0].CreateExpressionTree (ec);
3910 if (scope_initializers != null)
3911 expr = new BlockScopeExpression (expr, this);
3916 return base.CreateExpressionTree (ec);
3919 public override void Emit (EmitContext ec)
3921 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3922 DefineStoreyContainer (ec, state_machine);
3923 state_machine.EmitStoreyInstantiation (ec, this);
3929 public void EmitEmbedded (EmitContext ec)
3931 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3932 DefineStoreyContainer (ec, state_machine);
3933 state_machine.EmitStoreyInstantiation (ec, this);
3939 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3941 var res = base.DoFlowAnalysis (fc);
3943 if (HasReachableClosingBrace)
3944 CheckControlExit (fc);
3949 public override int GetDebugSymbolScopeIndex ()
3954 public LabeledStatement GetLabel (string name, Block block)
3957 // Cloned parameters blocks can have their own cloned version of top-level labels
3959 if (labels == null) {
3961 return Parent.ParametersBlock.GetLabel (name, block);
3967 if (!labels.TryGetValue (name, out value)) {
3971 var label = value as LabeledStatement;
3973 if (label != null) {
3974 if (IsLabelVisible (label, b))
3978 List<LabeledStatement> list = (List<LabeledStatement>) value;
3979 for (int i = 0; i < list.Count; ++i) {
3981 if (IsLabelVisible (label, b))
3989 static bool IsLabelVisible (LabeledStatement label, Block b)
3992 if (label.Block == b)
3995 } while (b != null);
4000 public ParameterInfo GetParameterInfo (Parameter p)
4002 for (int i = 0; i < parameters.Count; ++i) {
4003 if (parameters[i] == p)
4004 return parameter_info[i];
4007 throw new ArgumentException ("Invalid parameter");
4010 public ParameterReference GetParameterReference (int index, Location loc)
4012 return new ParameterReference (parameter_info[index], loc);
4015 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
4017 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
4019 CloneContext clonectx = new CloneContext ();
4020 return Clone (clonectx);
4023 protected void ProcessParameters ()
4025 if (parameters.Count == 0)
4028 parameter_info = new ParameterInfo[parameters.Count];
4029 for (int i = 0; i < parameter_info.Length; ++i) {
4030 var p = parameters.FixedParameters[i];
4034 // TODO: Should use Parameter only and more block there
4035 parameter_info[i] = new ParameterInfo (this, i);
4037 AddLocalName (p.Name, parameter_info[i]);
4041 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
4043 var src = stmt.Block;
4046 // Cannot remap label block if the label was not yet cloned which
4047 // can happen in case of anonymous method inside anoynymous method
4048 // with a label. But in this case we don't care because goto cannot
4049 // jump of out anonymous method
4051 if (src.ParametersBlock != this)
4054 var src_stmts = src.Statements;
4055 for (int i = 0; i < src_stmts.Count; ++i) {
4056 if (src_stmts[i] == stmt)
4057 return (LabeledStatement) dst.Statements[i];
4060 throw new InternalErrorException ("Should never be reached");
4063 public override bool Resolve (BlockContext bc)
4065 // TODO: if ((flags & Flags.Resolved) != 0)
4072 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4073 flags |= Flags.IsExpressionTree;
4076 PrepareAssignmentAnalysis (bc);
4078 if (!base.Resolve (bc))
4081 } catch (Exception e) {
4082 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4085 if (bc.CurrentBlock != null) {
4086 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4088 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4093 // If an asynchronous body of F is either an expression classified as nothing, or a
4094 // statement block where no return statements have expressions, the inferred return type is Task
4097 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4098 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4099 am.ReturnTypeInference = null;
4100 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4108 void PrepareAssignmentAnalysis (BlockContext bc)
4110 for (int i = 0; i < parameters.Count; ++i) {
4111 var par = parameters.FixedParameters[i];
4113 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4116 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4120 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4122 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4123 var stateMachine = new IteratorStorey (iterator);
4125 state_machine = stateMachine;
4126 iterator.SetStateMachine (stateMachine);
4128 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4129 tlb.Original = this;
4130 tlb.state_machine = stateMachine;
4131 tlb.AddStatement (new Return (iterator, iterator.Location));
4135 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4137 for (int i = 0; i < parameters.Count; i++) {
4138 Parameter p = parameters[i];
4139 Parameter.Modifier mod = p.ModFlags;
4140 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4141 host.Compiler.Report.Error (1988, p.Location,
4142 "Async methods cannot have ref or out parameters");
4146 if (p is ArglistParameter) {
4147 host.Compiler.Report.Error (4006, p.Location,
4148 "__arglist is not allowed in parameter list of async methods");
4152 if (parameters.Types[i].IsPointer) {
4153 host.Compiler.Report.Error (4005, p.Location,
4154 "Async methods cannot have unsafe parameters");
4160 host.Compiler.Report.Warning (1998, 1, loc,
4161 "Async block lacks `await' operator and will run synchronously");
4164 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4165 var initializer = new AsyncInitializer (this, host, block_type);
4166 initializer.Type = block_type;
4167 initializer.DelegateType = delegateType;
4169 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4171 state_machine = stateMachine;
4172 initializer.SetStateMachine (stateMachine);
4174 const Flags flags = Flags.CompilerGenerated;
4176 var b = this is ToplevelBlock ?
4177 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4178 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4181 b.state_machine = stateMachine;
4182 b.AddStatement (new AsyncInitializerStatement (initializer));
4190 public class ToplevelBlock : ParametersBlock
4192 LocalVariable this_variable;
4193 CompilerContext compiler;
4194 Dictionary<string, object> names;
4196 List<ExplicitBlock> this_references;
4198 public ToplevelBlock (CompilerContext ctx, Location loc)
4199 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4203 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4204 : base (parameters, start)
4206 this.compiler = ctx;
4210 ProcessParameters ();
4214 // Recreates a top level block from parameters block. Used for
4215 // compiler generated methods where the original block comes from
4216 // explicit child block. This works for already resolved blocks
4217 // only to ensure we resolve them in the correct flow order
4219 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4220 : base (source, parameters)
4222 this.compiler = source.TopBlock.compiler;
4226 public bool IsIterator {
4228 return (flags & Flags.Iterator) != 0;
4231 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4235 public Report Report {
4237 return compiler.Report;
4242 // Used by anonymous blocks to track references of `this' variable
4244 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4246 return this_references;
4251 // Returns the "this" instance variable of this block.
4252 // See AddThisVariable() for more information.
4254 public LocalVariable ThisVariable {
4256 return this_variable;
4260 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4263 names = new Dictionary<string, object> ();
4266 if (!names.TryGetValue (name, out value)) {
4267 names.Add (name, li);
4271 INamedBlockVariable existing = value as INamedBlockVariable;
4272 List<INamedBlockVariable> existing_list;
4273 if (existing != null) {
4274 existing_list = new List<INamedBlockVariable> ();
4275 existing_list.Add (existing);
4276 names[name] = existing_list;
4278 existing_list = (List<INamedBlockVariable>) value;
4282 // A collision checking between local names
4284 var variable_block = li.Block.Explicit;
4285 for (int i = 0; i < existing_list.Count; ++i) {
4286 existing = existing_list[i];
4287 Block b = existing.Block.Explicit;
4289 // Collision at same level
4290 if (variable_block == b) {
4291 li.Block.Error_AlreadyDeclared (name, li);
4295 // Collision with parent
4296 Block parent = variable_block;
4297 while ((parent = parent.Parent) != null) {
4299 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4300 i = existing_list.Count;
4305 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4306 // Collision with children
4307 while ((b = b.Parent) != null) {
4308 if (variable_block == b) {
4309 li.Block.Error_AlreadyDeclared (name, li, "child");
4310 i = existing_list.Count;
4317 existing_list.Add (li);
4320 public void AddLabel (string name, LabeledStatement label)
4323 labels = new Dictionary<string, object> ();
4326 if (!labels.TryGetValue (name, out value)) {
4327 labels.Add (name, label);
4331 LabeledStatement existing = value as LabeledStatement;
4332 List<LabeledStatement> existing_list;
4333 if (existing != null) {
4334 existing_list = new List<LabeledStatement> ();
4335 existing_list.Add (existing);
4336 labels[name] = existing_list;
4338 existing_list = (List<LabeledStatement>) value;
4342 // A collision checking between labels
4344 for (int i = 0; i < existing_list.Count; ++i) {
4345 existing = existing_list[i];
4346 Block b = existing.Block;
4348 // Collision at same level
4349 if (label.Block == b) {
4350 Report.SymbolRelatedToPreviousError (existing.loc, name);
4351 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4355 // Collision with parent
4357 while ((b = b.Parent) != null) {
4358 if (existing.Block == b) {
4359 Report.Error (158, label.loc,
4360 "The label `{0}' shadows another label by the same name in a contained scope", name);
4361 i = existing_list.Count;
4366 // Collision with with children
4368 while ((b = b.Parent) != null) {
4369 if (label.Block == b) {
4370 Report.Error (158, label.loc,
4371 "The label `{0}' shadows another label by the same name in a contained scope", name);
4372 i = existing_list.Count;
4378 existing_list.Add (label);
4381 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4383 if (this_references == null)
4384 this_references = new List<ExplicitBlock> ();
4386 if (!this_references.Contains (block))
4387 this_references.Add (block);
4390 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4392 this_references.Remove (block);
4396 // Creates an arguments set from all parameters, useful for method proxy calls
4398 public Arguments GetAllParametersArguments ()
4400 int count = parameters.Count;
4401 Arguments args = new Arguments (count);
4402 for (int i = 0; i < count; ++i) {
4403 var pi = parameter_info[i];
4404 var arg_expr = GetParameterReference (i, pi.Location);
4406 Argument.AType atype_modifier;
4407 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4408 case Parameter.Modifier.REF:
4409 atype_modifier = Argument.AType.Ref;
4411 case Parameter.Modifier.OUT:
4412 atype_modifier = Argument.AType.Out;
4419 args.Add (new Argument (arg_expr, atype_modifier));
4426 // Lookup inside a block, the returned value can represent 3 states
4428 // true+variable: A local name was found and it's valid
4429 // false+variable: A local name was found in a child block only
4430 // false+null: No local name was found
4432 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4438 if (!names.TryGetValue (name, out value))
4441 variable = value as INamedBlockVariable;
4443 if (variable != null) {
4445 if (variable.Block == b.Original)
4449 } while (b != null);
4457 } while (b != null);
4459 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4460 for (int i = 0; i < list.Count; ++i) {
4463 if (variable.Block == b.Original)
4467 } while (b != null);
4475 } while (b != null);
4485 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4487 if (block.names != null) {
4488 foreach (var n in block.names) {
4489 var variable = n.Value as INamedBlockVariable;
4490 if (variable != null) {
4491 if (variable.Block.ParametersBlock == pb)
4492 AddLocalName (n.Key, variable, false);
4496 foreach (var v in (List<INamedBlockVariable>) n.Value)
4497 if (v.Block.ParametersBlock == pb)
4498 AddLocalName (n.Key, v, false);
4504 // This is used by non-static `struct' constructors which do not have an
4505 // initializer - in this case, the constructor must initialize all of the
4506 // struct's fields. To do this, we add a "this" variable and use the flow
4507 // analysis code to ensure that it's been fully initialized before control
4508 // leaves the constructor.
4510 public void AddThisVariable (BlockContext bc)
4512 if (this_variable != null)
4513 throw new InternalErrorException (StartLocation.ToString ());
4515 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4516 this_variable.Type = bc.CurrentType;
4517 this_variable.PrepareAssignmentAnalysis (bc);
4520 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4523 // If we're a non-static struct constructor which doesn't have an
4524 // initializer, then we must initialize all of the struct's fields.
4526 if (this_variable != null)
4527 this_variable.IsThisAssigned (fc, this);
4529 base.CheckControlExit (fc, dat);
4532 public HashSet<LocalVariable> GetUndeclaredVariables ()
4537 HashSet<LocalVariable> variables = null;
4539 foreach (var entry in names) {
4540 var complex = entry.Value as List<INamedBlockVariable>;
4541 if (complex != null) {
4542 foreach (var centry in complex) {
4543 if (IsUndeclaredVariable (centry)) {
4544 if (variables == null)
4545 variables = new HashSet<LocalVariable> ();
4547 variables.Add ((LocalVariable) centry);
4550 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4551 if (variables == null)
4552 variables = new HashSet<LocalVariable> ();
4554 variables.Add ((LocalVariable)entry.Value);
4561 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4563 var lv = namedBlockVariable as LocalVariable;
4564 return lv != null && !lv.IsDeclared;
4567 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4572 foreach (var entry in names) {
4573 var complex = entry.Value as List<INamedBlockVariable>;
4574 if (complex != null) {
4575 foreach (var centry in complex) {
4576 var lv = centry as LocalVariable;
4577 if (lv != null && undeclaredVariables.Contains (lv)) {
4582 var lv = entry.Value as LocalVariable;
4583 if (lv != null && undeclaredVariables.Contains (lv))
4589 public override void Emit (EmitContext ec)
4591 if (Report.Errors > 0)
4595 if (IsCompilerGenerated) {
4596 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4604 // If `HasReturnLabel' is set, then we already emitted a
4605 // jump to the end of the method, so we must emit a `ret'
4608 // Unfortunately, System.Reflection.Emit automatically emits
4609 // a leave to the end of a finally block. This is a problem
4610 // if no code is following the try/finally block since we may
4611 // jump to a point after the end of the method.
4612 // As a workaround, we're always creating a return label in
4615 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4616 if (ec.HasReturnLabel)
4617 ec.MarkLabel (ec.ReturnLabel);
4619 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4620 ec.Mark (EndLocation);
4622 if (ec.ReturnType.Kind != MemberKind.Void)
4623 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4625 ec.Emit (OpCodes.Ret);
4628 } catch (Exception e) {
4629 throw new InternalErrorException (e, StartLocation);
4633 public bool Resolve (BlockContext bc, IMethodData md)
4638 var errors = bc.Report.Errors;
4642 if (bc.Report.Errors > errors)
4645 MarkReachable (new Reachability ());
4647 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4648 // TODO: var md = bc.CurrentMemberDefinition;
4649 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4652 if ((flags & Flags.NoFlowAnalysis) != 0)
4655 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4658 } catch (Exception e) {
4659 throw new InternalErrorException (e, StartLocation);
4666 public class SwitchLabel : Statement
4674 // if expr == null, then it is the default case.
4676 public SwitchLabel (Expression expr, Location l)
4682 public bool IsDefault {
4684 return label == null;
4688 public Expression Label {
4694 public Location Location {
4700 public Constant Converted {
4709 public bool PatternMatching { get; set; }
4711 public bool SectionStart { get; set; }
4713 public Label GetILLabel (EmitContext ec)
4715 if (il_label == null){
4716 il_label = ec.DefineLabel ();
4719 return il_label.Value;
4722 protected override void DoEmit (EmitContext ec)
4724 ec.MarkLabel (GetILLabel (ec));
4727 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4732 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4736 public override bool Resolve (BlockContext bc)
4738 if (ResolveAndReduce (bc))
4739 bc.Switch.RegisterLabel (bc, this);
4745 // Resolves the expression, reduces it to a literal if possible
4746 // and then converts it to the requested type.
4748 bool ResolveAndReduce (BlockContext bc)
4753 var switch_statement = bc.Switch;
4755 if (PatternMatching) {
4756 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4757 return label != null;
4760 var c = label.ResolveLabelConstant (bc);
4764 if (switch_statement.IsNullable && c is NullLiteral) {
4769 if (switch_statement.IsPatternMatching) {
4770 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4774 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4775 return converted != null;
4778 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4780 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4781 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4784 protected override void CloneTo (CloneContext clonectx, Statement target)
4786 var t = (SwitchLabel) target;
4788 t.label = label.Clone (clonectx);
4791 public override object Accept (StructuralVisitor visitor)
4793 return visitor.Visit (this);
4796 public string GetSignatureForError ()
4799 if (converted == null)
4802 label = converted.GetValueAsLiteral ();
4804 return string.Format ("case {0}:", label);
4808 public class Switch : LoopStatement
4810 // structure used to hold blocks of keys while calculating table switch
4811 sealed class LabelsRange : IComparable<LabelsRange>
4813 public readonly long min;
4815 public readonly List<long> label_values;
4817 public LabelsRange (long value)
4820 label_values = new List<long> ();
4821 label_values.Add (value);
4824 public LabelsRange (long min, long max, ICollection<long> values)
4828 this.label_values = new List<long> (values);
4833 return max - min + 1;
4837 public bool AddValue (long value)
4839 var gap = value - min + 1;
4840 // Ensure the range has > 50% occupancy
4841 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4845 label_values.Add (value);
4849 public int CompareTo (LabelsRange other)
4851 int nLength = label_values.Count;
4852 int nLengthOther = other.label_values.Count;
4853 if (nLengthOther == nLength)
4854 return (int) (other.min - min);
4856 return nLength - nLengthOther;
4860 sealed class DispatchStatement : Statement
4862 readonly Switch body;
4864 public DispatchStatement (Switch body)
4869 protected override void CloneTo (CloneContext clonectx, Statement target)
4871 throw new NotImplementedException ();
4874 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4879 protected override void DoEmit (EmitContext ec)
4881 body.EmitDispatch (ec);
4885 class MissingBreak : Statement
4887 readonly SwitchLabel label;
4889 public MissingBreak (SwitchLabel sl)
4895 public bool FallOut { get; set; }
4897 protected override void DoEmit (EmitContext ec)
4901 protected override void CloneTo (CloneContext clonectx, Statement target)
4905 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4908 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4909 label.GetSignatureForError ());
4911 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4912 label.GetSignatureForError ());
4918 public Expression Expr;
4921 // Mapping of all labels to their SwitchLabels
4923 Dictionary<long, SwitchLabel> labels;
4924 Dictionary<string, SwitchLabel> string_labels;
4925 List<SwitchLabel> case_labels;
4927 List<Tuple<GotoCase, Constant>> goto_cases;
4928 List<DefiniteAssignmentBitSet> end_reachable_das;
4931 /// The governing switch type
4933 public TypeSpec SwitchType;
4935 Expression new_expr;
4937 SwitchLabel case_null;
4938 SwitchLabel case_default;
4940 Label defaultLabel, nullLabel;
4941 VariableReference value;
4942 ExpressionStatement string_dictionary;
4943 FieldExpr switch_cache_field;
4944 ExplicitBlock block;
4948 // Nullable Types support
4950 Nullable.Unwrap unwrap;
4952 public Switch (Expression e, ExplicitBlock block, Location l)
4960 public SwitchLabel ActiveLabel { get; set; }
4962 public ExplicitBlock Block {
4968 public SwitchLabel DefaultLabel {
4970 return case_default;
4974 public bool IsNullable {
4976 return unwrap != null;
4980 public bool IsPatternMatching {
4982 return new_expr == null && SwitchType != null;
4986 public List<SwitchLabel> RegisteredLabels {
4992 public VariableReference ExpressionValue {
4999 // Determines the governing type for a switch. The returned
5000 // expression might be the expression from the switch, or an
5001 // expression that includes any potential conversions to
5003 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
5005 switch (expr.Type.BuiltinType) {
5006 case BuiltinTypeSpec.Type.Byte:
5007 case BuiltinTypeSpec.Type.SByte:
5008 case BuiltinTypeSpec.Type.UShort:
5009 case BuiltinTypeSpec.Type.Short:
5010 case BuiltinTypeSpec.Type.UInt:
5011 case BuiltinTypeSpec.Type.Int:
5012 case BuiltinTypeSpec.Type.ULong:
5013 case BuiltinTypeSpec.Type.Long:
5014 case BuiltinTypeSpec.Type.Char:
5015 case BuiltinTypeSpec.Type.String:
5016 case BuiltinTypeSpec.Type.Bool:
5020 if (expr.Type.IsEnum)
5024 // Try to find a *user* defined implicit conversion.
5026 // If there is no implicit conversion, or if there are multiple
5027 // conversions, we have to report an error
5029 Expression converted = null;
5030 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
5032 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
5035 var restr = Convert.UserConversionRestriction.ImplicitOnly |
5036 Convert.UserConversionRestriction.ProbingOnly;
5039 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
5041 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
5046 // Ignore over-worked ImplicitUserConversions that do
5047 // an implicit conversion in addition to the user conversion.
5049 var uc = e as UserCast;
5053 if (converted != null){
5054 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
5063 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
5065 var types = module.Compiler.BuiltinTypes;
5067 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5068 TypeSpec[] stypes = new[] {
5081 if (nullable != null) {
5083 Array.Resize (ref stypes, stypes.Length + 9);
5085 for (int i = 0; i < 9; ++i) {
5086 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5093 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5095 case_labels.Add (sl);
5098 if (case_default != null) {
5099 sl.Error_AlreadyOccurs (rc, case_default);
5107 if (sl.Converted == null)
5111 if (string_labels != null) {
5112 string string_value = sl.Converted.GetValue () as string;
5113 if (string_value == null)
5116 string_labels.Add (string_value, sl);
5118 if (sl.Converted.IsNull) {
5121 labels.Add (sl.Converted.GetValueAsLong (), sl);
5124 } catch (ArgumentException) {
5125 if (string_labels != null)
5126 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5128 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5133 // This method emits code for a lookup-based switch statement (non-string)
5134 // Basically it groups the cases into blocks that are at least half full,
5135 // and then spits out individual lookup opcodes for each block.
5136 // It emits the longest blocks first, and short blocks are just
5137 // handled with direct compares.
5139 void EmitTableSwitch (EmitContext ec, Expression val)
5141 if (labels != null && labels.Count > 0) {
5142 List<LabelsRange> ranges;
5143 if (string_labels != null) {
5144 // We have done all hard work for string already
5145 // setup single range only
5146 ranges = new List<LabelsRange> (1);
5147 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5149 var element_keys = new long[labels.Count];
5150 labels.Keys.CopyTo (element_keys, 0);
5151 Array.Sort (element_keys);
5154 // Build possible ranges of switch labes to reduce number
5157 ranges = new List<LabelsRange> (element_keys.Length);
5158 var range = new LabelsRange (element_keys[0]);
5160 for (int i = 1; i < element_keys.Length; ++i) {
5161 var l = element_keys[i];
5162 if (range.AddValue (l))
5165 range = new LabelsRange (l);
5169 // sort the blocks so we can tackle the largest ones first
5173 Label lbl_default = defaultLabel;
5174 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5176 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5177 LabelsRange kb = ranges[range_index];
5178 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5180 // Optimize small ranges using simple equality check
5181 if (kb.Range <= 2) {
5182 foreach (var key in kb.label_values) {
5183 SwitchLabel sl = labels[key];
5184 if (sl == case_default || sl == case_null)
5187 if (sl.Converted.IsZeroInteger) {
5188 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5191 sl.Converted.Emit (ec);
5192 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5196 // TODO: if all the keys in the block are the same and there are
5197 // no gaps/defaults then just use a range-check.
5198 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5199 // TODO: optimize constant/I4 cases
5201 // check block range (could be > 2^31)
5203 ec.EmitLong (kb.min);
5204 ec.Emit (OpCodes.Blt, lbl_default);
5207 ec.EmitLong (kb.max);
5208 ec.Emit (OpCodes.Bgt, lbl_default);
5213 ec.EmitLong (kb.min);
5214 ec.Emit (OpCodes.Sub);
5217 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5221 int first = (int) kb.min;
5224 ec.Emit (OpCodes.Sub);
5225 } else if (first < 0) {
5226 ec.EmitInt (-first);
5227 ec.Emit (OpCodes.Add);
5231 // first, build the list of labels for the switch
5233 long cJumps = kb.Range;
5234 Label[] switch_labels = new Label[cJumps];
5235 for (int iJump = 0; iJump < cJumps; iJump++) {
5236 var key = kb.label_values[iKey];
5237 if (key == kb.min + iJump) {
5238 switch_labels[iJump] = labels[key].GetILLabel (ec);
5241 switch_labels[iJump] = lbl_default;
5245 // emit the switch opcode
5246 ec.Emit (OpCodes.Switch, switch_labels);
5249 // mark the default for this block
5250 if (range_index != 0)
5251 ec.MarkLabel (lbl_default);
5254 // the last default just goes to the end
5255 if (ranges.Count > 0)
5256 ec.Emit (OpCodes.Br, lbl_default);
5260 public SwitchLabel FindLabel (Constant value)
5262 SwitchLabel sl = null;
5264 if (string_labels != null) {
5265 string s = value.GetValue () as string;
5267 if (case_null != null)
5269 else if (case_default != null)
5272 string_labels.TryGetValue (s, out sl);
5275 if (value is NullLiteral) {
5278 labels.TryGetValue (value.GetValueAsLong (), out sl);
5282 if (sl == null || sl.SectionStart)
5286 // Always return section start, it simplifies handling of switch labels
5288 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5289 var cs = case_labels [idx];
5290 if (cs.SectionStart)
5295 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5297 Expr.FlowAnalysis (fc);
5299 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5300 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5301 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5303 block.FlowAnalysis (fc);
5305 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5307 if (end_reachable_das != null) {
5308 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5309 InitialDefinitiveAssignment |= sections_das;
5310 end_reachable_das = null;
5313 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5315 return case_default != null && !end_reachable;
5318 public override bool Resolve (BlockContext ec)
5320 Expr = Expr.Resolve (ec);
5325 // LAMESPEC: User conversion from non-nullable governing type has a priority
5327 new_expr = SwitchGoverningType (ec, Expr, false);
5329 if (new_expr == null) {
5330 if (Expr.Type.IsNullableType) {
5331 unwrap = Nullable.Unwrap.Create (Expr, false);
5336 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5337 // involving nullable Expr and nullable governing type is
5339 new_expr = SwitchGoverningType (ec, unwrap, true);
5343 Expression switch_expr;
5344 if (new_expr == null) {
5345 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5346 if (Expr.Type != InternalType.ErrorType) {
5347 ec.Report.Error (151, loc,
5348 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5349 Expr.Type.GetSignatureForError ());
5356 SwitchType = Expr.Type;
5358 switch_expr = new_expr;
5359 SwitchType = new_expr.Type;
5360 if (SwitchType.IsNullableType) {
5361 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5362 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5365 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5366 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5370 if (block.Statements.Count == 0)
5373 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5374 string_labels = new Dictionary<string, SwitchLabel> ();
5376 labels = new Dictionary<long, SwitchLabel> ();
5380 var constant = switch_expr as Constant;
5383 // Don't need extra variable for constant switch or switch with
5384 // only default case
5386 if (constant == null) {
5388 // Store switch expression for comparison purposes
5390 value = switch_expr as VariableReference;
5391 if (value == null && !HasOnlyDefaultSection ()) {
5392 var current_block = ec.CurrentBlock;
5393 ec.CurrentBlock = Block;
5394 // Create temporary variable inside switch scope
5395 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5397 ec.CurrentBlock = current_block;
5401 case_labels = new List<SwitchLabel> ();
5403 Switch old_switch = ec.Switch;
5405 var parent_los = ec.EnclosingLoopOrSwitch;
5406 ec.EnclosingLoopOrSwitch = this;
5408 var ok = Statement.Resolve (ec);
5410 ec.EnclosingLoopOrSwitch = parent_los;
5411 ec.Switch = old_switch;
5414 // Check if all goto cases are valid. Needs to be done after switch
5415 // is resolved because goto can jump forward in the scope.
5417 if (goto_cases != null) {
5418 foreach (var gc in goto_cases) {
5419 if (gc.Item1 == null) {
5420 if (DefaultLabel == null) {
5421 Goto.Error_UnknownLabel (ec, "default", loc);
5427 var sl = FindLabel (gc.Item2);
5429 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5431 gc.Item1.Label = sl;
5439 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5440 ResolveStringSwitchMap (ec);
5444 // Anonymous storey initialization has to happen before
5445 // any generated switch dispatch
5447 block.InsertStatement (0, new DispatchStatement (this));
5452 bool HasOnlyDefaultSection ()
5454 for (int i = 0; i < block.Statements.Count; ++i) {
5455 var s = block.Statements[i] as SwitchLabel;
5457 if (s == null || s.IsDefault)
5466 public override Reachability MarkReachable (Reachability rc)
5468 if (rc.IsUnreachable)
5471 base.MarkReachable (rc);
5473 block.MarkReachableScope (rc);
5475 if (block.Statements.Count == 0)
5478 SwitchLabel constant_label = null;
5479 var constant = new_expr as Constant;
5481 if (constant != null) {
5482 constant_label = FindLabel (constant) ?? case_default;
5483 if (constant_label == null) {
5484 block.Statements.RemoveAt (0);
5489 var section_rc = new Reachability ();
5490 SwitchLabel prev_label = null;
5492 for (int i = 0; i < block.Statements.Count; ++i) {
5493 var s = block.Statements[i];
5494 var sl = s as SwitchLabel;
5496 if (sl != null && sl.SectionStart) {
5498 // Section is marked already via goto case
5500 if (!sl.IsUnreachable) {
5501 section_rc = new Reachability ();
5505 if (section_rc.IsUnreachable) {
5507 // Common case. Previous label section end is unreachable as
5508 // it ends with break, return, etc. For next section revert
5509 // to reachable again unless we have constant switch block
5511 section_rc = constant_label != null && constant_label != sl ?
5512 Reachability.CreateUnreachable () :
5513 new Reachability ();
5514 } else if (prev_label != null) {
5516 // Error case as control cannot fall through from one case label
5518 sl.SectionStart = false;
5519 s = new MissingBreak (prev_label);
5520 s.MarkReachable (rc);
5521 block.Statements.Insert (i - 1, s);
5523 } else if (constant_label != null && constant_label != sl) {
5525 // Special case for the first unreachable label in constant
5528 section_rc = Reachability.CreateUnreachable ();
5534 section_rc = s.MarkReachable (section_rc);
5537 if (!section_rc.IsUnreachable && prev_label != null) {
5538 prev_label.SectionStart = false;
5539 var s = new MissingBreak (prev_label) {
5543 s.MarkReachable (rc);
5544 block.Statements.Add (s);
5548 // Reachability can affect parent only when all possible paths are handled but
5549 // we still need to run reachability check on switch body to check for fall-through
5551 if (case_default == null && constant_label == null)
5555 // We have at least one local exit from the switch
5560 return Reachability.CreateUnreachable ();
5563 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5565 if (goto_cases == null)
5566 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5568 goto_cases.Add (Tuple.Create (gotoCase, value));
5572 // Converts string switch into string hashtable
5574 void ResolveStringSwitchMap (ResolveContext ec)
5576 FullNamedExpression string_dictionary_type;
5577 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5578 string_dictionary_type = new TypeExpression (
5579 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5580 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5582 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5583 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5585 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5589 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5590 Field field = new Field (ctype, string_dictionary_type,
5591 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5592 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5593 if (!field.Define ())
5595 ctype.AddField (field);
5597 var init = new List<Expression> ();
5599 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5600 string value = null;
5602 foreach (SwitchLabel sl in case_labels) {
5604 if (sl.SectionStart)
5605 labels.Add (++counter, sl);
5607 if (sl == case_default || sl == case_null)
5610 value = (string) sl.Converted.GetValue ();
5611 var init_args = new List<Expression> (2);
5612 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5614 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5615 init_args.Add (sl.Converted);
5617 init.Add (new CollectionElementInitializer (init_args, loc));
5620 Arguments args = new Arguments (1);
5621 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5622 Expression initializer = new NewInitialize (string_dictionary_type, args,
5623 new CollectionOrObjectInitializers (init, loc), loc);
5625 switch_cache_field = new FieldExpr (field, loc);
5626 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5629 void DoEmitStringSwitch (EmitContext ec)
5631 Label l_initialized = ec.DefineLabel ();
5634 // Skip initialization when value is null
5636 value.EmitBranchable (ec, nullLabel, false);
5639 // Check if string dictionary is initialized and initialize
5641 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5642 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5643 string_dictionary.EmitStatement (ec);
5645 ec.MarkLabel (l_initialized);
5647 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5649 ResolveContext rc = new ResolveContext (ec.MemberContext);
5651 if (switch_cache_field.Type.IsGeneric) {
5652 Arguments get_value_args = new Arguments (2);
5653 get_value_args.Add (new Argument (value));
5654 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5655 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5656 if (get_item == null)
5660 // A value was not found, go to default case
5662 get_item.EmitBranchable (ec, defaultLabel, false);
5664 Arguments get_value_args = new Arguments (1);
5665 get_value_args.Add (new Argument (value));
5667 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5668 if (get_item == null)
5671 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5672 get_item_object.EmitAssign (ec, get_item, true, false);
5673 ec.Emit (OpCodes.Brfalse, defaultLabel);
5675 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5676 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5678 get_item_int.EmitStatement (ec);
5679 get_item_object.Release (ec);
5682 EmitTableSwitch (ec, string_switch_variable);
5683 string_switch_variable.Release (ec);
5687 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5689 void EmitShortSwitch (EmitContext ec)
5691 MethodSpec equal_method = null;
5692 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5693 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5696 if (equal_method != null) {
5697 value.EmitBranchable (ec, nullLabel, false);
5700 for (int i = 0; i < case_labels.Count; ++i) {
5701 var label = case_labels [i];
5702 if (label == case_default || label == case_null)
5705 var constant = label.Converted;
5707 if (constant == null) {
5708 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5712 if (equal_method != null) {
5716 var call = new CallEmitter ();
5717 call.EmitPredefined (ec, equal_method, new Arguments (0));
5718 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5722 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5723 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5729 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5732 ec.Emit (OpCodes.Br, defaultLabel);
5735 void EmitDispatch (EmitContext ec)
5737 if (IsPatternMatching) {
5738 EmitShortSwitch (ec);
5742 if (value == null) {
5744 // Constant switch, we've already done the work if there is only 1 label
5748 foreach (var sl in case_labels) {
5749 if (sl.IsUnreachable)
5752 if (reachable++ > 0) {
5753 var constant = (Constant) new_expr;
5754 var constant_label = FindLabel (constant) ?? case_default;
5756 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5764 if (string_dictionary != null) {
5765 DoEmitStringSwitch (ec);
5766 } else if (case_labels.Count < 4 || string_labels != null) {
5767 EmitShortSwitch (ec);
5769 EmitTableSwitch (ec, value);
5773 protected override void DoEmit (EmitContext ec)
5776 // Setup the codegen context
5778 Label old_end = ec.LoopEnd;
5779 Switch old_switch = ec.Switch;
5781 ec.LoopEnd = ec.DefineLabel ();
5784 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5785 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5787 if (value != null) {
5790 var switch_expr = new_expr ?? Expr;
5792 unwrap.EmitCheck (ec);
5793 ec.Emit (OpCodes.Brfalse, nullLabel);
5794 value.EmitAssign (ec, switch_expr, false, false);
5795 } else if (switch_expr != value) {
5796 value.EmitAssign (ec, switch_expr, false, false);
5801 // Next statement is compiler generated we don't need extra
5802 // nop when we can use the statement for sequence point
5804 ec.Mark (block.StartLocation);
5805 block.IsCompilerGenerated = true;
5807 new_expr.EmitSideEffect (ec);
5812 // Restore context state.
5813 ec.MarkLabel (ec.LoopEnd);
5816 // Restore the previous context
5818 ec.LoopEnd = old_end;
5819 ec.Switch = old_switch;
5822 protected override void CloneTo (CloneContext clonectx, Statement t)
5824 Switch target = (Switch) t;
5826 target.Expr = Expr.Clone (clonectx);
5827 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5830 public override object Accept (StructuralVisitor visitor)
5832 return visitor.Visit (this);
5835 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5837 if (case_default == null && !(new_expr is Constant))
5840 if (end_reachable_das == null)
5841 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5843 end_reachable_das.Add (fc.DefiniteAssignment);
5846 public override void SetEndReachable ()
5848 end_reachable = true;
5852 // A place where execution can restart in a state machine
5853 public abstract class ResumableStatement : Statement
5856 protected Label resume_point;
5858 public Label PrepareForEmit (EmitContext ec)
5862 resume_point = ec.DefineLabel ();
5864 return resume_point;
5867 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5872 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5877 public abstract class TryFinallyBlock : ExceptionStatement
5879 protected Statement stmt;
5880 Label dispose_try_block;
5881 bool prepared_for_dispose, emitted_dispose;
5882 Method finally_host;
5884 protected TryFinallyBlock (Statement stmt, Location loc)
5892 public Statement Statement {
5900 protected abstract void EmitTryBody (EmitContext ec);
5901 public abstract void EmitFinallyBody (EmitContext ec);
5903 public override Label PrepareForDispose (EmitContext ec, Label end)
5905 if (!prepared_for_dispose) {
5906 prepared_for_dispose = true;
5907 dispose_try_block = ec.DefineLabel ();
5909 return dispose_try_block;
5912 protected sealed override void DoEmit (EmitContext ec)
5914 EmitTryBodyPrepare (ec);
5917 bool beginFinally = EmitBeginFinallyBlock (ec);
5919 Label start_finally = ec.DefineLabel ();
5920 if (resume_points != null && beginFinally) {
5921 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5923 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5924 ec.Emit (OpCodes.Brfalse_S, start_finally);
5925 ec.Emit (OpCodes.Endfinally);
5928 ec.MarkLabel (start_finally);
5930 if (finally_host != null) {
5931 finally_host.Define ();
5932 finally_host.PrepareEmit ();
5933 finally_host.Emit ();
5935 // Now it's safe to add, to close it properly and emit sequence points
5936 finally_host.Parent.AddMember (finally_host);
5938 var ce = new CallEmitter ();
5939 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5940 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5942 EmitFinallyBody (ec);
5946 ec.EndExceptionBlock ();
5949 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5951 if (emitted_dispose)
5954 emitted_dispose = true;
5956 Label end_of_try = ec.DefineLabel ();
5958 // Ensure that the only way we can get into this code is through a dispatcher
5959 if (have_dispatcher)
5960 ec.Emit (OpCodes.Br, end);
5962 ec.BeginExceptionBlock ();
5964 ec.MarkLabel (dispose_try_block);
5966 Label[] labels = null;
5967 for (int i = 0; i < resume_points.Count; ++i) {
5968 ResumableStatement s = resume_points[i];
5969 Label ret = s.PrepareForDispose (ec, end_of_try);
5970 if (ret.Equals (end_of_try) && labels == null)
5972 if (labels == null) {
5973 labels = new Label[resume_points.Count];
5974 for (int j = 0; j < i; ++j)
5975 labels[j] = end_of_try;
5980 if (labels != null) {
5982 for (j = 1; j < labels.Length; ++j)
5983 if (!labels[0].Equals (labels[j]))
5985 bool emit_dispatcher = j < labels.Length;
5987 if (emit_dispatcher) {
5988 ec.Emit (OpCodes.Ldloc, pc);
5989 ec.EmitInt (first_resume_pc);
5990 ec.Emit (OpCodes.Sub);
5991 ec.Emit (OpCodes.Switch, labels);
5994 foreach (ResumableStatement s in resume_points)
5995 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5998 ec.MarkLabel (end_of_try);
6000 ec.BeginFinallyBlock ();
6002 if (finally_host != null) {
6003 var ce = new CallEmitter ();
6004 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
6005 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
6007 EmitFinallyBody (ec);
6010 ec.EndExceptionBlock ();
6013 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6015 var res = stmt.FlowAnalysis (fc);
6016 parent_try_block = null;
6020 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
6022 ec.BeginFinallyBlock ();
6026 public override Reachability MarkReachable (Reachability rc)
6028 base.MarkReachable (rc);
6029 return Statement.MarkReachable (rc);
6032 public override bool Resolve (BlockContext bc)
6036 parent_try_block = bc.CurrentTryBlock;
6037 bc.CurrentTryBlock = this;
6039 if (stmt is TryCatch) {
6040 ok = stmt.Resolve (bc);
6042 using (bc.Set (ResolveContext.Options.TryScope)) {
6043 ok = stmt.Resolve (bc);
6047 bc.CurrentTryBlock = parent_try_block;
6050 // Finally block inside iterator is called from MoveNext and
6051 // Dispose methods that means we need to lift the block into
6052 // newly created host method to emit the body only once. The
6053 // original block then simply calls the newly generated method.
6055 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
6056 var b = stmt as Block;
6057 if (b != null && b.Explicit.HasYield) {
6058 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
6062 return base.Resolve (bc) && ok;
6067 // Base class for blocks using exception handling
6069 public abstract class ExceptionStatement : ResumableStatement
6071 protected List<ResumableStatement> resume_points;
6072 protected int first_resume_pc;
6073 protected ExceptionStatement parent_try_block;
6074 protected int first_catch_resume_pc = -1;
6076 protected ExceptionStatement (Location loc)
6081 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6083 StateMachineInitializer state_machine = null;
6084 if (resume_points != null) {
6085 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6087 ec.EmitInt ((int) IteratorStorey.State.Running);
6088 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6092 // The resume points in catch section when this is try-catch-finally
6094 if (IsRewrittenTryCatchFinally ()) {
6095 ec.BeginExceptionBlock ();
6097 if (first_catch_resume_pc >= 0) {
6099 ec.MarkLabel (resume_point);
6101 // For normal control flow, we want to fall-through the Switch
6102 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6103 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6104 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6105 ec.Emit (OpCodes.Sub);
6107 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6108 for (int i = 0; i < labels.Length; ++i)
6109 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6110 ec.Emit (OpCodes.Switch, labels);
6114 ec.BeginExceptionBlock ();
6117 // The resume points for try section
6119 if (resume_points != null && first_catch_resume_pc != 0) {
6120 if (first_catch_resume_pc < 0)
6121 ec.MarkLabel (resume_point);
6123 // For normal control flow, we want to fall-through the Switch
6124 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6125 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6126 ec.EmitInt (first_resume_pc);
6127 ec.Emit (OpCodes.Sub);
6129 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6130 for (int i = 0; i < labels.Length; ++i)
6131 labels[i] = resume_points[i].PrepareForEmit (ec);
6132 ec.Emit (OpCodes.Switch, labels);
6136 bool IsRewrittenTryCatchFinally ()
6138 var tf = this as TryFinally;
6142 var tc = tf.Statement as TryCatch;
6146 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6149 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6151 if (parent_try_block != null) {
6152 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6154 pc = stateMachine.AddResumePoint (this);
6157 if (resume_points == null) {
6158 resume_points = new List<ResumableStatement> ();
6159 first_resume_pc = pc;
6162 if (pc != first_resume_pc + resume_points.Count)
6163 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6165 var tf = this as TryFinally;
6166 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6167 first_catch_resume_pc = resume_points.Count;
6170 resume_points.Add (stmt);
6175 public class Lock : TryFinallyBlock
6178 TemporaryVariableReference expr_copy;
6179 TemporaryVariableReference lock_taken;
6181 public Lock (Expression expr, Statement stmt, Location loc)
6187 public Expression Expr {
6193 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6195 expr.FlowAnalysis (fc);
6196 return base.DoFlowAnalysis (fc);
6199 public override bool Resolve (BlockContext ec)
6201 expr = expr.Resolve (ec);
6205 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6206 ec.Report.Error (185, loc,
6207 "`{0}' is not a reference type as required by the lock statement",
6208 expr.Type.GetSignatureForError ());
6211 if (expr.Type.IsGenericParameter) {
6212 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6215 VariableReference lv = expr as VariableReference;
6218 locked = lv.IsLockedByStatement;
6219 lv.IsLockedByStatement = true;
6226 // Have to keep original lock value around to unlock same location
6227 // in the case of original value has changed or is null
6229 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6230 expr_copy.Resolve (ec);
6233 // Ensure Monitor methods are available
6235 if (ResolvePredefinedMethods (ec) > 1) {
6236 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6237 lock_taken.Resolve (ec);
6240 using (ec.Set (ResolveContext.Options.LockScope)) {
6245 lv.IsLockedByStatement = locked;
6251 protected override void EmitTryBodyPrepare (EmitContext ec)
6253 expr_copy.EmitAssign (ec, expr);
6255 if (lock_taken != null) {
6257 // Initialize ref variable
6259 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6262 // Monitor.Enter (expr_copy)
6264 expr_copy.Emit (ec);
6265 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6268 base.EmitTryBodyPrepare (ec);
6271 protected override void EmitTryBody (EmitContext ec)
6274 // Monitor.Enter (expr_copy, ref lock_taken)
6276 if (lock_taken != null) {
6277 expr_copy.Emit (ec);
6278 lock_taken.LocalInfo.CreateBuilder (ec);
6279 lock_taken.AddressOf (ec, AddressOp.Load);
6280 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6283 Statement.Emit (ec);
6286 public override void EmitFinallyBody (EmitContext ec)
6289 // if (lock_taken) Monitor.Exit (expr_copy)
6291 Label skip = ec.DefineLabel ();
6293 if (lock_taken != null) {
6294 lock_taken.Emit (ec);
6295 ec.Emit (OpCodes.Brfalse_S, skip);
6298 expr_copy.Emit (ec);
6299 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6301 ec.Emit (OpCodes.Call, m);
6303 ec.MarkLabel (skip);
6306 int ResolvePredefinedMethods (ResolveContext rc)
6308 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6309 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6313 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6317 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6321 protected override void CloneTo (CloneContext clonectx, Statement t)
6323 Lock target = (Lock) t;
6325 target.expr = expr.Clone (clonectx);
6326 target.stmt = Statement.Clone (clonectx);
6329 public override object Accept (StructuralVisitor visitor)
6331 return visitor.Visit (this);
6336 public class Unchecked : Statement {
6339 public Unchecked (Block b, Location loc)
6346 public override bool Resolve (BlockContext ec)
6348 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6349 return Block.Resolve (ec);
6352 protected override void DoEmit (EmitContext ec)
6354 using (ec.With (EmitContext.Options.CheckedScope, false))
6358 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6360 return Block.FlowAnalysis (fc);
6363 public override Reachability MarkReachable (Reachability rc)
6365 base.MarkReachable (rc);
6366 return Block.MarkReachable (rc);
6369 protected override void CloneTo (CloneContext clonectx, Statement t)
6371 Unchecked target = (Unchecked) t;
6373 target.Block = clonectx.LookupBlock (Block);
6376 public override object Accept (StructuralVisitor visitor)
6378 return visitor.Visit (this);
6382 public class Checked : Statement {
6385 public Checked (Block b, Location loc)
6388 b.Unchecked = false;
6392 public override bool Resolve (BlockContext ec)
6394 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6395 return Block.Resolve (ec);
6398 protected override void DoEmit (EmitContext ec)
6400 using (ec.With (EmitContext.Options.CheckedScope, true))
6404 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6406 return Block.FlowAnalysis (fc);
6409 public override Reachability MarkReachable (Reachability rc)
6411 base.MarkReachable (rc);
6412 return Block.MarkReachable (rc);
6415 protected override void CloneTo (CloneContext clonectx, Statement t)
6417 Checked target = (Checked) t;
6419 target.Block = clonectx.LookupBlock (Block);
6422 public override object Accept (StructuralVisitor visitor)
6424 return visitor.Visit (this);
6428 public class Unsafe : Statement {
6431 public Unsafe (Block b, Location loc)
6434 Block.Unsafe = true;
6438 public override bool Resolve (BlockContext ec)
6440 if (ec.CurrentIterator != null)
6441 Expression.UnsafeInsideIteratorError (ec, loc);
6443 using (ec.Set (ResolveContext.Options.UnsafeScope))
6444 return Block.Resolve (ec);
6447 protected override void DoEmit (EmitContext ec)
6452 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6454 return Block.FlowAnalysis (fc);
6457 public override Reachability MarkReachable (Reachability rc)
6459 base.MarkReachable (rc);
6460 return Block.MarkReachable (rc);
6463 protected override void CloneTo (CloneContext clonectx, Statement t)
6465 Unsafe target = (Unsafe) t;
6467 target.Block = clonectx.LookupBlock (Block);
6470 public override object Accept (StructuralVisitor visitor)
6472 return visitor.Visit (this);
6479 public class Fixed : Statement
6481 abstract class Emitter : ShimExpression
6483 protected LocalVariable vi;
6485 protected Emitter (Expression expr, LocalVariable li)
6491 public abstract void EmitExit (EmitContext ec);
6493 public override void FlowAnalysis (FlowAnalysisContext fc)
6495 expr.FlowAnalysis (fc);
6499 sealed class ExpressionEmitter : Emitter {
6500 public ExpressionEmitter (Expression converted, LocalVariable li)
6501 : base (converted, li)
6505 protected override Expression DoResolve (ResolveContext rc)
6507 throw new NotImplementedException ();
6510 public override void Emit (EmitContext ec) {
6512 // Store pointer in pinned location
6518 public override void EmitExit (EmitContext ec)
6521 ec.Emit (OpCodes.Conv_U);
6526 class StringEmitter : Emitter
6528 LocalVariable pinned_string;
6530 public StringEmitter (Expression expr, LocalVariable li)
6535 protected override Expression DoResolve (ResolveContext rc)
6537 pinned_string = new LocalVariable (vi.Block, "$pinned",
6538 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6540 pinned_string.Type = rc.BuiltinTypes.String;
6543 eclass = ExprClass.Variable;
6544 type = rc.BuiltinTypes.Int;
6548 public override void Emit (EmitContext ec)
6550 pinned_string.CreateBuilder (ec);
6553 pinned_string.EmitAssign (ec);
6555 // TODO: Should use Binary::Add
6556 pinned_string.Emit (ec);
6557 ec.Emit (OpCodes.Conv_I);
6559 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6563 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6564 //pe.InstanceExpression = pinned_string;
6565 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6567 ec.Emit (OpCodes.Add);
6571 public override void EmitExit (EmitContext ec)
6574 pinned_string.EmitAssign (ec);
6578 public class VariableDeclaration : BlockVariable
6580 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6585 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6587 if (!Variable.Type.IsPointer && li == Variable) {
6588 bc.Report.Error (209, TypeExpression.Location,
6589 "The type of locals declared in a fixed statement must be a pointer type");
6593 var res = initializer.Resolve (bc);
6600 var ac = res.Type as ArrayContainer;
6602 TypeSpec array_type = ac.Element;
6605 // Provided that array_type is unmanaged,
6607 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6610 Expression res_init;
6611 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6614 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6615 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6616 res = expr_variable.CreateReferenceExpression (bc, loc);
6620 // and T* is implicitly convertible to the
6621 // pointer type given in the fixed statement.
6623 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6625 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6626 if (converted == null)
6630 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6632 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6633 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6634 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6635 new NullLiteral (loc),
6638 converted = converted.Resolve (bc);
6640 return new ExpressionEmitter (converted, li);
6646 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6647 return new StringEmitter (res, li).Resolve (bc);
6650 // Case 3: fixed buffer
6651 if (res is FixedBufferPtr) {
6652 return new ExpressionEmitter (res, li);
6655 bool already_fixed = true;
6658 // Case 4: & object.
6660 Unary u = res as Unary;
6662 if (u.Oper == Unary.Operator.AddressOf) {
6663 IVariableReference vr = u.Expr as IVariableReference;
6664 if (vr == null || !vr.IsFixed) {
6665 already_fixed = false;
6668 } else if (initializer is Cast) {
6669 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6673 if (already_fixed) {
6674 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6677 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6678 return new ExpressionEmitter (res, li);
6683 VariableDeclaration decl;
6684 Statement statement;
6687 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6696 public Statement Statement {
6702 public BlockVariable Variables {
6710 public override bool Resolve (BlockContext bc)
6712 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6713 if (!decl.Resolve (bc))
6717 return statement.Resolve (bc);
6720 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6722 decl.FlowAnalysis (fc);
6723 return statement.FlowAnalysis (fc);
6726 protected override void DoEmit (EmitContext ec)
6728 decl.Variable.CreateBuilder (ec);
6729 decl.Initializer.Emit (ec);
6730 if (decl.Declarators != null) {
6731 foreach (var d in decl.Declarators) {
6732 d.Variable.CreateBuilder (ec);
6733 d.Initializer.Emit (ec);
6737 statement.Emit (ec);
6743 // Clear the pinned variable
6745 ((Emitter) decl.Initializer).EmitExit (ec);
6746 if (decl.Declarators != null) {
6747 foreach (var d in decl.Declarators) {
6748 ((Emitter)d.Initializer).EmitExit (ec);
6753 public override Reachability MarkReachable (Reachability rc)
6755 base.MarkReachable (rc);
6757 decl.MarkReachable (rc);
6759 rc = statement.MarkReachable (rc);
6761 // TODO: What if there is local exit?
6762 has_ret = rc.IsUnreachable;
6766 protected override void CloneTo (CloneContext clonectx, Statement t)
6768 Fixed target = (Fixed) t;
6770 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6771 target.statement = statement.Clone (clonectx);
6774 public override object Accept (StructuralVisitor visitor)
6776 return visitor.Visit (this);
6780 public class Catch : Statement
6782 class CatchVariableStore : Statement
6784 readonly Catch ctch;
6786 public CatchVariableStore (Catch ctch)
6791 protected override void CloneTo (CloneContext clonectx, Statement target)
6795 protected override void DoEmit (EmitContext ec)
6797 // Emits catch variable debug information inside correct block
6798 ctch.EmitCatchVariableStore (ec);
6801 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6807 class FilterStatement : Statement
6809 readonly Catch ctch;
6811 public FilterStatement (Catch ctch)
6816 protected override void CloneTo (CloneContext clonectx, Statement target)
6820 protected override void DoEmit (EmitContext ec)
6822 if (ctch.li != null) {
6823 if (ctch.hoisted_temp != null)
6824 ctch.hoisted_temp.Emit (ec);
6828 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6829 ec.Emit (OpCodes.Box, ctch.type);
6832 var expr_start = ec.DefineLabel ();
6833 var end = ec.DefineLabel ();
6835 ec.Emit (OpCodes.Brtrue_S, expr_start);
6837 ec.Emit (OpCodes.Br, end);
6838 ec.MarkLabel (expr_start);
6840 ctch.Filter.Emit (ec);
6843 ec.Emit (OpCodes.Endfilter);
6844 ec.BeginFilterHandler ();
6845 ec.Emit (OpCodes.Pop);
6848 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6850 ctch.Filter.FlowAnalysis (fc);
6854 public override bool Resolve (BlockContext bc)
6856 ctch.Filter = ctch.Filter.Resolve (bc);
6858 if (ctch.Filter != null) {
6859 if (ctch.Filter.ContainsEmitWithAwait ()) {
6860 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6863 var c = ctch.Filter as Constant;
6864 if (c != null && !c.IsDefaultValue) {
6865 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6873 ExplicitBlock block;
6875 FullNamedExpression type_expr;
6876 CompilerAssign assign;
6878 LocalTemporary hoisted_temp;
6880 public Catch (ExplicitBlock block, Location loc)
6888 public ExplicitBlock Block {
6894 public TypeSpec CatchType {
6900 public Expression Filter {
6904 public bool IsGeneral {
6906 return type_expr == null;
6910 public FullNamedExpression TypeExpression {
6919 public LocalVariable Variable {
6930 protected override void DoEmit (EmitContext ec)
6932 if (Filter != null) {
6933 ec.BeginExceptionFilterBlock ();
6934 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6936 if (Block.HasAwait) {
6937 Block.EmitScopeInitialization (ec);
6946 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6948 ec.BeginCatchBlock (CatchType);
6951 ec.Emit (OpCodes.Pop);
6953 if (Block.HasAwait) {
6955 EmitCatchVariableStore (ec);
6961 void EmitCatchVariableStore (EmitContext ec)
6963 li.CreateBuilder (ec);
6966 // For hoisted catch variable we have to use a temporary local variable
6967 // for captured variable initialization during storey setup because variable
6968 // needs to be on the stack after storey instance for stfld operation
6970 if (li.HoistedVariant != null) {
6971 hoisted_temp = new LocalTemporary (li.Type);
6972 hoisted_temp.Store (ec);
6974 // switch to assignment from temporary variable and not from top of the stack
6975 assign.UpdateSource (hoisted_temp);
6979 public override bool Resolve (BlockContext bc)
6981 using (bc.Set (ResolveContext.Options.CatchScope)) {
6982 if (type_expr == null) {
6983 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6984 if (!block.HasAwait || Filter != null)
6985 block.AddScopeStatement (new CatchVariableStore (this));
6987 Expression source = new EmptyExpression (li.Type);
6988 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6989 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6992 type = type_expr.ResolveAsType (bc);
6997 CreateExceptionVariable (type);
6999 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
7000 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
7001 } else if (li != null) {
7003 li.PrepareAssignmentAnalysis (bc);
7005 // source variable is at the top of the stack
7006 Expression source = new EmptyExpression (li.Type);
7007 if (li.Type.IsGenericParameter)
7008 source = new UnboxCast (source, li.Type);
7010 if (!block.HasAwait || Filter != null)
7011 block.AddScopeStatement (new CatchVariableStore (this));
7014 // Uses Location.Null to hide from symbol file
7016 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
7017 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
7021 if (Filter != null) {
7022 Block.AddScopeStatement (new FilterStatement (this));
7025 Block.SetCatchBlock ();
7026 return Block.Resolve (bc);
7030 bool CreateExceptionVariable (TypeSpec type)
7032 if (!Block.HasAwait)
7035 // TODO: Scan the block for rethrow expression
7036 //if (!Block.HasRethrow)
7039 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
7043 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7045 if (li != null && !li.IsCompilerGenerated) {
7046 fc.SetVariableAssigned (li.VariableInfo, true);
7049 return block.FlowAnalysis (fc);
7052 public override Reachability MarkReachable (Reachability rc)
7054 base.MarkReachable (rc);
7056 var c = Filter as Constant;
7057 if (c != null && c.IsDefaultValue)
7058 return Reachability.CreateUnreachable ();
7060 return block.MarkReachable (rc);
7063 protected override void CloneTo (CloneContext clonectx, Statement t)
7065 Catch target = (Catch) t;
7067 if (type_expr != null)
7068 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7071 target.Filter = Filter.Clone (clonectx);
7073 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7077 public class TryFinally : TryFinallyBlock
7080 List<DefiniteAssignmentBitSet> try_exit_dat;
7081 List<Tuple<Label, bool>> redirected_jumps;
7082 Label? start_fin_label;
7084 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7090 public ExplicitBlock FinallyBlock {
7096 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7098 if (try_exit_dat == null)
7099 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7101 try_exit_dat.Add (vector);
7104 public override bool Resolve (BlockContext bc)
7106 bool ok = base.Resolve (bc);
7108 fini.SetFinallyBlock ();
7109 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7110 ok &= fini.Resolve (bc);
7116 protected override void EmitTryBody (EmitContext ec)
7118 if (fini.HasAwait) {
7119 if (ec.TryFinallyUnwind == null)
7120 ec.TryFinallyUnwind = new List<TryFinally> ();
7122 ec.TryFinallyUnwind.Add (this);
7125 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7126 ec.EndExceptionBlock ();
7128 ec.TryFinallyUnwind.Remove (this);
7130 if (start_fin_label != null)
7131 ec.MarkLabel (start_fin_label.Value);
7139 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7144 return base.EmitBeginFinallyBlock (ec);
7147 public override void EmitFinallyBody (EmitContext ec)
7149 if (!fini.HasAwait) {
7155 // Emits catch block like
7157 // catch (object temp) {
7158 // this.exception_field = temp;
7161 var type = ec.BuiltinTypes.Object;
7162 ec.BeginCatchBlock (type);
7164 var temp = ec.GetTemporaryLocal (type);
7165 ec.Emit (OpCodes.Stloc, temp);
7167 var exception_field = ec.GetTemporaryField (type);
7168 exception_field.AutomaticallyReuse = false;
7170 ec.Emit (OpCodes.Ldloc, temp);
7171 exception_field.EmitAssignFromStack (ec);
7173 ec.EndExceptionBlock ();
7175 ec.FreeTemporaryLocal (temp, type);
7180 // Emits exception rethrow
7182 // if (this.exception_field != null)
7183 // throw this.exception_field;
7185 exception_field.Emit (ec);
7186 var skip_throw = ec.DefineLabel ();
7187 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7188 exception_field.Emit (ec);
7189 ec.Emit (OpCodes.Throw);
7190 ec.MarkLabel (skip_throw);
7192 exception_field.PrepareCleanup (ec);
7194 EmitUnwindFinallyTable (ec);
7197 bool IsParentBlock (Block block)
7199 for (Block b = fini; b != null; b = b.Parent) {
7207 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7210 if (labelBlock != null) {
7211 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7212 var fin = ec.TryFinallyUnwind [idx - 1];
7213 if (!fin.IsParentBlock (labelBlock))
7220 bool set_return_state = true;
7222 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7223 var fin = ec.TryFinallyUnwind [idx];
7224 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7227 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7228 set_return_state = false;
7230 if (fin.start_fin_label == null) {
7231 fin.start_fin_label = ec.DefineLabel ();
7234 label = fin.start_fin_label.Value;
7240 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7242 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7245 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7247 if (redirected_jumps == null) {
7248 redirected_jumps = new List<Tuple<Label, bool>> ();
7250 // Add fallthrough label
7251 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7254 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7257 int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7259 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7260 index = redirected_jumps.Count - 1;
7264 // Indicates we have captured exit jump
7266 if (setReturnState) {
7267 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7268 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7273 // Emits state table of jumps outside of try block and reload of return
7274 // value when try block returns value
7276 void EmitUnwindFinallyTable (EmitContext ec)
7278 if (redirected_jumps == null)
7281 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7282 initializer.HoistedReturnState.EmitLoad (ec);
7284 var jumps_table = new Label [redirected_jumps.Count];
7285 List<Tuple<Label, Label>> leave_redirect = null;
7286 for (int i = 0; i < jumps_table.Length; ++i) {
7287 var val = redirected_jumps [i];
7290 if (leave_redirect == null)
7291 leave_redirect = new List<Tuple<Label, Label>> ();
7292 var label = ec.DefineLabel ();
7293 leave_redirect.Add (Tuple.Create (label, val.Item1));
7294 jumps_table [i] = label;
7296 jumps_table [i] = val.Item1;
7300 ec.Emit (OpCodes.Switch, jumps_table);
7302 if (leave_redirect != null) {
7303 foreach (var entry in leave_redirect) {
7304 ec.MarkLabel (entry.Item1);
7305 ec.Emit (OpCodes.Leave, entry.Item2);
7309 // Mark fallthrough label
7310 ec.MarkLabel (jumps_table [0]);
7313 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7315 var da = fc.BranchDefiniteAssignment ();
7317 var tf = fc.TryFinally;
7318 fc.TryFinally = this;
7320 var res_stmt = Statement.FlowAnalysis (fc);
7324 var try_da = fc.DefiniteAssignment;
7325 fc.DefiniteAssignment = da;
7327 var res_fin = fini.FlowAnalysis (fc);
7329 if (try_exit_dat != null) {
7331 // try block has global exit but we need to run definite assignment check
7332 // for parameter block out parameter after finally block because it's always
7333 // executed before exit
7335 foreach (var try_da_part in try_exit_dat)
7336 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7338 try_exit_dat = null;
7341 fc.DefiniteAssignment |= try_da;
7342 return res_stmt | res_fin;
7345 public override Reachability MarkReachable (Reachability rc)
7348 // Mark finally block first for any exit statement in try block
7349 // to know whether the code which follows finally is reachable
7351 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7354 protected override void CloneTo (CloneContext clonectx, Statement t)
7356 TryFinally target = (TryFinally) t;
7358 target.stmt = stmt.Clone (clonectx);
7360 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7363 public override object Accept (StructuralVisitor visitor)
7365 return visitor.Visit (this);
7369 public class TryCatch : ExceptionStatement
7372 List<Catch> clauses;
7373 readonly bool inside_try_finally;
7374 List<Catch> catch_sm;
7376 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7380 this.clauses = catch_clauses;
7381 this.inside_try_finally = inside_try_finally;
7384 public List<Catch> Clauses {
7390 public bool HasClauseWithAwait {
7392 return catch_sm != null;
7396 public bool IsTryCatchFinally {
7398 return inside_try_finally;
7402 public override bool Resolve (BlockContext bc)
7406 using (bc.Set (ResolveContext.Options.TryScope)) {
7408 parent_try_block = bc.CurrentTryBlock;
7410 if (IsTryCatchFinally) {
7411 ok = Block.Resolve (bc);
7413 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7414 bc.CurrentTryBlock = this;
7415 ok = Block.Resolve (bc);
7416 bc.CurrentTryBlock = parent_try_block;
7421 var prev_catch = bc.CurrentTryCatch;
7422 bc.CurrentTryCatch = this;
7424 for (int i = 0; i < clauses.Count; ++i) {
7427 ok &= c.Resolve (bc);
7429 if (c.Block.HasAwait) {
7430 if (catch_sm == null)
7431 catch_sm = new List<Catch> ();
7436 if (c.Filter != null)
7439 TypeSpec resolved_type = c.CatchType;
7440 if (resolved_type == null)
7443 for (int ii = 0; ii < clauses.Count; ++ii) {
7447 if (clauses[ii].Filter != null)
7450 if (clauses[ii].IsGeneral) {
7451 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7454 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7457 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7460 bc.Report.Warning (1058, 1, c.loc,
7461 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7469 var ct = clauses[ii].CatchType;
7473 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7474 bc.Report.Error (160, c.loc,
7475 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7476 ct.GetSignatureForError ());
7482 bc.CurrentTryCatch = prev_catch;
7484 return base.Resolve (bc) && ok;
7487 protected sealed override void DoEmit (EmitContext ec)
7489 if (!inside_try_finally)
7490 EmitTryBodyPrepare (ec);
7494 LocalBuilder state_variable = null;
7495 foreach (Catch c in clauses) {
7498 if (catch_sm != null) {
7499 if (state_variable == null) {
7501 // Cannot reuse temp variable because non-catch path assumes the value is 0
7502 // which may not be true for reused local variable
7504 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7507 var index = catch_sm.IndexOf (c);
7511 ec.EmitInt (index + 1);
7512 ec.Emit (OpCodes.Stloc, state_variable);
7516 if (state_variable == null) {
7517 if (!inside_try_finally)
7518 ec.EndExceptionBlock ();
7520 ec.EndExceptionBlock ();
7522 ec.Emit (OpCodes.Ldloc, state_variable);
7524 var labels = new Label [catch_sm.Count + 1];
7525 for (int i = 0; i < labels.Length; ++i) {
7526 labels [i] = ec.DefineLabel ();
7529 var end = ec.DefineLabel ();
7530 ec.Emit (OpCodes.Switch, labels);
7532 // 0 value is default label
7533 ec.MarkLabel (labels [0]);
7534 ec.Emit (OpCodes.Br, end);
7536 var atv = ec.AsyncThrowVariable;
7538 for (int i = 0; i < catch_sm.Count; ++i) {
7539 if (c != null && c.Block.HasReachableClosingBrace)
7540 ec.Emit (OpCodes.Br, end);
7542 ec.MarkLabel (labels [i + 1]);
7545 ec.Emit (OpCodes.Stloc, state_variable);
7548 ec.AsyncThrowVariable = c.Variable;
7551 ec.AsyncThrowVariable = atv;
7557 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7559 var start_fc = fc.BranchDefiniteAssignment ();
7560 var res = Block.FlowAnalysis (fc);
7562 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7564 foreach (var c in clauses) {
7565 fc.BranchDefiniteAssignment (start_fc);
7566 if (!c.FlowAnalysis (fc)) {
7568 try_fc = fc.DefiniteAssignment;
7570 try_fc &= fc.DefiniteAssignment;
7576 fc.DefiniteAssignment = try_fc ?? start_fc;
7577 parent_try_block = null;
7581 public override Reachability MarkReachable (Reachability rc)
7583 if (rc.IsUnreachable)
7586 base.MarkReachable (rc);
7588 var tc_rc = Block.MarkReachable (rc);
7590 foreach (var c in clauses)
7591 tc_rc &= c.MarkReachable (rc);
7596 protected override void CloneTo (CloneContext clonectx, Statement t)
7598 TryCatch target = (TryCatch) t;
7600 target.Block = clonectx.LookupBlock (Block);
7601 if (clauses != null){
7602 target.clauses = new List<Catch> ();
7603 foreach (Catch c in clauses)
7604 target.clauses.Add ((Catch) c.Clone (clonectx));
7608 public override object Accept (StructuralVisitor visitor)
7610 return visitor.Visit (this);
7614 public class Using : TryFinallyBlock
7616 public class VariableDeclaration : BlockVariable
7618 Statement dispose_call;
7620 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7625 public VariableDeclaration (LocalVariable li, Location loc)
7632 public VariableDeclaration (Expression expr)
7635 loc = expr.Location;
7641 public bool IsNested { get; private set; }
7645 public void EmitDispose (EmitContext ec)
7647 dispose_call.Emit (ec);
7650 public override bool Resolve (BlockContext bc)
7655 return base.Resolve (bc, false);
7658 public Expression ResolveExpression (BlockContext bc)
7660 var e = Initializer.Resolve (bc);
7664 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7665 Initializer = ResolveInitializer (bc, Variable, e);
7669 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7671 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7672 initializer = initializer.Resolve (bc);
7673 if (initializer == null)
7676 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7677 Arguments args = new Arguments (1);
7678 args.Add (new Argument (initializer));
7679 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7680 if (initializer == null)
7683 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7684 dispose_call = CreateDisposeCall (bc, var);
7685 dispose_call.Resolve (bc);
7687 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7690 if (li == Variable) {
7691 CheckIDiposableConversion (bc, li, initializer);
7692 dispose_call = CreateDisposeCall (bc, li);
7693 dispose_call.Resolve (bc);
7696 return base.ResolveInitializer (bc, li, initializer);
7699 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7703 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7704 if (type.IsNullableType) {
7705 // it's handled in CreateDisposeCall
7709 if (type != InternalType.ErrorType) {
7710 bc.Report.SymbolRelatedToPreviousError (type);
7711 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7712 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7713 type.GetSignatureForError ());
7720 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7722 var target = bc.BuiltinTypes.IDisposable;
7723 var tp = type as TypeParameterSpec;
7725 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7727 return type.ImplementsInterface (target, false);
7730 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7732 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7734 var loc = lv.Location;
7736 var idt = bc.BuiltinTypes.IDisposable;
7737 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7739 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7740 dispose_mg.InstanceExpression = type.IsNullableType ?
7741 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7745 // Hide it from symbol file via null location
7747 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7749 // Add conditional call when disposing possible null variable
7750 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7751 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7756 public void ResolveDeclaratorInitializer (BlockContext bc)
7758 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7761 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7763 for (int i = declarators.Count - 1; i >= 0; --i) {
7764 var d = declarators [i];
7765 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7766 vd.Initializer = d.Initializer;
7768 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7769 vd.dispose_call.Resolve (bc);
7771 stmt = new Using (vd, stmt, d.Variable.Location);
7778 public override object Accept (StructuralVisitor visitor)
7780 return visitor.Visit (this);
7784 VariableDeclaration decl;
7786 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7792 public Using (Expression expr, Statement stmt, Location loc)
7795 this.decl = new VariableDeclaration (expr);
7800 public Expression Expr {
7802 return decl.Variable == null ? decl.Initializer : null;
7806 public BlockVariable Variables {
7814 public override void Emit (EmitContext ec)
7817 // Don't emit sequence point it will be set on variable declaration
7822 protected override void EmitTryBodyPrepare (EmitContext ec)
7825 base.EmitTryBodyPrepare (ec);
7828 protected override void EmitTryBody (EmitContext ec)
7833 public override void EmitFinallyBody (EmitContext ec)
7835 decl.EmitDispose (ec);
7838 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7840 decl.FlowAnalysis (fc);
7841 return stmt.FlowAnalysis (fc);
7844 public override Reachability MarkReachable (Reachability rc)
7846 decl.MarkReachable (rc);
7847 return base.MarkReachable (rc);
7850 public override bool Resolve (BlockContext ec)
7852 VariableReference vr;
7853 bool vr_locked = false;
7855 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7856 if (decl.Variable == null) {
7857 vr = decl.ResolveExpression (ec) as VariableReference;
7859 vr_locked = vr.IsLockedByStatement;
7860 vr.IsLockedByStatement = true;
7863 if (decl.IsNested) {
7864 decl.ResolveDeclaratorInitializer (ec);
7866 if (!decl.Resolve (ec))
7869 if (decl.Declarators != null) {
7870 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7878 var ok = base.Resolve (ec);
7881 vr.IsLockedByStatement = vr_locked;
7886 protected override void CloneTo (CloneContext clonectx, Statement t)
7888 Using target = (Using) t;
7890 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7891 target.stmt = stmt.Clone (clonectx);
7894 public override object Accept (StructuralVisitor visitor)
7896 return visitor.Visit (this);
7901 /// Implementation of the foreach C# statement
7903 public class Foreach : LoopStatement
7905 abstract class IteratorStatement : Statement
7907 protected readonly Foreach for_each;
7909 protected IteratorStatement (Foreach @foreach)
7911 this.for_each = @foreach;
7912 this.loc = @foreach.expr.Location;
7915 protected override void CloneTo (CloneContext clonectx, Statement target)
7917 throw new NotImplementedException ();
7920 public override void Emit (EmitContext ec)
7922 if (ec.EmitAccurateDebugInfo) {
7923 ec.Emit (OpCodes.Nop);
7929 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7931 throw new NotImplementedException ();
7935 sealed class ArrayForeach : IteratorStatement
7937 TemporaryVariableReference[] lengths;
7938 Expression [] length_exprs;
7939 StatementExpression[] counter;
7940 TemporaryVariableReference[] variables;
7942 TemporaryVariableReference copy;
7944 public ArrayForeach (Foreach @foreach, int rank)
7947 counter = new StatementExpression[rank];
7948 variables = new TemporaryVariableReference[rank];
7949 length_exprs = new Expression [rank];
7952 // Only use temporary length variables when dealing with
7953 // multi-dimensional arrays
7956 lengths = new TemporaryVariableReference [rank];
7959 public override bool Resolve (BlockContext ec)
7961 Block variables_block = for_each.variable.Block;
7962 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7965 int rank = length_exprs.Length;
7966 Arguments list = new Arguments (rank);
7967 for (int i = 0; i < rank; i++) {
7968 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7970 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7971 counter[i].Resolve (ec);
7974 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7976 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7977 lengths[i].Resolve (ec);
7979 Arguments args = new Arguments (1);
7980 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7981 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7984 list.Add (new Argument (v));
7987 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7992 if (for_each.type is VarExpr) {
7993 // Infer implicitly typed local variable from foreach array type
7994 var_type = access.Type;
7996 var_type = for_each.type.ResolveAsType (ec);
7998 if (var_type == null)
8001 access = Convert.ExplicitConversion (ec, access, var_type, loc);
8006 for_each.variable.Type = var_type;
8008 var prev_block = ec.CurrentBlock;
8009 ec.CurrentBlock = variables_block;
8010 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
8011 ec.CurrentBlock = prev_block;
8013 if (variable_ref == null)
8016 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
8018 return for_each.body.Resolve (ec);
8021 protected override void DoEmit (EmitContext ec)
8023 copy.EmitAssign (ec, for_each.expr);
8025 int rank = length_exprs.Length;
8026 Label[] test = new Label [rank];
8027 Label[] loop = new Label [rank];
8029 for (int i = 0; i < rank; i++) {
8030 test [i] = ec.DefineLabel ();
8031 loop [i] = ec.DefineLabel ();
8033 if (lengths != null)
8034 lengths [i].EmitAssign (ec, length_exprs [i]);
8037 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
8038 for (int i = 0; i < rank; i++) {
8039 variables [i].EmitAssign (ec, zero);
8041 ec.Emit (OpCodes.Br, test [i]);
8042 ec.MarkLabel (loop [i]);
8045 for_each.body.Emit (ec);
8047 ec.MarkLabel (ec.LoopBegin);
8048 ec.Mark (for_each.expr.Location);
8050 for (int i = rank - 1; i >= 0; i--){
8051 counter [i].Emit (ec);
8053 ec.MarkLabel (test [i]);
8054 variables [i].Emit (ec);
8056 if (lengths != null)
8057 lengths [i].Emit (ec);
8059 length_exprs [i].Emit (ec);
8061 ec.Emit (OpCodes.Blt, loop [i]);
8064 ec.MarkLabel (ec.LoopEnd);
8068 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8070 class RuntimeDispose : Using.VariableDeclaration
8072 public RuntimeDispose (LocalVariable lv, Location loc)
8078 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8080 // Defered to runtime check
8083 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8085 var idt = bc.BuiltinTypes.IDisposable;
8088 // Fabricates code like
8090 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8093 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8095 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8096 dispose_variable.CreateReferenceExpression (bc, loc),
8097 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8098 loc), new NullLiteral (loc));
8100 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8102 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8103 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8105 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8106 return new If (idisaposable_test, dispose, loc);
8110 LocalVariable variable;
8112 Statement statement;
8113 ExpressionStatement init;
8114 TemporaryVariableReference enumerator_variable;
8115 bool ambiguous_getenumerator_name;
8117 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8120 this.variable = var;
8124 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8126 rc.Report.SymbolRelatedToPreviousError (enumerator);
8127 rc.Report.Error (202, loc,
8128 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8129 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8132 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8135 // Option 1: Try to match by name GetEnumerator first
8137 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8138 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8140 var mg = mexpr as MethodGroupExpr;
8142 mg.InstanceExpression = expr;
8143 Arguments args = new Arguments (0);
8144 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8146 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8147 if (ambiguous_getenumerator_name)
8150 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8156 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8159 PredefinedMember<MethodSpec> iface_candidate = null;
8160 var ptypes = rc.Module.PredefinedTypes;
8161 var gen_ienumerable = ptypes.IEnumerableGeneric;
8162 if (!gen_ienumerable.Define ())
8163 gen_ienumerable = null;
8165 var ifaces = t.Interfaces;
8166 if (ifaces != null) {
8167 foreach (var iface in ifaces) {
8168 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8169 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8170 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8171 rc.Report.Error (1640, loc,
8172 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8173 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8178 // TODO: Cache this somehow
8179 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8180 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8185 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8186 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8191 if (iface_candidate == null) {
8192 if (expr.Type != InternalType.ErrorType) {
8193 rc.Report.Error (1579, loc,
8194 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8195 expr.Type.GetSignatureForError (), "GetEnumerator");
8201 var method = iface_candidate.Resolve (loc);
8205 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8206 mg.InstanceExpression = expr;
8210 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8212 var ms = MemberCache.FindMember (enumerator.ReturnType,
8213 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8214 BindingRestriction.InstanceOnly) as MethodSpec;
8216 if (ms == null || !ms.IsPublic) {
8217 Error_WrongEnumerator (rc, enumerator);
8221 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8224 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8226 var ps = MemberCache.FindMember (enumerator.ReturnType,
8227 MemberFilter.Property ("Current", null),
8228 BindingRestriction.InstanceOnly) as PropertySpec;
8230 if (ps == null || !ps.IsPublic) {
8231 Error_WrongEnumerator (rc, enumerator);
8238 public override bool Resolve (BlockContext ec)
8240 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8243 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8244 } else if (expr.Type.IsNullableType) {
8245 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8248 var get_enumerator_mg = ResolveGetEnumerator (ec);
8249 if (get_enumerator_mg == null) {
8253 var get_enumerator = get_enumerator_mg.BestCandidate;
8254 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8255 enumerator_variable.Resolve (ec);
8257 // Prepare bool MoveNext ()
8258 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8259 if (move_next_mg == null) {
8263 move_next_mg.InstanceExpression = enumerator_variable;
8265 // Prepare ~T~ Current { get; }
8266 var current_prop = ResolveCurrent (ec, get_enumerator);
8267 if (current_prop == null) {
8271 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8272 if (current_pe == null)
8275 VarExpr ve = for_each.type as VarExpr;
8279 // Source type is dynamic, set element type to dynamic too
8280 variable.Type = ec.BuiltinTypes.Dynamic;
8282 // Infer implicitly typed local variable from foreach enumerable type
8283 variable.Type = current_pe.Type;
8287 // Explicit cast of dynamic collection elements has to be done at runtime
8288 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8291 variable.Type = for_each.type.ResolveAsType (ec);
8293 if (variable.Type == null)
8296 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8297 if (current_pe == null)
8301 var prev_block = ec.CurrentBlock;
8302 ec.CurrentBlock = for_each.variable.Block;
8303 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8304 ec.CurrentBlock = prev_block;
8305 if (variable_ref == null)
8308 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8310 var init = new Invocation.Predefined (get_enumerator_mg, null);
8312 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8313 for_each.body, Location.Null);
8315 var enum_type = enumerator_variable.Type;
8318 // Add Dispose method call when enumerator can be IDisposable
8320 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8321 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8323 // Runtime Dispose check
8325 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8326 vd.Initializer = init;
8327 statement = new Using (vd, statement, Location.Null);
8330 // No Dispose call needed
8332 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8333 this.init.Resolve (ec);
8337 // Static Dispose check
8339 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8340 vd.Initializer = init;
8341 statement = new Using (vd, statement, Location.Null);
8344 return statement.Resolve (ec);
8347 protected override void DoEmit (EmitContext ec)
8349 enumerator_variable.LocalInfo.CreateBuilder (ec);
8352 init.EmitStatement (ec);
8354 statement.Emit (ec);
8357 #region IErrorHandler Members
8359 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8361 ec.Report.SymbolRelatedToPreviousError (best);
8362 ec.Report.Warning (278, 2, expr.Location,
8363 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8364 expr.Type.GetSignatureForError (), "enumerable",
8365 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8367 ambiguous_getenumerator_name = true;
8371 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8376 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8381 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8390 LocalVariable variable;
8394 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8398 this.variable = var;
8404 public Expression Expr {
8405 get { return expr; }
8408 public Expression TypeExpression {
8409 get { return type; }
8412 public LocalVariable Variable {
8413 get { return variable; }
8416 public override Reachability MarkReachable (Reachability rc)
8418 base.MarkReachable (rc);
8420 body.MarkReachable (rc);
8425 public override bool Resolve (BlockContext ec)
8427 expr = expr.Resolve (ec);
8432 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8436 body.AddStatement (Statement);
8438 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8439 Statement = new ArrayForeach (this, 1);
8440 } else if (expr.Type is ArrayContainer) {
8441 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8443 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8444 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8445 expr.ExprClassName);
8449 Statement = new CollectionForeach (this, variable, expr);
8452 return base.Resolve (ec);
8455 protected override void DoEmit (EmitContext ec)
8457 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8458 ec.LoopBegin = ec.DefineLabel ();
8459 ec.LoopEnd = ec.DefineLabel ();
8461 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8462 body.Explicit.DisableDebugScopeIndex ();
8464 variable.CreateBuilder (ec);
8466 Statement.Emit (ec);
8470 ec.LoopBegin = old_begin;
8471 ec.LoopEnd = old_end;
8474 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8476 expr.FlowAnalysis (fc);
8478 var da = fc.BranchDefiniteAssignment ();
8479 body.FlowAnalysis (fc);
8480 fc.DefiniteAssignment = da;
8484 protected override void CloneTo (CloneContext clonectx, Statement t)
8486 Foreach target = (Foreach) t;
8488 target.type = type.Clone (clonectx);
8489 target.expr = expr.Clone (clonectx);
8490 target.body = (Block) body.Clone (clonectx);
8491 target.Statement = Statement.Clone (clonectx);
8494 public override object Accept (StructuralVisitor visitor)
8496 return visitor.Visit (this);
8500 class SentinelStatement: Statement
8502 protected override void CloneTo (CloneContext clonectx, Statement target)
8506 protected override void DoEmit (EmitContext ec)
8508 var l = ec.DefineLabel ();
8510 ec.Emit (OpCodes.Br_S, l);
8513 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8515 throw new NotImplementedException ();