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);
93 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
98 // Special handling cases
101 return DoFlowAnalysis (fc);
104 if (this is EmptyStatement)
107 if (fc.UnreachableReported)
110 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
111 fc.UnreachableReported = true;
115 public virtual Reachability MarkReachable (Reachability rc)
117 if (!rc.IsUnreachable)
123 protected void CheckExitBoundaries (BlockContext bc, Block scope)
125 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
126 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
130 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
131 if (b.IsFinallyBlock) {
132 Error_FinallyClauseExit (bc);
138 protected void Error_FinallyClauseExit (BlockContext bc)
140 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
144 public sealed class EmptyStatement : Statement
146 public EmptyStatement (Location loc)
151 public override bool Resolve (BlockContext ec)
156 public override void Emit (EmitContext ec)
160 protected override void DoEmit (EmitContext ec)
162 throw new NotSupportedException ();
165 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
170 protected override void CloneTo (CloneContext clonectx, Statement target)
175 public override object Accept (StructuralVisitor visitor)
177 return visitor.Visit (this);
181 public class If : Statement {
183 public Statement TrueStatement;
184 public Statement FalseStatement;
186 bool true_returns, false_returns;
188 public If (Expression bool_expr, Statement true_statement, Location l)
189 : this (bool_expr, true_statement, null, l)
193 public If (Expression bool_expr,
194 Statement true_statement,
195 Statement false_statement,
198 this.expr = bool_expr;
199 TrueStatement = true_statement;
200 FalseStatement = false_statement;
204 public Expression Expr {
210 public override bool Resolve (BlockContext ec)
212 expr = expr.Resolve (ec);
214 var ok = TrueStatement.Resolve (ec);
216 if (FalseStatement != null) {
217 ok &= FalseStatement.Resolve (ec);
223 protected override void DoEmit (EmitContext ec)
225 Label false_target = ec.DefineLabel ();
229 // If we're a boolean constant, Resolve() already
230 // eliminated dead code for us.
232 Constant c = expr as Constant;
234 c.EmitSideEffect (ec);
236 if (!c.IsDefaultValue)
237 TrueStatement.Emit (ec);
238 else if (FalseStatement != null)
239 FalseStatement.Emit (ec);
244 expr.EmitBranchable (ec, false_target, false);
246 TrueStatement.Emit (ec);
248 if (FalseStatement != null){
249 bool branch_emitted = false;
251 end = ec.DefineLabel ();
253 ec.Emit (OpCodes.Br, end);
254 branch_emitted = true;
257 ec.MarkLabel (false_target);
258 FalseStatement.Emit (ec);
263 ec.MarkLabel (false_target);
267 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
269 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
271 expr.FlowAnalysis (fc);
273 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
275 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
276 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
278 var res = TrueStatement.FlowAnalysis (fc);
280 if (FalseStatement == null) {
282 fc.DefiniteAssignment = da_false;
284 fc.DefiniteAssignment &= da_false;
290 fc.DefiniteAssignment = da_false;
291 return FalseStatement.FlowAnalysis (fc);
294 var da_true = fc.DefiniteAssignment;
296 fc.DefiniteAssignment = da_false;
297 res &= FalseStatement.FlowAnalysis (fc);
299 if (!TrueStatement.IsUnreachable) {
300 if (false_returns || FalseStatement.IsUnreachable)
301 fc.DefiniteAssignment = da_true;
303 fc.DefiniteAssignment &= da_true;
309 public override Reachability MarkReachable (Reachability rc)
311 if (rc.IsUnreachable)
314 base.MarkReachable (rc);
316 var c = expr as Constant;
318 bool take = !c.IsDefaultValue;
320 rc = TrueStatement.MarkReachable (rc);
322 if (FalseStatement != null)
323 rc = FalseStatement.MarkReachable (rc);
329 var true_rc = TrueStatement.MarkReachable (rc);
330 true_returns = true_rc.IsUnreachable;
332 if (FalseStatement == null)
335 var false_rc = FalseStatement.MarkReachable (rc);
336 false_returns = false_rc.IsUnreachable;
338 return true_rc & false_rc;
341 protected override void CloneTo (CloneContext clonectx, Statement t)
345 target.expr = expr.Clone (clonectx);
346 target.TrueStatement = TrueStatement.Clone (clonectx);
347 if (FalseStatement != null)
348 target.FalseStatement = FalseStatement.Clone (clonectx);
351 public override object Accept (StructuralVisitor visitor)
353 return visitor.Visit (this);
357 public class Do : LoopStatement
359 public Expression expr;
360 bool iterator_reachable, end_reachable;
362 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
367 WhileLocation = whileLocation;
370 public Location WhileLocation {
374 public override bool Resolve (BlockContext bc)
376 var ok = base.Resolve (bc);
378 expr = expr.Resolve (bc);
383 protected override void DoEmit (EmitContext ec)
385 Label loop = ec.DefineLabel ();
386 Label old_begin = ec.LoopBegin;
387 Label old_end = ec.LoopEnd;
389 ec.LoopBegin = ec.DefineLabel ();
390 ec.LoopEnd = ec.DefineLabel ();
394 ec.MarkLabel (ec.LoopBegin);
396 // Mark start of while condition
397 ec.Mark (WhileLocation);
400 // Dead code elimination
402 if (expr is Constant) {
403 bool res = !((Constant) expr).IsDefaultValue;
405 expr.EmitSideEffect (ec);
407 ec.Emit (OpCodes.Br, loop);
409 expr.EmitBranchable (ec, loop, true);
412 ec.MarkLabel (ec.LoopEnd);
414 ec.LoopBegin = old_begin;
415 ec.LoopEnd = old_end;
418 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
420 var res = Statement.FlowAnalysis (fc);
422 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
423 expr.FlowAnalysis (fc);
425 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
427 if (res && !iterator_reachable)
428 return !end_reachable;
430 if (!end_reachable) {
431 var c = expr as Constant;
432 if (c != null && !c.IsDefaultValue)
439 public override Reachability MarkReachable (Reachability rc)
441 base.MarkReachable (rc);
443 var body_rc = Statement.MarkReachable (rc);
445 if (body_rc.IsUnreachable && !iterator_reachable) {
446 expr = new UnreachableExpression (expr);
447 return end_reachable ? rc : Reachability.CreateUnreachable ();
450 if (!end_reachable) {
451 var c = expr as Constant;
452 if (c != null && !c.IsDefaultValue)
453 return Reachability.CreateUnreachable ();
459 protected override void CloneTo (CloneContext clonectx, Statement t)
463 target.Statement = Statement.Clone (clonectx);
464 target.expr = expr.Clone (clonectx);
467 public override object Accept (StructuralVisitor visitor)
469 return visitor.Visit (this);
472 public override void SetEndReachable ()
474 end_reachable = true;
477 public override void SetIteratorReachable ()
479 iterator_reachable = true;
483 public class While : LoopStatement
485 public Expression expr;
486 bool empty, infinite, end_reachable;
487 List<DefiniteAssignmentBitSet> end_reachable_das;
489 public While (BooleanExpression bool_expr, Statement statement, Location l)
492 this.expr = bool_expr;
496 public override bool Resolve (BlockContext bc)
500 expr = expr.Resolve (bc);
504 var c = expr as Constant;
506 empty = c.IsDefaultValue;
510 ok &= base.Resolve (bc);
514 protected override void DoEmit (EmitContext ec)
517 expr.EmitSideEffect (ec);
521 Label old_begin = ec.LoopBegin;
522 Label old_end = ec.LoopEnd;
524 ec.LoopBegin = ec.DefineLabel ();
525 ec.LoopEnd = ec.DefineLabel ();
528 // Inform whether we are infinite or not
530 if (expr is Constant) {
531 // expr is 'true', since the 'empty' case above handles the 'false' case
532 ec.MarkLabel (ec.LoopBegin);
534 if (ec.EmitAccurateDebugInfo)
535 ec.Emit (OpCodes.Nop);
537 expr.EmitSideEffect (ec);
539 ec.Emit (OpCodes.Br, ec.LoopBegin);
542 // Inform that we are infinite (ie, `we return'), only
543 // if we do not `break' inside the code.
545 ec.MarkLabel (ec.LoopEnd);
547 Label while_loop = ec.DefineLabel ();
549 ec.Emit (OpCodes.Br, ec.LoopBegin);
550 ec.MarkLabel (while_loop);
554 ec.MarkLabel (ec.LoopBegin);
557 expr.EmitBranchable (ec, while_loop, true);
559 ec.MarkLabel (ec.LoopEnd);
562 ec.LoopBegin = old_begin;
563 ec.LoopEnd = old_end;
566 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
568 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
570 expr.FlowAnalysis (fc);
572 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
573 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
574 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
576 Statement.FlowAnalysis (fc);
579 // Special case infinite while with breaks
581 if (end_reachable_das != null) {
582 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
583 end_reachable_das = null;
586 fc.DefiniteAssignment = da_false;
588 if (infinite && !end_reachable)
594 public override Reachability MarkReachable (Reachability rc)
596 if (rc.IsUnreachable)
599 base.MarkReachable (rc);
602 // Special case unreachable while body
605 Statement.MarkReachable (Reachability.CreateUnreachable ());
609 Statement.MarkReachable (rc);
612 // When infinite while end is unreachable via break anything what follows is unreachable too
614 if (infinite && !end_reachable)
615 return Reachability.CreateUnreachable ();
620 protected override void CloneTo (CloneContext clonectx, Statement t)
622 While target = (While) t;
624 target.expr = expr.Clone (clonectx);
625 target.Statement = Statement.Clone (clonectx);
628 public override object Accept (StructuralVisitor visitor)
630 return visitor.Visit (this);
633 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
638 if (end_reachable_das == null)
639 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
641 end_reachable_das.Add (fc.DefiniteAssignment);
644 public override void SetEndReachable ()
646 end_reachable = true;
650 public class For : LoopStatement
652 bool infinite, empty, iterator_reachable, end_reachable;
653 List<DefiniteAssignmentBitSet> end_reachable_das;
655 public For (Location l)
661 public Statement Initializer {
665 public Expression Condition {
669 public Statement Iterator {
673 public override bool Resolve (BlockContext bc)
675 Initializer.Resolve (bc);
677 if (Condition != null) {
678 Condition = Condition.Resolve (bc);
679 var condition_constant = Condition as Constant;
680 if (condition_constant != null) {
681 if (condition_constant.IsDefaultValue) {
693 Iterator.Resolve (bc);
698 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
700 Initializer.FlowAnalysis (fc);
702 DefiniteAssignmentBitSet da_false;
703 if (Condition != null) {
704 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
706 Condition.FlowAnalysis (fc);
707 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
708 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
709 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
711 da_false = fc.BranchDefiniteAssignment ();
714 Statement.FlowAnalysis (fc);
716 Iterator.FlowAnalysis (fc);
719 // Special case infinite for with breaks
721 if (end_reachable_das != null) {
722 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
723 end_reachable_das = null;
726 fc.DefiniteAssignment = da_false;
728 if (infinite && !end_reachable)
734 public override Reachability MarkReachable (Reachability rc)
736 base.MarkReachable (rc);
738 Initializer.MarkReachable (rc);
740 var body_rc = Statement.MarkReachable (rc);
741 if (!body_rc.IsUnreachable || iterator_reachable) {
742 Iterator.MarkReachable (rc);
746 // When infinite for end is unreachable via break anything what follows is unreachable too
748 if (infinite && !end_reachable) {
749 return Reachability.CreateUnreachable ();
755 protected override void DoEmit (EmitContext ec)
757 if (Initializer != null)
758 Initializer.Emit (ec);
761 Condition.EmitSideEffect (ec);
765 Label old_begin = ec.LoopBegin;
766 Label old_end = ec.LoopEnd;
767 Label loop = ec.DefineLabel ();
768 Label test = ec.DefineLabel ();
770 ec.LoopBegin = ec.DefineLabel ();
771 ec.LoopEnd = ec.DefineLabel ();
773 ec.Emit (OpCodes.Br, test);
777 ec.MarkLabel (ec.LoopBegin);
782 // If test is null, there is no test, and we are just
785 if (Condition != null) {
786 ec.Mark (Condition.Location);
789 // The Resolve code already catches the case for
790 // Test == Constant (false) so we know that
793 if (Condition is Constant) {
794 Condition.EmitSideEffect (ec);
795 ec.Emit (OpCodes.Br, loop);
797 Condition.EmitBranchable (ec, loop, true);
801 ec.Emit (OpCodes.Br, loop);
802 ec.MarkLabel (ec.LoopEnd);
804 ec.LoopBegin = old_begin;
805 ec.LoopEnd = old_end;
808 protected override void CloneTo (CloneContext clonectx, Statement t)
810 For target = (For) t;
812 if (Initializer != null)
813 target.Initializer = Initializer.Clone (clonectx);
814 if (Condition != null)
815 target.Condition = Condition.Clone (clonectx);
816 if (Iterator != null)
817 target.Iterator = Iterator.Clone (clonectx);
818 target.Statement = Statement.Clone (clonectx);
821 public override object Accept (StructuralVisitor visitor)
823 return visitor.Visit (this);
826 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
831 if (end_reachable_das == null)
832 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
834 end_reachable_das.Add (fc.DefiniteAssignment);
837 public override void SetEndReachable ()
839 end_reachable = true;
842 public override void SetIteratorReachable ()
844 iterator_reachable = true;
848 public abstract class LoopStatement : Statement
850 protected LoopStatement (Statement statement)
852 Statement = statement;
855 public Statement Statement { get; set; }
857 public override bool Resolve (BlockContext bc)
859 var prev_loop = bc.EnclosingLoop;
860 var prev_los = bc.EnclosingLoopOrSwitch;
861 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
862 Statement.Resolve (bc);
863 bc.EnclosingLoopOrSwitch = prev_los;
864 bc.EnclosingLoop = prev_loop;
870 // Needed by possibly infinite loops statements (for, while) and switch statment
872 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
876 public virtual void SetEndReachable ()
880 public virtual void SetIteratorReachable ()
885 public class StatementExpression : Statement
887 ExpressionStatement expr;
889 public StatementExpression (ExpressionStatement expr)
892 loc = expr.StartLocation;
895 public StatementExpression (ExpressionStatement expr, Location loc)
901 public ExpressionStatement Expr {
907 protected override void CloneTo (CloneContext clonectx, Statement t)
909 StatementExpression target = (StatementExpression) t;
910 target.expr = (ExpressionStatement) expr.Clone (clonectx);
913 protected override void DoEmit (EmitContext ec)
915 expr.EmitStatement (ec);
918 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
920 expr.FlowAnalysis (fc);
924 public override Reachability MarkReachable (Reachability rc)
926 base.MarkReachable (rc);
927 expr.MarkReachable (rc);
931 public override bool Resolve (BlockContext ec)
933 expr = expr.ResolveStatement (ec);
937 public override object Accept (StructuralVisitor visitor)
939 return visitor.Visit (this);
943 public class StatementErrorExpression : Statement
947 public StatementErrorExpression (Expression expr)
950 this.loc = expr.StartLocation;
953 public Expression Expr {
959 public override bool Resolve (BlockContext bc)
961 expr.Error_InvalidExpressionStatement (bc);
965 protected override void DoEmit (EmitContext ec)
967 throw new NotSupportedException ();
970 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
975 protected override void CloneTo (CloneContext clonectx, Statement target)
977 var t = (StatementErrorExpression) target;
979 t.expr = expr.Clone (clonectx);
982 public override object Accept (StructuralVisitor visitor)
984 return visitor.Visit (this);
989 // Simple version of statement list not requiring a block
991 public class StatementList : Statement
993 List<Statement> statements;
995 public StatementList (Statement first, Statement second)
997 statements = new List<Statement> { first, second };
1001 public IList<Statement> Statements {
1008 public void Add (Statement statement)
1010 statements.Add (statement);
1013 public override bool Resolve (BlockContext ec)
1015 foreach (var s in statements)
1021 protected override void DoEmit (EmitContext ec)
1023 foreach (var s in statements)
1027 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1029 foreach (var s in statements)
1030 s.FlowAnalysis (fc);
1035 public override Reachability MarkReachable (Reachability rc)
1037 base.MarkReachable (rc);
1039 Reachability res = rc;
1040 foreach (var s in statements)
1041 res = s.MarkReachable (rc);
1046 protected override void CloneTo (CloneContext clonectx, Statement target)
1048 StatementList t = (StatementList) target;
1050 t.statements = new List<Statement> (statements.Count);
1051 foreach (Statement s in statements)
1052 t.statements.Add (s.Clone (clonectx));
1055 public override object Accept (StructuralVisitor visitor)
1057 return visitor.Visit (this);
1062 // For statements which require special handling when inside try or catch block
1064 public abstract class ExitStatement : Statement
1066 protected bool unwind_protect;
1068 protected abstract bool DoResolve (BlockContext bc);
1069 protected abstract bool IsLocalExit { get; }
1071 public override bool Resolve (BlockContext bc)
1073 var res = DoResolve (bc);
1077 // We are inside finally scope but is it the scope we are exiting
1079 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1081 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1082 if (b.IsFinallyBlock) {
1083 Error_FinallyClauseExit (bc);
1087 if (b is ParametersBlock)
1093 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1097 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1102 if (fc.TryFinally != null) {
1103 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1105 fc.ParametersBlock.CheckControlExit (fc);
1113 /// Implements the return statement
1115 public class Return : ExitStatement
1119 public Return (Expression expr, Location l)
1127 public Expression Expr {
1136 protected override bool IsLocalExit {
1144 protected override bool DoResolve (BlockContext ec)
1146 var block_return_type = ec.ReturnType;
1149 if (block_return_type.Kind == MemberKind.Void)
1153 // Return must not be followed by an expression when
1154 // the method return type is Task
1156 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1157 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1158 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1160 // Extra trick not to emit ret/leave inside awaiter body
1162 expr = EmptyExpression.Null;
1166 if (storey.ReturnType.IsGenericTask)
1167 block_return_type = storey.ReturnType.TypeArguments[0];
1170 if (ec.CurrentIterator != null) {
1171 Error_ReturnFromIterator (ec);
1172 } else if (block_return_type != InternalType.ErrorType) {
1173 ec.Report.Error (126, loc,
1174 "An object of a type convertible to `{0}' is required for the return statement",
1175 block_return_type.GetSignatureForError ());
1181 expr = expr.Resolve (ec);
1183 AnonymousExpression am = ec.CurrentAnonymousMethod;
1185 if (block_return_type.Kind == MemberKind.Void) {
1186 ec.Report.Error (127, loc,
1187 "`{0}': A return keyword must not be followed by any expression when method returns void",
1188 ec.GetSignatureForError ());
1193 if (am.IsIterator) {
1194 Error_ReturnFromIterator (ec);
1198 var async_block = am as AsyncInitializer;
1199 if (async_block != null) {
1201 var storey = (AsyncTaskStorey) am.Storey;
1202 var async_type = storey.ReturnType;
1204 if (async_type == null && async_block.ReturnTypeInference != null) {
1205 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1209 if (async_type.Kind == MemberKind.Void) {
1210 ec.Report.Error (127, loc,
1211 "`{0}': A return keyword must not be followed by any expression when method returns void",
1212 ec.GetSignatureForError ());
1217 if (!async_type.IsGenericTask) {
1218 if (this is ContextualReturn)
1221 // Same error code as .NET but better error message
1222 if (async_block.DelegateType != null) {
1223 ec.Report.Error (1997, loc,
1224 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
1225 async_block.DelegateType.GetSignatureForError ());
1227 ec.Report.Error (1997, loc,
1228 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1229 ec.GetSignatureForError ());
1237 // The return type is actually Task<T> type argument
1239 if (expr.Type == async_type) {
1240 ec.Report.Error (4016, loc,
1241 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1242 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1244 block_return_type = async_type.TypeArguments[0];
1248 // Same error code as .NET but better error message
1249 if (block_return_type.Kind == MemberKind.Void) {
1250 ec.Report.Error (127, loc,
1251 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
1252 am.GetSignatureForError ());
1257 var l = am as AnonymousMethodBody;
1258 if (l != null && expr != null) {
1259 if (l.ReturnTypeInference != null) {
1260 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1265 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1266 // unexpected debugging experience
1268 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1269 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1278 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1279 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1282 if (am != null && block_return_type == ec.ReturnType) {
1283 ec.Report.Error (1662, loc,
1284 "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",
1285 am.ContainerType, am.GetSignatureForError ());
1294 protected override void DoEmit (EmitContext ec)
1299 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1300 if (async_body != null) {
1301 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
1303 // It's null for await without async
1304 if (async_return != null) {
1305 async_return.EmitAssign (ec);
1310 ec.Emit (OpCodes.Leave, async_body.BodyEnd);
1316 if (unwind_protect || ec.EmitAccurateDebugInfo)
1317 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1320 if (unwind_protect) {
1321 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1322 } else if (ec.EmitAccurateDebugInfo) {
1323 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1325 ec.Emit (OpCodes.Ret);
1329 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1332 expr.FlowAnalysis (fc);
1334 base.DoFlowAnalysis (fc);
1338 void Error_ReturnFromIterator (ResolveContext rc)
1340 rc.Report.Error (1622, loc,
1341 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1344 public override Reachability MarkReachable (Reachability rc)
1346 base.MarkReachable (rc);
1347 return Reachability.CreateUnreachable ();
1350 protected override void CloneTo (CloneContext clonectx, Statement t)
1352 Return target = (Return) t;
1353 // It's null for simple return;
1355 target.expr = expr.Clone (clonectx);
1358 public override object Accept (StructuralVisitor visitor)
1360 return visitor.Visit (this);
1364 public class Goto : ExitStatement
1367 LabeledStatement label;
1368 TryFinally try_finally;
1370 public Goto (string label, Location l)
1376 public string Target {
1377 get { return target; }
1380 protected override bool IsLocalExit {
1386 protected override bool DoResolve (BlockContext bc)
1388 label = bc.CurrentBlock.LookupLabel (target);
1389 if (label == null) {
1390 Error_UnknownLabel (bc, target, loc);
1394 try_finally = bc.CurrentTryBlock as TryFinally;
1396 CheckExitBoundaries (bc, label.Block);
1401 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1403 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1407 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1409 if (fc.LabelStack == null) {
1410 fc.LabelStack = new List<LabeledStatement> ();
1411 } else if (fc.LabelStack.Contains (label)) {
1415 fc.LabelStack.Add (label);
1416 label.Block.ScanGotoJump (label, fc);
1417 fc.LabelStack.Remove (label);
1421 public override Reachability MarkReachable (Reachability rc)
1423 if (rc.IsUnreachable)
1426 base.MarkReachable (rc);
1428 if (try_finally != null) {
1429 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1430 label.AddGotoReference (rc, false);
1432 label.AddGotoReference (rc, true);
1437 label.AddGotoReference (rc, false);
1440 return Reachability.CreateUnreachable ();
1443 protected override void CloneTo (CloneContext clonectx, Statement target)
1448 protected override void DoEmit (EmitContext ec)
1451 throw new InternalErrorException ("goto emitted before target resolved");
1453 Label l = label.LabelTarget (ec);
1454 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1457 public override object Accept (StructuralVisitor visitor)
1459 return visitor.Visit (this);
1463 public class LabeledStatement : Statement {
1471 public LabeledStatement (string name, Block block, Location l)
1478 public Label LabelTarget (EmitContext ec)
1483 label = ec.DefineLabel ();
1488 public Block Block {
1494 public string Name {
1495 get { return name; }
1498 protected override void CloneTo (CloneContext clonectx, Statement target)
1500 var t = (LabeledStatement) target;
1502 t.block = clonectx.RemapBlockCopy (block);
1505 public override bool Resolve (BlockContext bc)
1510 protected override void DoEmit (EmitContext ec)
1513 ec.MarkLabel (label);
1516 ec.Emit (OpCodes.Br_S, label);
1519 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1522 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1528 public override Reachability MarkReachable (Reachability rc)
1530 base.MarkReachable (rc);
1533 rc = new Reachability ();
1538 public void AddGotoReference (Reachability rc, bool finalTarget)
1547 // Label is final target when goto jumps out of try block with
1548 // finally clause. In that case we need leave with target but in C#
1549 // terms the label is unreachable. Using finalTarget we emit
1550 // explicit label not just marker
1553 this.finalTarget = true;
1557 block.ScanGotoJump (this);
1560 public override object Accept (StructuralVisitor visitor)
1562 return visitor.Visit (this);
1568 /// `goto default' statement
1570 public class GotoDefault : SwitchGoto
1572 public GotoDefault (Location l)
1577 public override bool Resolve (BlockContext bc)
1579 if (bc.Switch == null) {
1580 Error_GotoCaseRequiresSwitchBlock (bc);
1584 bc.Switch.RegisterGotoCase (null, null);
1590 protected override void DoEmit (EmitContext ec)
1592 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1595 public override Reachability MarkReachable (Reachability rc)
1597 if (!rc.IsUnreachable) {
1598 var label = switch_statement.DefaultLabel;
1599 if (label.IsUnreachable) {
1600 label.MarkReachable (rc);
1601 switch_statement.Block.ScanGotoJump (label);
1605 return base.MarkReachable (rc);
1608 public override object Accept (StructuralVisitor visitor)
1610 return visitor.Visit (this);
1615 /// `goto case' statement
1617 public class GotoCase : SwitchGoto
1621 public GotoCase (Expression e, Location l)
1627 public Expression Expr {
1633 public SwitchLabel Label { get; set; }
1635 public override bool Resolve (BlockContext ec)
1637 if (ec.Switch == null) {
1638 Error_GotoCaseRequiresSwitchBlock (ec);
1642 Constant c = expr.ResolveLabelConstant (ec);
1648 if (ec.Switch.IsNullable && c is NullLiteral) {
1651 TypeSpec type = ec.Switch.SwitchType;
1652 res = c.Reduce (ec, type);
1654 c.Error_ValueCannotBeConverted (ec, type, true);
1658 if (!Convert.ImplicitStandardConversionExists (c, type))
1659 ec.Report.Warning (469, 2, loc,
1660 "The `goto case' value is not implicitly convertible to type `{0}'",
1661 type.GetSignatureForError ());
1665 ec.Switch.RegisterGotoCase (this, res);
1672 protected override void DoEmit (EmitContext ec)
1674 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1677 protected override void CloneTo (CloneContext clonectx, Statement t)
1679 GotoCase target = (GotoCase) t;
1681 target.expr = expr.Clone (clonectx);
1684 public override Reachability MarkReachable (Reachability rc)
1686 if (!rc.IsUnreachable) {
1687 var label = switch_statement.FindLabel ((Constant) expr);
1688 if (label.IsUnreachable) {
1689 label.MarkReachable (rc);
1690 switch_statement.Block.ScanGotoJump (label);
1694 return base.MarkReachable (rc);
1697 public override object Accept (StructuralVisitor visitor)
1699 return visitor.Visit (this);
1703 public abstract class SwitchGoto : Statement
1705 protected bool unwind_protect;
1706 protected Switch switch_statement;
1708 protected SwitchGoto (Location loc)
1713 protected override void CloneTo (CloneContext clonectx, Statement target)
1718 public override bool Resolve (BlockContext bc)
1720 CheckExitBoundaries (bc, bc.Switch.Block);
1722 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1723 switch_statement = bc.Switch;
1728 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1733 public override Reachability MarkReachable (Reachability rc)
1735 base.MarkReachable (rc);
1736 return Reachability.CreateUnreachable ();
1739 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1741 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1745 public class Throw : Statement {
1748 public Throw (Expression expr, Location l)
1754 public Expression Expr {
1760 public override bool Resolve (BlockContext ec)
1763 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1764 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1765 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1766 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1767 if (b.IsFinallyBlock) {
1768 ec.Report.Error (724, loc,
1769 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1778 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1783 var et = ec.BuiltinTypes.Exception;
1784 if (Convert.ImplicitConversionExists (ec, expr, et))
1785 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1787 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1792 protected override void DoEmit (EmitContext ec)
1795 ec.Emit (OpCodes.Rethrow);
1799 ec.Emit (OpCodes.Throw);
1803 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1806 expr.FlowAnalysis (fc);
1811 public override Reachability MarkReachable (Reachability rc)
1813 base.MarkReachable (rc);
1814 return Reachability.CreateUnreachable ();
1817 protected override void CloneTo (CloneContext clonectx, Statement t)
1819 Throw target = (Throw) t;
1822 target.expr = expr.Clone (clonectx);
1825 public override object Accept (StructuralVisitor visitor)
1827 return visitor.Visit (this);
1831 public class Break : LocalExitStatement
1833 public Break (Location l)
1838 public override object Accept (StructuralVisitor visitor)
1840 return visitor.Visit (this);
1843 protected override void DoEmit (EmitContext ec)
1845 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1848 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1850 enclosing_loop.AddEndDefiniteAssignment (fc);
1854 protected override bool DoResolve (BlockContext bc)
1856 enclosing_loop = bc.EnclosingLoopOrSwitch;
1857 return base.DoResolve (bc);
1860 public override Reachability MarkReachable (Reachability rc)
1862 base.MarkReachable (rc);
1864 if (!rc.IsUnreachable)
1865 enclosing_loop.SetEndReachable ();
1867 return Reachability.CreateUnreachable ();
1871 public class Continue : LocalExitStatement
1873 public Continue (Location l)
1878 public override object Accept (StructuralVisitor visitor)
1880 return visitor.Visit (this);
1884 protected override void DoEmit (EmitContext ec)
1886 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1889 protected override bool DoResolve (BlockContext bc)
1891 enclosing_loop = bc.EnclosingLoop;
1892 return base.DoResolve (bc);
1895 public override Reachability MarkReachable (Reachability rc)
1897 base.MarkReachable (rc);
1899 if (!rc.IsUnreachable)
1900 enclosing_loop.SetIteratorReachable ();
1902 return Reachability.CreateUnreachable ();
1906 public abstract class LocalExitStatement : ExitStatement
1908 protected LoopStatement enclosing_loop;
1910 protected LocalExitStatement (Location loc)
1915 protected override bool IsLocalExit {
1921 protected override void CloneTo (CloneContext clonectx, Statement t)
1926 protected override bool DoResolve (BlockContext bc)
1928 if (enclosing_loop == null) {
1929 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1933 var block = enclosing_loop.Statement as Block;
1935 // Don't need to do extra checks for simple statements loops
1936 if (block != null) {
1937 CheckExitBoundaries (bc, block);
1944 public interface ILocalVariable
1946 void Emit (EmitContext ec);
1947 void EmitAssign (EmitContext ec);
1948 void EmitAddressOf (EmitContext ec);
1951 public interface INamedBlockVariable
1953 Block Block { get; }
1954 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1955 bool IsDeclared { get; }
1956 bool IsParameter { get; }
1957 Location Location { get; }
1960 public class BlockVariableDeclarator
1963 Expression initializer;
1965 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1967 if (li.Type != null)
1968 throw new ArgumentException ("Expected null variable type");
1971 this.initializer = initializer;
1976 public LocalVariable Variable {
1982 public Expression Initializer {
1987 initializer = value;
1993 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1995 var t = (BlockVariableDeclarator) MemberwiseClone ();
1996 if (initializer != null)
1997 t.initializer = initializer.Clone (cloneCtx);
2003 public class BlockVariable : Statement
2005 Expression initializer;
2006 protected FullNamedExpression type_expr;
2007 protected LocalVariable li;
2008 protected List<BlockVariableDeclarator> declarators;
2011 public BlockVariable (FullNamedExpression type, LocalVariable li)
2013 this.type_expr = type;
2015 this.loc = type_expr.Location;
2018 protected BlockVariable (LocalVariable li)
2025 public List<BlockVariableDeclarator> Declarators {
2031 public Expression Initializer {
2036 initializer = value;
2040 public FullNamedExpression TypeExpression {
2046 public LocalVariable Variable {
2054 public void AddDeclarator (BlockVariableDeclarator decl)
2056 if (declarators == null)
2057 declarators = new List<BlockVariableDeclarator> ();
2059 declarators.Add (decl);
2062 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2064 if (bc.Report.Errors != 0)
2067 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2069 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2070 new MemberName (li.Name, li.Location), null);
2072 container.AddField (f);
2075 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2079 public override bool Resolve (BlockContext bc)
2081 return Resolve (bc, true);
2084 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2086 if (type == null && !li.IsCompilerGenerated) {
2087 var vexpr = type_expr as VarExpr;
2090 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2091 // same name exists or as a keyword when no type was found
2093 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2094 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2095 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2098 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2102 if (li.IsConstant) {
2103 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2107 if (Initializer == null) {
2108 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2112 if (declarators != null) {
2113 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2117 Initializer = Initializer.Resolve (bc);
2118 if (Initializer != null) {
2119 ((VarExpr) type_expr).InferType (bc, Initializer);
2120 type = type_expr.Type;
2122 // Set error type to indicate the var was placed correctly but could
2125 // var a = missing ();
2127 type = InternalType.ErrorType;
2132 type = type_expr.ResolveAsType (bc);
2136 if (li.IsConstant && !type.IsConstantCompatible) {
2137 Const.Error_InvalidConstantType (type, loc, bc.Report);
2142 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2147 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2149 CreateEvaluatorVariable (bc, li);
2150 } else if (type != InternalType.ErrorType) {
2151 li.PrepareAssignmentAnalysis (bc);
2154 if (initializer != null) {
2155 initializer = ResolveInitializer (bc, li, initializer);
2156 // li.Variable.DefinitelyAssigned
2159 if (declarators != null) {
2160 foreach (var d in declarators) {
2161 d.Variable.Type = li.Type;
2163 CreateEvaluatorVariable (bc, d.Variable);
2164 } else if (type != InternalType.ErrorType) {
2165 d.Variable.PrepareAssignmentAnalysis (bc);
2168 if (d.Initializer != null && resolveDeclaratorInitializers) {
2169 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2170 // d.Variable.DefinitelyAssigned
2178 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2180 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2181 return a.ResolveStatement (bc);
2184 protected override void DoEmit (EmitContext ec)
2186 li.CreateBuilder (ec);
2188 if (Initializer != null)
2189 ((ExpressionStatement) Initializer).EmitStatement (ec);
2191 if (declarators != null) {
2192 foreach (var d in declarators) {
2193 d.Variable.CreateBuilder (ec);
2194 if (d.Initializer != null) {
2195 ec.Mark (d.Variable.Location);
2196 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2202 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2204 if (Initializer != null)
2205 Initializer.FlowAnalysis (fc);
2207 if (declarators != null) {
2208 foreach (var d in declarators) {
2209 if (d.Initializer != null)
2210 d.Initializer.FlowAnalysis (fc);
2217 public override Reachability MarkReachable (Reachability rc)
2219 var init = initializer as ExpressionStatement;
2221 init.MarkReachable (rc);
2223 return base.MarkReachable (rc);
2226 protected override void CloneTo (CloneContext clonectx, Statement target)
2228 BlockVariable t = (BlockVariable) target;
2230 if (type_expr != null)
2231 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2233 if (initializer != null)
2234 t.initializer = initializer.Clone (clonectx);
2236 if (declarators != null) {
2237 t.declarators = null;
2238 foreach (var d in declarators)
2239 t.AddDeclarator (d.Clone (clonectx));
2243 public override object Accept (StructuralVisitor visitor)
2245 return visitor.Visit (this);
2249 public class BlockConstant : BlockVariable
2251 public BlockConstant (FullNamedExpression type, LocalVariable li)
2256 public override void Emit (EmitContext ec)
2258 // Nothing to emit, not even sequence point
2261 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2263 initializer = initializer.Resolve (bc);
2264 if (initializer == null)
2267 var c = initializer as Constant;
2269 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2273 c = c.ConvertImplicitly (li.Type);
2275 if (TypeSpec.IsReferenceType (li.Type))
2276 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2278 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2283 li.ConstantValue = c;
2287 public override object Accept (StructuralVisitor visitor)
2289 return visitor.Visit (this);
2294 // The information about a user-perceived local variable
2296 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2303 AddressTaken = 1 << 2,
2304 CompilerGenerated = 1 << 3,
2306 ForeachVariable = 1 << 5,
2307 FixedVariable = 1 << 6,
2308 UsingVariable = 1 << 7,
2311 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2315 readonly string name;
2316 readonly Location loc;
2317 readonly Block block;
2319 Constant const_value;
2321 public VariableInfo VariableInfo;
2322 HoistedVariable hoisted_variant;
2324 LocalBuilder builder;
2326 public LocalVariable (Block block, string name, Location loc)
2333 public LocalVariable (Block block, string name, Flags flags, Location loc)
2334 : this (block, name, loc)
2340 // Used by variable declarators
2342 public LocalVariable (LocalVariable li, string name, Location loc)
2343 : this (li.block, name, li.flags, loc)
2349 public bool AddressTaken {
2351 return (flags & Flags.AddressTaken) != 0;
2355 public Block Block {
2361 public Constant ConstantValue {
2366 const_value = value;
2371 // Hoisted local variable variant
2373 public HoistedVariable HoistedVariant {
2375 return hoisted_variant;
2378 hoisted_variant = value;
2382 public bool IsDeclared {
2384 return type != null;
2388 public bool IsCompilerGenerated {
2390 return (flags & Flags.CompilerGenerated) != 0;
2394 public bool IsConstant {
2396 return (flags & Flags.Constant) != 0;
2400 public bool IsLocked {
2402 return (flags & Flags.IsLocked) != 0;
2405 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2409 public bool IsThis {
2411 return (flags & Flags.IsThis) != 0;
2415 public bool IsFixed {
2417 return (flags & Flags.FixedVariable) != 0;
2421 bool INamedBlockVariable.IsParameter {
2427 public bool IsReadonly {
2429 return (flags & Flags.ReadonlyMask) != 0;
2433 public Location Location {
2439 public string Name {
2445 public TypeSpec Type {
2456 public void CreateBuilder (EmitContext ec)
2458 if ((flags & Flags.Used) == 0) {
2459 if (VariableInfo == null) {
2460 // Missing flow analysis or wrong variable flags
2461 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2464 if (VariableInfo.IsEverAssigned)
2465 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2467 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2470 if (HoistedVariant != null)
2473 if (builder != null) {
2474 if ((flags & Flags.CompilerGenerated) != 0)
2477 // To avoid Used warning duplicates
2478 throw new InternalErrorException ("Already created variable `{0}'", name);
2482 // All fixed variabled are pinned, a slot has to be alocated
2484 builder = ec.DeclareLocal (Type, IsFixed);
2485 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2486 ec.DefineLocalVariable (name, builder);
2489 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2491 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2496 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2498 if (IsConstant && const_value != null)
2499 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2501 return new LocalVariableReference (this, loc);
2504 public void Emit (EmitContext ec)
2506 // TODO: Need something better for temporary variables
2507 if ((flags & Flags.CompilerGenerated) != 0)
2510 ec.Emit (OpCodes.Ldloc, builder);
2513 public void EmitAssign (EmitContext ec)
2515 // TODO: Need something better for temporary variables
2516 if ((flags & Flags.CompilerGenerated) != 0)
2519 ec.Emit (OpCodes.Stloc, builder);
2522 public void EmitAddressOf (EmitContext ec)
2524 ec.Emit (OpCodes.Ldloca, builder);
2527 public static string GetCompilerGeneratedName (Block block)
2529 // HACK: Debugger depends on the name semantics
2530 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2533 public string GetReadOnlyContext ()
2535 switch (flags & Flags.ReadonlyMask) {
2536 case Flags.FixedVariable:
2537 return "fixed variable";
2538 case Flags.ForeachVariable:
2539 return "foreach iteration variable";
2540 case Flags.UsingVariable:
2541 return "using variable";
2544 throw new InternalErrorException ("Variable is not readonly");
2547 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2549 if (VariableInfo == null)
2550 throw new Exception ();
2552 if (IsAssigned (fc))
2555 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2558 public bool IsAssigned (FlowAnalysisContext fc)
2560 return fc.IsDefinitelyAssigned (VariableInfo);
2563 public void PrepareAssignmentAnalysis (BlockContext bc)
2566 // No need to run assignment analysis for these guys
2568 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2571 VariableInfo = VariableInfo.Create (bc, this);
2575 // Mark the variables as referenced in the user code
2577 public void SetIsUsed ()
2579 flags |= Flags.Used;
2582 public void SetHasAddressTaken ()
2584 flags |= (Flags.AddressTaken | Flags.Used);
2587 public override string ToString ()
2589 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2594 /// Block represents a C# block.
2598 /// This class is used in a number of places: either to represent
2599 /// explicit blocks that the programmer places or implicit blocks.
2601 /// Implicit blocks are used as labels or to introduce variable
2604 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2605 /// they contain extra information that is not necessary on normal blocks.
2607 public class Block : Statement {
2614 HasCapturedVariable = 64,
2615 HasCapturedThis = 1 << 7,
2616 IsExpressionTree = 1 << 8,
2617 CompilerGenerated = 1 << 9,
2618 HasAsyncModifier = 1 << 10,
2620 YieldBlock = 1 << 12,
2621 AwaitBlock = 1 << 13,
2622 FinallyBlock = 1 << 14,
2623 CatchBlock = 1 << 15,
2625 NoFlowAnalysis = 1 << 21
2628 public Block Parent;
2629 public Location StartLocation;
2630 public Location EndLocation;
2632 public ExplicitBlock Explicit;
2633 public ParametersBlock ParametersBlock;
2635 protected Flags flags;
2638 // The statements in this block
2640 protected List<Statement> statements;
2642 protected List<Statement> scope_initializers;
2644 int? resolving_init_idx;
2650 public int ID = id++;
2652 static int clone_id_counter;
2656 // int assignable_slots;
2658 public Block (Block parent, Location start, Location end)
2659 : this (parent, 0, start, end)
2663 public Block (Block parent, Flags flags, Location start, Location end)
2665 if (parent != null) {
2666 // the appropriate constructors will fixup these fields
2667 ParametersBlock = parent.ParametersBlock;
2668 Explicit = parent.Explicit;
2671 this.Parent = parent;
2673 this.StartLocation = start;
2674 this.EndLocation = end;
2676 statements = new List<Statement> (4);
2678 this.original = this;
2683 public Block Original {
2692 public bool IsCompilerGenerated {
2693 get { return (flags & Flags.CompilerGenerated) != 0; }
2694 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2698 public bool IsCatchBlock {
2700 return (flags & Flags.CatchBlock) != 0;
2704 public bool IsFinallyBlock {
2706 return (flags & Flags.FinallyBlock) != 0;
2710 public bool Unchecked {
2711 get { return (flags & Flags.Unchecked) != 0; }
2712 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2715 public bool Unsafe {
2716 get { return (flags & Flags.Unsafe) != 0; }
2717 set { flags |= Flags.Unsafe; }
2720 public List<Statement> Statements {
2721 get { return statements; }
2726 public void SetEndLocation (Location loc)
2731 public void AddLabel (LabeledStatement target)
2733 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2736 public void AddLocalName (LocalVariable li)
2738 AddLocalName (li.Name, li);
2741 public void AddLocalName (string name, INamedBlockVariable li)
2743 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2746 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2748 if (reason == null) {
2749 Error_AlreadyDeclared (name, variable);
2753 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2754 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2755 "to `{0}', which is already used in a `{1}' scope to denote something else",
2759 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2761 var pi = variable as ParametersBlock.ParameterInfo;
2763 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2765 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2766 "A local variable named `{0}' is already defined in this scope", name);
2770 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2772 ParametersBlock.TopBlock.Report.Error (412, loc,
2773 "The type parameter name `{0}' is the same as local variable or parameter name",
2778 // It should be used by expressions which require to
2779 // register a statement during resolve process.
2781 public void AddScopeStatement (Statement s)
2783 if (scope_initializers == null)
2784 scope_initializers = new List<Statement> ();
2787 // Simple recursive helper, when resolve scope initializer another
2788 // new scope initializer can be added, this ensures it's initialized
2789 // before existing one. For now this can happen with expression trees
2790 // in base ctor initializer only
2792 if (resolving_init_idx.HasValue) {
2793 scope_initializers.Insert (resolving_init_idx.Value, s);
2794 ++resolving_init_idx;
2796 scope_initializers.Add (s);
2800 public void InsertStatement (int index, Statement s)
2802 statements.Insert (index, s);
2805 public void AddStatement (Statement s)
2810 public LabeledStatement LookupLabel (string name)
2812 return ParametersBlock.GetLabel (name, this);
2815 public override Reachability MarkReachable (Reachability rc)
2817 if (rc.IsUnreachable)
2820 base.MarkReachable (rc);
2822 if (scope_initializers != null) {
2823 foreach (var si in scope_initializers)
2824 si.MarkReachable (rc);
2827 foreach (var s in statements) {
2828 rc = s.MarkReachable (rc);
2829 if (rc.IsUnreachable) {
2830 if ((flags & Flags.ReachableEnd) != 0)
2831 return new Reachability ();
2837 flags |= Flags.ReachableEnd;
2842 public override bool Resolve (BlockContext bc)
2844 if ((flags & Flags.Resolved) != 0)
2847 Block prev_block = bc.CurrentBlock;
2848 bc.CurrentBlock = this;
2851 // Compiler generated scope statements
2853 if (scope_initializers != null) {
2854 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2855 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2858 resolving_init_idx = null;
2862 int statement_count = statements.Count;
2863 for (int ix = 0; ix < statement_count; ix++){
2864 Statement s = statements [ix];
2866 if (!s.Resolve (bc)) {
2868 if (!bc.IsInProbingMode)
2869 statements [ix] = new EmptyStatement (s.loc);
2875 bc.CurrentBlock = prev_block;
2877 flags |= Flags.Resolved;
2881 protected override void DoEmit (EmitContext ec)
2883 for (int ix = 0; ix < statements.Count; ix++){
2884 statements [ix].Emit (ec);
2888 public override void Emit (EmitContext ec)
2890 if (scope_initializers != null)
2891 EmitScopeInitializers (ec);
2896 protected void EmitScopeInitializers (EmitContext ec)
2898 foreach (Statement s in scope_initializers)
2902 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2904 if (scope_initializers != null) {
2905 foreach (var si in scope_initializers)
2906 si.FlowAnalysis (fc);
2909 return DoFlowAnalysis (fc, 0);
2912 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2914 bool end_unreachable = !reachable;
2915 for (; startIndex < statements.Count; ++startIndex) {
2916 var s = statements[startIndex];
2918 end_unreachable = s.FlowAnalysis (fc);
2919 if (s.IsUnreachable) {
2920 statements[startIndex] = new EmptyStatement (s.loc);
2925 // Statement end reachability is needed mostly due to goto support. Consider
2934 // X label is reachable only via goto not as another statement after if. We need
2935 // this for flow-analysis only to carry variable info correctly.
2937 if (end_unreachable) {
2938 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2939 s = statements[startIndex];
2940 if (s is SwitchLabel) {
2941 s.FlowAnalysis (fc);
2945 if (s.IsUnreachable) {
2946 s.FlowAnalysis (fc);
2947 statements[startIndex] = new EmptyStatement (s.loc);
2954 // The condition should be true unless there is forward jumping goto
2956 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
2959 return !Explicit.HasReachableClosingBrace;
2962 public void ScanGotoJump (Statement label)
2965 for (i = 0; i < statements.Count; ++i) {
2966 if (statements[i] == label)
2970 var rc = new Reachability ();
2971 for (++i; i < statements.Count; ++i) {
2972 var s = statements[i];
2973 rc = s.MarkReachable (rc);
2974 if (rc.IsUnreachable)
2978 flags |= Flags.ReachableEnd;
2981 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
2984 for (i = 0; i < statements.Count; ++i) {
2985 if (statements[i] == label)
2989 DoFlowAnalysis (fc, ++i);
2993 public override string ToString ()
2995 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
2999 protected override void CloneTo (CloneContext clonectx, Statement t)
3001 Block target = (Block) t;
3003 target.clone_id = ++clone_id_counter;
3006 clonectx.AddBlockMap (this, target);
3007 if (original != this)
3008 clonectx.AddBlockMap (original, target);
3010 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3011 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3014 target.Parent = clonectx.RemapBlockCopy (Parent);
3016 target.statements = new List<Statement> (statements.Count);
3017 foreach (Statement s in statements)
3018 target.statements.Add (s.Clone (clonectx));
3021 public override object Accept (StructuralVisitor visitor)
3023 return visitor.Visit (this);
3027 public class ExplicitBlock : Block
3029 protected AnonymousMethodStorey am_storey;
3031 public ExplicitBlock (Block parent, Location start, Location end)
3032 : this (parent, (Flags) 0, start, end)
3036 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3037 : base (parent, flags, start, end)
3039 this.Explicit = this;
3044 public AnonymousMethodStorey AnonymousMethodStorey {
3050 public bool HasAwait {
3052 return (flags & Flags.AwaitBlock) != 0;
3056 public bool HasCapturedThis {
3058 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3061 return (flags & Flags.HasCapturedThis) != 0;
3066 // Used to indicate that the block has reference to parent
3067 // block and cannot be made static when defining anonymous method
3069 public bool HasCapturedVariable {
3071 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3074 return (flags & Flags.HasCapturedVariable) != 0;
3078 public bool HasReachableClosingBrace {
3080 return (flags & Flags.ReachableEnd) != 0;
3083 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3087 public bool HasYield {
3089 return (flags & Flags.YieldBlock) != 0;
3096 // Creates anonymous method storey in current block
3098 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3101 // Return same story for iterator and async blocks unless we are
3102 // in nested anonymous method
3104 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3105 return ec.CurrentAnonymousMethod.Storey;
3107 if (am_storey == null) {
3108 MemberBase mc = ec.MemberContext as MemberBase;
3111 // Creates anonymous method storey for this block
3113 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3119 public override void Emit (EmitContext ec)
3121 if (am_storey != null) {
3122 DefineStoreyContainer (ec, am_storey);
3123 am_storey.EmitStoreyInstantiation (ec, this);
3126 if (scope_initializers != null)
3127 EmitScopeInitializers (ec);
3129 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3130 ec.Emit (OpCodes.Nop);
3141 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3142 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3143 ec.Emit (OpCodes.Nop);
3147 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3149 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3150 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3151 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3155 // Creates anonymous method storey
3157 storey.CreateContainer ();
3158 storey.DefineContainer ();
3160 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3163 // Only first storey in path will hold this reference. All children blocks will
3164 // reference it indirectly using $ref field
3166 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3167 if (b.Parent != null) {
3168 var s = b.Parent.Explicit.AnonymousMethodStorey;
3170 storey.HoistedThis = s.HoistedThis;
3175 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3176 if (storey.HoistedThis == null)
3177 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3179 if (storey.HoistedThis != null)
3185 // We are the first storey on path and 'this' has to be hoisted
3187 if (storey.HoistedThis == null) {
3188 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3190 // ThisReferencesFromChildrenBlock holds all reference even if they
3191 // are not on this path. It saves some memory otherwise it'd have to
3192 // be in every explicit block. We run this check to see if the reference
3193 // is valid for this storey
3195 Block block_on_path = ref_block;
3196 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3198 if (block_on_path == null)
3201 if (storey.HoistedThis == null) {
3202 storey.AddCapturedThisField (ec, null);
3205 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3208 if (b.AnonymousMethodStorey != null) {
3210 // Don't add storey cross reference for `this' when the storey ends up not
3211 // beeing attached to any parent
3213 if (b.ParametersBlock.StateMachine == null) {
3214 AnonymousMethodStorey s = null;
3215 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3216 s = ab.Explicit.AnonymousMethodStorey;
3221 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3223 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3224 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3229 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3230 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
3233 // Stop propagation inside same top block
3235 if (b.ParametersBlock == ParametersBlock.Original)
3238 b = b.ParametersBlock;
3241 pb = b as ParametersBlock;
3242 if (pb != null && pb.StateMachine != null) {
3243 if (pb.StateMachine == storey)
3247 // If we are state machine with no parent. We can hook into parent without additional
3248 // reference and capture this directly
3250 ExplicitBlock parent_storey_block = pb;
3251 while (parent_storey_block.Parent != null) {
3252 parent_storey_block = parent_storey_block.Parent.Explicit;
3253 if (parent_storey_block.AnonymousMethodStorey != null) {
3258 if (parent_storey_block.AnonymousMethodStorey == null) {
3259 pb.StateMachine.AddCapturedThisField (ec, null);
3260 b.HasCapturedThis = true;
3264 pb.StateMachine.AddParentStoreyReference (ec, storey);
3267 b.HasCapturedVariable = true;
3273 var ref_blocks = storey.ReferencesFromChildrenBlock;
3274 if (ref_blocks != null) {
3275 foreach (ExplicitBlock ref_block in ref_blocks) {
3276 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3277 if (b.AnonymousMethodStorey != null) {
3278 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3281 // Stop propagation inside same top block
3283 if (b.ParametersBlock == ParametersBlock.Original)
3286 b = b.ParametersBlock;
3289 var pb = b as ParametersBlock;
3290 if (pb != null && pb.StateMachine != null) {
3291 if (pb.StateMachine == storey)
3294 pb.StateMachine.AddParentStoreyReference (ec, storey);
3297 b.HasCapturedVariable = true;
3303 storey.PrepareEmit ();
3304 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3307 public void RegisterAsyncAwait ()
3310 while ((block.flags & Flags.AwaitBlock) == 0) {
3311 block.flags |= Flags.AwaitBlock;
3313 if (block is ParametersBlock)
3316 block = block.Parent.Explicit;
3320 public void RegisterIteratorYield ()
3322 ParametersBlock.TopBlock.IsIterator = true;
3325 while ((block.flags & Flags.YieldBlock) == 0) {
3326 block.flags |= Flags.YieldBlock;
3328 if (block.Parent == null)
3331 block = block.Parent.Explicit;
3335 public void SetCatchBlock ()
3337 flags |= Flags.CatchBlock;
3340 public void SetFinallyBlock ()
3342 flags |= Flags.FinallyBlock;
3345 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3347 tryBlock.statements = statements;
3348 statements = new List<Statement> (1);
3349 statements.Add (tf);
3354 // ParametersBlock was introduced to support anonymous methods
3355 // and lambda expressions
3357 public class ParametersBlock : ExplicitBlock
3359 public class ParameterInfo : INamedBlockVariable
3361 readonly ParametersBlock block;
3363 public VariableInfo VariableInfo;
3366 public ParameterInfo (ParametersBlock block, int index)
3374 public ParametersBlock Block {
3380 Block INamedBlockVariable.Block {
3386 public bool IsDeclared {
3392 public bool IsParameter {
3398 public bool IsLocked {
3407 public Location Location {
3409 return Parameter.Location;
3413 public Parameter Parameter {
3415 return block.Parameters [index];
3419 public TypeSpec ParameterType {
3421 return Parameter.Type;
3427 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3429 return new ParameterReference (this, loc);
3434 // Block is converted into an expression
3436 sealed class BlockScopeExpression : Expression
3439 readonly ParametersBlock block;
3441 public BlockScopeExpression (Expression child, ParametersBlock block)
3447 public override bool ContainsEmitWithAwait ()
3449 return child.ContainsEmitWithAwait ();
3452 public override Expression CreateExpressionTree (ResolveContext ec)
3454 throw new NotSupportedException ();
3457 protected override Expression DoResolve (ResolveContext ec)
3462 child = child.Resolve (ec);
3466 eclass = child.eclass;
3471 public override void Emit (EmitContext ec)
3473 block.EmitScopeInitializers (ec);
3478 protected ParametersCompiled parameters;
3479 protected ParameterInfo[] parameter_info;
3480 protected bool resolved;
3481 protected ToplevelBlock top_block;
3482 protected StateMachine state_machine;
3483 protected Dictionary<string, object> labels;
3485 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3486 : base (parent, 0, start, start)
3488 if (parameters == null)
3489 throw new ArgumentNullException ("parameters");
3491 this.parameters = parameters;
3492 ParametersBlock = this;
3494 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3496 this.top_block = parent.ParametersBlock.top_block;
3497 ProcessParameters ();
3500 protected ParametersBlock (ParametersCompiled parameters, Location start)
3501 : base (null, 0, start, start)
3503 if (parameters == null)
3504 throw new ArgumentNullException ("parameters");
3506 this.parameters = parameters;
3507 ParametersBlock = this;
3511 // It's supposed to be used by method body implementation of anonymous methods
3513 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3514 : base (null, 0, source.StartLocation, source.EndLocation)
3516 this.parameters = parameters;
3517 this.statements = source.statements;
3518 this.scope_initializers = source.scope_initializers;
3520 this.resolved = true;
3521 this.reachable = source.reachable;
3522 this.am_storey = source.am_storey;
3523 this.state_machine = source.state_machine;
3524 this.flags = source.flags & Flags.ReachableEnd;
3526 ParametersBlock = this;
3529 // Overwrite original for comparison purposes when linking cross references
3530 // between anonymous methods
3532 Original = source.Original;
3537 public bool IsAsync {
3539 return (flags & Flags.HasAsyncModifier) != 0;
3542 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3547 // Block has been converted to expression tree
3549 public bool IsExpressionTree {
3551 return (flags & Flags.IsExpressionTree) != 0;
3556 // The parameters for the block.
3558 public ParametersCompiled Parameters {
3564 public StateMachine StateMachine {
3566 return state_machine;
3570 public ToplevelBlock TopBlock {
3576 public bool Resolved {
3578 return (flags & Flags.Resolved) != 0;
3582 public int TemporaryLocalsCount { get; set; }
3587 // Checks whether all `out' parameters have been assigned.
3589 public void CheckControlExit (FlowAnalysisContext fc)
3591 CheckControlExit (fc, fc.DefiniteAssignment);
3594 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3596 if (parameter_info == null)
3599 foreach (var p in parameter_info) {
3600 if (p.VariableInfo == null)
3603 if (p.VariableInfo.IsAssigned (dat))
3606 fc.Report.Error (177, p.Location,
3607 "The out parameter `{0}' must be assigned to before control leaves the current method",
3612 protected override void CloneTo (CloneContext clonectx, Statement t)
3614 base.CloneTo (clonectx, t);
3616 var target = (ParametersBlock) t;
3619 // Clone label statements as well as they contain block reference
3623 if (pb.labels != null) {
3624 target.labels = new Dictionary<string, object> ();
3626 foreach (var entry in pb.labels) {
3627 var list = entry.Value as List<LabeledStatement>;
3630 var list_clone = new List<LabeledStatement> ();
3631 foreach (var lentry in list) {
3632 list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block)));
3635 target.labels.Add (entry.Key, list_clone);
3637 var labeled = (LabeledStatement) entry.Value;
3638 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block)));
3645 if (pb.Parent == null)
3648 pb = pb.Parent.ParametersBlock;
3652 public override Expression CreateExpressionTree (ResolveContext ec)
3654 if (statements.Count == 1) {
3655 Expression expr = statements[0].CreateExpressionTree (ec);
3656 if (scope_initializers != null)
3657 expr = new BlockScopeExpression (expr, this);
3662 return base.CreateExpressionTree (ec);
3665 public override void Emit (EmitContext ec)
3667 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3668 DefineStoreyContainer (ec, state_machine);
3669 state_machine.EmitStoreyInstantiation (ec, this);
3675 public void EmitEmbedded (EmitContext ec)
3677 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3678 DefineStoreyContainer (ec, state_machine);
3679 state_machine.EmitStoreyInstantiation (ec, this);
3685 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3687 var res = base.DoFlowAnalysis (fc);
3689 if (HasReachableClosingBrace)
3690 CheckControlExit (fc);
3695 public LabeledStatement GetLabel (string name, Block block)
3698 // Cloned parameters blocks can have their own cloned version of top-level labels
3700 if (labels == null) {
3702 return Parent.ParametersBlock.GetLabel (name, block);
3708 if (!labels.TryGetValue (name, out value)) {
3712 var label = value as LabeledStatement;
3714 if (label != null) {
3716 if (label.Block == b)
3719 } while (b != null);
3721 List<LabeledStatement> list = (List<LabeledStatement>) value;
3722 for (int i = 0; i < list.Count; ++i) {
3724 if (label.Block == b)
3732 public ParameterInfo GetParameterInfo (Parameter p)
3734 for (int i = 0; i < parameters.Count; ++i) {
3735 if (parameters[i] == p)
3736 return parameter_info[i];
3739 throw new ArgumentException ("Invalid parameter");
3742 public ParameterReference GetParameterReference (int index, Location loc)
3744 return new ParameterReference (parameter_info[index], loc);
3747 public Statement PerformClone ()
3749 CloneContext clonectx = new CloneContext ();
3750 return Clone (clonectx);
3753 protected void ProcessParameters ()
3755 if (parameters.Count == 0)
3758 parameter_info = new ParameterInfo[parameters.Count];
3759 for (int i = 0; i < parameter_info.Length; ++i) {
3760 var p = parameters.FixedParameters[i];
3764 // TODO: Should use Parameter only and more block there
3765 parameter_info[i] = new ParameterInfo (this, i);
3767 AddLocalName (p.Name, parameter_info[i]);
3771 static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst)
3773 var src_stmts = src.Statements;
3774 for (int i = 0; i < src_stmts.Count; ++i) {
3775 if (src_stmts[i] == stmt)
3776 return (LabeledStatement) dst.Statements[i];
3779 throw new InternalErrorException ("Should never be reached");
3782 public override bool Resolve (BlockContext bc)
3784 // TODO: if ((flags & Flags.Resolved) != 0)
3791 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3792 flags |= Flags.IsExpressionTree;
3795 PrepareAssignmentAnalysis (bc);
3797 if (!base.Resolve (bc))
3800 } catch (Exception e) {
3801 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3804 if (bc.CurrentBlock != null) {
3805 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3807 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3812 // If an asynchronous body of F is either an expression classified as nothing, or a
3813 // statement block where no return statements have expressions, the inferred return type is Task
3816 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3817 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3818 am.ReturnTypeInference = null;
3819 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3827 void PrepareAssignmentAnalysis (BlockContext bc)
3829 for (int i = 0; i < parameters.Count; ++i) {
3830 var par = parameters.FixedParameters[i];
3832 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3835 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3839 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3841 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3842 var stateMachine = new IteratorStorey (iterator);
3844 state_machine = stateMachine;
3845 iterator.SetStateMachine (stateMachine);
3847 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3848 tlb.Original = this;
3849 tlb.state_machine = stateMachine;
3850 tlb.AddStatement (new Return (iterator, iterator.Location));
3854 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3856 for (int i = 0; i < parameters.Count; i++) {
3857 Parameter p = parameters[i];
3858 Parameter.Modifier mod = p.ModFlags;
3859 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3860 host.Compiler.Report.Error (1988, p.Location,
3861 "Async methods cannot have ref or out parameters");
3865 if (p is ArglistParameter) {
3866 host.Compiler.Report.Error (4006, p.Location,
3867 "__arglist is not allowed in parameter list of async methods");
3871 if (parameters.Types[i].IsPointer) {
3872 host.Compiler.Report.Error (4005, p.Location,
3873 "Async methods cannot have unsafe parameters");
3879 host.Compiler.Report.Warning (1998, 1, loc,
3880 "Async block lacks `await' operator and will run synchronously");
3883 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3884 var initializer = new AsyncInitializer (this, host, block_type);
3885 initializer.Type = block_type;
3886 initializer.DelegateType = delegateType;
3888 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3890 state_machine = stateMachine;
3891 initializer.SetStateMachine (stateMachine);
3893 const Flags flags = Flags.CompilerGenerated;
3895 var b = this is ToplevelBlock ?
3896 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
3897 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
3900 b.state_machine = stateMachine;
3901 b.AddStatement (new AsyncInitializerStatement (initializer));
3909 public class ToplevelBlock : ParametersBlock
3911 LocalVariable this_variable;
3912 CompilerContext compiler;
3913 Dictionary<string, object> names;
3915 List<ExplicitBlock> this_references;
3917 public ToplevelBlock (CompilerContext ctx, Location loc)
3918 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3922 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
3923 : base (parameters, start)
3925 this.compiler = ctx;
3929 ProcessParameters ();
3933 // Recreates a top level block from parameters block. Used for
3934 // compiler generated methods where the original block comes from
3935 // explicit child block. This works for already resolved blocks
3936 // only to ensure we resolve them in the correct flow order
3938 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3939 : base (source, parameters)
3941 this.compiler = source.TopBlock.compiler;
3945 public bool IsIterator {
3947 return (flags & Flags.Iterator) != 0;
3950 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3954 public Report Report {
3956 return compiler.Report;
3961 // Used by anonymous blocks to track references of `this' variable
3963 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3965 return this_references;
3970 // Returns the "this" instance variable of this block.
3971 // See AddThisVariable() for more information.
3973 public LocalVariable ThisVariable {
3975 return this_variable;
3979 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3982 names = new Dictionary<string, object> ();
3985 if (!names.TryGetValue (name, out value)) {
3986 names.Add (name, li);
3990 INamedBlockVariable existing = value as INamedBlockVariable;
3991 List<INamedBlockVariable> existing_list;
3992 if (existing != null) {
3993 existing_list = new List<INamedBlockVariable> ();
3994 existing_list.Add (existing);
3995 names[name] = existing_list;
3997 existing_list = (List<INamedBlockVariable>) value;
4001 // A collision checking between local names
4003 var variable_block = li.Block.Explicit;
4004 for (int i = 0; i < existing_list.Count; ++i) {
4005 existing = existing_list[i];
4006 Block b = existing.Block.Explicit;
4008 // Collision at same level
4009 if (variable_block == b) {
4010 li.Block.Error_AlreadyDeclared (name, li);
4014 // Collision with parent
4015 Block parent = variable_block;
4016 while ((parent = parent.Parent) != null) {
4018 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4019 i = existing_list.Count;
4024 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4025 // Collision with children
4026 while ((b = b.Parent) != null) {
4027 if (variable_block == b) {
4028 li.Block.Error_AlreadyDeclared (name, li, "child");
4029 i = existing_list.Count;
4036 existing_list.Add (li);
4039 public void AddLabel (string name, LabeledStatement label)
4042 labels = new Dictionary<string, object> ();
4045 if (!labels.TryGetValue (name, out value)) {
4046 labels.Add (name, label);
4050 LabeledStatement existing = value as LabeledStatement;
4051 List<LabeledStatement> existing_list;
4052 if (existing != null) {
4053 existing_list = new List<LabeledStatement> ();
4054 existing_list.Add (existing);
4055 labels[name] = existing_list;
4057 existing_list = (List<LabeledStatement>) value;
4061 // A collision checking between labels
4063 for (int i = 0; i < existing_list.Count; ++i) {
4064 existing = existing_list[i];
4065 Block b = existing.Block;
4067 // Collision at same level
4068 if (label.Block == b) {
4069 Report.SymbolRelatedToPreviousError (existing.loc, name);
4070 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4074 // Collision with parent
4076 while ((b = b.Parent) != null) {
4077 if (existing.Block == b) {
4078 Report.Error (158, label.loc,
4079 "The label `{0}' shadows another label by the same name in a contained scope", name);
4080 i = existing_list.Count;
4085 // Collision with with children
4087 while ((b = b.Parent) != null) {
4088 if (label.Block == b) {
4089 Report.Error (158, label.loc,
4090 "The label `{0}' shadows another label by the same name in a contained scope", name);
4091 i = existing_list.Count;
4097 existing_list.Add (label);
4100 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4102 if (this_references == null)
4103 this_references = new List<ExplicitBlock> ();
4105 if (!this_references.Contains (block))
4106 this_references.Add (block);
4109 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4111 this_references.Remove (block);
4115 // Creates an arguments set from all parameters, useful for method proxy calls
4117 public Arguments GetAllParametersArguments ()
4119 int count = parameters.Count;
4120 Arguments args = new Arguments (count);
4121 for (int i = 0; i < count; ++i) {
4122 var pi = parameter_info[i];
4123 var arg_expr = GetParameterReference (i, pi.Location);
4125 Argument.AType atype_modifier;
4126 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4127 case Parameter.Modifier.REF:
4128 atype_modifier = Argument.AType.Ref;
4130 case Parameter.Modifier.OUT:
4131 atype_modifier = Argument.AType.Out;
4138 args.Add (new Argument (arg_expr, atype_modifier));
4145 // Lookup inside a block, the returned value can represent 3 states
4147 // true+variable: A local name was found and it's valid
4148 // false+variable: A local name was found in a child block only
4149 // false+null: No local name was found
4151 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4157 if (!names.TryGetValue (name, out value))
4160 variable = value as INamedBlockVariable;
4162 if (variable != null) {
4164 if (variable.Block == b.Original)
4168 } while (b != null);
4176 } while (b != null);
4178 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4179 for (int i = 0; i < list.Count; ++i) {
4182 if (variable.Block == b.Original)
4186 } while (b != null);
4194 } while (b != null);
4205 // This is used by non-static `struct' constructors which do not have an
4206 // initializer - in this case, the constructor must initialize all of the
4207 // struct's fields. To do this, we add a "this" variable and use the flow
4208 // analysis code to ensure that it's been fully initialized before control
4209 // leaves the constructor.
4211 public void AddThisVariable (BlockContext bc)
4213 if (this_variable != null)
4214 throw new InternalErrorException (StartLocation.ToString ());
4216 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4217 this_variable.Type = bc.CurrentType;
4218 this_variable.PrepareAssignmentAnalysis (bc);
4221 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4224 // If we're a non-static struct constructor which doesn't have an
4225 // initializer, then we must initialize all of the struct's fields.
4227 if (this_variable != null)
4228 this_variable.IsThisAssigned (fc, this);
4230 base.CheckControlExit (fc, dat);
4233 public override void Emit (EmitContext ec)
4235 if (Report.Errors > 0)
4239 if (IsCompilerGenerated) {
4240 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4248 // If `HasReturnLabel' is set, then we already emitted a
4249 // jump to the end of the method, so we must emit a `ret'
4252 // Unfortunately, System.Reflection.Emit automatically emits
4253 // a leave to the end of a finally block. This is a problem
4254 // if no code is following the try/finally block since we may
4255 // jump to a point after the end of the method.
4256 // As a workaround, we're always creating a return label in
4259 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4260 if (ec.HasReturnLabel)
4261 ec.MarkLabel (ec.ReturnLabel);
4263 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4264 ec.Mark (EndLocation);
4266 if (ec.ReturnType.Kind != MemberKind.Void)
4267 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4269 ec.Emit (OpCodes.Ret);
4272 } catch (Exception e) {
4273 throw new InternalErrorException (e, StartLocation);
4277 public bool Resolve (BlockContext bc, IMethodData md)
4282 var errors = bc.Report.Errors;
4286 if (bc.Report.Errors > errors)
4289 MarkReachable (new Reachability ());
4291 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4292 // TODO: var md = bc.CurrentMemberDefinition;
4293 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4296 if ((flags & Flags.NoFlowAnalysis) != 0)
4299 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4302 } catch (Exception e) {
4303 throw new InternalErrorException (e, StartLocation);
4310 public class SwitchLabel : Statement
4318 // if expr == null, then it is the default case.
4320 public SwitchLabel (Expression expr, Location l)
4326 public bool IsDefault {
4328 return label == null;
4332 public Expression Label {
4338 public Location Location {
4344 public Constant Converted {
4353 public bool SectionStart { get; set; }
4355 public Label GetILLabel (EmitContext ec)
4357 if (il_label == null){
4358 il_label = ec.DefineLabel ();
4361 return il_label.Value;
4364 protected override void DoEmit (EmitContext ec)
4366 ec.MarkLabel (GetILLabel (ec));
4369 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4374 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
4378 public override bool Resolve (BlockContext bc)
4380 if (ResolveAndReduce (bc))
4381 bc.Switch.RegisterLabel (bc, this);
4387 // Resolves the expression, reduces it to a literal if possible
4388 // and then converts it to the requested type.
4390 bool ResolveAndReduce (BlockContext rc)
4395 var c = label.ResolveLabelConstant (rc);
4399 if (rc.Switch.IsNullable && c is NullLiteral) {
4404 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType);
4405 return converted != null;
4408 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4410 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4411 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4414 protected override void CloneTo (CloneContext clonectx, Statement target)
4416 var t = (SwitchLabel) target;
4418 t.label = label.Clone (clonectx);
4421 public override object Accept (StructuralVisitor visitor)
4423 return visitor.Visit (this);
4426 public string GetSignatureForError ()
4429 if (converted == null)
4432 label = converted.GetValueAsLiteral ();
4434 return string.Format ("case {0}:", label);
4438 public class Switch : LoopStatement
4440 // structure used to hold blocks of keys while calculating table switch
4441 sealed class LabelsRange : IComparable<LabelsRange>
4443 public readonly long min;
4445 public readonly List<long> label_values;
4447 public LabelsRange (long value)
4450 label_values = new List<long> ();
4451 label_values.Add (value);
4454 public LabelsRange (long min, long max, ICollection<long> values)
4458 this.label_values = new List<long> (values);
4463 return max - min + 1;
4467 public bool AddValue (long value)
4469 var gap = value - min + 1;
4470 // Ensure the range has > 50% occupancy
4471 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4475 label_values.Add (value);
4479 public int CompareTo (LabelsRange other)
4481 int nLength = label_values.Count;
4482 int nLengthOther = other.label_values.Count;
4483 if (nLengthOther == nLength)
4484 return (int) (other.min - min);
4486 return nLength - nLengthOther;
4490 sealed class DispatchStatement : Statement
4492 readonly Switch body;
4494 public DispatchStatement (Switch body)
4499 protected override void CloneTo (CloneContext clonectx, Statement target)
4501 throw new NotImplementedException ();
4504 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4509 protected override void DoEmit (EmitContext ec)
4511 body.EmitDispatch (ec);
4515 class MissingBreak : Statement
4519 public MissingBreak (SwitchLabel sl)
4525 protected override void DoEmit (EmitContext ec)
4529 protected override void CloneTo (CloneContext clonectx, Statement target)
4533 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4535 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4536 label.GetSignatureForError ());
4542 public Expression Expr;
4545 // Mapping of all labels to their SwitchLabels
4547 Dictionary<long, SwitchLabel> labels;
4548 Dictionary<string, SwitchLabel> string_labels;
4549 List<SwitchLabel> case_labels;
4551 List<Tuple<GotoCase, Constant>> goto_cases;
4552 List<DefiniteAssignmentBitSet> end_reachable_das;
4555 /// The governing switch type
4557 public TypeSpec SwitchType;
4559 Expression new_expr;
4561 SwitchLabel case_null;
4562 SwitchLabel case_default;
4564 Label defaultLabel, nullLabel;
4565 VariableReference value;
4566 ExpressionStatement string_dictionary;
4567 FieldExpr switch_cache_field;
4568 ExplicitBlock block;
4572 // Nullable Types support
4574 Nullable.Unwrap unwrap;
4576 public Switch (Expression e, ExplicitBlock block, Location l)
4584 public SwitchLabel ActiveLabel { get; set; }
4586 public ExplicitBlock Block {
4592 public SwitchLabel DefaultLabel {
4594 return case_default;
4598 public bool IsNullable {
4600 return unwrap != null;
4604 public List<SwitchLabel> RegisteredLabels {
4611 // Determines the governing type for a switch. The returned
4612 // expression might be the expression from the switch, or an
4613 // expression that includes any potential conversions to
4615 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
4617 switch (expr.Type.BuiltinType) {
4618 case BuiltinTypeSpec.Type.Byte:
4619 case BuiltinTypeSpec.Type.SByte:
4620 case BuiltinTypeSpec.Type.UShort:
4621 case BuiltinTypeSpec.Type.Short:
4622 case BuiltinTypeSpec.Type.UInt:
4623 case BuiltinTypeSpec.Type.Int:
4624 case BuiltinTypeSpec.Type.ULong:
4625 case BuiltinTypeSpec.Type.Long:
4626 case BuiltinTypeSpec.Type.Char:
4627 case BuiltinTypeSpec.Type.String:
4628 case BuiltinTypeSpec.Type.Bool:
4632 if (expr.Type.IsEnum)
4636 // Try to find a *user* defined implicit conversion.
4638 // If there is no implicit conversion, or if there are multiple
4639 // conversions, we have to report an error
4641 Expression converted = null;
4642 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
4645 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
4650 // Ignore over-worked ImplicitUserConversions that do
4651 // an implicit conversion in addition to the user conversion.
4653 if (!(e is UserCast))
4656 if (converted != null){
4657 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4666 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
4668 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4683 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4685 case_labels.Add (sl);
4688 if (case_default != null) {
4689 sl.Error_AlreadyOccurs (rc, case_default);
4698 if (string_labels != null) {
4699 string string_value = sl.Converted.GetValue () as string;
4700 if (string_value == null)
4703 string_labels.Add (string_value, sl);
4705 if (sl.Converted is NullLiteral) {
4708 labels.Add (sl.Converted.GetValueAsLong (), sl);
4711 } catch (ArgumentException) {
4712 if (string_labels != null)
4713 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4715 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4720 // This method emits code for a lookup-based switch statement (non-string)
4721 // Basically it groups the cases into blocks that are at least half full,
4722 // and then spits out individual lookup opcodes for each block.
4723 // It emits the longest blocks first, and short blocks are just
4724 // handled with direct compares.
4726 void EmitTableSwitch (EmitContext ec, Expression val)
4728 if (labels != null && labels.Count > 0) {
4729 List<LabelsRange> ranges;
4730 if (string_labels != null) {
4731 // We have done all hard work for string already
4732 // setup single range only
4733 ranges = new List<LabelsRange> (1);
4734 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4736 var element_keys = new long[labels.Count];
4737 labels.Keys.CopyTo (element_keys, 0);
4738 Array.Sort (element_keys);
4741 // Build possible ranges of switch labes to reduce number
4744 ranges = new List<LabelsRange> (element_keys.Length);
4745 var range = new LabelsRange (element_keys[0]);
4747 for (int i = 1; i < element_keys.Length; ++i) {
4748 var l = element_keys[i];
4749 if (range.AddValue (l))
4752 range = new LabelsRange (l);
4756 // sort the blocks so we can tackle the largest ones first
4760 Label lbl_default = defaultLabel;
4761 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4763 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4764 LabelsRange kb = ranges[range_index];
4765 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4767 // Optimize small ranges using simple equality check
4768 if (kb.Range <= 2) {
4769 foreach (var key in kb.label_values) {
4770 SwitchLabel sl = labels[key];
4771 if (sl == case_default || sl == case_null)
4774 if (sl.Converted.IsZeroInteger) {
4775 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4778 sl.Converted.Emit (ec);
4779 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4783 // TODO: if all the keys in the block are the same and there are
4784 // no gaps/defaults then just use a range-check.
4785 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4786 // TODO: optimize constant/I4 cases
4788 // check block range (could be > 2^31)
4790 ec.EmitLong (kb.min);
4791 ec.Emit (OpCodes.Blt, lbl_default);
4794 ec.EmitLong (kb.max);
4795 ec.Emit (OpCodes.Bgt, lbl_default);
4800 ec.EmitLong (kb.min);
4801 ec.Emit (OpCodes.Sub);
4804 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4808 int first = (int) kb.min;
4811 ec.Emit (OpCodes.Sub);
4812 } else if (first < 0) {
4813 ec.EmitInt (-first);
4814 ec.Emit (OpCodes.Add);
4818 // first, build the list of labels for the switch
4820 long cJumps = kb.Range;
4821 Label[] switch_labels = new Label[cJumps];
4822 for (int iJump = 0; iJump < cJumps; iJump++) {
4823 var key = kb.label_values[iKey];
4824 if (key == kb.min + iJump) {
4825 switch_labels[iJump] = labels[key].GetILLabel (ec);
4828 switch_labels[iJump] = lbl_default;
4832 // emit the switch opcode
4833 ec.Emit (OpCodes.Switch, switch_labels);
4836 // mark the default for this block
4837 if (range_index != 0)
4838 ec.MarkLabel (lbl_default);
4841 // the last default just goes to the end
4842 if (ranges.Count > 0)
4843 ec.Emit (OpCodes.Br, lbl_default);
4847 public SwitchLabel FindLabel (Constant value)
4849 SwitchLabel sl = null;
4851 if (string_labels != null) {
4852 string s = value.GetValue () as string;
4854 if (case_null != null)
4856 else if (case_default != null)
4859 string_labels.TryGetValue (s, out sl);
4862 if (value is NullLiteral) {
4865 labels.TryGetValue (value.GetValueAsLong (), out sl);
4872 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4874 Expr.FlowAnalysis (fc);
4876 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
4877 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
4878 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
4880 block.FlowAnalysis (fc);
4882 fc.SwitchInitialDefinitiveAssignment = prev_switch;
4884 if (end_reachable_das != null) {
4885 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
4886 InitialDefinitiveAssignment |= sections_das;
4887 end_reachable_das = null;
4890 fc.DefiniteAssignment = InitialDefinitiveAssignment;
4892 return case_default != null && !end_reachable;
4895 public override bool Resolve (BlockContext ec)
4897 Expr = Expr.Resolve (ec);
4901 new_expr = SwitchGoverningType (ec, Expr);
4903 if (new_expr == null && Expr.Type.IsNullableType) {
4904 unwrap = Nullable.Unwrap.Create (Expr, false);
4908 new_expr = SwitchGoverningType (ec, unwrap);
4911 if (new_expr == null) {
4912 if (Expr.Type != InternalType.ErrorType) {
4913 ec.Report.Error (151, loc,
4914 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4915 Expr.Type.GetSignatureForError ());
4922 SwitchType = new_expr.Type;
4924 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4925 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4929 if (block.Statements.Count == 0)
4932 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4933 string_labels = new Dictionary<string, SwitchLabel> ();
4935 labels = new Dictionary<long, SwitchLabel> ();
4938 case_labels = new List<SwitchLabel> ();
4940 var constant = new_expr as Constant;
4943 // Don't need extra variable for constant switch or switch with
4944 // only default case
4946 if (constant == null) {
4948 // Store switch expression for comparison purposes
4950 value = new_expr as VariableReference;
4951 if (value == null && !HasOnlyDefaultSection ()) {
4952 var current_block = ec.CurrentBlock;
4953 ec.CurrentBlock = Block;
4954 // Create temporary variable inside switch scope
4955 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4957 ec.CurrentBlock = current_block;
4961 Switch old_switch = ec.Switch;
4963 var parent_los = ec.EnclosingLoopOrSwitch;
4964 ec.EnclosingLoopOrSwitch = this;
4966 var ok = Statement.Resolve (ec);
4968 ec.EnclosingLoopOrSwitch = parent_los;
4969 ec.Switch = old_switch;
4972 // Check if all goto cases are valid. Needs to be done after switch
4973 // is resolved because goto can jump forward in the scope.
4975 if (goto_cases != null) {
4976 foreach (var gc in goto_cases) {
4977 if (gc.Item1 == null) {
4978 if (DefaultLabel == null) {
4979 Goto.Error_UnknownLabel (ec, "default", loc);
4985 var sl = FindLabel (gc.Item2);
4987 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
4989 gc.Item1.Label = sl;
4997 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4998 ResolveStringSwitchMap (ec);
5002 // Anonymous storey initialization has to happen before
5003 // any generated switch dispatch
5005 block.InsertStatement (0, new DispatchStatement (this));
5010 bool HasOnlyDefaultSection ()
5012 for (int i = 0; i < block.Statements.Count; ++i) {
5013 var s = block.Statements[i] as SwitchLabel;
5015 if (s == null || s.IsDefault)
5024 public override Reachability MarkReachable (Reachability rc)
5026 if (rc.IsUnreachable)
5029 base.MarkReachable (rc);
5031 if (block.Statements.Count == 0)
5034 SwitchLabel constant_label = null;
5035 var constant = new_expr as Constant;
5037 if (constant != null) {
5038 constant_label = FindLabel (constant) ?? case_default;
5039 if (constant_label == null) {
5040 block.Statements.RemoveAt (0);
5045 var section_rc = new Reachability ();
5046 SwitchLabel prev_label = null;
5048 for (int i = 0; i < block.Statements.Count; ++i) {
5049 var s = block.Statements[i];
5050 var sl = s as SwitchLabel;
5052 if (sl != null && sl.SectionStart) {
5054 // Section is marked already via constant switch or goto case
5056 if (!sl.IsUnreachable) {
5057 section_rc = new Reachability ();
5061 if (section_rc.IsUnreachable) {
5062 section_rc = new Reachability ();
5064 if (prev_label != null) {
5065 sl.SectionStart = false;
5066 s = new MissingBreak (prev_label);
5067 s.MarkReachable (rc);
5068 block.Statements.Insert (i - 1, s);
5075 if (constant_label != null && constant_label != sl)
5076 section_rc = Reachability.CreateUnreachable ();
5079 section_rc = s.MarkReachable (section_rc);
5082 if (!section_rc.IsUnreachable && prev_label != null) {
5083 prev_label.SectionStart = false;
5084 var s = new MissingBreak (prev_label);
5085 s.MarkReachable (rc);
5086 block.Statements.Add (s);
5090 // Reachability can affect parent only when all possible paths are handled but
5091 // we still need to run reachability check on switch body to check for fall-through
5093 if (case_default == null && constant_label == null)
5097 // We have at least one local exit from the switch
5102 return Reachability.CreateUnreachable ();
5105 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5107 if (goto_cases == null)
5108 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5110 goto_cases.Add (Tuple.Create (gotoCase, value));
5114 // Converts string switch into string hashtable
5116 void ResolveStringSwitchMap (ResolveContext ec)
5118 FullNamedExpression string_dictionary_type;
5119 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5120 string_dictionary_type = new TypeExpression (
5121 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5122 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5124 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5125 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5127 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5131 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5132 Field field = new Field (ctype, string_dictionary_type,
5133 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5134 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5135 if (!field.Define ())
5137 ctype.AddField (field);
5139 var init = new List<Expression> ();
5141 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5142 string value = null;
5144 foreach (SwitchLabel sl in case_labels) {
5146 if (sl.SectionStart)
5147 labels.Add (++counter, sl);
5149 if (sl == case_default || sl == case_null)
5152 value = (string) sl.Converted.GetValue ();
5153 var init_args = new List<Expression> (2);
5154 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5156 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5157 init_args.Add (sl.Converted);
5159 init.Add (new CollectionElementInitializer (init_args, loc));
5162 Arguments args = new Arguments (1);
5163 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5164 Expression initializer = new NewInitialize (string_dictionary_type, args,
5165 new CollectionOrObjectInitializers (init, loc), loc);
5167 switch_cache_field = new FieldExpr (field, loc);
5168 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5171 void DoEmitStringSwitch (EmitContext ec)
5173 Label l_initialized = ec.DefineLabel ();
5176 // Skip initialization when value is null
5178 value.EmitBranchable (ec, nullLabel, false);
5181 // Check if string dictionary is initialized and initialize
5183 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5184 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5185 string_dictionary.EmitStatement (ec);
5187 ec.MarkLabel (l_initialized);
5189 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5191 ResolveContext rc = new ResolveContext (ec.MemberContext);
5193 if (switch_cache_field.Type.IsGeneric) {
5194 Arguments get_value_args = new Arguments (2);
5195 get_value_args.Add (new Argument (value));
5196 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5197 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5198 if (get_item == null)
5202 // A value was not found, go to default case
5204 get_item.EmitBranchable (ec, defaultLabel, false);
5206 Arguments get_value_args = new Arguments (1);
5207 get_value_args.Add (new Argument (value));
5209 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5210 if (get_item == null)
5213 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5214 get_item_object.EmitAssign (ec, get_item, true, false);
5215 ec.Emit (OpCodes.Brfalse, defaultLabel);
5217 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5218 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5220 get_item_int.EmitStatement (ec);
5221 get_item_object.Release (ec);
5224 EmitTableSwitch (ec, string_switch_variable);
5225 string_switch_variable.Release (ec);
5229 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5231 void EmitShortSwitch (EmitContext ec)
5233 MethodSpec equal_method = null;
5234 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5235 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5238 if (equal_method != null) {
5239 value.EmitBranchable (ec, nullLabel, false);
5242 for (int i = 0; i < case_labels.Count; ++i) {
5243 var label = case_labels [i];
5244 if (label == case_default || label == case_null)
5247 var constant = label.Converted;
5249 if (equal_method != null) {
5253 var call = new CallEmitter ();
5254 call.EmitPredefined (ec, equal_method, new Arguments (0));
5255 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5259 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5260 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5266 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5269 ec.Emit (OpCodes.Br, defaultLabel);
5272 void EmitDispatch (EmitContext ec)
5274 if (value == null) {
5276 // Constant switch, we've already done the work if there is only 1 label
5280 foreach (var sl in case_labels) {
5281 if (sl.IsUnreachable)
5284 if (reachable++ > 0) {
5285 var constant = (Constant) new_expr;
5286 var constant_label = FindLabel (constant) ?? case_default;
5288 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5296 if (string_dictionary != null) {
5297 DoEmitStringSwitch (ec);
5298 } else if (case_labels.Count < 4 || string_labels != null) {
5299 EmitShortSwitch (ec);
5301 EmitTableSwitch (ec, value);
5305 protected override void DoEmit (EmitContext ec)
5308 // Setup the codegen context
5310 Label old_end = ec.LoopEnd;
5311 Switch old_switch = ec.Switch;
5313 ec.LoopEnd = ec.DefineLabel ();
5316 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5317 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5319 if (value != null) {
5322 unwrap.EmitCheck (ec);
5323 ec.Emit (OpCodes.Brfalse, nullLabel);
5324 value.EmitAssign (ec, new_expr, false, false);
5325 } else if (new_expr != value) {
5326 value.EmitAssign (ec, new_expr, false, false);
5331 // Next statement is compiler generated we don't need extra
5332 // nop when we can use the statement for sequence point
5334 ec.Mark (block.StartLocation);
5335 block.IsCompilerGenerated = true;
5340 // Restore context state.
5341 ec.MarkLabel (ec.LoopEnd);
5344 // Restore the previous context
5346 ec.LoopEnd = old_end;
5347 ec.Switch = old_switch;
5350 protected override void CloneTo (CloneContext clonectx, Statement t)
5352 Switch target = (Switch) t;
5354 target.Expr = Expr.Clone (clonectx);
5355 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5358 public override object Accept (StructuralVisitor visitor)
5360 return visitor.Visit (this);
5363 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5365 if (case_default == null)
5368 if (end_reachable_das == null)
5369 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5371 end_reachable_das.Add (fc.DefiniteAssignment);
5374 public override void SetEndReachable ()
5376 end_reachable = true;
5380 // A place where execution can restart in a state machine
5381 public abstract class ResumableStatement : Statement
5384 protected Label resume_point;
5386 public Label PrepareForEmit (EmitContext ec)
5390 resume_point = ec.DefineLabel ();
5392 return resume_point;
5395 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5400 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5405 public abstract class TryFinallyBlock : ExceptionStatement
5407 protected Statement stmt;
5408 Label dispose_try_block;
5409 bool prepared_for_dispose, emitted_dispose;
5410 Method finally_host;
5412 protected TryFinallyBlock (Statement stmt, Location loc)
5420 public Statement Statement {
5428 protected abstract void EmitTryBody (EmitContext ec);
5429 public abstract void EmitFinallyBody (EmitContext ec);
5431 public override Label PrepareForDispose (EmitContext ec, Label end)
5433 if (!prepared_for_dispose) {
5434 prepared_for_dispose = true;
5435 dispose_try_block = ec.DefineLabel ();
5437 return dispose_try_block;
5440 protected sealed override void DoEmit (EmitContext ec)
5442 EmitTryBodyPrepare (ec);
5445 ec.BeginFinallyBlock ();
5447 Label start_finally = ec.DefineLabel ();
5448 if (resume_points != null) {
5449 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5451 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5452 ec.Emit (OpCodes.Brfalse_S, start_finally);
5453 ec.Emit (OpCodes.Endfinally);
5456 ec.MarkLabel (start_finally);
5458 if (finally_host != null) {
5459 finally_host.Define ();
5460 finally_host.PrepareEmit ();
5461 finally_host.Emit ();
5463 // Now it's safe to add, to close it properly and emit sequence points
5464 finally_host.Parent.AddMember (finally_host);
5466 var ce = new CallEmitter ();
5467 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5468 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5470 EmitFinallyBody (ec);
5473 ec.EndExceptionBlock ();
5476 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5478 if (emitted_dispose)
5481 emitted_dispose = true;
5483 Label end_of_try = ec.DefineLabel ();
5485 // Ensure that the only way we can get into this code is through a dispatcher
5486 if (have_dispatcher)
5487 ec.Emit (OpCodes.Br, end);
5489 ec.BeginExceptionBlock ();
5491 ec.MarkLabel (dispose_try_block);
5493 Label[] labels = null;
5494 for (int i = 0; i < resume_points.Count; ++i) {
5495 ResumableStatement s = resume_points[i];
5496 Label ret = s.PrepareForDispose (ec, end_of_try);
5497 if (ret.Equals (end_of_try) && labels == null)
5499 if (labels == null) {
5500 labels = new Label[resume_points.Count];
5501 for (int j = 0; j < i; ++j)
5502 labels[j] = end_of_try;
5507 if (labels != null) {
5509 for (j = 1; j < labels.Length; ++j)
5510 if (!labels[0].Equals (labels[j]))
5512 bool emit_dispatcher = j < labels.Length;
5514 if (emit_dispatcher) {
5515 ec.Emit (OpCodes.Ldloc, pc);
5516 ec.EmitInt (first_resume_pc);
5517 ec.Emit (OpCodes.Sub);
5518 ec.Emit (OpCodes.Switch, labels);
5521 foreach (ResumableStatement s in resume_points)
5522 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5525 ec.MarkLabel (end_of_try);
5527 ec.BeginFinallyBlock ();
5529 if (finally_host != null) {
5530 var ce = new CallEmitter ();
5531 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5532 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5534 EmitFinallyBody (ec);
5537 ec.EndExceptionBlock ();
5540 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5542 var res = stmt.FlowAnalysis (fc);
5547 public override Reachability MarkReachable (Reachability rc)
5549 base.MarkReachable (rc);
5550 return Statement.MarkReachable (rc);
5553 public override bool Resolve (BlockContext bc)
5557 parent = bc.CurrentTryBlock;
5558 bc.CurrentTryBlock = this;
5560 using (bc.Set (ResolveContext.Options.TryScope)) {
5561 ok = stmt.Resolve (bc);
5564 bc.CurrentTryBlock = parent;
5567 // Finally block inside iterator is called from MoveNext and
5568 // Dispose methods that means we need to lift the block into
5569 // newly created host method to emit the body only once. The
5570 // original block then simply calls the newly generated method.
5572 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5573 var b = stmt as Block;
5574 if (b != null && b.Explicit.HasYield) {
5575 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5579 return base.Resolve (bc) && ok;
5584 // Base class for blocks using exception handling
5586 public abstract class ExceptionStatement : ResumableStatement
5588 protected List<ResumableStatement> resume_points;
5589 protected int first_resume_pc;
5590 protected ExceptionStatement parent;
5592 protected ExceptionStatement (Location loc)
5597 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5599 StateMachineInitializer state_machine = null;
5600 if (resume_points != null) {
5601 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5603 ec.EmitInt ((int) IteratorStorey.State.Running);
5604 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5607 ec.BeginExceptionBlock ();
5609 if (resume_points != null) {
5610 ec.MarkLabel (resume_point);
5612 // For normal control flow, we want to fall-through the Switch
5613 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5614 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5615 ec.EmitInt (first_resume_pc);
5616 ec.Emit (OpCodes.Sub);
5618 Label[] labels = new Label[resume_points.Count];
5619 for (int i = 0; i < resume_points.Count; ++i)
5620 labels[i] = resume_points[i].PrepareForEmit (ec);
5621 ec.Emit (OpCodes.Switch, labels);
5625 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5627 if (parent != null) {
5628 // TODO: MOVE to virtual TryCatch
5629 var tc = this as TryCatch;
5630 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5632 pc = parent.AddResumePoint (s, pc, stateMachine);
5634 pc = stateMachine.AddResumePoint (this);
5637 if (resume_points == null) {
5638 resume_points = new List<ResumableStatement> ();
5639 first_resume_pc = pc;
5642 if (pc != first_resume_pc + resume_points.Count)
5643 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5645 resume_points.Add (stmt);
5650 public class Lock : TryFinallyBlock
5653 TemporaryVariableReference expr_copy;
5654 TemporaryVariableReference lock_taken;
5656 public Lock (Expression expr, Statement stmt, Location loc)
5662 public Expression Expr {
5668 public override bool Resolve (BlockContext ec)
5670 expr = expr.Resolve (ec);
5674 if (!TypeSpec.IsReferenceType (expr.Type)) {
5675 ec.Report.Error (185, loc,
5676 "`{0}' is not a reference type as required by the lock statement",
5677 expr.Type.GetSignatureForError ());
5680 if (expr.Type.IsGenericParameter) {
5681 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5684 VariableReference lv = expr as VariableReference;
5687 locked = lv.IsLockedByStatement;
5688 lv.IsLockedByStatement = true;
5695 // Have to keep original lock value around to unlock same location
5696 // in the case of original value has changed or is null
5698 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5699 expr_copy.Resolve (ec);
5702 // Ensure Monitor methods are available
5704 if (ResolvePredefinedMethods (ec) > 1) {
5705 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5706 lock_taken.Resolve (ec);
5709 using (ec.Set (ResolveContext.Options.LockScope)) {
5714 lv.IsLockedByStatement = locked;
5720 protected override void EmitTryBodyPrepare (EmitContext ec)
5722 expr_copy.EmitAssign (ec, expr);
5724 if (lock_taken != null) {
5726 // Initialize ref variable
5728 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5731 // Monitor.Enter (expr_copy)
5733 expr_copy.Emit (ec);
5734 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5737 base.EmitTryBodyPrepare (ec);
5740 protected override void EmitTryBody (EmitContext ec)
5743 // Monitor.Enter (expr_copy, ref lock_taken)
5745 if (lock_taken != null) {
5746 expr_copy.Emit (ec);
5747 lock_taken.LocalInfo.CreateBuilder (ec);
5748 lock_taken.AddressOf (ec, AddressOp.Load);
5749 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
5752 Statement.Emit (ec);
5755 public override void EmitFinallyBody (EmitContext ec)
5758 // if (lock_taken) Monitor.Exit (expr_copy)
5760 Label skip = ec.DefineLabel ();
5762 if (lock_taken != null) {
5763 lock_taken.Emit (ec);
5764 ec.Emit (OpCodes.Brfalse_S, skip);
5767 expr_copy.Emit (ec);
5768 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
5770 ec.Emit (OpCodes.Call, m);
5772 ec.MarkLabel (skip);
5775 int ResolvePredefinedMethods (ResolveContext rc)
5777 // Try 4.0 Monitor.Enter (object, ref bool) overload first
5778 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
5782 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
5786 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
5790 protected override void CloneTo (CloneContext clonectx, Statement t)
5792 Lock target = (Lock) t;
5794 target.expr = expr.Clone (clonectx);
5795 target.stmt = Statement.Clone (clonectx);
5798 public override object Accept (StructuralVisitor visitor)
5800 return visitor.Visit (this);
5805 public class Unchecked : Statement {
5808 public Unchecked (Block b, Location loc)
5815 public override bool Resolve (BlockContext ec)
5817 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
5818 return Block.Resolve (ec);
5821 protected override void DoEmit (EmitContext ec)
5823 using (ec.With (EmitContext.Options.CheckedScope, false))
5827 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5829 return Block.FlowAnalysis (fc);
5832 public override Reachability MarkReachable (Reachability rc)
5834 base.MarkReachable (rc);
5835 return Block.MarkReachable (rc);
5838 protected override void CloneTo (CloneContext clonectx, Statement t)
5840 Unchecked target = (Unchecked) t;
5842 target.Block = clonectx.LookupBlock (Block);
5845 public override object Accept (StructuralVisitor visitor)
5847 return visitor.Visit (this);
5851 public class Checked : Statement {
5854 public Checked (Block b, Location loc)
5857 b.Unchecked = false;
5861 public override bool Resolve (BlockContext ec)
5863 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
5864 return Block.Resolve (ec);
5867 protected override void DoEmit (EmitContext ec)
5869 using (ec.With (EmitContext.Options.CheckedScope, true))
5873 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5875 return Block.FlowAnalysis (fc);
5878 public override Reachability MarkReachable (Reachability rc)
5880 base.MarkReachable (rc);
5881 return Block.MarkReachable (rc);
5884 protected override void CloneTo (CloneContext clonectx, Statement t)
5886 Checked target = (Checked) t;
5888 target.Block = clonectx.LookupBlock (Block);
5891 public override object Accept (StructuralVisitor visitor)
5893 return visitor.Visit (this);
5897 public class Unsafe : Statement {
5900 public Unsafe (Block b, Location loc)
5903 Block.Unsafe = true;
5907 public override bool Resolve (BlockContext ec)
5909 if (ec.CurrentIterator != null)
5910 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5912 using (ec.Set (ResolveContext.Options.UnsafeScope))
5913 return Block.Resolve (ec);
5916 protected override void DoEmit (EmitContext ec)
5921 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5923 return Block.FlowAnalysis (fc);
5926 public override Reachability MarkReachable (Reachability rc)
5928 base.MarkReachable (rc);
5929 return Block.MarkReachable (rc);
5932 protected override void CloneTo (CloneContext clonectx, Statement t)
5934 Unsafe target = (Unsafe) t;
5936 target.Block = clonectx.LookupBlock (Block);
5939 public override object Accept (StructuralVisitor visitor)
5941 return visitor.Visit (this);
5948 public class Fixed : Statement
5950 abstract class Emitter : ShimExpression
5952 protected LocalVariable vi;
5954 protected Emitter (Expression expr, LocalVariable li)
5960 public abstract void EmitExit (EmitContext ec);
5962 public override void FlowAnalysis (FlowAnalysisContext fc)
5964 expr.FlowAnalysis (fc);
5968 class ExpressionEmitter : Emitter {
5969 public ExpressionEmitter (Expression converted, LocalVariable li) :
5970 base (converted, li)
5974 protected override Expression DoResolve (ResolveContext rc)
5976 throw new NotImplementedException ();
5979 public override void Emit (EmitContext ec) {
5981 // Store pointer in pinned location
5987 public override void EmitExit (EmitContext ec)
5990 ec.Emit (OpCodes.Conv_U);
5995 class StringEmitter : Emitter
5997 LocalVariable pinned_string;
5999 public StringEmitter (Expression expr, LocalVariable li)
6004 protected override Expression DoResolve (ResolveContext rc)
6006 pinned_string = new LocalVariable (vi.Block, "$pinned",
6007 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6009 pinned_string.Type = rc.BuiltinTypes.String;
6011 eclass = ExprClass.Variable;
6012 type = rc.BuiltinTypes.Int;
6016 public override void Emit (EmitContext ec)
6018 pinned_string.CreateBuilder (ec);
6021 pinned_string.EmitAssign (ec);
6023 // TODO: Should use Binary::Add
6024 pinned_string.Emit (ec);
6025 ec.Emit (OpCodes.Conv_I);
6027 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6031 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6032 //pe.InstanceExpression = pinned_string;
6033 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6035 ec.Emit (OpCodes.Add);
6039 public override void EmitExit (EmitContext ec)
6042 pinned_string.EmitAssign (ec);
6046 public class VariableDeclaration : BlockVariable
6048 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6053 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6055 if (!Variable.Type.IsPointer && li == Variable) {
6056 bc.Report.Error (209, TypeExpression.Location,
6057 "The type of locals declared in a fixed statement must be a pointer type");
6062 // The rules for the possible declarators are pretty wise,
6063 // but the production on the grammar is more concise.
6065 // So we have to enforce these rules here.
6067 // We do not resolve before doing the case 1 test,
6068 // because the grammar is explicit in that the token &
6069 // is present, so we need to test for this particular case.
6072 if (initializer is Cast) {
6073 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6077 initializer = initializer.Resolve (bc);
6079 if (initializer == null)
6085 if (initializer.Type.IsArray) {
6086 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
6089 // Provided that array_type is unmanaged,
6091 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6095 // and T* is implicitly convertible to the
6096 // pointer type given in the fixed statement.
6098 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
6100 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6101 if (converted == null)
6105 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6107 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6108 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
6109 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6110 new NullLiteral (loc),
6113 converted = converted.Resolve (bc);
6115 return new ExpressionEmitter (converted, li);
6121 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6122 return new StringEmitter (initializer, li).Resolve (bc);
6125 // Case 3: fixed buffer
6126 if (initializer is FixedBufferPtr) {
6127 return new ExpressionEmitter (initializer, li);
6131 // Case 4: & object.
6133 bool already_fixed = true;
6134 Unary u = initializer as Unary;
6135 if (u != null && u.Oper == Unary.Operator.AddressOf) {
6136 IVariableReference vr = u.Expr as IVariableReference;
6137 if (vr == null || !vr.IsFixed) {
6138 already_fixed = false;
6142 if (already_fixed) {
6143 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6146 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
6147 return new ExpressionEmitter (initializer, li);
6152 VariableDeclaration decl;
6153 Statement statement;
6156 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6165 public Statement Statement {
6171 public BlockVariable Variables {
6179 public override bool Resolve (BlockContext bc)
6181 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6182 if (!decl.Resolve (bc))
6186 return statement.Resolve (bc);
6189 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6191 decl.FlowAnalysis (fc);
6192 return statement.FlowAnalysis (fc);
6195 protected override void DoEmit (EmitContext ec)
6197 decl.Variable.CreateBuilder (ec);
6198 decl.Initializer.Emit (ec);
6199 if (decl.Declarators != null) {
6200 foreach (var d in decl.Declarators) {
6201 d.Variable.CreateBuilder (ec);
6202 d.Initializer.Emit (ec);
6206 statement.Emit (ec);
6212 // Clear the pinned variable
6214 ((Emitter) decl.Initializer).EmitExit (ec);
6215 if (decl.Declarators != null) {
6216 foreach (var d in decl.Declarators) {
6217 ((Emitter)d.Initializer).EmitExit (ec);
6222 public override Reachability MarkReachable (Reachability rc)
6224 base.MarkReachable (rc);
6226 decl.MarkReachable (rc);
6228 rc = statement.MarkReachable (rc);
6230 // TODO: What if there is local exit?
6231 has_ret = rc.IsUnreachable;
6235 protected override void CloneTo (CloneContext clonectx, Statement t)
6237 Fixed target = (Fixed) t;
6239 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6240 target.statement = statement.Clone (clonectx);
6243 public override object Accept (StructuralVisitor visitor)
6245 return visitor.Visit (this);
6249 public class Catch : Statement
6251 ExplicitBlock block;
6253 FullNamedExpression type_expr;
6254 CompilerAssign assign;
6257 public Catch (ExplicitBlock block, Location loc)
6265 public ExplicitBlock Block {
6271 public TypeSpec CatchType {
6277 public bool IsGeneral {
6279 return type_expr == null;
6283 public FullNamedExpression TypeExpression {
6292 public LocalVariable Variable {
6303 protected override void DoEmit (EmitContext ec)
6306 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6308 ec.BeginCatchBlock (CatchType);
6311 li.CreateBuilder (ec);
6314 // Special case hoisted catch variable, we have to use a temporary variable
6315 // to pass via anonymous storey initialization with the value still on top
6318 if (li.HoistedVariant != null) {
6319 LocalTemporary lt = new LocalTemporary (li.Type);
6322 // switch to assigning from the temporary variable and not from top of the stack
6323 assign.UpdateSource (lt);
6326 ec.Emit (OpCodes.Pop);
6332 public override bool Resolve (BlockContext ec)
6334 using (ec.Set (ResolveContext.Options.CatchScope)) {
6335 if (type_expr != null) {
6336 type = type_expr.ResolveAsType (ec);
6340 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
6341 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6342 } else if (li != null) {
6344 li.PrepareAssignmentAnalysis (ec);
6346 // source variable is at the top of the stack
6347 Expression source = new EmptyExpression (li.Type);
6348 if (li.Type.IsGenericParameter)
6349 source = new UnboxCast (source, li.Type);
6352 // Uses Location.Null to hide from symbol file
6354 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6355 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6359 Block.SetCatchBlock ();
6360 return Block.Resolve (ec);
6364 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6367 fc.SetVariableAssigned (li.VariableInfo, true);
6370 return block.FlowAnalysis (fc);
6373 public override Reachability MarkReachable (Reachability rc)
6375 base.MarkReachable (rc);
6377 return block.MarkReachable (rc);
6380 protected override void CloneTo (CloneContext clonectx, Statement t)
6382 Catch target = (Catch) t;
6384 if (type_expr != null)
6385 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6387 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6391 public class TryFinally : TryFinallyBlock
6394 List<DefiniteAssignmentBitSet> try_exit_dat;
6396 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6402 public ExplicitBlock FinallyBlock {
6408 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6410 if (try_exit_dat == null)
6411 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6413 try_exit_dat.Add (vector);
6416 public override bool Resolve (BlockContext bc)
6418 bool ok = base.Resolve (bc);
6420 fini.SetFinallyBlock ();
6421 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6422 ok &= fini.Resolve (bc);
6428 protected override void EmitTryBody (EmitContext ec)
6433 public override void EmitFinallyBody (EmitContext ec)
6438 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6440 var da = fc.BranchDefiniteAssignment ();
6442 var tf = fc.TryFinally;
6443 fc.TryFinally = this;
6445 var res_stmt = Statement.FlowAnalysis (fc);
6449 var try_da = fc.DefiniteAssignment;
6450 fc.DefiniteAssignment = da;
6452 var res_fin = fini.FlowAnalysis (fc);
6454 if (try_exit_dat != null) {
6456 // try block has global exit but we need to run definite assignment check
6457 // for parameter block out parameter after finally block because it's always
6458 // executed before exit
6460 foreach (var try_da_part in try_exit_dat)
6461 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
6463 try_exit_dat = null;
6466 fc.DefiniteAssignment |= try_da;
6467 return res_stmt | res_fin;
6470 public override Reachability MarkReachable (Reachability rc)
6473 // Mark finally block first for any exit statement in try block
6474 // to know whether the code which follows finally is reachable
6476 return fini.MarkReachable (rc) | base.MarkReachable (rc);
6479 protected override void CloneTo (CloneContext clonectx, Statement t)
6481 TryFinally target = (TryFinally) t;
6483 target.stmt = stmt.Clone (clonectx);
6485 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
6488 public override object Accept (StructuralVisitor visitor)
6490 return visitor.Visit (this);
6494 public class TryCatch : ExceptionStatement
6497 List<Catch> clauses;
6498 readonly bool inside_try_finally;
6500 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
6504 this.clauses = catch_clauses;
6505 this.inside_try_finally = inside_try_finally;
6508 public List<Catch> Clauses {
6514 public bool IsTryCatchFinally {
6516 return inside_try_finally;
6520 public override bool Resolve (BlockContext bc)
6524 using (bc.Set (ResolveContext.Options.TryScope)) {
6525 parent = bc.CurrentTryBlock;
6527 if (IsTryCatchFinally) {
6528 ok = Block.Resolve (bc);
6530 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
6531 bc.CurrentTryBlock = this;
6532 ok = Block.Resolve (bc);
6533 bc.CurrentTryBlock = parent;
6538 for (int i = 0; i < clauses.Count; ++i) {
6541 ok &= c.Resolve (bc);
6543 TypeSpec resolved_type = c.CatchType;
6544 for (int ii = 0; ii < clauses.Count; ++ii) {
6548 if (clauses[ii].IsGeneral) {
6549 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
6552 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
6555 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
6558 bc.Report.Warning (1058, 1, c.loc,
6559 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
6567 var ct = clauses[ii].CatchType;
6571 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
6572 bc.Report.Error (160, c.loc,
6573 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
6574 ct.GetSignatureForError ());
6580 return base.Resolve (bc) && ok;
6583 protected sealed override void DoEmit (EmitContext ec)
6585 if (!inside_try_finally)
6586 EmitTryBodyPrepare (ec);
6590 foreach (Catch c in clauses)
6593 if (!inside_try_finally)
6594 ec.EndExceptionBlock ();
6597 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6599 var start_fc = fc.BranchDefiniteAssignment ();
6600 var res = Block.FlowAnalysis (fc);
6602 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
6604 foreach (var c in clauses) {
6605 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
6606 if (!c.FlowAnalysis (fc)) {
6608 try_fc = fc.DefiniteAssignment;
6610 try_fc &= fc.DefiniteAssignment;
6616 fc.DefiniteAssignment = try_fc ?? start_fc;
6621 public override Reachability MarkReachable (Reachability rc)
6623 if (rc.IsUnreachable)
6626 base.MarkReachable (rc);
6628 var tc_rc = Block.MarkReachable (rc);
6630 foreach (var c in clauses)
6631 tc_rc &= c.MarkReachable (rc);
6636 protected override void CloneTo (CloneContext clonectx, Statement t)
6638 TryCatch target = (TryCatch) t;
6640 target.Block = clonectx.LookupBlock (Block);
6641 if (clauses != null){
6642 target.clauses = new List<Catch> ();
6643 foreach (Catch c in clauses)
6644 target.clauses.Add ((Catch) c.Clone (clonectx));
6648 public override object Accept (StructuralVisitor visitor)
6650 return visitor.Visit (this);
6654 public class Using : TryFinallyBlock
6656 public class VariableDeclaration : BlockVariable
6658 Statement dispose_call;
6660 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6665 public VariableDeclaration (LocalVariable li, Location loc)
6671 public VariableDeclaration (Expression expr)
6674 loc = expr.Location;
6680 public bool IsNested { get; private set; }
6684 public void EmitDispose (EmitContext ec)
6686 dispose_call.Emit (ec);
6689 public override bool Resolve (BlockContext bc)
6694 return base.Resolve (bc, false);
6697 public Expression ResolveExpression (BlockContext bc)
6699 var e = Initializer.Resolve (bc);
6703 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
6704 Initializer = ResolveInitializer (bc, Variable, e);
6708 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6710 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
6711 initializer = initializer.Resolve (bc);
6712 if (initializer == null)
6715 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
6716 Arguments args = new Arguments (1);
6717 args.Add (new Argument (initializer));
6718 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
6719 if (initializer == null)
6722 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
6723 dispose_call = CreateDisposeCall (bc, var);
6724 dispose_call.Resolve (bc);
6726 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
6729 if (li == Variable) {
6730 CheckIDiposableConversion (bc, li, initializer);
6731 dispose_call = CreateDisposeCall (bc, li);
6732 dispose_call.Resolve (bc);
6735 return base.ResolveInitializer (bc, li, initializer);
6738 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6742 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
6743 if (type.IsNullableType) {
6744 // it's handled in CreateDisposeCall
6748 if (type != InternalType.ErrorType) {
6749 bc.Report.SymbolRelatedToPreviousError (type);
6750 var loc = type_expr == null ? initializer.Location : type_expr.Location;
6751 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
6752 type.GetSignatureForError ());
6759 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6761 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
6763 var loc = lv.Location;
6765 var idt = bc.BuiltinTypes.IDisposable;
6766 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6768 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6769 dispose_mg.InstanceExpression = type.IsNullableType ?
6770 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
6774 // Hide it from symbol file via null location
6776 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
6778 // Add conditional call when disposing possible null variable
6779 if (!type.IsStruct || type.IsNullableType)
6780 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
6785 public void ResolveDeclaratorInitializer (BlockContext bc)
6787 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
6790 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
6792 for (int i = declarators.Count - 1; i >= 0; --i) {
6793 var d = declarators [i];
6794 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
6795 vd.Initializer = d.Initializer;
6797 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
6798 vd.dispose_call.Resolve (bc);
6800 stmt = new Using (vd, stmt, d.Variable.Location);
6807 public override object Accept (StructuralVisitor visitor)
6809 return visitor.Visit (this);
6813 VariableDeclaration decl;
6815 public Using (VariableDeclaration decl, Statement stmt, Location loc)
6821 public Using (Expression expr, Statement stmt, Location loc)
6824 this.decl = new VariableDeclaration (expr);
6829 public Expression Expr {
6831 return decl.Variable == null ? decl.Initializer : null;
6835 public BlockVariable Variables {
6843 public override void Emit (EmitContext ec)
6846 // Don't emit sequence point it will be set on variable declaration
6851 protected override void EmitTryBodyPrepare (EmitContext ec)
6854 base.EmitTryBodyPrepare (ec);
6857 protected override void EmitTryBody (EmitContext ec)
6862 public override void EmitFinallyBody (EmitContext ec)
6864 decl.EmitDispose (ec);
6867 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6869 decl.FlowAnalysis (fc);
6870 return stmt.FlowAnalysis (fc);
6873 public override Reachability MarkReachable (Reachability rc)
6875 decl.MarkReachable (rc);
6876 return base.MarkReachable (rc);
6879 public override bool Resolve (BlockContext ec)
6881 VariableReference vr;
6882 bool vr_locked = false;
6884 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
6885 if (decl.Variable == null) {
6886 vr = decl.ResolveExpression (ec) as VariableReference;
6888 vr_locked = vr.IsLockedByStatement;
6889 vr.IsLockedByStatement = true;
6892 if (decl.IsNested) {
6893 decl.ResolveDeclaratorInitializer (ec);
6895 if (!decl.Resolve (ec))
6898 if (decl.Declarators != null) {
6899 stmt = decl.RewriteUsingDeclarators (ec, stmt);
6910 vr.IsLockedByStatement = vr_locked;
6915 protected override void CloneTo (CloneContext clonectx, Statement t)
6917 Using target = (Using) t;
6919 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6920 target.stmt = stmt.Clone (clonectx);
6923 public override object Accept (StructuralVisitor visitor)
6925 return visitor.Visit (this);
6930 /// Implementation of the foreach C# statement
6932 public class Foreach : LoopStatement
6934 abstract class IteratorStatement : Statement
6936 protected readonly Foreach for_each;
6938 protected IteratorStatement (Foreach @foreach)
6940 this.for_each = @foreach;
6941 this.loc = @foreach.expr.Location;
6944 protected override void CloneTo (CloneContext clonectx, Statement target)
6946 throw new NotImplementedException ();
6949 public override void Emit (EmitContext ec)
6951 if (ec.EmitAccurateDebugInfo) {
6952 ec.Emit (OpCodes.Nop);
6958 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6960 throw new NotImplementedException ();
6964 sealed class ArrayForeach : IteratorStatement
6966 TemporaryVariableReference[] lengths;
6967 Expression [] length_exprs;
6968 StatementExpression[] counter;
6969 TemporaryVariableReference[] variables;
6971 TemporaryVariableReference copy;
6973 public ArrayForeach (Foreach @foreach, int rank)
6976 counter = new StatementExpression[rank];
6977 variables = new TemporaryVariableReference[rank];
6978 length_exprs = new Expression [rank];
6981 // Only use temporary length variables when dealing with
6982 // multi-dimensional arrays
6985 lengths = new TemporaryVariableReference [rank];
6988 public override bool Resolve (BlockContext ec)
6990 Block variables_block = for_each.variable.Block;
6991 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
6994 int rank = length_exprs.Length;
6995 Arguments list = new Arguments (rank);
6996 for (int i = 0; i < rank; i++) {
6997 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6999 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7000 counter[i].Resolve (ec);
7003 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7005 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7006 lengths[i].Resolve (ec);
7008 Arguments args = new Arguments (1);
7009 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7010 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7013 list.Add (new Argument (v));
7016 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7021 if (for_each.type is VarExpr) {
7022 // Infer implicitly typed local variable from foreach array type
7023 var_type = access.Type;
7025 var_type = for_each.type.ResolveAsType (ec);
7027 if (var_type == null)
7030 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7035 for_each.variable.Type = var_type;
7037 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7038 if (variable_ref == null)
7041 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7043 return for_each.body.Resolve (ec);
7046 protected override void DoEmit (EmitContext ec)
7048 copy.EmitAssign (ec, for_each.expr);
7050 int rank = length_exprs.Length;
7051 Label[] test = new Label [rank];
7052 Label[] loop = new Label [rank];
7054 for (int i = 0; i < rank; i++) {
7055 test [i] = ec.DefineLabel ();
7056 loop [i] = ec.DefineLabel ();
7058 if (lengths != null)
7059 lengths [i].EmitAssign (ec, length_exprs [i]);
7062 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7063 for (int i = 0; i < rank; i++) {
7064 variables [i].EmitAssign (ec, zero);
7066 ec.Emit (OpCodes.Br, test [i]);
7067 ec.MarkLabel (loop [i]);
7070 for_each.body.Emit (ec);
7072 ec.MarkLabel (ec.LoopBegin);
7073 ec.Mark (for_each.expr.Location);
7075 for (int i = rank - 1; i >= 0; i--){
7076 counter [i].Emit (ec);
7078 ec.MarkLabel (test [i]);
7079 variables [i].Emit (ec);
7081 if (lengths != null)
7082 lengths [i].Emit (ec);
7084 length_exprs [i].Emit (ec);
7086 ec.Emit (OpCodes.Blt, loop [i]);
7089 ec.MarkLabel (ec.LoopEnd);
7093 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7095 class RuntimeDispose : Using.VariableDeclaration
7097 public RuntimeDispose (LocalVariable lv, Location loc)
7102 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7104 // Defered to runtime check
7107 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7109 var idt = bc.BuiltinTypes.IDisposable;
7112 // Fabricates code like
7114 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7117 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7119 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7120 dispose_variable.CreateReferenceExpression (bc, loc),
7121 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7122 loc), new NullLiteral (loc));
7124 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7126 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7127 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7129 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7130 return new If (idisaposable_test, dispose, loc);
7134 LocalVariable variable;
7136 Statement statement;
7137 ExpressionStatement init;
7138 TemporaryVariableReference enumerator_variable;
7139 bool ambiguous_getenumerator_name;
7141 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7144 this.variable = var;
7148 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7150 rc.Report.SymbolRelatedToPreviousError (enumerator);
7151 rc.Report.Error (202, loc,
7152 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7153 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7156 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7159 // Option 1: Try to match by name GetEnumerator first
7161 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7162 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7164 var mg = mexpr as MethodGroupExpr;
7166 mg.InstanceExpression = expr;
7167 Arguments args = new Arguments (0);
7168 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
7170 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7171 if (ambiguous_getenumerator_name)
7174 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7180 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7183 PredefinedMember<MethodSpec> iface_candidate = null;
7184 var ptypes = rc.Module.PredefinedTypes;
7185 var gen_ienumerable = ptypes.IEnumerableGeneric;
7186 if (!gen_ienumerable.Define ())
7187 gen_ienumerable = null;
7189 var ifaces = t.Interfaces;
7190 if (ifaces != null) {
7191 foreach (var iface in ifaces) {
7192 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7193 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7194 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7195 rc.Report.Error (1640, loc,
7196 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7197 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7202 // TODO: Cache this somehow
7203 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7204 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7209 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7210 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7215 if (iface_candidate == null) {
7216 if (expr.Type != InternalType.ErrorType) {
7217 rc.Report.Error (1579, loc,
7218 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7219 expr.Type.GetSignatureForError (), "GetEnumerator");
7225 var method = iface_candidate.Resolve (loc);
7229 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7230 mg.InstanceExpression = expr;
7234 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7236 var ms = MemberCache.FindMember (enumerator.ReturnType,
7237 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7238 BindingRestriction.InstanceOnly) as MethodSpec;
7240 if (ms == null || !ms.IsPublic) {
7241 Error_WrongEnumerator (rc, enumerator);
7245 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7248 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7250 var ps = MemberCache.FindMember (enumerator.ReturnType,
7251 MemberFilter.Property ("Current", null),
7252 BindingRestriction.InstanceOnly) as PropertySpec;
7254 if (ps == null || !ps.IsPublic) {
7255 Error_WrongEnumerator (rc, enumerator);
7262 public override bool Resolve (BlockContext ec)
7264 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7267 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7268 } else if (expr.Type.IsNullableType) {
7269 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7272 var get_enumerator_mg = ResolveGetEnumerator (ec);
7273 if (get_enumerator_mg == null) {
7277 var get_enumerator = get_enumerator_mg.BestCandidate;
7278 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7279 enumerator_variable.Resolve (ec);
7281 // Prepare bool MoveNext ()
7282 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7283 if (move_next_mg == null) {
7287 move_next_mg.InstanceExpression = enumerator_variable;
7289 // Prepare ~T~ Current { get; }
7290 var current_prop = ResolveCurrent (ec, get_enumerator);
7291 if (current_prop == null) {
7295 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7296 if (current_pe == null)
7299 VarExpr ve = for_each.type as VarExpr;
7303 // Source type is dynamic, set element type to dynamic too
7304 variable.Type = ec.BuiltinTypes.Dynamic;
7306 // Infer implicitly typed local variable from foreach enumerable type
7307 variable.Type = current_pe.Type;
7311 // Explicit cast of dynamic collection elements has to be done at runtime
7312 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7315 variable.Type = for_each.type.ResolveAsType (ec);
7317 if (variable.Type == null)
7320 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7321 if (current_pe == null)
7325 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7326 if (variable_ref == null)
7329 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7331 var init = new Invocation (get_enumerator_mg, null);
7333 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7334 for_each.body, Location.Null);
7336 var enum_type = enumerator_variable.Type;
7339 // Add Dispose method call when enumerator can be IDisposable
7341 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7342 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7344 // Runtime Dispose check
7346 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7347 vd.Initializer = init;
7348 statement = new Using (vd, statement, Location.Null);
7351 // No Dispose call needed
7353 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7354 this.init.Resolve (ec);
7358 // Static Dispose check
7360 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
7361 vd.Initializer = init;
7362 statement = new Using (vd, statement, Location.Null);
7365 return statement.Resolve (ec);
7368 protected override void DoEmit (EmitContext ec)
7370 enumerator_variable.LocalInfo.CreateBuilder (ec);
7373 init.EmitStatement (ec);
7375 statement.Emit (ec);
7378 #region IErrorHandler Members
7380 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
7382 ec.Report.SymbolRelatedToPreviousError (best);
7383 ec.Report.Warning (278, 2, expr.Location,
7384 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
7385 expr.Type.GetSignatureForError (), "enumerable",
7386 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
7388 ambiguous_getenumerator_name = true;
7392 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
7397 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
7402 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
7411 LocalVariable variable;
7415 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
7419 this.variable = var;
7425 public Expression Expr {
7426 get { return expr; }
7429 public Expression TypeExpression {
7430 get { return type; }
7433 public LocalVariable Variable {
7434 get { return variable; }
7437 public override Reachability MarkReachable (Reachability rc)
7439 base.MarkReachable (rc);
7441 body.MarkReachable (rc);
7446 public override bool Resolve (BlockContext ec)
7448 expr = expr.Resolve (ec);
7453 ec.Report.Error (186, loc, "Use of null is not valid in this context");
7457 body.AddStatement (Statement);
7459 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
7460 Statement = new ArrayForeach (this, 1);
7461 } else if (expr.Type is ArrayContainer) {
7462 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
7464 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
7465 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
7466 expr.ExprClassName);
7470 Statement = new CollectionForeach (this, variable, expr);
7477 protected override void DoEmit (EmitContext ec)
7479 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
7480 ec.LoopBegin = ec.DefineLabel ();
7481 ec.LoopEnd = ec.DefineLabel ();
7483 if (!(Statement is Block))
7484 ec.BeginCompilerScope ();
7486 variable.CreateBuilder (ec);
7488 Statement.Emit (ec);
7490 if (!(Statement is Block))
7493 ec.LoopBegin = old_begin;
7494 ec.LoopEnd = old_end;
7497 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7499 expr.FlowAnalysis (fc);
7501 var da = fc.BranchDefiniteAssignment ();
7502 body.FlowAnalysis (fc);
7503 fc.DefiniteAssignment = da;
7507 protected override void CloneTo (CloneContext clonectx, Statement t)
7509 Foreach target = (Foreach) t;
7511 target.type = type.Clone (clonectx);
7512 target.expr = expr.Clone (clonectx);
7513 target.body = (Block) body.Clone (clonectx);
7514 target.Statement = Statement.Clone (clonectx);
7517 public override object Accept (StructuralVisitor visitor)
7519 return visitor.Visit (this);