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);
778 Condition?.EmitPrepare (ec);
781 ec.MarkLabel (ec.LoopBegin);
786 // If test is null, there is no test, and we are just
789 if (Condition != null) {
790 ec.Mark (Condition.Location);
793 // The Resolve code already catches the case for
794 // Test == Constant (false) so we know that
797 if (Condition is Constant) {
798 Condition.EmitSideEffect (ec);
799 ec.Emit (OpCodes.Br, loop);
801 Condition.EmitBranchable (ec, loop, true);
805 ec.Emit (OpCodes.Br, loop);
806 ec.MarkLabel (ec.LoopEnd);
808 ec.LoopBegin = old_begin;
809 ec.LoopEnd = old_end;
812 protected override void CloneTo (CloneContext clonectx, Statement t)
814 For target = (For) t;
816 if (Initializer != null)
817 target.Initializer = Initializer.Clone (clonectx);
818 if (Condition != null)
819 target.Condition = Condition.Clone (clonectx);
820 if (Iterator != null)
821 target.Iterator = Iterator.Clone (clonectx);
822 target.Statement = Statement.Clone (clonectx);
825 public override object Accept (StructuralVisitor visitor)
827 return visitor.Visit (this);
830 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
835 if (end_reachable_das == null)
836 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
838 end_reachable_das.Add (fc.DefiniteAssignment);
841 public override void SetEndReachable ()
843 end_reachable = true;
846 public override void SetIteratorReachable ()
848 iterator_reachable = true;
852 public abstract class LoopStatement : Statement
854 protected LoopStatement (Statement statement)
856 Statement = statement;
859 public Statement Statement { get; set; }
861 public override bool Resolve (BlockContext bc)
863 var prev_loop = bc.EnclosingLoop;
864 var prev_los = bc.EnclosingLoopOrSwitch;
865 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
866 var ok = Statement.Resolve (bc);
867 bc.EnclosingLoopOrSwitch = prev_los;
868 bc.EnclosingLoop = prev_loop;
874 // Needed by possibly infinite loops statements (for, while) and switch statment
876 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
880 public virtual void SetEndReachable ()
884 public virtual void SetIteratorReachable ()
889 public class StatementExpression : Statement
891 ExpressionStatement expr;
893 public StatementExpression (ExpressionStatement expr)
896 loc = expr.StartLocation;
899 public StatementExpression (ExpressionStatement expr, Location loc)
905 public ExpressionStatement Expr {
911 protected override void CloneTo (CloneContext clonectx, Statement t)
913 StatementExpression target = (StatementExpression) t;
914 target.expr = (ExpressionStatement) expr.Clone (clonectx);
917 protected override void DoEmit (EmitContext ec)
919 expr.EmitStatement (ec);
922 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
924 expr.FlowAnalysis (fc);
928 public override Reachability MarkReachable (Reachability rc)
930 base.MarkReachable (rc);
931 expr.MarkReachable (rc);
935 public override bool Resolve (BlockContext ec)
937 expr = expr.ResolveStatement (ec);
941 public override object Accept (StructuralVisitor visitor)
943 return visitor.Visit (this);
947 public class StatementErrorExpression : Statement
951 public StatementErrorExpression (Expression expr)
954 this.loc = expr.StartLocation;
957 public Expression Expr {
963 public override bool Resolve (BlockContext bc)
965 expr.Error_InvalidExpressionStatement (bc);
969 protected override void DoEmit (EmitContext ec)
971 throw new NotSupportedException ();
974 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
979 protected override void CloneTo (CloneContext clonectx, Statement target)
981 var t = (StatementErrorExpression) target;
983 t.expr = expr.Clone (clonectx);
986 public override object Accept (StructuralVisitor visitor)
988 return visitor.Visit (this);
993 // Simple version of statement list not requiring a block
995 public class StatementList : Statement
997 List<Statement> statements;
999 public StatementList (Statement first, Statement second)
1001 statements = new List<Statement> { first, second };
1005 public IList<Statement> Statements {
1012 public void Add (Statement statement)
1014 statements.Add (statement);
1017 public override bool Resolve (BlockContext ec)
1019 foreach (var s in statements)
1025 protected override void DoEmit (EmitContext ec)
1027 foreach (var s in statements)
1031 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1033 foreach (var s in statements)
1034 s.FlowAnalysis (fc);
1039 public override Reachability MarkReachable (Reachability rc)
1041 base.MarkReachable (rc);
1043 Reachability res = rc;
1044 foreach (var s in statements)
1045 res = s.MarkReachable (rc);
1050 protected override void CloneTo (CloneContext clonectx, Statement target)
1052 StatementList t = (StatementList) target;
1054 t.statements = new List<Statement> (statements.Count);
1055 foreach (Statement s in statements)
1056 t.statements.Add (s.Clone (clonectx));
1059 public override object Accept (StructuralVisitor visitor)
1061 return visitor.Visit (this);
1066 // For statements which require special handling when inside try or catch block
1068 public abstract class ExitStatement : Statement
1070 protected bool unwind_protect;
1072 protected abstract bool DoResolve (BlockContext bc);
1073 protected abstract bool IsLocalExit { get; }
1075 public override bool Resolve (BlockContext bc)
1077 var res = DoResolve (bc);
1081 // We are inside finally scope but is it the scope we are exiting
1083 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1085 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1086 if (b.IsFinallyBlock) {
1087 Error_FinallyClauseExit (bc);
1091 if (b is ParametersBlock)
1097 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1101 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1106 if (fc.TryFinally != null) {
1107 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1109 fc.ParametersBlock.CheckControlExit (fc);
1117 /// Implements the return statement
1119 public class Return : ExitStatement
1124 public Return (Expression expr, Location l)
1132 public Expression Expr {
1141 protected override bool IsLocalExit {
1149 protected override bool DoResolve (BlockContext ec)
1151 var block_return_type = ec.ReturnType;
1154 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1158 // Return must not be followed by an expression when
1159 // the method return type is Task
1161 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1162 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1163 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1165 // Extra trick not to emit ret/leave inside awaiter body
1167 expr = EmptyExpression.Null;
1171 if (storey.ReturnType.IsGenericTask)
1172 block_return_type = storey.ReturnType.TypeArguments[0];
1175 if (ec.CurrentIterator != null) {
1176 Error_ReturnFromIterator (ec);
1177 } else if (block_return_type != InternalType.ErrorType) {
1178 ec.Report.Error (126, loc,
1179 "An object of a type convertible to `{0}' is required for the return statement",
1180 block_return_type.GetSignatureForError ());
1186 expr = expr.Resolve (ec);
1188 AnonymousExpression am = ec.CurrentAnonymousMethod;
1190 if (block_return_type.Kind == MemberKind.Void) {
1191 ec.Report.Error (127, loc,
1192 "`{0}': A return keyword must not be followed by any expression when method returns void",
1193 ec.GetSignatureForError ());
1198 if (am.IsIterator) {
1199 Error_ReturnFromIterator (ec);
1203 var async_block = am as AsyncInitializer;
1204 if (async_block != null) {
1206 var storey = (AsyncTaskStorey) am.Storey;
1207 var async_type = storey.ReturnType;
1209 if (async_type == null && async_block.ReturnTypeInference != null) {
1210 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1211 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1213 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1217 if (async_type.Kind == MemberKind.Void) {
1218 ec.Report.Error (8030, loc,
1219 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1223 if (!async_type.IsGenericTask) {
1224 if (this is ContextualReturn)
1227 if (async_block.DelegateType != null) {
1228 ec.Report.Error (8031, loc,
1229 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1231 ec.Report.Error (1997, loc,
1232 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1233 ec.GetSignatureForError ());
1239 // The return type is actually Task<T> type argument
1241 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1242 ec.Report.Error (4016, loc,
1243 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1244 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1246 block_return_type = async_type.TypeArguments[0];
1250 if (block_return_type.Kind == MemberKind.Void) {
1251 ec.Report.Error (8030, loc,
1252 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1256 var l = am as AnonymousMethodBody;
1257 if (l != null && expr != null) {
1258 if (l.ReturnTypeInference != null) {
1259 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1264 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1265 // unexpected debugging experience
1267 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1268 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1277 if (expr is ReferenceExpression && block_return_type.Kind != MemberKind.ByRef) {
1278 ec.Report.Error (8149, loc, "By-reference returns can only be used in methods that return by reference");
1282 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1283 if (block_return_type.Kind == MemberKind.ByRef) {
1284 var ref_expr = Expr as ReferenceExpression;
1285 if (ref_expr == null) {
1286 ec.Report.Error (8150, loc, "By-reference return is required when method returns by reference");
1290 var byref_return = (ReferenceContainer)block_return_type;
1292 if (expr.Type != byref_return.Element) {
1293 ec.Report.Error (8151, loc, "The return by reference expression must be of type `{0}' because this method returns by reference",
1294 byref_return.GetSignatureForError ());
1299 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1302 if (am != null && block_return_type == ec.ReturnType) {
1303 ec.Report.Error (1662, loc,
1304 "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",
1305 am.ContainerType, am.GetSignatureForError ());
1315 protected override void DoEmit (EmitContext ec)
1319 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1320 if (async_body != null) {
1321 var storey = (AsyncTaskStorey)async_body.Storey;
1322 Label exit_label = async_body.BodyEnd;
1325 // It's null for await without async
1327 if (storey.HoistedReturnValue != null) {
1329 // Special case hoisted return value (happens in try/finally scenario)
1331 if (ec.TryFinallyUnwind != null) {
1332 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1335 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1336 async_return.EmitAssign (ec, expr, false, false);
1341 if (ec.TryFinallyUnwind != null)
1342 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body, unwind_protect);
1345 ec.Emit (OpCodes.Leave, exit_label);
1352 if (unwind_protect || ec.EmitAccurateDebugInfo)
1353 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1356 if (unwind_protect) {
1357 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1358 } else if (ec.EmitAccurateDebugInfo) {
1359 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1361 ec.Emit (OpCodes.Ret);
1365 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1368 expr.FlowAnalysis (fc);
1371 base.DoFlowAnalysis (fc);
1376 void Error_ReturnFromIterator (ResolveContext rc)
1378 rc.Report.Error (1622, loc,
1379 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1382 public override Reachability MarkReachable (Reachability rc)
1384 base.MarkReachable (rc);
1387 rc = Expr.MarkReachable (rc);
1388 expr_returns = rc.IsUnreachable;
1391 return Reachability.CreateUnreachable ();
1394 protected override void CloneTo (CloneContext clonectx, Statement t)
1396 Return target = (Return) t;
1397 // It's null for simple return;
1399 target.expr = expr.Clone (clonectx);
1402 public override object Accept (StructuralVisitor visitor)
1404 return visitor.Visit (this);
1408 public class Goto : ExitStatement
1411 LabeledStatement label;
1412 TryFinally try_finally;
1414 public Goto (string label, Location l)
1420 public string Target {
1421 get { return target; }
1424 protected override bool IsLocalExit {
1430 protected override bool DoResolve (BlockContext bc)
1432 label = bc.CurrentBlock.LookupLabel (target);
1433 if (label == null) {
1434 Error_UnknownLabel (bc, target, loc);
1438 try_finally = bc.CurrentTryBlock as TryFinally;
1440 CheckExitBoundaries (bc, label.Block);
1445 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1447 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1451 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1453 // Goto to unreachable label
1457 if (fc.AddReachedLabel (label))
1460 label.Block.ScanGotoJump (label, fc);
1464 public override Reachability MarkReachable (Reachability rc)
1466 if (rc.IsUnreachable)
1469 base.MarkReachable (rc);
1471 if (try_finally != null) {
1472 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1473 label.AddGotoReference (rc);
1478 label.AddGotoReference (rc);
1481 return Reachability.CreateUnreachable ();
1484 protected override void CloneTo (CloneContext clonectx, Statement target)
1489 protected override void DoEmit (EmitContext ec)
1491 // This should only happen for goto from try block to unrechable label
1495 Label l = label.LabelTarget (ec);
1497 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1498 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1499 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block, unwind_protect);
1502 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1505 bool IsLeavingFinally (Block labelBlock)
1507 var b = try_finally.Statement as Block;
1509 if (b == labelBlock)
1518 public override object Accept (StructuralVisitor visitor)
1520 return visitor.Visit (this);
1524 public class LabeledStatement : Statement {
1531 public LabeledStatement (string name, Block block, Location l)
1538 public Label LabelTarget (EmitContext ec)
1543 label = ec.DefineLabel ();
1548 public Block Block {
1554 public string Name {
1555 get { return name; }
1558 protected override void CloneTo (CloneContext clonectx, Statement target)
1560 var t = (LabeledStatement) target;
1562 t.block = clonectx.RemapBlockCopy (block);
1565 public override bool Resolve (BlockContext bc)
1570 protected override void DoEmit (EmitContext ec)
1573 ec.MarkLabel (label);
1576 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1579 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1585 public override Reachability MarkReachable (Reachability rc)
1587 base.MarkReachable (rc);
1590 rc = new Reachability ();
1595 public void AddGotoReference (Reachability rc)
1603 block.ScanGotoJump (this);
1606 public override object Accept (StructuralVisitor visitor)
1608 return visitor.Visit (this);
1614 /// `goto default' statement
1616 public class GotoDefault : SwitchGoto
1618 public GotoDefault (Location l)
1623 public override bool Resolve (BlockContext bc)
1625 if (bc.Switch == null) {
1626 Error_GotoCaseRequiresSwitchBlock (bc);
1630 bc.Switch.RegisterGotoCase (null, null);
1636 protected override void DoEmit (EmitContext ec)
1638 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1641 public override Reachability MarkReachable (Reachability rc)
1643 if (!rc.IsUnreachable) {
1644 var label = switch_statement.DefaultLabel;
1645 if (label.IsUnreachable) {
1646 label.MarkReachable (rc);
1647 switch_statement.Block.ScanGotoJump (label);
1651 return base.MarkReachable (rc);
1654 public override object Accept (StructuralVisitor visitor)
1656 return visitor.Visit (this);
1661 /// `goto case' statement
1663 public class GotoCase : SwitchGoto
1667 public GotoCase (Expression e, Location l)
1673 public Expression Expr {
1679 public SwitchLabel Label { get; set; }
1681 public override bool Resolve (BlockContext ec)
1683 if (ec.Switch == null) {
1684 Error_GotoCaseRequiresSwitchBlock (ec);
1688 Constant c = expr.ResolveLabelConstant (ec);
1694 if (ec.Switch.IsNullable && c is NullLiteral) {
1697 TypeSpec type = ec.Switch.SwitchType;
1698 res = c.Reduce (ec, type);
1700 c.Error_ValueCannotBeConverted (ec, type, true);
1704 if (!Convert.ImplicitStandardConversionExists (c, type))
1705 ec.Report.Warning (469, 2, loc,
1706 "The `goto case' value is not implicitly convertible to type `{0}'",
1707 type.GetSignatureForError ());
1711 ec.Switch.RegisterGotoCase (this, res);
1718 protected override void DoEmit (EmitContext ec)
1720 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1723 protected override void CloneTo (CloneContext clonectx, Statement t)
1725 GotoCase target = (GotoCase) t;
1727 target.expr = expr.Clone (clonectx);
1730 public override Reachability MarkReachable (Reachability rc)
1732 if (!rc.IsUnreachable) {
1733 var label = switch_statement.FindLabel ((Constant) expr);
1734 if (label.IsUnreachable) {
1735 label.MarkReachable (rc);
1736 switch_statement.Block.ScanGotoJump (label);
1740 return base.MarkReachable (rc);
1743 public override object Accept (StructuralVisitor visitor)
1745 return visitor.Visit (this);
1749 public abstract class SwitchGoto : Statement
1751 protected bool unwind_protect;
1752 protected Switch switch_statement;
1754 protected SwitchGoto (Location loc)
1759 protected override void CloneTo (CloneContext clonectx, Statement target)
1764 public override bool Resolve (BlockContext bc)
1766 CheckExitBoundaries (bc, bc.Switch.Block);
1768 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1769 switch_statement = bc.Switch;
1774 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1779 public override Reachability MarkReachable (Reachability rc)
1781 base.MarkReachable (rc);
1782 return Reachability.CreateUnreachable ();
1785 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1787 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1791 public class Throw : Statement {
1794 public Throw (Expression expr, Location l)
1800 public Expression Expr {
1806 public static Expression ConvertType (ResolveContext rc, Expression expr)
1808 var et = rc.BuiltinTypes.Exception;
1809 if (Convert.ImplicitConversionExists (rc, expr, et))
1810 expr = Convert.ImplicitConversion (rc, expr, et, expr.Location);
1812 rc.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1813 expr = EmptyCast.Create (expr, et);
1819 public override bool Resolve (BlockContext ec)
1822 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1823 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1824 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1825 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1826 if (b.IsFinallyBlock) {
1827 ec.Report.Error (724, loc,
1828 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1837 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1842 expr = ConvertType (ec, expr);
1847 protected override void DoEmit (EmitContext ec)
1850 var atv = ec.AsyncThrowVariable;
1852 if (atv.HoistedVariant != null) {
1853 atv.HoistedVariant.Emit (ec);
1858 ec.Emit (OpCodes.Throw);
1860 ec.Emit (OpCodes.Rethrow);
1865 ec.Emit (OpCodes.Throw);
1869 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1872 expr.FlowAnalysis (fc);
1877 public override Reachability MarkReachable (Reachability rc)
1879 base.MarkReachable (rc);
1880 return Reachability.CreateUnreachable ();
1883 protected override void CloneTo (CloneContext clonectx, Statement t)
1885 Throw target = (Throw) t;
1888 target.expr = expr.Clone (clonectx);
1891 public override object Accept (StructuralVisitor visitor)
1893 return visitor.Visit (this);
1897 public class Break : LocalExitStatement
1899 public Break (Location l)
1904 public override object Accept (StructuralVisitor visitor)
1906 return visitor.Visit (this);
1909 protected override void DoEmit (EmitContext ec)
1913 if (ec.TryFinallyUnwind != null) {
1914 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1915 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1918 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1921 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1923 enclosing_loop.AddEndDefiniteAssignment (fc);
1927 protected override bool DoResolve (BlockContext bc)
1929 enclosing_loop = bc.EnclosingLoopOrSwitch;
1930 return base.DoResolve (bc);
1933 public override Reachability MarkReachable (Reachability rc)
1935 base.MarkReachable (rc);
1937 if (!rc.IsUnreachable)
1938 enclosing_loop.SetEndReachable ();
1940 return Reachability.CreateUnreachable ();
1944 public class Continue : LocalExitStatement
1946 public Continue (Location l)
1951 public override object Accept (StructuralVisitor visitor)
1953 return visitor.Visit (this);
1957 protected override void DoEmit (EmitContext ec)
1959 var l = ec.LoopBegin;
1961 if (ec.TryFinallyUnwind != null) {
1962 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1963 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block, unwind_protect);
1966 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1969 protected override bool DoResolve (BlockContext bc)
1971 enclosing_loop = bc.EnclosingLoop;
1972 return base.DoResolve (bc);
1975 public override Reachability MarkReachable (Reachability rc)
1977 base.MarkReachable (rc);
1979 if (!rc.IsUnreachable)
1980 enclosing_loop.SetIteratorReachable ();
1982 return Reachability.CreateUnreachable ();
1986 public abstract class LocalExitStatement : ExitStatement
1988 protected LoopStatement enclosing_loop;
1990 protected LocalExitStatement (Location loc)
1995 protected override bool IsLocalExit {
2001 protected override void CloneTo (CloneContext clonectx, Statement t)
2006 protected override bool DoResolve (BlockContext bc)
2008 if (enclosing_loop == null) {
2009 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
2013 var block = enclosing_loop.Statement as Block;
2015 // Don't need to do extra checks for simple statements loops
2016 if (block != null) {
2017 CheckExitBoundaries (bc, block);
2024 public interface ILocalVariable
2026 void Emit (EmitContext ec);
2027 void EmitAssign (EmitContext ec);
2028 void EmitAddressOf (EmitContext ec);
2031 public interface INamedBlockVariable
2033 Block Block { get; }
2034 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
2035 bool IsDeclared { get; }
2036 bool IsParameter { get; }
2037 Location Location { get; }
2040 public class BlockVariableDeclarator
2043 Expression initializer;
2045 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2047 if (li.Type != null)
2048 throw new ArgumentException ("Expected null variable type");
2051 this.initializer = initializer;
2056 public LocalVariable Variable {
2062 public Expression Initializer {
2067 initializer = value;
2073 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2075 var t = (BlockVariableDeclarator) MemberwiseClone ();
2076 if (initializer != null)
2077 t.initializer = initializer.Clone (cloneCtx);
2083 public class BlockVariable : Statement
2085 Expression initializer;
2086 protected FullNamedExpression type_expr;
2087 protected LocalVariable li;
2088 protected List<BlockVariableDeclarator> declarators;
2091 public BlockVariable (FullNamedExpression type, LocalVariable li)
2093 this.type_expr = type;
2095 this.loc = type_expr.Location;
2098 protected BlockVariable (LocalVariable li)
2105 public List<BlockVariableDeclarator> Declarators {
2111 public Expression Initializer {
2116 initializer = value;
2120 public FullNamedExpression TypeExpression {
2126 public LocalVariable Variable {
2134 public void AddDeclarator (BlockVariableDeclarator decl)
2136 if (declarators == null)
2137 declarators = new List<BlockVariableDeclarator> ();
2139 declarators.Add (decl);
2142 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2144 if (bc.Report.Errors != 0)
2147 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2149 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2150 new MemberName (li.Name, li.Location), null);
2152 container.AddField (f);
2155 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2159 public override bool Resolve (BlockContext bc)
2161 return Resolve (bc, true);
2164 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2166 if (type == null && !li.IsCompilerGenerated) {
2167 var vexpr = type_expr as VarExpr;
2170 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2171 // same name exists or as a keyword when no type was found
2173 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2174 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2175 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2178 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2182 if (li.IsConstant) {
2183 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2187 if (Initializer == null) {
2188 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2192 if (declarators != null) {
2193 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2197 Initializer = Initializer.Resolve (bc);
2198 if (Initializer != null) {
2199 ((VarExpr) type_expr).InferType (bc, Initializer);
2200 type = type_expr.Type;
2202 // Set error type to indicate the var was placed correctly but could
2205 // var a = missing ();
2207 type = InternalType.ErrorType;
2212 type = ResolveTypeExpression (bc);
2216 if (li.IsConstant && !type.IsConstantCompatible) {
2217 Const.Error_InvalidConstantType (type, loc, bc.Report);
2222 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2227 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2229 CreateEvaluatorVariable (bc, li);
2230 } else if (type != InternalType.ErrorType) {
2231 li.PrepareAssignmentAnalysis (bc);
2234 if (initializer != null) {
2236 if (!(initializer is ReferenceExpression)) {
2237 bc.Report.Error (8172, loc, "Cannot initialize a by-reference variable `{0}' with a value", li.Name);
2241 if (bc.CurrentAnonymousMethod is AsyncInitializer) {
2242 bc.Report.Error (8177, loc, "Async methods cannot use by-reference variables");
2243 } else if (bc.CurrentIterator != null) {
2244 bc.Report.Error (8176, loc, "Iterators cannot use by-reference variables");
2248 if (initializer is ReferenceExpression) {
2249 bc.Report.Error (8171, loc, "Cannot initialize a by-value variable `{0}' with a reference expression", li.Name);
2254 initializer = ResolveInitializer (bc, li, initializer);
2255 // li.Variable.DefinitelyAssigned
2258 if (declarators != null) {
2259 foreach (var d in declarators) {
2260 d.Variable.Type = li.Type;
2262 CreateEvaluatorVariable (bc, d.Variable);
2263 } else if (type != InternalType.ErrorType) {
2264 d.Variable.PrepareAssignmentAnalysis (bc);
2267 if (d.Initializer != null && resolveDeclaratorInitializers) {
2268 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2269 // d.Variable.DefinitelyAssigned
2277 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2279 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2280 return a.ResolveStatement (bc);
2283 protected virtual TypeSpec ResolveTypeExpression (BlockContext bc)
2285 return type_expr.ResolveAsType (bc);
2288 protected override void DoEmit (EmitContext ec)
2290 li.CreateBuilder (ec);
2292 if (Initializer != null && !IsUnreachable)
2293 ((ExpressionStatement) Initializer).EmitStatement (ec);
2295 if (declarators != null) {
2296 foreach (var d in declarators) {
2297 d.Variable.CreateBuilder (ec);
2298 if (d.Initializer != null && !IsUnreachable) {
2299 ec.Mark (d.Variable.Location);
2300 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2306 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2308 if (Initializer != null)
2309 Initializer.FlowAnalysis (fc);
2311 if (declarators != null) {
2312 foreach (var d in declarators) {
2313 if (d.Initializer != null)
2314 d.Initializer.FlowAnalysis (fc);
2321 public override Reachability MarkReachable (Reachability rc)
2323 base.MarkReachable (rc);
2324 return initializer == null ? rc : initializer.MarkReachable (rc);
2327 protected override void CloneTo (CloneContext clonectx, Statement target)
2329 BlockVariable t = (BlockVariable) target;
2331 if (type_expr != null)
2332 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2334 if (initializer != null)
2335 t.initializer = initializer.Clone (clonectx);
2337 if (declarators != null) {
2338 t.declarators = null;
2339 foreach (var d in declarators)
2340 t.AddDeclarator (d.Clone (clonectx));
2344 public override object Accept (StructuralVisitor visitor)
2346 return visitor.Visit (this);
2350 public class BlockConstant : BlockVariable
2352 public BlockConstant (FullNamedExpression type, LocalVariable li)
2357 public override void Emit (EmitContext ec)
2359 if (!Variable.IsUsed)
2360 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2362 // Nothing to emit, not even sequence point
2365 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2367 initializer = initializer.Resolve (bc);
2368 if (initializer == null)
2371 var c = initializer as Constant;
2373 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2377 c = c.ConvertImplicitly (li.Type);
2379 if (TypeSpec.IsReferenceType (li.Type))
2380 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2382 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2387 li.ConstantValue = c;
2391 public override object Accept (StructuralVisitor visitor)
2393 return visitor.Visit (this);
2398 // The information about a user-perceived local variable
2400 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2407 AddressTaken = 1 << 2,
2408 CompilerGenerated = 1 << 3,
2410 ForeachVariable = 1 << 5,
2411 FixedVariable = 1 << 6,
2412 UsingVariable = 1 << 7,
2414 SymbolFileHidden = 1 << 9,
2417 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2421 readonly string name;
2422 readonly Location loc;
2423 readonly Block block;
2425 Constant const_value;
2427 public VariableInfo VariableInfo;
2428 HoistedVariable hoisted_variant;
2430 LocalBuilder builder;
2432 public LocalVariable (Block block, string name, Location loc)
2439 public LocalVariable (Block block, string name, Flags flags, Location loc)
2440 : this (block, name, loc)
2446 // Used by variable declarators
2448 public LocalVariable (LocalVariable li, string name, Location loc)
2449 : this (li.block, name, li.flags, loc)
2455 public bool AddressTaken {
2457 return (flags & Flags.AddressTaken) != 0;
2461 public Block Block {
2467 public Constant ConstantValue {
2472 const_value = value;
2477 // Hoisted local variable variant
2479 public HoistedVariable HoistedVariant {
2481 return hoisted_variant;
2484 hoisted_variant = value;
2488 public bool Created {
2490 return builder != null;
2494 public bool IsDeclared {
2496 return type != null;
2500 public bool IsByRef => (flags & Flags.ByRef) != 0;
2502 public bool IsCompilerGenerated {
2504 return (flags & Flags.CompilerGenerated) != 0;
2508 public bool IsConstant {
2510 return (flags & Flags.Constant) != 0;
2514 public bool IsLocked {
2516 return (flags & Flags.IsLocked) != 0;
2519 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2523 public bool IsThis {
2525 return (flags & Flags.IsThis) != 0;
2529 public bool IsUsed {
2531 return (flags & Flags.Used) != 0;
2535 public bool IsFixed {
2537 return (flags & Flags.FixedVariable) != 0;
2540 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2544 bool INamedBlockVariable.IsParameter {
2550 public bool IsReadonly {
2552 return (flags & Flags.ReadonlyMask) != 0;
2556 public Location Location {
2562 public string Name {
2568 public TypeSpec Type {
2579 public void CreateBuilder (EmitContext ec)
2581 if ((flags & Flags.Used) == 0) {
2582 if (VariableInfo == null) {
2583 // Missing flow analysis or wrong variable flags
2584 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2587 if (VariableInfo.IsEverAssigned)
2588 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2590 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2593 if (HoistedVariant != null)
2596 if (builder != null) {
2597 if ((flags & Flags.CompilerGenerated) != 0)
2600 // To avoid Used warning duplicates
2601 throw new InternalErrorException ("Already created variable `{0}'", name);
2605 builder = ec.DeclareLocal (ReferenceContainer.MakeType (ec.Module, Type), IsFixed);
2608 // All fixed variabled are pinned, a slot has to be alocated
2610 builder = ec.DeclareLocal(Type, IsFixed);
2613 if ((flags & Flags.SymbolFileHidden) == 0)
2614 ec.DefineLocalVariable (name, builder);
2617 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2619 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2620 if (!writeToSymbolFile)
2621 li.flags |= Flags.SymbolFileHidden;
2627 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2629 if (IsConstant && const_value != null) {
2631 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2634 return new LocalVariableReference (this, loc);
2637 public void Emit (EmitContext ec)
2639 // TODO: Need something better for temporary variables
2640 if ((flags & Flags.CompilerGenerated) != 0)
2643 ec.Emit (OpCodes.Ldloc, builder);
2646 public void EmitAssign (EmitContext ec)
2648 // TODO: Need something better for temporary variables
2649 if ((flags & Flags.CompilerGenerated) != 0)
2652 ec.Emit (OpCodes.Stloc, builder);
2655 public void EmitAddressOf (EmitContext ec)
2657 // TODO: Need something better for temporary variables
2658 if ((flags & Flags.CompilerGenerated) != 0)
2662 ec.Emit (OpCodes.Ldloc, builder);
2664 ec.Emit (OpCodes.Ldloca, builder);
2667 public static string GetCompilerGeneratedName (Block block)
2669 // HACK: Debugger depends on the name semantics
2670 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2673 public string GetReadOnlyContext ()
2675 switch (flags & Flags.ReadonlyMask) {
2676 case Flags.FixedVariable:
2677 return "fixed variable";
2678 case Flags.ForeachVariable:
2679 return "foreach iteration variable";
2680 case Flags.UsingVariable:
2681 return "using variable";
2684 throw new InternalErrorException ("Variable is not readonly");
2687 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2689 if (VariableInfo == null)
2690 throw new Exception ();
2692 if (IsAssigned (fc))
2695 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2698 public bool IsAssigned (FlowAnalysisContext fc)
2700 return fc.IsDefinitelyAssigned (VariableInfo);
2703 public void PrepareAssignmentAnalysis (BlockContext bc)
2706 // No need to run assignment analysis for these guys
2708 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2711 VariableInfo = VariableInfo.Create (bc, this);
2715 // Mark the variables as referenced in the user code
2717 public void SetIsUsed ()
2719 flags |= Flags.Used;
2722 public void SetHasAddressTaken ()
2724 flags |= (Flags.AddressTaken | Flags.Used);
2727 public override string ToString ()
2729 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2734 /// Block represents a C# block.
2738 /// This class is used in a number of places: either to represent
2739 /// explicit blocks that the programmer places or implicit blocks.
2741 /// Implicit blocks are used as labels or to introduce variable
2744 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2745 /// they contain extra information that is not necessary on normal blocks.
2747 public class Block : Statement {
2754 HasCapturedVariable = 64,
2755 HasCapturedThis = 1 << 7,
2756 IsExpressionTree = 1 << 8,
2757 CompilerGenerated = 1 << 9,
2758 HasAsyncModifier = 1 << 10,
2760 YieldBlock = 1 << 12,
2761 AwaitBlock = 1 << 13,
2762 FinallyBlock = 1 << 14,
2763 CatchBlock = 1 << 15,
2764 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2766 NoFlowAnalysis = 1 << 21,
2767 InitializationEmitted = 1 << 22
2770 public Block Parent;
2771 public Location StartLocation;
2772 public Location EndLocation;
2774 public ExplicitBlock Explicit;
2775 public ParametersBlock ParametersBlock;
2777 protected Flags flags;
2780 // The statements in this block
2782 protected List<Statement> statements;
2784 protected List<Statement> scope_initializers;
2786 int? resolving_init_idx;
2792 public int ID = id++;
2794 static int clone_id_counter;
2798 // int assignable_slots;
2800 public Block (Block parent, Location start, Location end)
2801 : this (parent, 0, start, end)
2805 public Block (Block parent, Flags flags, Location start, Location end)
2807 if (parent != null) {
2808 // the appropriate constructors will fixup these fields
2809 ParametersBlock = parent.ParametersBlock;
2810 Explicit = parent.Explicit;
2813 this.Parent = parent;
2815 this.StartLocation = start;
2816 this.EndLocation = end;
2818 statements = new List<Statement> (4);
2820 this.original = this;
2825 public Block Original {
2834 public bool IsCompilerGenerated {
2835 get { return (flags & Flags.CompilerGenerated) != 0; }
2836 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2840 public bool IsCatchBlock {
2842 return (flags & Flags.CatchBlock) != 0;
2846 public bool IsFinallyBlock {
2848 return (flags & Flags.FinallyBlock) != 0;
2852 public bool Unchecked {
2853 get { return (flags & Flags.Unchecked) != 0; }
2854 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2857 public bool Unsafe {
2858 get { return (flags & Flags.Unsafe) != 0; }
2859 set { flags |= Flags.Unsafe; }
2862 public List<Statement> Statements {
2863 get { return statements; }
2868 public void SetEndLocation (Location loc)
2873 public void AddLabel (LabeledStatement target)
2875 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2878 public void AddLocalName (LocalVariable li)
2880 AddLocalName (li.Name, li);
2883 public virtual void AddLocalName (string name, INamedBlockVariable li, bool canShadowChildrenBlockName = false)
2885 ParametersBlock.TopBlock.AddLocalName (name, li, canShadowChildrenBlockName);
2888 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2890 if (reason == null) {
2891 Error_AlreadyDeclared (name, variable);
2895 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2896 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2897 "to `{0}', which is already used in a `{1}' scope to denote something else",
2901 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2903 var pi = variable as ParametersBlock.ParameterInfo;
2905 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2907 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2908 "A local variable named `{0}' is already defined in this scope", name);
2912 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2914 ParametersBlock.TopBlock.Report.Error (412, loc,
2915 "The type parameter name `{0}' is the same as local variable or parameter name",
2920 // It should be used by expressions which require to
2921 // register a statement during resolve process.
2923 public void AddScopeStatement (Statement s)
2925 if (scope_initializers == null)
2926 scope_initializers = new List<Statement> ();
2929 // Simple recursive helper, when resolve scope initializer another
2930 // new scope initializer can be added, this ensures it's initialized
2931 // before existing one. For now this can happen with expression trees
2932 // in base ctor initializer only
2934 if (resolving_init_idx.HasValue) {
2935 scope_initializers.Insert (resolving_init_idx.Value, s);
2936 ++resolving_init_idx;
2938 scope_initializers.Add (s);
2942 public void InsertStatement (int index, Statement s)
2944 statements.Insert (index, s);
2947 public void AddStatement (Statement s)
2952 public LabeledStatement LookupLabel (string name)
2954 return ParametersBlock.GetLabel (name, this);
2957 public override Reachability MarkReachable (Reachability rc)
2959 if (rc.IsUnreachable)
2962 MarkReachableScope (rc);
2964 foreach (var s in statements) {
2965 rc = s.MarkReachable (rc);
2966 if (rc.IsUnreachable) {
2967 if ((flags & Flags.ReachableEnd) != 0)
2968 return new Reachability ();
2974 flags |= Flags.ReachableEnd;
2979 public void MarkReachableScope (Reachability rc)
2981 base.MarkReachable (rc);
2983 if (scope_initializers != null) {
2984 foreach (var si in scope_initializers)
2985 si.MarkReachable (rc);
2989 public override bool Resolve (BlockContext bc)
2991 if ((flags & Flags.Resolved) != 0)
2994 Block prev_block = bc.CurrentBlock;
2995 bc.CurrentBlock = this;
2998 // Compiler generated scope statements
3000 if (scope_initializers != null) {
3001 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
3002 scope_initializers[resolving_init_idx.Value].Resolve (bc);
3005 resolving_init_idx = null;
3009 int statement_count = statements.Count;
3010 for (int ix = 0; ix < statement_count; ix++){
3011 Statement s = statements [ix];
3013 if (!s.Resolve (bc)) {
3015 statements [ix] = new EmptyStatement (s.loc);
3020 bc.CurrentBlock = prev_block;
3022 flags |= Flags.Resolved;
3026 protected override void DoEmit (EmitContext ec)
3028 for (int ix = 0; ix < statements.Count; ix++){
3029 statements [ix].Emit (ec);
3033 public override void Emit (EmitContext ec)
3035 if (scope_initializers != null)
3036 EmitScopeInitializers (ec);
3041 protected void EmitScopeInitializers (EmitContext ec)
3043 foreach (Statement s in scope_initializers)
3047 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3049 if (scope_initializers != null) {
3050 foreach (var si in scope_initializers)
3051 si.FlowAnalysis (fc);
3054 return DoFlowAnalysis (fc, 0);
3057 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
3059 bool end_unreachable = !reachable;
3060 bool goto_flow_analysis = startIndex != 0;
3061 for (; startIndex < statements.Count; ++startIndex) {
3062 var s = statements[startIndex];
3064 end_unreachable = s.FlowAnalysis (fc);
3065 if (s.IsUnreachable) {
3066 statements [startIndex] = RewriteUnreachableStatement (s);
3071 // Statement end reachability is needed mostly due to goto support. Consider
3080 // X label is reachable only via goto not as another statement after if. We need
3081 // this for flow-analysis only to carry variable info correctly.
3083 if (end_unreachable) {
3084 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3086 var f = s as TryFinally;
3087 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3089 // Special case for try-finally with unreachable code after
3090 // finally block. Try block has to include leave opcode but there is
3091 // no label to leave to after unreachable finally block closing
3092 // brace. This sentinel ensures there is always IL instruction to
3093 // leave to even if we know it'll never be reached.
3095 statements.Insert (startIndex + 1, new SentinelStatement ());
3097 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3098 s = statements [startIndex];
3099 if (s is SwitchLabel) {
3100 if (!after_goto_case)
3101 s.FlowAnalysis (fc);
3106 if (s.IsUnreachable) {
3107 s.FlowAnalysis (fc);
3108 statements [startIndex] = RewriteUnreachableStatement (s);
3114 // Idea is to stop after goto case because goto case will always have at least same
3115 // variable assigned as switch case label. This saves a lot for complex goto case tests
3117 if (after_goto_case)
3123 var lb = s as LabeledStatement;
3124 if (lb != null && fc.AddReachedLabel (lb))
3129 // The condition should be true unless there is forward jumping goto
3131 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3134 return !Explicit.HasReachableClosingBrace;
3137 static Statement RewriteUnreachableStatement (Statement s)
3139 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3140 // analysis. Even csc report unreachable warning for it but it's actually used hence
3141 // we try to emulate this behaviour
3149 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3152 return new EmptyStatement (s.loc);
3155 public void ScanGotoJump (Statement label)
3158 for (i = 0; i < statements.Count; ++i) {
3159 if (statements[i] == label)
3163 var rc = new Reachability ();
3164 for (++i; i < statements.Count; ++i) {
3165 var s = statements[i];
3166 rc = s.MarkReachable (rc);
3167 if (rc.IsUnreachable)
3171 flags |= Flags.ReachableEnd;
3174 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3177 for (i = 0; i < statements.Count; ++i) {
3178 if (statements[i] == label)
3182 DoFlowAnalysis (fc, ++i);
3186 public override string ToString ()
3188 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3192 protected override void CloneTo (CloneContext clonectx, Statement t)
3194 Block target = (Block) t;
3196 target.clone_id = ++clone_id_counter;
3199 clonectx.AddBlockMap (this, target);
3200 if (original != this)
3201 clonectx.AddBlockMap (original, target);
3203 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3204 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3207 target.Parent = clonectx.RemapBlockCopy (Parent);
3209 target.statements = new List<Statement> (statements.Count);
3210 foreach (Statement s in statements)
3211 target.statements.Add (s.Clone (clonectx));
3214 public override object Accept (StructuralVisitor visitor)
3216 return visitor.Visit (this);
3220 public class ExplicitBlock : Block
3222 protected AnonymousMethodStorey am_storey;
3223 int debug_scope_index;
3225 public ExplicitBlock (Block parent, Location start, Location end)
3226 : this (parent, (Flags) 0, start, end)
3230 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3231 : base (parent, flags, start, end)
3233 this.Explicit = this;
3238 public AnonymousMethodStorey AnonymousMethodStorey {
3244 public bool HasAwait {
3246 return (flags & Flags.AwaitBlock) != 0;
3250 public bool HasCapturedThis {
3252 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3255 return (flags & Flags.HasCapturedThis) != 0;
3260 // Used to indicate that the block has reference to parent
3261 // block and cannot be made static when defining anonymous method
3263 public bool HasCapturedVariable {
3265 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3268 return (flags & Flags.HasCapturedVariable) != 0;
3272 public bool HasReachableClosingBrace {
3274 return (flags & Flags.ReachableEnd) != 0;
3277 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3281 public bool HasYield {
3283 return (flags & Flags.YieldBlock) != 0;
3290 // Creates anonymous method storey in current block
3292 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3295 // Return same story for iterator and async blocks unless we are
3296 // in nested anonymous method
3298 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3299 return ec.CurrentAnonymousMethod.Storey;
3301 if (am_storey == null) {
3302 MemberBase mc = ec.MemberContext as MemberBase;
3305 // Creates anonymous method storey for this block
3307 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3313 public void EmitScopeInitialization (EmitContext ec)
3315 if ((flags & Flags.InitializationEmitted) != 0)
3318 if (am_storey != null) {
3319 DefineStoreyContainer (ec, am_storey);
3320 am_storey.EmitStoreyInstantiation (ec, this);
3323 if (scope_initializers != null)
3324 EmitScopeInitializers (ec);
3326 flags |= Flags.InitializationEmitted;
3329 public override void Emit (EmitContext ec)
3331 // TODO: It's needed only when scope has variable (normal or lifted)
3332 var scopeIndex = GetDebugSymbolScopeIndex ();
3333 if (scopeIndex > 0) {
3334 ec.BeginScope (scopeIndex);
3337 EmitScopeInitialization (ec);
3339 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3340 ec.Emit (OpCodes.Nop);
3348 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3349 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3350 ec.Emit (OpCodes.Nop);
3354 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3356 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3357 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3358 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3362 // Creates anonymous method storey
3364 storey.CreateContainer ();
3365 storey.DefineContainer ();
3366 storey.ExpandBaseInterfaces ();
3368 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3371 // Only first storey in path will hold this reference. All children blocks will
3372 // reference it indirectly using $ref field
3374 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3375 if (b.Parent != null) {
3376 var s = b.Parent.Explicit.AnonymousMethodStorey;
3378 storey.HoistedThis = s.HoistedThis;
3383 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3384 if (storey.HoistedThis == null)
3385 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3387 if (storey.HoistedThis != null)
3393 // We are the first storey on path and 'this' has to be hoisted
3395 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3396 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3398 // ThisReferencesFromChildrenBlock holds all reference even if they
3399 // are not on this path. It saves some memory otherwise it'd have to
3400 // be in every explicit block. We run this check to see if the reference
3401 // is valid for this storey
3403 Block block_on_path = ref_block;
3404 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3406 if (block_on_path == null)
3409 if (storey.HoistedThis == null) {
3410 storey.AddCapturedThisField (ec, null);
3413 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3415 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3417 if (b_storey != null) {
3419 // Don't add storey cross reference for `this' when the storey ends up not
3420 // beeing attached to any parent
3422 if (b.ParametersBlock.StateMachine == null) {
3423 AnonymousMethodStorey s = null;
3424 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3425 s = ab.Explicit.AnonymousMethodStorey;
3430 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3432 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3433 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3440 // Stop propagation inside same top block
3442 if (b.ParametersBlock == ParametersBlock.Original) {
3443 b_storey.AddParentStoreyReference (ec, storey);
3444 // b_storey.HoistedThis = storey.HoistedThis;
3448 b = pb = b.ParametersBlock;
3450 pb = b as ParametersBlock;
3453 if (pb != null && pb.StateMachine != null) {
3454 if (pb.StateMachine == storey)
3458 // If we are state machine with no parent. We can hook into parent without additional
3459 // reference and capture this directly
3461 ExplicitBlock parent_storey_block = pb;
3462 while (parent_storey_block.Parent != null) {
3463 parent_storey_block = parent_storey_block.Parent.Explicit;
3464 if (parent_storey_block.AnonymousMethodStorey != null) {
3469 if (parent_storey_block.AnonymousMethodStorey == null) {
3470 if (pb.StateMachine.HoistedThis == null) {
3471 pb.StateMachine.AddCapturedThisField (ec, null);
3472 b.HasCapturedThis = true;
3478 var parent_this_block = pb;
3479 while (parent_this_block.Parent != null) {
3480 parent_this_block = parent_this_block.Parent.ParametersBlock;
3481 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3487 // Add reference to closest storey which holds captured this
3489 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3493 // Add parent storey reference only when this is not captured directly
3495 if (b_storey != null) {
3496 b_storey.AddParentStoreyReference (ec, storey);
3497 b_storey.HoistedThis = storey.HoistedThis;
3504 var ref_blocks = storey.ReferencesFromChildrenBlock;
3505 if (ref_blocks != null) {
3506 foreach (ExplicitBlock ref_block in ref_blocks) {
3507 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3508 if (b.AnonymousMethodStorey != null) {
3509 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3512 // Stop propagation inside same top block
3514 if (b.ParametersBlock == ParametersBlock.Original)
3517 b = b.ParametersBlock;
3520 var pb = b as ParametersBlock;
3521 if (pb != null && pb.StateMachine != null) {
3522 if (pb.StateMachine == storey)
3525 pb.StateMachine.AddParentStoreyReference (ec, storey);
3528 b.HasCapturedVariable = true;
3534 storey.PrepareEmit ();
3535 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3538 public void DisableDebugScopeIndex ()
3540 debug_scope_index = -1;
3543 public virtual int GetDebugSymbolScopeIndex ()
3545 if (debug_scope_index == 0)
3546 debug_scope_index = ++ParametersBlock.debug_scope_index;
3548 return debug_scope_index;
3551 public void RegisterAsyncAwait ()
3554 while ((block.flags & Flags.AwaitBlock) == 0) {
3555 block.flags |= Flags.AwaitBlock;
3557 if (block is ParametersBlock)
3560 block = block.Parent.Explicit;
3564 public void RegisterIteratorYield ()
3566 ParametersBlock.TopBlock.IsIterator = true;
3569 while ((block.flags & Flags.YieldBlock) == 0) {
3570 block.flags |= Flags.YieldBlock;
3572 if (block.Parent == null)
3575 block = block.Parent.Explicit;
3579 public void SetCatchBlock ()
3581 flags |= Flags.CatchBlock;
3584 public void SetFinallyBlock ()
3586 flags |= Flags.FinallyBlock;
3589 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3591 tryBlock.statements = statements;
3592 statements = new List<Statement> (1);
3593 statements.Add (tf);
3598 // ParametersBlock was introduced to support anonymous methods
3599 // and lambda expressions
3601 public class ParametersBlock : ExplicitBlock
3603 public class ParameterInfo : INamedBlockVariable
3605 readonly ParametersBlock block;
3607 public VariableInfo VariableInfo;
3610 public ParameterInfo (ParametersBlock block, int index)
3618 public ParametersBlock Block {
3624 Block INamedBlockVariable.Block {
3630 public bool IsDeclared {
3636 public bool IsParameter {
3642 public bool IsLocked {
3651 public Location Location {
3653 return Parameter.Location;
3657 public Parameter Parameter {
3659 return block.Parameters [index];
3663 public TypeSpec ParameterType {
3665 return Parameter.Type;
3671 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3673 return new ParameterReference (this, loc);
3678 // Block is converted into an expression
3680 sealed class BlockScopeExpression : Expression
3683 readonly ParametersBlock block;
3685 public BlockScopeExpression (Expression child, ParametersBlock block)
3691 public override bool ContainsEmitWithAwait ()
3693 return child.ContainsEmitWithAwait ();
3696 public override Expression CreateExpressionTree (ResolveContext ec)
3698 throw new NotSupportedException ();
3701 protected override Expression DoResolve (ResolveContext ec)
3706 child = child.Resolve (ec);
3710 eclass = child.eclass;
3715 public override void Emit (EmitContext ec)
3717 block.EmitScopeInitializers (ec);
3722 protected ParametersCompiled parameters;
3723 protected ParameterInfo[] parameter_info;
3724 protected bool resolved;
3725 protected ToplevelBlock top_block;
3726 protected StateMachine state_machine;
3727 protected Dictionary<string, object> labels;
3729 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3730 : base (parent, 0, start, start)
3732 if (parameters == null)
3733 throw new ArgumentNullException ("parameters");
3735 this.parameters = parameters;
3736 ParametersBlock = this;
3738 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3740 this.top_block = parent.ParametersBlock.top_block;
3741 ProcessParameters ();
3744 protected ParametersBlock (ParametersCompiled parameters, Location start)
3745 : base (null, 0, start, start)
3747 if (parameters == null)
3748 throw new ArgumentNullException ("parameters");
3750 this.parameters = parameters;
3751 ParametersBlock = this;
3755 // It's supposed to be used by method body implementation of anonymous methods
3757 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3758 : base (null, 0, source.StartLocation, source.EndLocation)
3760 this.parameters = parameters;
3761 this.statements = source.statements;
3762 this.scope_initializers = source.scope_initializers;
3764 this.resolved = true;
3765 this.reachable = source.reachable;
3766 this.am_storey = source.am_storey;
3767 this.state_machine = source.state_machine;
3768 this.flags = source.flags & Flags.ReachableEnd;
3770 ParametersBlock = this;
3773 // Overwrite original for comparison purposes when linking cross references
3774 // between anonymous methods
3776 Original = source.Original;
3781 public bool HasReferenceToStoreyForInstanceLambdas {
3783 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3786 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3790 public bool IsAsync {
3792 return (flags & Flags.HasAsyncModifier) != 0;
3795 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3800 // Block has been converted to expression tree
3802 public bool IsExpressionTree {
3804 return (flags & Flags.IsExpressionTree) != 0;
3809 // The parameters for the block.
3811 public ParametersCompiled Parameters {
3817 public StateMachine StateMachine {
3819 return state_machine;
3823 public ToplevelBlock TopBlock {
3832 public bool Resolved {
3834 return (flags & Flags.Resolved) != 0;
3838 public int TemporaryLocalsCount { get; set; }
3843 // Checks whether all `out' parameters have been assigned.
3845 public void CheckControlExit (FlowAnalysisContext fc)
3847 CheckControlExit (fc, fc.DefiniteAssignment);
3850 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3852 if (parameter_info == null)
3855 foreach (var p in parameter_info) {
3856 if (p.VariableInfo == null)
3859 if (p.VariableInfo.IsAssigned (dat))
3862 fc.Report.Error (177, p.Location,
3863 "The out parameter `{0}' must be assigned to before control leaves the current method",
3868 protected override void CloneTo (CloneContext clonectx, Statement t)
3870 base.CloneTo (clonectx, t);
3872 var target = (ParametersBlock) t;
3875 // Clone label statements as well as they contain block reference
3879 if (pb.labels != null) {
3880 target.labels = new Dictionary<string, object> ();
3882 foreach (var entry in pb.labels) {
3883 var list = entry.Value as List<LabeledStatement>;
3886 var list_clone = new List<LabeledStatement> ();
3887 foreach (var lentry in list) {
3888 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3891 target.labels.Add (entry.Key, list_clone);
3893 var labeled = (LabeledStatement) entry.Value;
3894 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3901 if (pb.Parent == null)
3904 pb = pb.Parent.ParametersBlock;
3908 public override Expression CreateExpressionTree (ResolveContext ec)
3910 if (statements.Count == 1) {
3911 Expression expr = statements[0].CreateExpressionTree (ec);
3912 if (scope_initializers != null)
3913 expr = new BlockScopeExpression (expr, this);
3918 return base.CreateExpressionTree (ec);
3921 public override void Emit (EmitContext ec)
3923 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3924 DefineStoreyContainer (ec, state_machine);
3925 state_machine.EmitStoreyInstantiation (ec, this);
3931 public void EmitEmbedded (EmitContext ec)
3933 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3934 DefineStoreyContainer (ec, state_machine);
3935 state_machine.EmitStoreyInstantiation (ec, this);
3941 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3943 var res = base.DoFlowAnalysis (fc);
3945 if (HasReachableClosingBrace)
3946 CheckControlExit (fc);
3951 public override int GetDebugSymbolScopeIndex ()
3956 public LabeledStatement GetLabel (string name, Block block)
3959 // Cloned parameters blocks can have their own cloned version of top-level labels
3961 if (labels == null) {
3963 return Parent.ParametersBlock.GetLabel (name, block);
3969 if (!labels.TryGetValue (name, out value)) {
3973 var label = value as LabeledStatement;
3975 if (label != null) {
3976 if (IsLabelVisible (label, b))
3980 List<LabeledStatement> list = (List<LabeledStatement>) value;
3981 for (int i = 0; i < list.Count; ++i) {
3983 if (IsLabelVisible (label, b))
3991 static bool IsLabelVisible (LabeledStatement label, Block b)
3994 if (label.Block == b)
3997 } while (b != null);
4002 public ParameterInfo GetParameterInfo (Parameter p)
4004 for (int i = 0; i < parameters.Count; ++i) {
4005 if (parameters[i] == p)
4006 return parameter_info[i];
4009 throw new ArgumentException ("Invalid parameter");
4012 public ParameterReference GetParameterReference (int index, Location loc)
4014 return new ParameterReference (parameter_info[index], loc);
4017 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
4019 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
4021 CloneContext clonectx = new CloneContext ();
4022 return Clone (clonectx);
4025 protected void ProcessParameters ()
4027 if (parameters.Count == 0)
4030 parameter_info = new ParameterInfo[parameters.Count];
4031 for (int i = 0; i < parameter_info.Length; ++i) {
4032 var p = parameters.FixedParameters[i];
4036 // TODO: Should use Parameter only and more block there
4037 parameter_info[i] = new ParameterInfo (this, i);
4039 AddLocalName (p.Name, parameter_info[i]);
4043 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
4045 var src = stmt.Block;
4048 // Cannot remap label block if the label was not yet cloned which
4049 // can happen in case of anonymous method inside anoynymous method
4050 // with a label. But in this case we don't care because goto cannot
4051 // jump of out anonymous method
4053 if (src.ParametersBlock != this)
4056 var src_stmts = src.Statements;
4057 for (int i = 0; i < src_stmts.Count; ++i) {
4058 if (src_stmts[i] == stmt)
4059 return (LabeledStatement) dst.Statements[i];
4062 throw new InternalErrorException ("Should never be reached");
4065 public override bool Resolve (BlockContext bc)
4067 // TODO: if ((flags & Flags.Resolved) != 0)
4074 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
4075 flags |= Flags.IsExpressionTree;
4078 PrepareAssignmentAnalysis (bc);
4080 if (!base.Resolve (bc))
4083 } catch (Exception e) {
4084 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4087 if (bc.CurrentBlock != null) {
4088 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4090 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4095 // If an asynchronous body of F is either an expression classified as nothing, or a
4096 // statement block where no return statements have expressions, the inferred return type is Task
4099 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4100 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4101 am.ReturnTypeInference = null;
4102 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4110 void PrepareAssignmentAnalysis (BlockContext bc)
4112 for (int i = 0; i < parameters.Count; ++i) {
4113 var par = parameters.FixedParameters[i];
4115 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4118 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4122 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4124 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4125 var stateMachine = new IteratorStorey (iterator);
4127 state_machine = stateMachine;
4128 iterator.SetStateMachine (stateMachine);
4130 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4131 tlb.Original = this;
4132 tlb.state_machine = stateMachine;
4133 tlb.AddStatement (new Return (iterator, iterator.Location));
4137 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4139 for (int i = 0; i < parameters.Count; i++) {
4140 Parameter p = parameters[i];
4141 Parameter.Modifier mod = p.ModFlags;
4142 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4143 host.Compiler.Report.Error (1988, p.Location,
4144 "Async methods cannot have ref or out parameters");
4148 if (p is ArglistParameter) {
4149 host.Compiler.Report.Error (4006, p.Location,
4150 "__arglist is not allowed in parameter list of async methods");
4154 if (parameters.Types[i].IsPointer) {
4155 host.Compiler.Report.Error (4005, p.Location,
4156 "Async methods cannot have unsafe parameters");
4162 host.Compiler.Report.Warning (1998, 1, loc,
4163 "Async block lacks `await' operator and will run synchronously");
4166 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4167 var initializer = new AsyncInitializer (this, host, block_type);
4168 initializer.Type = block_type;
4169 initializer.DelegateType = delegateType;
4171 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4173 state_machine = stateMachine;
4174 initializer.SetStateMachine (stateMachine);
4176 const Flags flags = Flags.CompilerGenerated;
4178 var b = this is ToplevelBlock ?
4179 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4180 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4183 b.state_machine = stateMachine;
4184 b.AddStatement (new AsyncInitializerStatement (initializer));
4192 public class ToplevelBlock : ParametersBlock
4194 LocalVariable this_variable;
4195 CompilerContext compiler;
4196 Dictionary<string, object> names;
4198 List<ExplicitBlock> this_references;
4200 public ToplevelBlock (CompilerContext ctx, Location loc)
4201 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4205 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4206 : base (parameters, start)
4208 this.compiler = ctx;
4212 ProcessParameters ();
4216 // Recreates a top level block from parameters block. Used for
4217 // compiler generated methods where the original block comes from
4218 // explicit child block. This works for already resolved blocks
4219 // only to ensure we resolve them in the correct flow order
4221 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4222 : base (source, parameters)
4224 this.compiler = source.TopBlock.compiler;
4228 public bool IsIterator {
4230 return (flags & Flags.Iterator) != 0;
4233 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4237 public Report Report {
4239 return compiler.Report;
4244 // Used by anonymous blocks to track references of `this' variable
4246 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4248 return this_references;
4253 // Returns the "this" instance variable of this block.
4254 // See AddThisVariable() for more information.
4256 public LocalVariable ThisVariable {
4258 return this_variable;
4262 public override void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4265 names = new Dictionary<string, object> ();
4268 if (!names.TryGetValue (name, out value)) {
4269 names.Add (name, li);
4273 INamedBlockVariable existing = value as INamedBlockVariable;
4274 List<INamedBlockVariable> existing_list;
4275 if (existing != null) {
4276 existing_list = new List<INamedBlockVariable> ();
4277 existing_list.Add (existing);
4278 names[name] = existing_list;
4280 existing_list = (List<INamedBlockVariable>) value;
4284 // A collision checking between local names
4286 var variable_block = li.Block.Explicit;
4287 for (int i = 0; i < existing_list.Count; ++i) {
4288 existing = existing_list[i];
4289 Block b = existing.Block.Explicit;
4291 // Collision at same level
4292 if (variable_block == b) {
4293 li.Block.Error_AlreadyDeclared (name, li);
4297 // Collision with parent
4298 Block parent = variable_block;
4299 while ((parent = parent.Parent) != null) {
4301 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4302 i = existing_list.Count;
4307 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4308 // Collision with children
4309 while ((b = b.Parent) != null) {
4310 if (variable_block == b) {
4311 li.Block.Error_AlreadyDeclared (name, li, "child");
4312 i = existing_list.Count;
4319 existing_list.Add (li);
4322 public void AddLabel (string name, LabeledStatement label)
4325 labels = new Dictionary<string, object> ();
4328 if (!labels.TryGetValue (name, out value)) {
4329 labels.Add (name, label);
4333 LabeledStatement existing = value as LabeledStatement;
4334 List<LabeledStatement> existing_list;
4335 if (existing != null) {
4336 existing_list = new List<LabeledStatement> ();
4337 existing_list.Add (existing);
4338 labels[name] = existing_list;
4340 existing_list = (List<LabeledStatement>) value;
4344 // A collision checking between labels
4346 for (int i = 0; i < existing_list.Count; ++i) {
4347 existing = existing_list[i];
4348 Block b = existing.Block;
4350 // Collision at same level
4351 if (label.Block == b) {
4352 Report.SymbolRelatedToPreviousError (existing.loc, name);
4353 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4357 // Collision with parent
4359 while ((b = b.Parent) != null) {
4360 if (existing.Block == b) {
4361 Report.Error (158, label.loc,
4362 "The label `{0}' shadows another label by the same name in a contained scope", name);
4363 i = existing_list.Count;
4368 // Collision with with children
4370 while ((b = b.Parent) != null) {
4371 if (label.Block == b) {
4372 Report.Error (158, label.loc,
4373 "The label `{0}' shadows another label by the same name in a contained scope", name);
4374 i = existing_list.Count;
4380 existing_list.Add (label);
4383 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4385 if (this_references == null)
4386 this_references = new List<ExplicitBlock> ();
4388 if (!this_references.Contains (block))
4389 this_references.Add (block);
4392 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4394 this_references.Remove (block);
4398 // Creates an arguments set from all parameters, useful for method proxy calls
4400 public Arguments GetAllParametersArguments ()
4402 int count = parameters.Count;
4403 Arguments args = new Arguments (count);
4404 for (int i = 0; i < count; ++i) {
4405 var pi = parameter_info[i];
4406 var arg_expr = GetParameterReference (i, pi.Location);
4408 Argument.AType atype_modifier;
4409 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4410 case Parameter.Modifier.REF:
4411 atype_modifier = Argument.AType.Ref;
4413 case Parameter.Modifier.OUT:
4414 atype_modifier = Argument.AType.Out;
4421 args.Add (new Argument (arg_expr, atype_modifier));
4428 // Lookup inside a block, the returned value can represent 3 states
4430 // true+variable: A local name was found and it's valid
4431 // false+variable: A local name was found in a child block only
4432 // false+null: No local name was found
4434 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4440 if (!names.TryGetValue (name, out value))
4443 variable = value as INamedBlockVariable;
4445 if (variable != null) {
4447 if (variable.Block == b.Original)
4451 } while (b != null);
4459 } while (b != null);
4461 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4462 for (int i = 0; i < list.Count; ++i) {
4465 if (variable.Block == b.Original)
4469 } while (b != null);
4477 } while (b != null);
4487 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4489 if (block.names != null) {
4490 foreach (var n in block.names) {
4491 var variable = n.Value as INamedBlockVariable;
4492 if (variable != null) {
4493 if (variable.Block.ParametersBlock == pb)
4494 AddLocalName (n.Key, variable, false);
4498 foreach (var v in (List<INamedBlockVariable>) n.Value)
4499 if (v.Block.ParametersBlock == pb)
4500 AddLocalName (n.Key, v, false);
4506 // This is used by non-static `struct' constructors which do not have an
4507 // initializer - in this case, the constructor must initialize all of the
4508 // struct's fields. To do this, we add a "this" variable and use the flow
4509 // analysis code to ensure that it's been fully initialized before control
4510 // leaves the constructor.
4512 public void AddThisVariable (BlockContext bc)
4514 if (this_variable != null)
4515 throw new InternalErrorException (StartLocation.ToString ());
4517 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4518 this_variable.Type = bc.CurrentType;
4519 this_variable.PrepareAssignmentAnalysis (bc);
4522 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4525 // If we're a non-static struct constructor which doesn't have an
4526 // initializer, then we must initialize all of the struct's fields.
4528 if (this_variable != null)
4529 this_variable.IsThisAssigned (fc, this);
4531 base.CheckControlExit (fc, dat);
4534 public HashSet<LocalVariable> GetUndeclaredVariables ()
4539 HashSet<LocalVariable> variables = null;
4541 foreach (var entry in names) {
4542 var complex = entry.Value as List<INamedBlockVariable>;
4543 if (complex != null) {
4544 foreach (var centry in complex) {
4545 if (IsUndeclaredVariable (centry)) {
4546 if (variables == null)
4547 variables = new HashSet<LocalVariable> ();
4549 variables.Add ((LocalVariable) centry);
4552 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4553 if (variables == null)
4554 variables = new HashSet<LocalVariable> ();
4556 variables.Add ((LocalVariable)entry.Value);
4563 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4565 var lv = namedBlockVariable as LocalVariable;
4566 return lv != null && !lv.IsDeclared;
4569 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4574 foreach (var entry in names) {
4575 var complex = entry.Value as List<INamedBlockVariable>;
4576 if (complex != null) {
4577 foreach (var centry in complex) {
4578 var lv = centry as LocalVariable;
4579 if (lv != null && undeclaredVariables.Contains (lv)) {
4584 var lv = entry.Value as LocalVariable;
4585 if (lv != null && undeclaredVariables.Contains (lv))
4591 public override void Emit (EmitContext ec)
4593 if (Report.Errors > 0)
4597 if (IsCompilerGenerated) {
4598 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4606 // If `HasReturnLabel' is set, then we already emitted a
4607 // jump to the end of the method, so we must emit a `ret'
4610 // Unfortunately, System.Reflection.Emit automatically emits
4611 // a leave to the end of a finally block. This is a problem
4612 // if no code is following the try/finally block since we may
4613 // jump to a point after the end of the method.
4614 // As a workaround, we're always creating a return label in
4617 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4618 if (ec.HasReturnLabel)
4619 ec.MarkLabel (ec.ReturnLabel);
4621 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4622 ec.Mark (EndLocation);
4624 if (ec.ReturnType.Kind != MemberKind.Void)
4625 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4627 ec.Emit (OpCodes.Ret);
4630 } catch (Exception e) {
4631 throw new InternalErrorException (e, StartLocation);
4635 public bool Resolve (BlockContext bc, IMethodData md)
4640 var errors = bc.Report.Errors;
4644 if (bc.Report.Errors > errors)
4647 MarkReachable (new Reachability ());
4649 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4650 // TODO: var md = bc.CurrentMemberDefinition;
4651 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4654 if ((flags & Flags.NoFlowAnalysis) != 0)
4657 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4660 } catch (Exception e) {
4661 throw new InternalErrorException (e, StartLocation);
4668 public class SwitchLabel : Statement
4676 // if expr == null, then it is the default case.
4678 public SwitchLabel (Expression expr, Location l)
4684 public bool IsDefault {
4686 return label == null;
4690 public Expression Label {
4696 public Location Location {
4702 public Constant Converted {
4711 public bool PatternMatching { get; set; }
4713 public bool SectionStart { get; set; }
4715 public Label GetILLabel (EmitContext ec)
4717 if (il_label == null){
4718 il_label = ec.DefineLabel ();
4721 return il_label.Value;
4724 protected override void DoEmit (EmitContext ec)
4726 ec.MarkLabel (GetILLabel (ec));
4729 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4734 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4738 public override bool Resolve (BlockContext bc)
4740 if (ResolveAndReduce (bc))
4741 bc.Switch.RegisterLabel (bc, this);
4747 // Resolves the expression, reduces it to a literal if possible
4748 // and then converts it to the requested type.
4750 bool ResolveAndReduce (BlockContext bc)
4755 var switch_statement = bc.Switch;
4757 if (PatternMatching) {
4758 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4759 return label != null;
4762 var c = label.ResolveLabelConstant (bc);
4766 if (switch_statement.IsNullable && c is NullLiteral) {
4771 if (switch_statement.IsPatternMatching) {
4772 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4776 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4777 return converted != null;
4780 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4782 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4783 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4786 protected override void CloneTo (CloneContext clonectx, Statement target)
4788 var t = (SwitchLabel) target;
4790 t.label = label.Clone (clonectx);
4793 public override object Accept (StructuralVisitor visitor)
4795 return visitor.Visit (this);
4798 public string GetSignatureForError ()
4801 if (converted == null)
4804 label = converted.GetValueAsLiteral ();
4806 return string.Format ("case {0}:", label);
4810 public class Switch : LoopStatement
4812 // structure used to hold blocks of keys while calculating table switch
4813 sealed class LabelsRange : IComparable<LabelsRange>
4815 public readonly long min;
4817 public readonly List<long> label_values;
4819 public LabelsRange (long value)
4822 label_values = new List<long> ();
4823 label_values.Add (value);
4826 public LabelsRange (long min, long max, ICollection<long> values)
4830 this.label_values = new List<long> (values);
4835 return max - min + 1;
4839 public bool AddValue (long value)
4841 var gap = value - min + 1;
4842 // Ensure the range has > 50% occupancy
4843 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4847 label_values.Add (value);
4851 public int CompareTo (LabelsRange other)
4853 int nLength = label_values.Count;
4854 int nLengthOther = other.label_values.Count;
4855 if (nLengthOther == nLength)
4856 return (int) (other.min - min);
4858 return nLength - nLengthOther;
4862 sealed class DispatchStatement : Statement
4864 readonly Switch body;
4866 public DispatchStatement (Switch body)
4871 protected override void CloneTo (CloneContext clonectx, Statement target)
4873 throw new NotImplementedException ();
4876 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4881 protected override void DoEmit (EmitContext ec)
4883 body.EmitDispatch (ec);
4887 class MissingBreak : Statement
4889 readonly SwitchLabel label;
4891 public MissingBreak (SwitchLabel sl)
4897 public bool FallOut { get; set; }
4899 protected override void DoEmit (EmitContext ec)
4903 protected override void CloneTo (CloneContext clonectx, Statement target)
4907 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4910 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4911 label.GetSignatureForError ());
4913 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4914 label.GetSignatureForError ());
4920 public Expression Expr;
4923 // Mapping of all labels to their SwitchLabels
4925 Dictionary<long, SwitchLabel> labels;
4926 Dictionary<string, SwitchLabel> string_labels;
4927 List<SwitchLabel> case_labels;
4929 List<Tuple<GotoCase, Constant>> goto_cases;
4930 List<DefiniteAssignmentBitSet> end_reachable_das;
4933 /// The governing switch type
4935 public TypeSpec SwitchType;
4937 Expression new_expr;
4939 SwitchLabel case_null;
4940 SwitchLabel case_default;
4942 Label defaultLabel, nullLabel;
4943 VariableReference value;
4944 ExpressionStatement string_dictionary;
4945 FieldExpr switch_cache_field;
4946 ExplicitBlock block;
4950 // Nullable Types support
4952 Nullable.Unwrap unwrap;
4954 public Switch (Expression e, ExplicitBlock block, Location l)
4962 public SwitchLabel ActiveLabel { get; set; }
4964 public ExplicitBlock Block {
4970 public SwitchLabel DefaultLabel {
4972 return case_default;
4976 public bool IsNullable {
4978 return unwrap != null;
4982 public bool IsPatternMatching {
4984 return new_expr == null && SwitchType != null;
4988 public List<SwitchLabel> RegisteredLabels {
4994 public VariableReference ExpressionValue {
5001 // Determines the governing type for a switch. The returned
5002 // expression might be the expression from the switch, or an
5003 // expression that includes any potential conversions to
5005 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
5007 switch (expr.Type.BuiltinType) {
5008 case BuiltinTypeSpec.Type.Byte:
5009 case BuiltinTypeSpec.Type.SByte:
5010 case BuiltinTypeSpec.Type.UShort:
5011 case BuiltinTypeSpec.Type.Short:
5012 case BuiltinTypeSpec.Type.UInt:
5013 case BuiltinTypeSpec.Type.Int:
5014 case BuiltinTypeSpec.Type.ULong:
5015 case BuiltinTypeSpec.Type.Long:
5016 case BuiltinTypeSpec.Type.Char:
5017 case BuiltinTypeSpec.Type.String:
5018 case BuiltinTypeSpec.Type.Bool:
5022 if (expr.Type.IsEnum)
5026 // Try to find a *user* defined implicit conversion.
5028 // If there is no implicit conversion, or if there are multiple
5029 // conversions, we have to report an error
5031 Expression converted = null;
5032 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
5034 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
5037 var restr = Convert.UserConversionRestriction.ImplicitOnly |
5038 Convert.UserConversionRestriction.ProbingOnly;
5041 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
5043 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
5048 // Ignore over-worked ImplicitUserConversions that do
5049 // an implicit conversion in addition to the user conversion.
5051 var uc = e as UserCast;
5055 if (converted != null){
5056 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
5065 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
5067 var types = module.Compiler.BuiltinTypes;
5069 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
5070 TypeSpec[] stypes = new[] {
5083 if (nullable != null) {
5085 Array.Resize (ref stypes, stypes.Length + 9);
5087 for (int i = 0; i < 9; ++i) {
5088 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5095 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5097 case_labels.Add (sl);
5100 if (case_default != null) {
5101 sl.Error_AlreadyOccurs (rc, case_default);
5109 if (sl.Converted == null)
5113 if (string_labels != null) {
5114 string string_value = sl.Converted.GetValue () as string;
5115 if (string_value == null)
5118 string_labels.Add (string_value, sl);
5120 if (sl.Converted.IsNull) {
5123 labels.Add (sl.Converted.GetValueAsLong (), sl);
5126 } catch (ArgumentException) {
5127 if (string_labels != null)
5128 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5130 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5135 // This method emits code for a lookup-based switch statement (non-string)
5136 // Basically it groups the cases into blocks that are at least half full,
5137 // and then spits out individual lookup opcodes for each block.
5138 // It emits the longest blocks first, and short blocks are just
5139 // handled with direct compares.
5141 void EmitTableSwitch (EmitContext ec, Expression val)
5143 if (labels != null && labels.Count > 0) {
5144 List<LabelsRange> ranges;
5145 if (string_labels != null) {
5146 // We have done all hard work for string already
5147 // setup single range only
5148 ranges = new List<LabelsRange> (1);
5149 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5151 var element_keys = new long[labels.Count];
5152 labels.Keys.CopyTo (element_keys, 0);
5153 Array.Sort (element_keys);
5156 // Build possible ranges of switch labes to reduce number
5159 ranges = new List<LabelsRange> (element_keys.Length);
5160 var range = new LabelsRange (element_keys[0]);
5162 for (int i = 1; i < element_keys.Length; ++i) {
5163 var l = element_keys[i];
5164 if (range.AddValue (l))
5167 range = new LabelsRange (l);
5171 // sort the blocks so we can tackle the largest ones first
5175 Label lbl_default = defaultLabel;
5176 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5178 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5179 LabelsRange kb = ranges[range_index];
5180 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5182 // Optimize small ranges using simple equality check
5183 if (kb.Range <= 2) {
5184 foreach (var key in kb.label_values) {
5185 SwitchLabel sl = labels[key];
5186 if (sl == case_default || sl == case_null)
5189 if (sl.Converted.IsZeroInteger) {
5190 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5193 sl.Converted.Emit (ec);
5194 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5198 // TODO: if all the keys in the block are the same and there are
5199 // no gaps/defaults then just use a range-check.
5200 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5201 // TODO: optimize constant/I4 cases
5203 // check block range (could be > 2^31)
5205 ec.EmitLong (kb.min);
5206 ec.Emit (OpCodes.Blt, lbl_default);
5209 ec.EmitLong (kb.max);
5210 ec.Emit (OpCodes.Bgt, lbl_default);
5215 ec.EmitLong (kb.min);
5216 ec.Emit (OpCodes.Sub);
5219 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5223 int first = (int) kb.min;
5226 ec.Emit (OpCodes.Sub);
5227 } else if (first < 0) {
5228 ec.EmitInt (-first);
5229 ec.Emit (OpCodes.Add);
5233 // first, build the list of labels for the switch
5235 long cJumps = kb.Range;
5236 Label[] switch_labels = new Label[cJumps];
5237 for (int iJump = 0; iJump < cJumps; iJump++) {
5238 var key = kb.label_values[iKey];
5239 if (key == kb.min + iJump) {
5240 switch_labels[iJump] = labels[key].GetILLabel (ec);
5243 switch_labels[iJump] = lbl_default;
5247 // emit the switch opcode
5248 ec.Emit (OpCodes.Switch, switch_labels);
5251 // mark the default for this block
5252 if (range_index != 0)
5253 ec.MarkLabel (lbl_default);
5256 // the last default just goes to the end
5257 if (ranges.Count > 0)
5258 ec.Emit (OpCodes.Br, lbl_default);
5262 public SwitchLabel FindLabel (Constant value)
5264 SwitchLabel sl = null;
5266 if (string_labels != null) {
5267 string s = value.GetValue () as string;
5269 if (case_null != null)
5271 else if (case_default != null)
5274 string_labels.TryGetValue (s, out sl);
5277 if (value is NullLiteral) {
5280 labels.TryGetValue (value.GetValueAsLong (), out sl);
5284 if (sl == null || sl.SectionStart)
5288 // Always return section start, it simplifies handling of switch labels
5290 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5291 var cs = case_labels [idx];
5292 if (cs.SectionStart)
5297 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5299 Expr.FlowAnalysis (fc);
5301 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5302 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5303 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5305 block.FlowAnalysis (fc);
5307 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5309 if (end_reachable_das != null) {
5310 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5311 InitialDefinitiveAssignment |= sections_das;
5312 end_reachable_das = null;
5315 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5317 return case_default != null && !end_reachable;
5320 public override bool Resolve (BlockContext ec)
5322 Expr = Expr.Resolve (ec);
5327 // LAMESPEC: User conversion from non-nullable governing type has a priority
5329 new_expr = SwitchGoverningType (ec, Expr, false);
5331 if (new_expr == null) {
5332 if (Expr.Type.IsNullableType) {
5333 unwrap = Nullable.Unwrap.Create (Expr, false);
5338 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5339 // involving nullable Expr and nullable governing type is
5341 new_expr = SwitchGoverningType (ec, unwrap, true);
5345 Expression switch_expr;
5346 if (new_expr == null) {
5347 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5348 if (Expr.Type != InternalType.ErrorType) {
5349 ec.Report.Error (151, loc,
5350 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5351 Expr.Type.GetSignatureForError ());
5358 SwitchType = Expr.Type;
5360 switch_expr = new_expr;
5361 SwitchType = new_expr.Type;
5362 if (SwitchType.IsNullableType) {
5363 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5364 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5367 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5368 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5372 if (block.Statements.Count == 0)
5375 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5376 string_labels = new Dictionary<string, SwitchLabel> ();
5378 labels = new Dictionary<long, SwitchLabel> ();
5382 var constant = switch_expr as Constant;
5385 // Don't need extra variable for constant switch or switch with
5386 // only default case
5388 if (constant == null) {
5390 // Store switch expression for comparison purposes
5392 value = switch_expr as VariableReference;
5393 if (value == null && !HasOnlyDefaultSection ()) {
5394 var current_block = ec.CurrentBlock;
5395 ec.CurrentBlock = Block;
5396 // Create temporary variable inside switch scope
5397 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5399 ec.CurrentBlock = current_block;
5403 case_labels = new List<SwitchLabel> ();
5405 Switch old_switch = ec.Switch;
5407 var parent_los = ec.EnclosingLoopOrSwitch;
5408 ec.EnclosingLoopOrSwitch = this;
5410 var ok = Statement.Resolve (ec);
5412 ec.EnclosingLoopOrSwitch = parent_los;
5413 ec.Switch = old_switch;
5416 // Check if all goto cases are valid. Needs to be done after switch
5417 // is resolved because goto can jump forward in the scope.
5419 if (goto_cases != null) {
5420 foreach (var gc in goto_cases) {
5421 if (gc.Item1 == null) {
5422 if (DefaultLabel == null) {
5423 Goto.Error_UnknownLabel (ec, "default", loc);
5429 var sl = FindLabel (gc.Item2);
5431 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5433 gc.Item1.Label = sl;
5441 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5442 ResolveStringSwitchMap (ec);
5446 // Anonymous storey initialization has to happen before
5447 // any generated switch dispatch
5449 block.InsertStatement (0, new DispatchStatement (this));
5454 bool HasOnlyDefaultSection ()
5456 for (int i = 0; i < block.Statements.Count; ++i) {
5457 var s = block.Statements[i] as SwitchLabel;
5459 if (s == null || s.IsDefault)
5468 public override Reachability MarkReachable (Reachability rc)
5470 if (rc.IsUnreachable)
5473 base.MarkReachable (rc);
5475 block.MarkReachableScope (rc);
5477 if (block.Statements.Count == 0)
5480 SwitchLabel constant_label = null;
5481 var constant = new_expr as Constant;
5483 if (constant != null) {
5484 constant_label = FindLabel (constant) ?? case_default;
5485 if (constant_label == null) {
5486 block.Statements.RemoveAt (0);
5491 var section_rc = new Reachability ();
5492 SwitchLabel prev_label = null;
5494 for (int i = 0; i < block.Statements.Count; ++i) {
5495 var s = block.Statements[i];
5496 var sl = s as SwitchLabel;
5498 if (sl != null && sl.SectionStart) {
5500 // Section is marked already via goto case
5502 if (!sl.IsUnreachable) {
5503 section_rc = new Reachability ();
5507 if (section_rc.IsUnreachable) {
5509 // Common case. Previous label section end is unreachable as
5510 // it ends with break, return, etc. For next section revert
5511 // to reachable again unless we have constant switch block
5513 section_rc = constant_label != null && constant_label != sl ?
5514 Reachability.CreateUnreachable () :
5515 new Reachability ();
5516 } else if (prev_label != null) {
5518 // Error case as control cannot fall through from one case label
5520 sl.SectionStart = false;
5521 s = new MissingBreak (prev_label);
5522 s.MarkReachable (rc);
5523 block.Statements.Insert (i - 1, s);
5525 } else if (constant_label != null && constant_label != sl) {
5527 // Special case for the first unreachable label in constant
5530 section_rc = Reachability.CreateUnreachable ();
5536 section_rc = s.MarkReachable (section_rc);
5539 if (!section_rc.IsUnreachable && prev_label != null) {
5540 prev_label.SectionStart = false;
5541 var s = new MissingBreak (prev_label) {
5545 s.MarkReachable (rc);
5546 block.Statements.Add (s);
5550 // Reachability can affect parent only when all possible paths are handled but
5551 // we still need to run reachability check on switch body to check for fall-through
5553 if (case_default == null && constant_label == null)
5557 // We have at least one local exit from the switch
5562 return Reachability.CreateUnreachable ();
5565 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5567 if (goto_cases == null)
5568 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5570 goto_cases.Add (Tuple.Create (gotoCase, value));
5574 // Converts string switch into string hashtable
5576 void ResolveStringSwitchMap (ResolveContext ec)
5578 FullNamedExpression string_dictionary_type;
5579 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5580 string_dictionary_type = new TypeExpression (
5581 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5582 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5584 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5585 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5587 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5591 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5592 Field field = new Field (ctype, string_dictionary_type,
5593 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5594 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5595 if (!field.Define ())
5597 ctype.AddField (field);
5599 var init = new List<Expression> ();
5601 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5602 string value = null;
5604 foreach (SwitchLabel sl in case_labels) {
5606 if (sl.SectionStart)
5607 labels.Add (++counter, sl);
5609 if (sl == case_default || sl == case_null)
5612 value = (string) sl.Converted.GetValue ();
5613 var init_args = new List<Expression> (2);
5614 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5616 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5617 init_args.Add (sl.Converted);
5619 init.Add (new CollectionElementInitializer (init_args, loc));
5622 Arguments args = new Arguments (1);
5623 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5624 Expression initializer = new NewInitialize (string_dictionary_type, args,
5625 new CollectionOrObjectInitializers (init, loc), loc);
5627 switch_cache_field = new FieldExpr (field, loc);
5628 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5631 void DoEmitStringSwitch (EmitContext ec)
5633 Label l_initialized = ec.DefineLabel ();
5636 // Skip initialization when value is null
5638 value.EmitBranchable (ec, nullLabel, false);
5641 // Check if string dictionary is initialized and initialize
5643 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5644 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5645 string_dictionary.EmitStatement (ec);
5647 ec.MarkLabel (l_initialized);
5649 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5651 ResolveContext rc = new ResolveContext (ec.MemberContext);
5653 if (switch_cache_field.Type.IsGeneric) {
5654 Arguments get_value_args = new Arguments (2);
5655 get_value_args.Add (new Argument (value));
5656 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5657 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5658 if (get_item == null)
5662 // A value was not found, go to default case
5664 get_item.EmitBranchable (ec, defaultLabel, false);
5666 Arguments get_value_args = new Arguments (1);
5667 get_value_args.Add (new Argument (value));
5669 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5670 if (get_item == null)
5673 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5674 get_item_object.EmitAssign (ec, get_item, true, false);
5675 ec.Emit (OpCodes.Brfalse, defaultLabel);
5677 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5678 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5680 get_item_int.EmitStatement (ec);
5681 get_item_object.Release (ec);
5684 EmitTableSwitch (ec, string_switch_variable);
5685 string_switch_variable.Release (ec);
5689 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5691 void EmitShortSwitch (EmitContext ec)
5693 MethodSpec equal_method = null;
5694 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5695 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5698 if (equal_method != null) {
5699 value.EmitBranchable (ec, nullLabel, false);
5702 for (int i = 0; i < case_labels.Count; ++i) {
5703 var label = case_labels [i];
5704 if (label == case_default || label == case_null)
5707 var constant = label.Converted;
5709 if (constant == null) {
5710 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5714 if (equal_method != null) {
5718 var call = new CallEmitter ();
5719 call.EmitPredefined (ec, equal_method, new Arguments (0));
5720 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5724 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5725 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5731 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5734 ec.Emit (OpCodes.Br, defaultLabel);
5737 void EmitDispatch (EmitContext ec)
5739 if (IsPatternMatching) {
5740 EmitShortSwitch (ec);
5744 if (value == null) {
5746 // Constant switch, we've already done the work if there is only 1 label
5750 foreach (var sl in case_labels) {
5751 if (sl.IsUnreachable)
5754 if (reachable++ > 0) {
5755 var constant = (Constant) new_expr;
5756 var constant_label = FindLabel (constant) ?? case_default;
5758 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5766 if (string_dictionary != null) {
5767 DoEmitStringSwitch (ec);
5768 } else if (case_labels.Count < 4 || string_labels != null) {
5769 EmitShortSwitch (ec);
5771 EmitTableSwitch (ec, value);
5775 protected override void DoEmit (EmitContext ec)
5778 // Setup the codegen context
5780 Label old_end = ec.LoopEnd;
5781 Switch old_switch = ec.Switch;
5783 ec.LoopEnd = ec.DefineLabel ();
5786 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5787 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5789 if (value != null) {
5792 var switch_expr = new_expr ?? Expr;
5794 unwrap.EmitCheck (ec);
5795 ec.Emit (OpCodes.Brfalse, nullLabel);
5796 value.EmitAssign (ec, switch_expr, false, false);
5797 } else if (switch_expr != value) {
5798 value.EmitAssign (ec, switch_expr, false, false);
5803 // Next statement is compiler generated we don't need extra
5804 // nop when we can use the statement for sequence point
5806 ec.Mark (block.StartLocation);
5807 block.IsCompilerGenerated = true;
5809 new_expr.EmitSideEffect (ec);
5814 // Restore context state.
5815 ec.MarkLabel (ec.LoopEnd);
5818 // Restore the previous context
5820 ec.LoopEnd = old_end;
5821 ec.Switch = old_switch;
5824 protected override void CloneTo (CloneContext clonectx, Statement t)
5826 Switch target = (Switch) t;
5828 target.Expr = Expr.Clone (clonectx);
5829 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5832 public override object Accept (StructuralVisitor visitor)
5834 return visitor.Visit (this);
5837 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5839 if (case_default == null && !(new_expr is Constant))
5842 if (end_reachable_das == null)
5843 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5845 end_reachable_das.Add (fc.DefiniteAssignment);
5848 public override void SetEndReachable ()
5850 end_reachable = true;
5854 // A place where execution can restart in a state machine
5855 public abstract class ResumableStatement : Statement
5858 protected Label resume_point;
5860 public Label PrepareForEmit (EmitContext ec)
5864 resume_point = ec.DefineLabel ();
5866 return resume_point;
5869 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5874 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5879 public abstract class TryFinallyBlock : ExceptionStatement
5881 protected Statement stmt;
5882 Label dispose_try_block;
5883 bool prepared_for_dispose, emitted_dispose;
5884 Method finally_host;
5886 protected TryFinallyBlock (Statement stmt, Location loc)
5894 public Statement Statement {
5902 protected abstract void EmitTryBody (EmitContext ec);
5903 public abstract void EmitFinallyBody (EmitContext ec);
5905 public override Label PrepareForDispose (EmitContext ec, Label end)
5907 if (!prepared_for_dispose) {
5908 prepared_for_dispose = true;
5909 dispose_try_block = ec.DefineLabel ();
5911 return dispose_try_block;
5914 protected sealed override void DoEmit (EmitContext ec)
5916 EmitTryBodyPrepare (ec);
5919 bool beginFinally = EmitBeginFinallyBlock (ec);
5921 Label start_finally = ec.DefineLabel ();
5922 if (resume_points != null && beginFinally) {
5923 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5925 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5926 ec.Emit (OpCodes.Brfalse_S, start_finally);
5927 ec.Emit (OpCodes.Endfinally);
5930 ec.MarkLabel (start_finally);
5932 if (finally_host != null) {
5933 finally_host.Define ();
5934 finally_host.PrepareEmit ();
5935 finally_host.Emit ();
5937 // Now it's safe to add, to close it properly and emit sequence points
5938 finally_host.Parent.AddMember (finally_host);
5940 var ce = new CallEmitter ();
5941 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5942 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5944 EmitFinallyBody (ec);
5948 ec.EndExceptionBlock ();
5951 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5953 if (emitted_dispose)
5956 emitted_dispose = true;
5958 Label end_of_try = ec.DefineLabel ();
5960 // Ensure that the only way we can get into this code is through a dispatcher
5961 if (have_dispatcher)
5962 ec.Emit (OpCodes.Br, end);
5964 ec.BeginExceptionBlock ();
5966 ec.MarkLabel (dispose_try_block);
5968 Label[] labels = null;
5969 for (int i = 0; i < resume_points.Count; ++i) {
5970 ResumableStatement s = resume_points[i];
5971 Label ret = s.PrepareForDispose (ec, end_of_try);
5972 if (ret.Equals (end_of_try) && labels == null)
5974 if (labels == null) {
5975 labels = new Label[resume_points.Count];
5976 for (int j = 0; j < i; ++j)
5977 labels[j] = end_of_try;
5982 if (labels != null) {
5984 for (j = 1; j < labels.Length; ++j)
5985 if (!labels[0].Equals (labels[j]))
5987 bool emit_dispatcher = j < labels.Length;
5989 if (emit_dispatcher) {
5990 ec.Emit (OpCodes.Ldloc, pc);
5991 ec.EmitInt (first_resume_pc);
5992 ec.Emit (OpCodes.Sub);
5993 ec.Emit (OpCodes.Switch, labels);
5996 foreach (ResumableStatement s in resume_points)
5997 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
6000 ec.MarkLabel (end_of_try);
6002 ec.BeginFinallyBlock ();
6004 if (finally_host != null) {
6005 var ce = new CallEmitter ();
6006 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
6007 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
6009 EmitFinallyBody (ec);
6012 ec.EndExceptionBlock ();
6015 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6017 var res = stmt.FlowAnalysis (fc);
6018 parent_try_block = null;
6022 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
6024 ec.BeginFinallyBlock ();
6028 public override Reachability MarkReachable (Reachability rc)
6030 base.MarkReachable (rc);
6031 return Statement.MarkReachable (rc);
6034 public override bool Resolve (BlockContext bc)
6038 parent_try_block = bc.CurrentTryBlock;
6039 bc.CurrentTryBlock = this;
6041 if (stmt is TryCatch) {
6042 ok = stmt.Resolve (bc);
6044 using (bc.Set (ResolveContext.Options.TryScope)) {
6045 ok = stmt.Resolve (bc);
6049 bc.CurrentTryBlock = parent_try_block;
6052 // Finally block inside iterator is called from MoveNext and
6053 // Dispose methods that means we need to lift the block into
6054 // newly created host method to emit the body only once. The
6055 // original block then simply calls the newly generated method.
6057 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
6058 var b = stmt as Block;
6059 if (b != null && b.Explicit.HasYield) {
6060 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
6064 return base.Resolve (bc) && ok;
6069 // Base class for blocks using exception handling
6071 public abstract class ExceptionStatement : ResumableStatement
6073 protected List<ResumableStatement> resume_points;
6074 protected int first_resume_pc;
6075 protected ExceptionStatement parent_try_block;
6076 protected int first_catch_resume_pc = -1;
6078 protected ExceptionStatement (Location loc)
6083 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6085 StateMachineInitializer state_machine = null;
6086 if (resume_points != null) {
6087 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6089 ec.EmitInt ((int) IteratorStorey.State.Running);
6090 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6094 // The resume points in catch section when this is try-catch-finally
6096 if (IsRewrittenTryCatchFinally ()) {
6097 ec.BeginExceptionBlock ();
6099 if (first_catch_resume_pc >= 0) {
6101 ec.MarkLabel (resume_point);
6103 // For normal control flow, we want to fall-through the Switch
6104 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6105 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6106 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6107 ec.Emit (OpCodes.Sub);
6109 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6110 for (int i = 0; i < labels.Length; ++i)
6111 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6112 ec.Emit (OpCodes.Switch, labels);
6116 ec.BeginExceptionBlock ();
6119 // The resume points for try section
6121 if (resume_points != null && first_catch_resume_pc != 0) {
6122 if (first_catch_resume_pc < 0)
6123 ec.MarkLabel (resume_point);
6125 // For normal control flow, we want to fall-through the Switch
6126 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6127 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6128 ec.EmitInt (first_resume_pc);
6129 ec.Emit (OpCodes.Sub);
6131 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6132 for (int i = 0; i < labels.Length; ++i)
6133 labels[i] = resume_points[i].PrepareForEmit (ec);
6134 ec.Emit (OpCodes.Switch, labels);
6138 bool IsRewrittenTryCatchFinally ()
6140 var tf = this as TryFinally;
6144 var tc = tf.Statement as TryCatch;
6148 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6151 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6153 if (parent_try_block != null) {
6154 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6156 pc = stateMachine.AddResumePoint (this);
6159 if (resume_points == null) {
6160 resume_points = new List<ResumableStatement> ();
6161 first_resume_pc = pc;
6164 if (pc != first_resume_pc + resume_points.Count)
6165 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6167 var tf = this as TryFinally;
6168 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6169 first_catch_resume_pc = resume_points.Count;
6172 resume_points.Add (stmt);
6177 public class Lock : TryFinallyBlock
6180 TemporaryVariableReference expr_copy;
6181 TemporaryVariableReference lock_taken;
6183 public Lock (Expression expr, Statement stmt, Location loc)
6189 public Expression Expr {
6195 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6197 expr.FlowAnalysis (fc);
6198 return base.DoFlowAnalysis (fc);
6201 public override bool Resolve (BlockContext ec)
6203 expr = expr.Resolve (ec);
6207 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6208 ec.Report.Error (185, loc,
6209 "`{0}' is not a reference type as required by the lock statement",
6210 expr.Type.GetSignatureForError ());
6213 if (expr.Type.IsGenericParameter) {
6214 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6217 VariableReference lv = expr as VariableReference;
6220 locked = lv.IsLockedByStatement;
6221 lv.IsLockedByStatement = true;
6228 // Have to keep original lock value around to unlock same location
6229 // in the case of original value has changed or is null
6231 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6232 expr_copy.Resolve (ec);
6235 // Ensure Monitor methods are available
6237 if (ResolvePredefinedMethods (ec) > 1) {
6238 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6239 lock_taken.Resolve (ec);
6242 using (ec.Set (ResolveContext.Options.LockScope)) {
6247 lv.IsLockedByStatement = locked;
6253 protected override void EmitTryBodyPrepare (EmitContext ec)
6255 expr_copy.EmitAssign (ec, expr);
6257 if (lock_taken != null) {
6259 // Initialize ref variable
6261 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6264 // Monitor.Enter (expr_copy)
6266 expr_copy.Emit (ec);
6267 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6270 base.EmitTryBodyPrepare (ec);
6273 protected override void EmitTryBody (EmitContext ec)
6276 // Monitor.Enter (expr_copy, ref lock_taken)
6278 if (lock_taken != null) {
6279 expr_copy.Emit (ec);
6280 lock_taken.LocalInfo.CreateBuilder (ec);
6281 lock_taken.AddressOf (ec, AddressOp.Load);
6282 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6285 Statement.Emit (ec);
6288 public override void EmitFinallyBody (EmitContext ec)
6291 // if (lock_taken) Monitor.Exit (expr_copy)
6293 Label skip = ec.DefineLabel ();
6295 if (lock_taken != null) {
6296 lock_taken.Emit (ec);
6297 ec.Emit (OpCodes.Brfalse_S, skip);
6300 expr_copy.Emit (ec);
6301 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6303 ec.Emit (OpCodes.Call, m);
6305 ec.MarkLabel (skip);
6308 int ResolvePredefinedMethods (ResolveContext rc)
6310 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6311 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6315 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6319 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6323 protected override void CloneTo (CloneContext clonectx, Statement t)
6325 Lock target = (Lock) t;
6327 target.expr = expr.Clone (clonectx);
6328 target.stmt = Statement.Clone (clonectx);
6331 public override object Accept (StructuralVisitor visitor)
6333 return visitor.Visit (this);
6338 public class Unchecked : Statement {
6341 public Unchecked (Block b, Location loc)
6348 public override bool Resolve (BlockContext ec)
6350 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6351 return Block.Resolve (ec);
6354 protected override void DoEmit (EmitContext ec)
6356 using (ec.With (EmitContext.Options.CheckedScope, false))
6360 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6362 return Block.FlowAnalysis (fc);
6365 public override Reachability MarkReachable (Reachability rc)
6367 base.MarkReachable (rc);
6368 return Block.MarkReachable (rc);
6371 protected override void CloneTo (CloneContext clonectx, Statement t)
6373 Unchecked target = (Unchecked) t;
6375 target.Block = clonectx.LookupBlock (Block);
6378 public override object Accept (StructuralVisitor visitor)
6380 return visitor.Visit (this);
6384 public class Checked : Statement {
6387 public Checked (Block b, Location loc)
6390 b.Unchecked = false;
6394 public override bool Resolve (BlockContext ec)
6396 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6397 return Block.Resolve (ec);
6400 protected override void DoEmit (EmitContext ec)
6402 using (ec.With (EmitContext.Options.CheckedScope, true))
6406 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6408 return Block.FlowAnalysis (fc);
6411 public override Reachability MarkReachable (Reachability rc)
6413 base.MarkReachable (rc);
6414 return Block.MarkReachable (rc);
6417 protected override void CloneTo (CloneContext clonectx, Statement t)
6419 Checked target = (Checked) t;
6421 target.Block = clonectx.LookupBlock (Block);
6424 public override object Accept (StructuralVisitor visitor)
6426 return visitor.Visit (this);
6430 public class Unsafe : Statement {
6433 public Unsafe (Block b, Location loc)
6436 Block.Unsafe = true;
6440 public override bool Resolve (BlockContext ec)
6442 if (ec.CurrentIterator != null)
6443 Expression.UnsafeInsideIteratorError (ec, loc);
6445 using (ec.Set (ResolveContext.Options.UnsafeScope))
6446 return Block.Resolve (ec);
6449 protected override void DoEmit (EmitContext ec)
6454 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6456 return Block.FlowAnalysis (fc);
6459 public override Reachability MarkReachable (Reachability rc)
6461 base.MarkReachable (rc);
6462 return Block.MarkReachable (rc);
6465 protected override void CloneTo (CloneContext clonectx, Statement t)
6467 Unsafe target = (Unsafe) t;
6469 target.Block = clonectx.LookupBlock (Block);
6472 public override object Accept (StructuralVisitor visitor)
6474 return visitor.Visit (this);
6481 public class Fixed : Statement
6483 abstract class Emitter : ShimExpression
6485 protected LocalVariable vi;
6487 protected Emitter (Expression expr, LocalVariable li)
6493 public abstract void EmitExit (EmitContext ec);
6495 public override void FlowAnalysis (FlowAnalysisContext fc)
6497 expr.FlowAnalysis (fc);
6501 sealed class ExpressionEmitter : Emitter {
6502 public ExpressionEmitter (Expression converted, LocalVariable li)
6503 : base (converted, li)
6507 protected override Expression DoResolve (ResolveContext rc)
6509 throw new NotImplementedException ();
6512 public override void Emit (EmitContext ec) {
6514 // Store pointer in pinned location
6520 public override void EmitExit (EmitContext ec)
6523 ec.Emit (OpCodes.Conv_U);
6528 class StringEmitter : Emitter
6530 LocalVariable pinned_string;
6532 public StringEmitter (Expression expr, LocalVariable li)
6537 protected override Expression DoResolve (ResolveContext rc)
6539 pinned_string = new LocalVariable (vi.Block, "$pinned",
6540 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6542 pinned_string.Type = rc.BuiltinTypes.String;
6545 eclass = ExprClass.Variable;
6546 type = rc.BuiltinTypes.Int;
6550 public override void Emit (EmitContext ec)
6552 pinned_string.CreateBuilder (ec);
6555 pinned_string.EmitAssign (ec);
6557 // TODO: Should use Binary::Add
6558 pinned_string.Emit (ec);
6559 ec.Emit (OpCodes.Conv_I);
6561 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6565 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6566 //pe.InstanceExpression = pinned_string;
6567 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6569 ec.Emit (OpCodes.Add);
6573 public override void EmitExit (EmitContext ec)
6576 pinned_string.EmitAssign (ec);
6580 public class VariableDeclaration : BlockVariable
6582 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6587 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6589 if (!Variable.Type.IsPointer && li == Variable) {
6590 bc.Report.Error (209, TypeExpression.Location,
6591 "The type of locals declared in a fixed statement must be a pointer type");
6595 var res = initializer.Resolve (bc);
6602 var ac = res.Type as ArrayContainer;
6604 TypeSpec array_type = ac.Element;
6607 // Provided that array_type is unmanaged,
6609 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6612 Expression res_init;
6613 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6616 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6617 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6618 res = expr_variable.CreateReferenceExpression (bc, loc);
6622 // and T* is implicitly convertible to the
6623 // pointer type given in the fixed statement.
6625 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6627 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6628 if (converted == null)
6632 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6634 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6635 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6636 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6637 new NullLiteral (loc),
6640 converted = converted.Resolve (bc);
6642 return new ExpressionEmitter (converted, li);
6648 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6649 return new StringEmitter (res, li).Resolve (bc);
6652 // Case 3: fixed buffer
6653 if (res is FixedBufferPtr) {
6654 return new ExpressionEmitter (res, li);
6657 bool already_fixed = true;
6660 // Case 4: & object.
6662 Unary u = res as Unary;
6664 if (u.Oper == Unary.Operator.AddressOf) {
6665 IVariableReference vr = u.Expr as IVariableReference;
6666 if (vr == null || !vr.IsFixed) {
6667 already_fixed = false;
6670 } else if (initializer is Cast) {
6671 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6675 if (already_fixed) {
6676 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6679 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6680 return new ExpressionEmitter (res, li);
6685 VariableDeclaration decl;
6686 Statement statement;
6689 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6698 public Statement Statement {
6704 public BlockVariable Variables {
6712 public override bool Resolve (BlockContext bc)
6714 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6715 if (!decl.Resolve (bc))
6719 return statement.Resolve (bc);
6722 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6724 decl.FlowAnalysis (fc);
6725 return statement.FlowAnalysis (fc);
6728 protected override void DoEmit (EmitContext ec)
6730 decl.Variable.CreateBuilder (ec);
6731 decl.Initializer.Emit (ec);
6732 if (decl.Declarators != null) {
6733 foreach (var d in decl.Declarators) {
6734 d.Variable.CreateBuilder (ec);
6735 d.Initializer.Emit (ec);
6739 statement.Emit (ec);
6745 // Clear the pinned variable
6747 ((Emitter) decl.Initializer).EmitExit (ec);
6748 if (decl.Declarators != null) {
6749 foreach (var d in decl.Declarators) {
6750 ((Emitter)d.Initializer).EmitExit (ec);
6755 public override Reachability MarkReachable (Reachability rc)
6757 base.MarkReachable (rc);
6759 decl.MarkReachable (rc);
6761 rc = statement.MarkReachable (rc);
6763 // TODO: What if there is local exit?
6764 has_ret = rc.IsUnreachable;
6768 protected override void CloneTo (CloneContext clonectx, Statement t)
6770 Fixed target = (Fixed) t;
6772 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6773 target.statement = statement.Clone (clonectx);
6776 public override object Accept (StructuralVisitor visitor)
6778 return visitor.Visit (this);
6782 public class Catch : Statement
6784 class CatchVariableStore : Statement
6786 readonly Catch ctch;
6788 public CatchVariableStore (Catch ctch)
6793 protected override void CloneTo (CloneContext clonectx, Statement target)
6797 protected override void DoEmit (EmitContext ec)
6799 // Emits catch variable debug information inside correct block
6800 ctch.EmitCatchVariableStore (ec);
6803 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6809 class FilterStatement : Statement
6811 readonly Catch ctch;
6813 public FilterStatement (Catch ctch)
6818 protected override void CloneTo (CloneContext clonectx, Statement target)
6822 protected override void DoEmit (EmitContext ec)
6824 if (ctch.li != null) {
6825 if (ctch.hoisted_temp != null)
6826 ctch.hoisted_temp.Emit (ec);
6830 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6831 ec.Emit (OpCodes.Box, ctch.type);
6834 var expr_start = ec.DefineLabel ();
6835 var end = ec.DefineLabel ();
6837 ec.Emit (OpCodes.Brtrue_S, expr_start);
6839 ec.Emit (OpCodes.Br, end);
6840 ec.MarkLabel (expr_start);
6842 ctch.Filter.Emit (ec);
6845 ec.Emit (OpCodes.Endfilter);
6846 ec.BeginFilterHandler ();
6847 ec.Emit (OpCodes.Pop);
6850 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6852 ctch.Filter.FlowAnalysis (fc);
6856 public override bool Resolve (BlockContext bc)
6858 ctch.Filter = ctch.Filter.Resolve (bc);
6860 if (ctch.Filter != null) {
6861 if (ctch.Filter.ContainsEmitWithAwait ()) {
6862 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6865 var c = ctch.Filter as Constant;
6866 if (c != null && !c.IsDefaultValue) {
6867 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6875 ExplicitBlock block;
6877 FullNamedExpression type_expr;
6878 CompilerAssign assign;
6880 LocalTemporary hoisted_temp;
6882 public Catch (ExplicitBlock block, Location loc)
6890 public ExplicitBlock Block {
6896 public TypeSpec CatchType {
6902 public Expression Filter {
6906 public bool IsGeneral {
6908 return type_expr == null;
6912 public FullNamedExpression TypeExpression {
6921 public LocalVariable Variable {
6932 protected override void DoEmit (EmitContext ec)
6934 if (Filter != null) {
6935 ec.BeginExceptionFilterBlock ();
6936 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6938 if (Block.HasAwait) {
6939 Block.EmitScopeInitialization (ec);
6948 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6950 ec.BeginCatchBlock (CatchType);
6953 ec.Emit (OpCodes.Pop);
6955 if (Block.HasAwait) {
6957 EmitCatchVariableStore (ec);
6963 void EmitCatchVariableStore (EmitContext ec)
6965 li.CreateBuilder (ec);
6968 // For hoisted catch variable we have to use a temporary local variable
6969 // for captured variable initialization during storey setup because variable
6970 // needs to be on the stack after storey instance for stfld operation
6972 if (li.HoistedVariant != null) {
6973 hoisted_temp = new LocalTemporary (li.Type);
6974 hoisted_temp.Store (ec);
6976 // switch to assignment from temporary variable and not from top of the stack
6977 assign.UpdateSource (hoisted_temp);
6981 public override bool Resolve (BlockContext bc)
6983 using (bc.Set (ResolveContext.Options.CatchScope)) {
6984 if (type_expr == null) {
6985 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6986 if (!block.HasAwait || Filter != null)
6987 block.AddScopeStatement (new CatchVariableStore (this));
6989 Expression source = new EmptyExpression (li.Type);
6990 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6991 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6994 type = type_expr.ResolveAsType (bc);
6999 CreateExceptionVariable (type);
7001 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
7002 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
7003 } else if (li != null) {
7005 li.PrepareAssignmentAnalysis (bc);
7007 // source variable is at the top of the stack
7008 Expression source = new EmptyExpression (li.Type);
7009 if (li.Type.IsGenericParameter)
7010 source = new UnboxCast (source, li.Type);
7012 if (!block.HasAwait || Filter != null)
7013 block.AddScopeStatement (new CatchVariableStore (this));
7016 // Uses Location.Null to hide from symbol file
7018 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
7019 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
7023 if (Filter != null) {
7024 Block.AddScopeStatement (new FilterStatement (this));
7027 Block.SetCatchBlock ();
7028 return Block.Resolve (bc);
7032 bool CreateExceptionVariable (TypeSpec type)
7034 if (!Block.HasAwait)
7037 // TODO: Scan the block for rethrow expression
7038 //if (!Block.HasRethrow)
7041 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
7045 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7047 if (li != null && !li.IsCompilerGenerated) {
7048 fc.SetVariableAssigned (li.VariableInfo, true);
7051 return block.FlowAnalysis (fc);
7054 public override Reachability MarkReachable (Reachability rc)
7056 base.MarkReachable (rc);
7058 var c = Filter as Constant;
7059 if (c != null && c.IsDefaultValue)
7060 return Reachability.CreateUnreachable ();
7062 return block.MarkReachable (rc);
7065 protected override void CloneTo (CloneContext clonectx, Statement t)
7067 Catch target = (Catch) t;
7069 if (type_expr != null)
7070 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
7073 target.Filter = Filter.Clone (clonectx);
7075 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
7079 public class TryFinally : TryFinallyBlock
7082 List<DefiniteAssignmentBitSet> try_exit_dat;
7083 List<Tuple<Label, bool>> redirected_jumps;
7084 Label? start_fin_label;
7086 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7092 public ExplicitBlock FinallyBlock {
7098 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7100 if (try_exit_dat == null)
7101 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7103 try_exit_dat.Add (vector);
7106 public override bool Resolve (BlockContext bc)
7108 bool ok = base.Resolve (bc);
7110 fini.SetFinallyBlock ();
7111 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7112 ok &= fini.Resolve (bc);
7118 protected override void EmitTryBody (EmitContext ec)
7120 if (fini.HasAwait) {
7121 if (ec.TryFinallyUnwind == null)
7122 ec.TryFinallyUnwind = new List<TryFinally> ();
7124 ec.TryFinallyUnwind.Add (this);
7127 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7128 ec.EndExceptionBlock ();
7130 ec.TryFinallyUnwind.Remove (this);
7132 if (start_fin_label != null)
7133 ec.MarkLabel (start_fin_label.Value);
7141 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7146 return base.EmitBeginFinallyBlock (ec);
7149 public override void EmitFinallyBody (EmitContext ec)
7151 if (!fini.HasAwait) {
7157 // Emits catch block like
7159 // catch (object temp) {
7160 // this.exception_field = temp;
7163 var type = ec.BuiltinTypes.Object;
7164 ec.BeginCatchBlock (type);
7166 var temp = ec.GetTemporaryLocal (type);
7167 ec.Emit (OpCodes.Stloc, temp);
7169 var exception_field = ec.GetTemporaryField (type);
7170 exception_field.AutomaticallyReuse = false;
7172 ec.Emit (OpCodes.Ldloc, temp);
7173 exception_field.EmitAssignFromStack (ec);
7175 ec.EndExceptionBlock ();
7177 ec.FreeTemporaryLocal (temp, type);
7182 // Emits exception rethrow
7184 // if (this.exception_field != null)
7185 // throw this.exception_field;
7187 exception_field.Emit (ec);
7188 var skip_throw = ec.DefineLabel ();
7189 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7190 exception_field.Emit (ec);
7191 ec.Emit (OpCodes.Throw);
7192 ec.MarkLabel (skip_throw);
7194 exception_field.PrepareCleanup (ec);
7196 EmitUnwindFinallyTable (ec);
7199 bool IsParentBlock (Block block)
7201 for (Block b = fini; b != null; b = b.Parent) {
7209 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock, bool unwindProtect)
7212 if (labelBlock != null) {
7213 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7214 var fin = ec.TryFinallyUnwind [idx - 1];
7215 if (!fin.IsParentBlock (labelBlock))
7222 bool set_return_state = true;
7224 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7225 var fin = ec.TryFinallyUnwind [idx];
7226 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7229 fin.EmitRedirectedExit (ec, label, initializer, set_return_state, unwindProtect);
7230 set_return_state = false;
7232 if (fin.start_fin_label == null) {
7233 fin.start_fin_label = ec.DefineLabel ();
7236 label = fin.start_fin_label.Value;
7242 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer, bool unwindProtect)
7244 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null, unwindProtect);
7247 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState, bool unwindProtect)
7249 if (redirected_jumps == null) {
7250 redirected_jumps = new List<Tuple<Label, bool>> ();
7252 // Add fallthrough label
7253 redirected_jumps.Add (Tuple.Create (ec.DefineLabel (), false));
7256 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7259 int index = redirected_jumps.FindIndex (l => l.Item1 == label);
7261 redirected_jumps.Add (Tuple.Create (label, unwindProtect));
7262 index = redirected_jumps.Count - 1;
7266 // Indicates we have captured exit jump
7268 if (setReturnState) {
7269 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7270 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7275 // Emits state table of jumps outside of try block and reload of return
7276 // value when try block returns value
7278 void EmitUnwindFinallyTable (EmitContext ec)
7280 if (redirected_jumps == null)
7283 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7284 initializer.HoistedReturnState.EmitLoad (ec);
7286 var jumps_table = new Label [redirected_jumps.Count];
7287 List<Tuple<Label, Label>> leave_redirect = null;
7288 for (int i = 0; i < jumps_table.Length; ++i) {
7289 var val = redirected_jumps [i];
7292 if (leave_redirect == null)
7293 leave_redirect = new List<Tuple<Label, Label>> ();
7294 var label = ec.DefineLabel ();
7295 leave_redirect.Add (Tuple.Create (label, val.Item1));
7296 jumps_table [i] = label;
7298 jumps_table [i] = val.Item1;
7302 ec.Emit (OpCodes.Switch, jumps_table);
7304 if (leave_redirect != null) {
7305 foreach (var entry in leave_redirect) {
7306 ec.MarkLabel (entry.Item1);
7307 ec.Emit (OpCodes.Leave, entry.Item2);
7311 // Mark fallthrough label
7312 ec.MarkLabel (jumps_table [0]);
7315 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7317 var da = fc.BranchDefiniteAssignment ();
7319 var tf = fc.TryFinally;
7320 fc.TryFinally = this;
7322 var res_stmt = Statement.FlowAnalysis (fc);
7326 var try_da = fc.DefiniteAssignment;
7327 fc.DefiniteAssignment = da;
7329 var res_fin = fini.FlowAnalysis (fc);
7331 if (try_exit_dat != null) {
7333 // try block has global exit but we need to run definite assignment check
7334 // for parameter block out parameter after finally block because it's always
7335 // executed before exit
7337 foreach (var try_da_part in try_exit_dat)
7338 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7340 try_exit_dat = null;
7343 fc.DefiniteAssignment |= try_da;
7344 return res_stmt | res_fin;
7347 public override Reachability MarkReachable (Reachability rc)
7350 // Mark finally block first for any exit statement in try block
7351 // to know whether the code which follows finally is reachable
7353 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7356 protected override void CloneTo (CloneContext clonectx, Statement t)
7358 TryFinally target = (TryFinally) t;
7360 target.stmt = stmt.Clone (clonectx);
7362 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7365 public override object Accept (StructuralVisitor visitor)
7367 return visitor.Visit (this);
7371 public class TryCatch : ExceptionStatement
7374 List<Catch> clauses;
7375 readonly bool inside_try_finally;
7376 List<Catch> catch_sm;
7378 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7382 this.clauses = catch_clauses;
7383 this.inside_try_finally = inside_try_finally;
7386 public List<Catch> Clauses {
7392 public bool HasClauseWithAwait {
7394 return catch_sm != null;
7398 public bool IsTryCatchFinally {
7400 return inside_try_finally;
7404 public override bool Resolve (BlockContext bc)
7408 using (bc.Set (ResolveContext.Options.TryScope)) {
7410 parent_try_block = bc.CurrentTryBlock;
7412 if (IsTryCatchFinally) {
7413 ok = Block.Resolve (bc);
7415 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7416 bc.CurrentTryBlock = this;
7417 ok = Block.Resolve (bc);
7418 bc.CurrentTryBlock = parent_try_block;
7423 var prev_catch = bc.CurrentTryCatch;
7424 bc.CurrentTryCatch = this;
7426 for (int i = 0; i < clauses.Count; ++i) {
7429 ok &= c.Resolve (bc);
7431 if (c.Block.HasAwait) {
7432 if (catch_sm == null)
7433 catch_sm = new List<Catch> ();
7438 if (c.Filter != null)
7441 TypeSpec resolved_type = c.CatchType;
7442 if (resolved_type == null)
7445 for (int ii = 0; ii < clauses.Count; ++ii) {
7449 if (clauses[ii].Filter != null)
7452 if (clauses[ii].IsGeneral) {
7453 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7456 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7459 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7462 bc.Report.Warning (1058, 1, c.loc,
7463 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7471 var ct = clauses[ii].CatchType;
7475 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7476 bc.Report.Error (160, c.loc,
7477 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7478 ct.GetSignatureForError ());
7484 bc.CurrentTryCatch = prev_catch;
7486 return base.Resolve (bc) && ok;
7489 protected sealed override void DoEmit (EmitContext ec)
7491 if (!inside_try_finally)
7492 EmitTryBodyPrepare (ec);
7496 LocalBuilder state_variable = null;
7497 foreach (Catch c in clauses) {
7500 if (catch_sm != null) {
7501 if (state_variable == null) {
7503 // Cannot reuse temp variable because non-catch path assumes the value is 0
7504 // which may not be true for reused local variable
7506 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7509 var index = catch_sm.IndexOf (c);
7513 ec.EmitInt (index + 1);
7514 ec.Emit (OpCodes.Stloc, state_variable);
7518 if (state_variable == null) {
7519 if (!inside_try_finally)
7520 ec.EndExceptionBlock ();
7522 ec.EndExceptionBlock ();
7524 ec.Emit (OpCodes.Ldloc, state_variable);
7526 var labels = new Label [catch_sm.Count + 1];
7527 for (int i = 0; i < labels.Length; ++i) {
7528 labels [i] = ec.DefineLabel ();
7531 var end = ec.DefineLabel ();
7532 ec.Emit (OpCodes.Switch, labels);
7534 // 0 value is default label
7535 ec.MarkLabel (labels [0]);
7536 ec.Emit (OpCodes.Br, end);
7538 var atv = ec.AsyncThrowVariable;
7540 for (int i = 0; i < catch_sm.Count; ++i) {
7541 if (c != null && c.Block.HasReachableClosingBrace)
7542 ec.Emit (OpCodes.Br, end);
7544 ec.MarkLabel (labels [i + 1]);
7547 ec.Emit (OpCodes.Stloc, state_variable);
7550 ec.AsyncThrowVariable = c.Variable;
7553 ec.AsyncThrowVariable = atv;
7559 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7561 var start_fc = fc.BranchDefiniteAssignment ();
7562 var res = Block.FlowAnalysis (fc);
7564 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7566 foreach (var c in clauses) {
7567 fc.BranchDefiniteAssignment (start_fc);
7568 if (!c.FlowAnalysis (fc)) {
7570 try_fc = fc.DefiniteAssignment;
7572 try_fc &= fc.DefiniteAssignment;
7578 fc.DefiniteAssignment = try_fc ?? start_fc;
7579 parent_try_block = null;
7583 public override Reachability MarkReachable (Reachability rc)
7585 if (rc.IsUnreachable)
7588 base.MarkReachable (rc);
7590 var tc_rc = Block.MarkReachable (rc);
7592 foreach (var c in clauses)
7593 tc_rc &= c.MarkReachable (rc);
7598 protected override void CloneTo (CloneContext clonectx, Statement t)
7600 TryCatch target = (TryCatch) t;
7602 target.Block = clonectx.LookupBlock (Block);
7603 if (clauses != null){
7604 target.clauses = new List<Catch> ();
7605 foreach (Catch c in clauses)
7606 target.clauses.Add ((Catch) c.Clone (clonectx));
7610 public override object Accept (StructuralVisitor visitor)
7612 return visitor.Visit (this);
7616 public class Using : TryFinallyBlock
7618 public class VariableDeclaration : BlockVariable
7620 Statement dispose_call;
7622 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7627 public VariableDeclaration (LocalVariable li, Location loc)
7634 public VariableDeclaration (Expression expr)
7637 loc = expr.Location;
7643 public bool IsNested { get; private set; }
7647 public void EmitDispose (EmitContext ec)
7649 dispose_call.Emit (ec);
7652 public override bool Resolve (BlockContext bc)
7657 return base.Resolve (bc, false);
7660 public Expression ResolveExpression (BlockContext bc)
7662 var e = Initializer.Resolve (bc);
7666 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7667 Initializer = ResolveInitializer (bc, Variable, e);
7671 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7673 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7674 initializer = initializer.Resolve (bc);
7675 if (initializer == null)
7678 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7679 Arguments args = new Arguments (1);
7680 args.Add (new Argument (initializer));
7681 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7682 if (initializer == null)
7685 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7686 dispose_call = CreateDisposeCall (bc, var);
7687 dispose_call.Resolve (bc);
7689 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7692 if (li == Variable) {
7693 CheckIDiposableConversion (bc, li, initializer);
7694 dispose_call = CreateDisposeCall (bc, li);
7695 dispose_call.Resolve (bc);
7698 return base.ResolveInitializer (bc, li, initializer);
7701 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7705 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7706 if (type.IsNullableType) {
7707 // it's handled in CreateDisposeCall
7711 if (type != InternalType.ErrorType) {
7712 bc.Report.SymbolRelatedToPreviousError (type);
7713 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7714 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7715 type.GetSignatureForError ());
7722 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7724 var target = bc.BuiltinTypes.IDisposable;
7725 var tp = type as TypeParameterSpec;
7727 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7729 return type.ImplementsInterface (target, false);
7732 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7734 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7736 var loc = lv.Location;
7738 var idt = bc.BuiltinTypes.IDisposable;
7739 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7741 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7742 dispose_mg.InstanceExpression = type.IsNullableType ?
7743 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7747 // Hide it from symbol file via null location
7749 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7751 // Add conditional call when disposing possible null variable
7752 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7753 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7758 public void ResolveDeclaratorInitializer (BlockContext bc)
7760 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7763 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7765 for (int i = declarators.Count - 1; i >= 0; --i) {
7766 var d = declarators [i];
7767 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7768 vd.Initializer = d.Initializer;
7770 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7771 vd.dispose_call.Resolve (bc);
7773 stmt = new Using (vd, stmt, d.Variable.Location);
7780 public override object Accept (StructuralVisitor visitor)
7782 return visitor.Visit (this);
7786 VariableDeclaration decl;
7788 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7794 public Using (Expression expr, Statement stmt, Location loc)
7797 this.decl = new VariableDeclaration (expr);
7802 public Expression Expr {
7804 return decl.Variable == null ? decl.Initializer : null;
7808 public BlockVariable Variables {
7816 public override void Emit (EmitContext ec)
7819 // Don't emit sequence point it will be set on variable declaration
7824 protected override void EmitTryBodyPrepare (EmitContext ec)
7827 base.EmitTryBodyPrepare (ec);
7830 protected override void EmitTryBody (EmitContext ec)
7835 public override void EmitFinallyBody (EmitContext ec)
7837 decl.EmitDispose (ec);
7840 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7842 decl.FlowAnalysis (fc);
7843 return stmt.FlowAnalysis (fc);
7846 public override Reachability MarkReachable (Reachability rc)
7848 decl.MarkReachable (rc);
7849 return base.MarkReachable (rc);
7852 public override bool Resolve (BlockContext ec)
7854 VariableReference vr;
7855 bool vr_locked = false;
7857 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7858 if (decl.Variable == null) {
7859 vr = decl.ResolveExpression (ec) as VariableReference;
7861 vr_locked = vr.IsLockedByStatement;
7862 vr.IsLockedByStatement = true;
7865 if (decl.IsNested) {
7866 decl.ResolveDeclaratorInitializer (ec);
7868 if (!decl.Resolve (ec))
7871 if (decl.Declarators != null) {
7872 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7880 var ok = base.Resolve (ec);
7883 vr.IsLockedByStatement = vr_locked;
7888 protected override void CloneTo (CloneContext clonectx, Statement t)
7890 Using target = (Using) t;
7892 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7893 target.stmt = stmt.Clone (clonectx);
7896 public override object Accept (StructuralVisitor visitor)
7898 return visitor.Visit (this);
7903 /// Implementation of the foreach C# statement
7905 public class Foreach : LoopStatement
7907 abstract class IteratorStatement : Statement
7909 protected readonly Foreach for_each;
7911 protected IteratorStatement (Foreach @foreach)
7913 this.for_each = @foreach;
7914 this.loc = @foreach.expr.Location;
7917 protected override void CloneTo (CloneContext clonectx, Statement target)
7919 throw new NotImplementedException ();
7922 public override void Emit (EmitContext ec)
7924 if (ec.EmitAccurateDebugInfo) {
7925 ec.Emit (OpCodes.Nop);
7931 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7933 throw new NotImplementedException ();
7937 sealed class ArrayForeach : IteratorStatement
7939 TemporaryVariableReference[] lengths;
7940 Expression [] length_exprs;
7941 StatementExpression[] counter;
7942 TemporaryVariableReference[] variables;
7944 TemporaryVariableReference copy;
7946 public ArrayForeach (Foreach @foreach, int rank)
7949 counter = new StatementExpression[rank];
7950 variables = new TemporaryVariableReference[rank];
7951 length_exprs = new Expression [rank];
7954 // Only use temporary length variables when dealing with
7955 // multi-dimensional arrays
7958 lengths = new TemporaryVariableReference [rank];
7961 public override bool Resolve (BlockContext ec)
7963 Block variables_block = for_each.variable.Block;
7964 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7967 int rank = length_exprs.Length;
7968 Arguments list = new Arguments (rank);
7969 for (int i = 0; i < rank; i++) {
7970 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7972 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7973 counter[i].Resolve (ec);
7976 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7978 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7979 lengths[i].Resolve (ec);
7981 Arguments args = new Arguments (1);
7982 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7983 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7986 list.Add (new Argument (v));
7989 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7994 if (for_each.type is VarExpr) {
7995 // Infer implicitly typed local variable from foreach array type
7996 var_type = access.Type;
7998 var_type = for_each.type.ResolveAsType (ec);
8000 if (var_type == null)
8003 access = Convert.ExplicitConversion (ec, access, var_type, loc);
8008 for_each.variable.Type = var_type;
8010 var prev_block = ec.CurrentBlock;
8011 ec.CurrentBlock = variables_block;
8012 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
8013 ec.CurrentBlock = prev_block;
8015 if (variable_ref == null)
8018 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
8020 return for_each.body.Resolve (ec);
8023 protected override void DoEmit (EmitContext ec)
8025 copy.EmitAssign (ec, for_each.expr);
8027 int rank = length_exprs.Length;
8028 Label[] test = new Label [rank];
8029 Label[] loop = new Label [rank];
8031 for (int i = 0; i < rank; i++) {
8032 test [i] = ec.DefineLabel ();
8033 loop [i] = ec.DefineLabel ();
8035 if (lengths != null)
8036 lengths [i].EmitAssign (ec, length_exprs [i]);
8039 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
8040 for (int i = 0; i < rank; i++) {
8041 variables [i].EmitAssign (ec, zero);
8043 ec.Emit (OpCodes.Br, test [i]);
8044 ec.MarkLabel (loop [i]);
8047 for_each.body.Emit (ec);
8049 ec.MarkLabel (ec.LoopBegin);
8050 ec.Mark (for_each.expr.Location);
8052 for (int i = rank - 1; i >= 0; i--){
8053 counter [i].Emit (ec);
8055 ec.MarkLabel (test [i]);
8056 variables [i].Emit (ec);
8058 if (lengths != null)
8059 lengths [i].Emit (ec);
8061 length_exprs [i].Emit (ec);
8063 ec.Emit (OpCodes.Blt, loop [i]);
8066 ec.MarkLabel (ec.LoopEnd);
8070 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
8072 class RuntimeDispose : Using.VariableDeclaration
8074 public RuntimeDispose (LocalVariable lv, Location loc)
8080 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
8082 // Defered to runtime check
8085 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
8087 var idt = bc.BuiltinTypes.IDisposable;
8090 // Fabricates code like
8092 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
8095 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
8097 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
8098 dispose_variable.CreateReferenceExpression (bc, loc),
8099 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
8100 loc), new NullLiteral (loc));
8102 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
8104 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
8105 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
8107 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
8108 return new If (idisaposable_test, dispose, loc);
8112 LocalVariable variable;
8114 Statement statement;
8115 ExpressionStatement init;
8116 TemporaryVariableReference enumerator_variable;
8117 bool ambiguous_getenumerator_name;
8119 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8122 this.variable = var;
8126 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8128 rc.Report.SymbolRelatedToPreviousError (enumerator);
8129 rc.Report.Error (202, loc,
8130 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8131 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8134 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8137 // Option 1: Try to match by name GetEnumerator first
8139 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8140 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8142 var mg = mexpr as MethodGroupExpr;
8144 mg.InstanceExpression = expr;
8145 Arguments args = new Arguments (0);
8146 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8148 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8149 if (ambiguous_getenumerator_name)
8152 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8158 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8161 PredefinedMember<MethodSpec> iface_candidate = null;
8162 var ptypes = rc.Module.PredefinedTypes;
8163 var gen_ienumerable = ptypes.IEnumerableGeneric;
8164 if (!gen_ienumerable.Define ())
8165 gen_ienumerable = null;
8167 var ifaces = t.Interfaces;
8168 if (ifaces != null) {
8169 foreach (var iface in ifaces) {
8170 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8171 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8172 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8173 rc.Report.Error (1640, loc,
8174 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8175 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8180 // TODO: Cache this somehow
8181 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8182 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8187 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8188 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8193 if (iface_candidate == null) {
8194 if (expr.Type != InternalType.ErrorType) {
8195 rc.Report.Error (1579, loc,
8196 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8197 expr.Type.GetSignatureForError (), "GetEnumerator");
8203 var method = iface_candidate.Resolve (loc);
8207 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8208 mg.InstanceExpression = expr;
8212 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8214 var ms = MemberCache.FindMember (enumerator.ReturnType,
8215 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8216 BindingRestriction.InstanceOnly) as MethodSpec;
8218 if (ms == null || !ms.IsPublic) {
8219 Error_WrongEnumerator (rc, enumerator);
8223 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8226 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8228 var ps = MemberCache.FindMember (enumerator.ReturnType,
8229 MemberFilter.Property ("Current", null),
8230 BindingRestriction.InstanceOnly) as PropertySpec;
8232 if (ps == null || !ps.IsPublic) {
8233 Error_WrongEnumerator (rc, enumerator);
8240 public override bool Resolve (BlockContext ec)
8242 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8245 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8246 } else if (expr.Type.IsNullableType) {
8247 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8250 var get_enumerator_mg = ResolveGetEnumerator (ec);
8251 if (get_enumerator_mg == null) {
8255 var get_enumerator = get_enumerator_mg.BestCandidate;
8256 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8257 enumerator_variable.Resolve (ec);
8259 // Prepare bool MoveNext ()
8260 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8261 if (move_next_mg == null) {
8265 move_next_mg.InstanceExpression = enumerator_variable;
8267 // Prepare ~T~ Current { get; }
8268 var current_prop = ResolveCurrent (ec, get_enumerator);
8269 if (current_prop == null) {
8273 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8274 if (current_pe == null)
8277 VarExpr ve = for_each.type as VarExpr;
8281 // Source type is dynamic, set element type to dynamic too
8282 variable.Type = ec.BuiltinTypes.Dynamic;
8284 // Infer implicitly typed local variable from foreach enumerable type
8285 variable.Type = current_pe.Type;
8289 // Explicit cast of dynamic collection elements has to be done at runtime
8290 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8293 variable.Type = for_each.type.ResolveAsType (ec);
8295 if (variable.Type == null)
8298 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8299 if (current_pe == null)
8303 var prev_block = ec.CurrentBlock;
8304 ec.CurrentBlock = for_each.variable.Block;
8305 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8306 ec.CurrentBlock = prev_block;
8307 if (variable_ref == null)
8310 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8312 var init = new Invocation.Predefined (get_enumerator_mg, null);
8314 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8315 for_each.body, Location.Null);
8317 var enum_type = enumerator_variable.Type;
8320 // Add Dispose method call when enumerator can be IDisposable
8322 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8323 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8325 // Runtime Dispose check
8327 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8328 vd.Initializer = init;
8329 statement = new Using (vd, statement, Location.Null);
8332 // No Dispose call needed
8334 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8335 this.init.Resolve (ec);
8339 // Static Dispose check
8341 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8342 vd.Initializer = init;
8343 statement = new Using (vd, statement, Location.Null);
8346 return statement.Resolve (ec);
8349 protected override void DoEmit (EmitContext ec)
8351 enumerator_variable.LocalInfo.CreateBuilder (ec);
8354 init.EmitStatement (ec);
8356 statement.Emit (ec);
8359 #region IErrorHandler Members
8361 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8363 ec.Report.SymbolRelatedToPreviousError (best);
8364 ec.Report.Warning (278, 2, expr.Location,
8365 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8366 expr.Type.GetSignatureForError (), "enumerable",
8367 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8369 ambiguous_getenumerator_name = true;
8373 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8378 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8383 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8392 LocalVariable variable;
8396 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8400 this.variable = var;
8406 public Expression Expr {
8407 get { return expr; }
8410 public Expression TypeExpression {
8411 get { return type; }
8414 public LocalVariable Variable {
8415 get { return variable; }
8418 public override Reachability MarkReachable (Reachability rc)
8420 base.MarkReachable (rc);
8422 body.MarkReachable (rc);
8427 public override bool Resolve (BlockContext ec)
8429 expr = expr.Resolve (ec);
8434 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8438 body.AddStatement (Statement);
8440 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8441 Statement = new ArrayForeach (this, 1);
8442 } else if (expr.Type is ArrayContainer) {
8443 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8445 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8446 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8447 expr.ExprClassName);
8451 Statement = new CollectionForeach (this, variable, expr);
8454 return base.Resolve (ec);
8457 protected override void DoEmit (EmitContext ec)
8459 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8460 ec.LoopBegin = ec.DefineLabel ();
8461 ec.LoopEnd = ec.DefineLabel ();
8463 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8464 body.Explicit.DisableDebugScopeIndex ();
8466 variable.CreateBuilder (ec);
8468 Statement.Emit (ec);
8472 ec.LoopBegin = old_begin;
8473 ec.LoopEnd = old_end;
8476 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8478 expr.FlowAnalysis (fc);
8480 var da = fc.BranchDefiniteAssignment ();
8481 body.FlowAnalysis (fc);
8482 fc.DefiniteAssignment = da;
8486 protected override void CloneTo (CloneContext clonectx, Statement t)
8488 Foreach target = (Foreach) t;
8490 target.type = type.Clone (clonectx);
8491 target.expr = expr.Clone (clonectx);
8492 target.body = (Block) body.Clone (clonectx);
8493 target.Statement = Statement.Clone (clonectx);
8496 public override object Accept (StructuralVisitor visitor)
8498 return visitor.Visit (this);
8502 class SentinelStatement: Statement
8504 protected override void CloneTo (CloneContext clonectx, Statement target)
8508 protected override void DoEmit (EmitContext ec)
8510 var l = ec.DefineLabel ();
8512 ec.Emit (OpCodes.Br_S, l);
8515 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8517 throw new NotImplementedException ();