2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
27 protected bool reachable;
29 public bool IsUnreachable {
36 /// Resolves the statement, true means that all sub-statements
39 public virtual bool Resolve (BlockContext bc)
45 /// Return value indicates whether all code paths emitted return.
47 protected abstract void DoEmit (EmitContext ec);
49 public virtual void Emit (EmitContext ec)
54 if (ec.StatementEpilogue != null) {
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
63 protected abstract void CloneTo (CloneContext clonectx, Statement target);
65 public Statement Clone (CloneContext clonectx)
67 Statement s = (Statement) this.MemberwiseClone ();
68 CloneTo (clonectx, s);
72 public virtual Expression CreateExpressionTree (ResolveContext ec)
74 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
78 public virtual object Accept (StructuralVisitor visitor)
80 return visitor.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
88 public bool FlowAnalysis (FlowAnalysisContext fc)
91 fc.UnreachableReported = false;
92 var res = DoFlowAnalysis (fc);
97 // Special handling cases
100 return DoFlowAnalysis (fc);
103 if (this is EmptyStatement || loc.IsNull)
106 if (fc.UnreachableReported)
109 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
110 fc.UnreachableReported = true;
114 public virtual Reachability MarkReachable (Reachability rc)
116 if (!rc.IsUnreachable)
122 protected void CheckExitBoundaries (BlockContext bc, Block scope)
124 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
125 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
129 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
130 if (b.IsFinallyBlock) {
131 Error_FinallyClauseExit (bc);
137 protected void Error_FinallyClauseExit (BlockContext bc)
139 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
143 public sealed class EmptyStatement : Statement
145 public EmptyStatement (Location loc)
150 public override bool Resolve (BlockContext ec)
155 public override void Emit (EmitContext ec)
159 protected override void DoEmit (EmitContext ec)
161 throw new NotSupportedException ();
164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
169 protected override void CloneTo (CloneContext clonectx, Statement target)
174 public override object Accept (StructuralVisitor visitor)
176 return visitor.Visit (this);
180 public class If : Statement {
182 public Statement TrueStatement;
183 public Statement FalseStatement;
185 bool true_returns, false_returns;
187 public If (Expression bool_expr, Statement true_statement, Location l)
188 : this (bool_expr, true_statement, null, l)
192 public If (Expression bool_expr,
193 Statement true_statement,
194 Statement false_statement,
197 this.expr = bool_expr;
198 TrueStatement = true_statement;
199 FalseStatement = false_statement;
203 public Expression Expr {
209 public override bool Resolve (BlockContext ec)
211 expr = expr.Resolve (ec);
213 var ok = TrueStatement.Resolve (ec);
215 if (FalseStatement != null) {
216 ok &= FalseStatement.Resolve (ec);
222 protected override void DoEmit (EmitContext ec)
224 Label false_target = ec.DefineLabel ();
228 // If we're a boolean constant, Resolve() already
229 // eliminated dead code for us.
231 Constant c = expr as Constant;
233 c.EmitSideEffect (ec);
235 if (!c.IsDefaultValue)
236 TrueStatement.Emit (ec);
237 else if (FalseStatement != null)
238 FalseStatement.Emit (ec);
243 expr.EmitBranchable (ec, false_target, false);
245 TrueStatement.Emit (ec);
247 if (FalseStatement != null){
248 bool branch_emitted = false;
250 end = ec.DefineLabel ();
252 ec.Emit (OpCodes.Br, end);
253 branch_emitted = true;
256 ec.MarkLabel (false_target);
257 FalseStatement.Emit (ec);
262 ec.MarkLabel (false_target);
266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
268 expr.FlowAnalysisConditional (fc);
270 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
272 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
274 var res = TrueStatement.FlowAnalysis (fc);
276 if (FalseStatement == null) {
277 var c = expr as Constant;
278 if (c != null && !c.IsDefaultValue)
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 expr.FlowAnalysisConditional (fc);
424 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
426 if (res && !iterator_reachable)
427 return !end_reachable;
429 if (!end_reachable) {
430 var c = expr as Constant;
431 if (c != null && !c.IsDefaultValue)
438 public override Reachability MarkReachable (Reachability rc)
440 base.MarkReachable (rc);
442 var body_rc = Statement.MarkReachable (rc);
444 if (body_rc.IsUnreachable && !iterator_reachable) {
445 expr = new UnreachableExpression (expr);
446 return end_reachable ? rc : Reachability.CreateUnreachable ();
449 if (!end_reachable) {
450 var c = expr as Constant;
451 if (c != null && !c.IsDefaultValue)
452 return Reachability.CreateUnreachable ();
458 protected override void CloneTo (CloneContext clonectx, Statement t)
462 target.Statement = Statement.Clone (clonectx);
463 target.expr = expr.Clone (clonectx);
466 public override object Accept (StructuralVisitor visitor)
468 return visitor.Visit (this);
471 public override void SetEndReachable ()
473 end_reachable = true;
476 public override void SetIteratorReachable ()
478 iterator_reachable = true;
482 public class While : LoopStatement
484 public Expression expr;
485 bool empty, infinite, end_reachable;
486 List<DefiniteAssignmentBitSet> end_reachable_das;
488 public While (BooleanExpression bool_expr, Statement statement, Location l)
491 this.expr = bool_expr;
495 public override bool Resolve (BlockContext bc)
499 expr = expr.Resolve (bc);
503 var c = expr as Constant;
505 empty = c.IsDefaultValue;
509 ok &= base.Resolve (bc);
513 protected override void DoEmit (EmitContext ec)
516 expr.EmitSideEffect (ec);
520 Label old_begin = ec.LoopBegin;
521 Label old_end = ec.LoopEnd;
523 ec.LoopBegin = ec.DefineLabel ();
524 ec.LoopEnd = ec.DefineLabel ();
527 // Inform whether we are infinite or not
529 if (expr is Constant) {
530 // expr is 'true', since the 'empty' case above handles the 'false' case
531 ec.MarkLabel (ec.LoopBegin);
533 if (ec.EmitAccurateDebugInfo)
534 ec.Emit (OpCodes.Nop);
536 expr.EmitSideEffect (ec);
538 ec.Emit (OpCodes.Br, ec.LoopBegin);
541 // Inform that we are infinite (ie, `we return'), only
542 // if we do not `break' inside the code.
544 ec.MarkLabel (ec.LoopEnd);
546 Label while_loop = ec.DefineLabel ();
548 ec.Emit (OpCodes.Br, ec.LoopBegin);
549 ec.MarkLabel (while_loop);
553 ec.MarkLabel (ec.LoopBegin);
556 expr.EmitBranchable (ec, while_loop, true);
558 ec.MarkLabel (ec.LoopEnd);
561 ec.LoopBegin = old_begin;
562 ec.LoopEnd = old_end;
565 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
567 expr.FlowAnalysisConditional (fc);
569 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
570 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
572 Statement.FlowAnalysis (fc);
575 // Special case infinite while with breaks
577 if (end_reachable_das != null) {
578 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
579 end_reachable_das = null;
582 fc.DefiniteAssignment = da_false;
584 if (infinite && !end_reachable)
590 public override Reachability MarkReachable (Reachability rc)
592 if (rc.IsUnreachable)
595 base.MarkReachable (rc);
598 // Special case unreachable while body
601 Statement.MarkReachable (Reachability.CreateUnreachable ());
605 Statement.MarkReachable (rc);
608 // When infinite while end is unreachable via break anything what follows is unreachable too
610 if (infinite && !end_reachable)
611 return Reachability.CreateUnreachable ();
616 protected override void CloneTo (CloneContext clonectx, Statement t)
618 While target = (While) t;
620 target.expr = expr.Clone (clonectx);
621 target.Statement = Statement.Clone (clonectx);
624 public override object Accept (StructuralVisitor visitor)
626 return visitor.Visit (this);
629 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
634 if (end_reachable_das == null)
635 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
637 end_reachable_das.Add (fc.DefiniteAssignment);
640 public override void SetEndReachable ()
642 end_reachable = true;
646 public class For : LoopStatement
648 bool infinite, empty, iterator_reachable, end_reachable;
649 List<DefiniteAssignmentBitSet> end_reachable_das;
651 public For (Location l)
657 public Statement Initializer {
661 public Expression Condition {
665 public Statement Iterator {
669 public override bool Resolve (BlockContext bc)
671 Initializer.Resolve (bc);
673 if (Condition != null) {
674 Condition = Condition.Resolve (bc);
675 var condition_constant = Condition as Constant;
676 if (condition_constant != null) {
677 if (condition_constant.IsDefaultValue) {
687 return base.Resolve (bc) && Iterator.Resolve (bc);
690 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
692 Initializer.FlowAnalysis (fc);
694 DefiniteAssignmentBitSet da_false;
695 if (Condition != null) {
696 Condition.FlowAnalysisConditional (fc);
697 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
698 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
700 da_false = fc.BranchDefiniteAssignment ();
703 Statement.FlowAnalysis (fc);
705 Iterator.FlowAnalysis (fc);
708 // Special case infinite for with breaks
710 if (end_reachable_das != null) {
711 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
712 end_reachable_das = null;
715 fc.DefiniteAssignment = da_false;
717 if (infinite && !end_reachable)
723 public override Reachability MarkReachable (Reachability rc)
725 base.MarkReachable (rc);
727 Initializer.MarkReachable (rc);
729 var body_rc = Statement.MarkReachable (rc);
730 if (!body_rc.IsUnreachable || iterator_reachable) {
731 Iterator.MarkReachable (rc);
735 // When infinite for end is unreachable via break anything what follows is unreachable too
737 if (infinite && !end_reachable) {
738 return Reachability.CreateUnreachable ();
744 protected override void DoEmit (EmitContext ec)
746 if (Initializer != null)
747 Initializer.Emit (ec);
750 Condition.EmitSideEffect (ec);
754 Label old_begin = ec.LoopBegin;
755 Label old_end = ec.LoopEnd;
756 Label loop = ec.DefineLabel ();
757 Label test = ec.DefineLabel ();
759 ec.LoopBegin = ec.DefineLabel ();
760 ec.LoopEnd = ec.DefineLabel ();
762 ec.Emit (OpCodes.Br, test);
766 ec.MarkLabel (ec.LoopBegin);
771 // If test is null, there is no test, and we are just
774 if (Condition != null) {
775 ec.Mark (Condition.Location);
778 // The Resolve code already catches the case for
779 // Test == Constant (false) so we know that
782 if (Condition is Constant) {
783 Condition.EmitSideEffect (ec);
784 ec.Emit (OpCodes.Br, loop);
786 Condition.EmitBranchable (ec, loop, true);
790 ec.Emit (OpCodes.Br, loop);
791 ec.MarkLabel (ec.LoopEnd);
793 ec.LoopBegin = old_begin;
794 ec.LoopEnd = old_end;
797 protected override void CloneTo (CloneContext clonectx, Statement t)
799 For target = (For) t;
801 if (Initializer != null)
802 target.Initializer = Initializer.Clone (clonectx);
803 if (Condition != null)
804 target.Condition = Condition.Clone (clonectx);
805 if (Iterator != null)
806 target.Iterator = Iterator.Clone (clonectx);
807 target.Statement = Statement.Clone (clonectx);
810 public override object Accept (StructuralVisitor visitor)
812 return visitor.Visit (this);
815 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
820 if (end_reachable_das == null)
821 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
823 end_reachable_das.Add (fc.DefiniteAssignment);
826 public override void SetEndReachable ()
828 end_reachable = true;
831 public override void SetIteratorReachable ()
833 iterator_reachable = true;
837 public abstract class LoopStatement : Statement
839 protected LoopStatement (Statement statement)
841 Statement = statement;
844 public Statement Statement { get; set; }
846 public override bool Resolve (BlockContext bc)
848 var prev_loop = bc.EnclosingLoop;
849 var prev_los = bc.EnclosingLoopOrSwitch;
850 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
851 var ok = Statement.Resolve (bc);
852 bc.EnclosingLoopOrSwitch = prev_los;
853 bc.EnclosingLoop = prev_loop;
859 // Needed by possibly infinite loops statements (for, while) and switch statment
861 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
865 public virtual void SetEndReachable ()
869 public virtual void SetIteratorReachable ()
874 public class StatementExpression : Statement
876 ExpressionStatement expr;
878 public StatementExpression (ExpressionStatement expr)
881 loc = expr.StartLocation;
884 public StatementExpression (ExpressionStatement expr, Location loc)
890 public ExpressionStatement Expr {
896 protected override void CloneTo (CloneContext clonectx, Statement t)
898 StatementExpression target = (StatementExpression) t;
899 target.expr = (ExpressionStatement) expr.Clone (clonectx);
902 protected override void DoEmit (EmitContext ec)
904 expr.EmitStatement (ec);
907 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
909 expr.FlowAnalysis (fc);
913 public override Reachability MarkReachable (Reachability rc)
915 base.MarkReachable (rc);
916 expr.MarkReachable (rc);
920 public override bool Resolve (BlockContext ec)
922 expr = expr.ResolveStatement (ec);
926 public override object Accept (StructuralVisitor visitor)
928 return visitor.Visit (this);
932 public class StatementErrorExpression : Statement
936 public StatementErrorExpression (Expression expr)
939 this.loc = expr.StartLocation;
942 public Expression Expr {
948 public override bool Resolve (BlockContext bc)
950 expr.Error_InvalidExpressionStatement (bc);
954 protected override void DoEmit (EmitContext ec)
956 throw new NotSupportedException ();
959 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
964 protected override void CloneTo (CloneContext clonectx, Statement target)
966 var t = (StatementErrorExpression) target;
968 t.expr = expr.Clone (clonectx);
971 public override object Accept (StructuralVisitor visitor)
973 return visitor.Visit (this);
978 // Simple version of statement list not requiring a block
980 public class StatementList : Statement
982 List<Statement> statements;
984 public StatementList (Statement first, Statement second)
986 statements = new List<Statement> { first, second };
990 public IList<Statement> Statements {
997 public void Add (Statement statement)
999 statements.Add (statement);
1002 public override bool Resolve (BlockContext ec)
1004 foreach (var s in statements)
1010 protected override void DoEmit (EmitContext ec)
1012 foreach (var s in statements)
1016 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1018 foreach (var s in statements)
1019 s.FlowAnalysis (fc);
1024 public override Reachability MarkReachable (Reachability rc)
1026 base.MarkReachable (rc);
1028 Reachability res = rc;
1029 foreach (var s in statements)
1030 res = s.MarkReachable (rc);
1035 protected override void CloneTo (CloneContext clonectx, Statement target)
1037 StatementList t = (StatementList) target;
1039 t.statements = new List<Statement> (statements.Count);
1040 foreach (Statement s in statements)
1041 t.statements.Add (s.Clone (clonectx));
1044 public override object Accept (StructuralVisitor visitor)
1046 return visitor.Visit (this);
1051 // For statements which require special handling when inside try or catch block
1053 public abstract class ExitStatement : Statement
1055 protected bool unwind_protect;
1057 protected abstract bool DoResolve (BlockContext bc);
1058 protected abstract bool IsLocalExit { get; }
1060 public override bool Resolve (BlockContext bc)
1062 var res = DoResolve (bc);
1066 // We are inside finally scope but is it the scope we are exiting
1068 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1070 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1071 if (b.IsFinallyBlock) {
1072 Error_FinallyClauseExit (bc);
1076 if (b is ParametersBlock)
1082 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1086 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1091 if (fc.TryFinally != null) {
1092 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1094 fc.ParametersBlock.CheckControlExit (fc);
1102 /// Implements the return statement
1104 public class Return : ExitStatement
1108 public Return (Expression expr, Location l)
1116 public Expression Expr {
1125 protected override bool IsLocalExit {
1133 protected override bool DoResolve (BlockContext ec)
1135 var block_return_type = ec.ReturnType;
1138 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1142 // Return must not be followed by an expression when
1143 // the method return type is Task
1145 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1146 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1147 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1149 // Extra trick not to emit ret/leave inside awaiter body
1151 expr = EmptyExpression.Null;
1155 if (storey.ReturnType.IsGenericTask)
1156 block_return_type = storey.ReturnType.TypeArguments[0];
1159 if (ec.CurrentIterator != null) {
1160 Error_ReturnFromIterator (ec);
1161 } else if (block_return_type != InternalType.ErrorType) {
1162 ec.Report.Error (126, loc,
1163 "An object of a type convertible to `{0}' is required for the return statement",
1164 block_return_type.GetSignatureForError ());
1170 expr = expr.Resolve (ec);
1172 AnonymousExpression am = ec.CurrentAnonymousMethod;
1174 if (block_return_type.Kind == MemberKind.Void) {
1175 ec.Report.Error (127, loc,
1176 "`{0}': A return keyword must not be followed by any expression when method returns void",
1177 ec.GetSignatureForError ());
1182 if (am.IsIterator) {
1183 Error_ReturnFromIterator (ec);
1187 var async_block = am as AsyncInitializer;
1188 if (async_block != null) {
1190 var storey = (AsyncTaskStorey) am.Storey;
1191 var async_type = storey.ReturnType;
1193 if (async_type == null && async_block.ReturnTypeInference != null) {
1194 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1195 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1197 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1201 if (async_type.Kind == MemberKind.Void) {
1202 ec.Report.Error (8030, loc,
1203 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1207 if (!async_type.IsGenericTask) {
1208 if (this is ContextualReturn)
1211 if (async_block.DelegateType != null) {
1212 ec.Report.Error (8031, loc,
1213 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1215 ec.Report.Error (1997, loc,
1216 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1217 ec.GetSignatureForError ());
1223 // The return type is actually Task<T> type argument
1225 if (expr.Type == async_type) {
1226 ec.Report.Error (4016, loc,
1227 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1228 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1230 block_return_type = async_type.TypeArguments[0];
1234 if (block_return_type.Kind == MemberKind.Void) {
1235 ec.Report.Error (8030, loc,
1236 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1240 var l = am as AnonymousMethodBody;
1241 if (l != null && expr != null) {
1242 if (l.ReturnTypeInference != null) {
1243 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1248 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1249 // unexpected debugging experience
1251 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1252 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1261 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1262 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1265 if (am != null && block_return_type == ec.ReturnType) {
1266 ec.Report.Error (1662, loc,
1267 "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",
1268 am.ContainerType, am.GetSignatureForError ());
1277 protected override void DoEmit (EmitContext ec)
1281 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1282 if (async_body != null) {
1283 var storey = (AsyncTaskStorey)async_body.Storey;
1284 Label exit_label = async_body.BodyEnd;
1287 // It's null for await without async
1289 if (storey.HoistedReturnValue != null) {
1291 // Special case hoisted return value (happens in try/finally scenario)
1293 if (ec.TryFinallyUnwind != null) {
1294 if (storey.HoistedReturnValue is VariableReference) {
1295 storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type);
1298 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1301 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1302 async_return.EmitAssign (ec, expr, false, false);
1307 if (ec.TryFinallyUnwind != null)
1308 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1311 ec.Emit (OpCodes.Leave, exit_label);
1318 if (unwind_protect || ec.EmitAccurateDebugInfo)
1319 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1322 if (unwind_protect) {
1323 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1324 } else if (ec.EmitAccurateDebugInfo) {
1325 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1327 ec.Emit (OpCodes.Ret);
1331 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1334 expr.FlowAnalysis (fc);
1336 base.DoFlowAnalysis (fc);
1340 void Error_ReturnFromIterator (ResolveContext rc)
1342 rc.Report.Error (1622, loc,
1343 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1346 public override Reachability MarkReachable (Reachability rc)
1348 base.MarkReachable (rc);
1349 return Reachability.CreateUnreachable ();
1352 protected override void CloneTo (CloneContext clonectx, Statement t)
1354 Return target = (Return) t;
1355 // It's null for simple return;
1357 target.expr = expr.Clone (clonectx);
1360 public override object Accept (StructuralVisitor visitor)
1362 return visitor.Visit (this);
1366 public class Goto : ExitStatement
1369 LabeledStatement label;
1370 TryFinally try_finally;
1372 public Goto (string label, Location l)
1378 public string Target {
1379 get { return target; }
1382 protected override bool IsLocalExit {
1388 protected override bool DoResolve (BlockContext bc)
1390 label = bc.CurrentBlock.LookupLabel (target);
1391 if (label == null) {
1392 Error_UnknownLabel (bc, target, loc);
1396 try_finally = bc.CurrentTryBlock as TryFinally;
1398 CheckExitBoundaries (bc, label.Block);
1403 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1405 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1409 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1411 if (fc.AddReachedLabel (label))
1414 label.Block.ScanGotoJump (label, fc);
1418 public override Reachability MarkReachable (Reachability rc)
1420 if (rc.IsUnreachable)
1423 base.MarkReachable (rc);
1425 if (try_finally != null) {
1426 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1427 label.AddGotoReference (rc, false);
1429 label.AddGotoReference (rc, true);
1432 label.AddGotoReference (rc, false);
1435 return Reachability.CreateUnreachable ();
1438 protected override void CloneTo (CloneContext clonectx, Statement target)
1443 protected override void DoEmit (EmitContext ec)
1446 throw new InternalErrorException ("goto emitted before target resolved");
1448 Label l = label.LabelTarget (ec);
1450 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1451 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1452 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1455 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1458 bool IsLeavingFinally (Block labelBlock)
1460 var b = try_finally.Statement as Block;
1462 if (b == labelBlock)
1471 public override object Accept (StructuralVisitor visitor)
1473 return visitor.Visit (this);
1477 public class LabeledStatement : Statement {
1485 public LabeledStatement (string name, Block block, Location l)
1492 public Label LabelTarget (EmitContext ec)
1497 label = ec.DefineLabel ();
1502 public Block Block {
1508 public string Name {
1509 get { return name; }
1512 protected override void CloneTo (CloneContext clonectx, Statement target)
1514 var t = (LabeledStatement) target;
1516 t.block = clonectx.RemapBlockCopy (block);
1519 public override bool Resolve (BlockContext bc)
1524 protected override void DoEmit (EmitContext ec)
1527 ec.MarkLabel (label);
1530 ec.Emit (OpCodes.Br_S, label);
1533 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1536 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1542 public override Reachability MarkReachable (Reachability rc)
1544 base.MarkReachable (rc);
1547 rc = new Reachability ();
1552 public void AddGotoReference (Reachability rc, bool finalTarget)
1561 // Label is final target when goto jumps out of try block with
1562 // finally clause. In that case we need leave with target but in C#
1563 // terms the label is unreachable. Using finalTarget we emit
1564 // explicit label not just marker
1567 this.finalTarget = true;
1571 block.ScanGotoJump (this);
1574 public override object Accept (StructuralVisitor visitor)
1576 return visitor.Visit (this);
1582 /// `goto default' statement
1584 public class GotoDefault : SwitchGoto
1586 public GotoDefault (Location l)
1591 public override bool Resolve (BlockContext bc)
1593 if (bc.Switch == null) {
1594 Error_GotoCaseRequiresSwitchBlock (bc);
1598 bc.Switch.RegisterGotoCase (null, null);
1604 protected override void DoEmit (EmitContext ec)
1606 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1609 public override Reachability MarkReachable (Reachability rc)
1611 if (!rc.IsUnreachable) {
1612 var label = switch_statement.DefaultLabel;
1613 if (label.IsUnreachable) {
1614 label.MarkReachable (rc);
1615 switch_statement.Block.ScanGotoJump (label);
1619 return base.MarkReachable (rc);
1622 public override object Accept (StructuralVisitor visitor)
1624 return visitor.Visit (this);
1629 /// `goto case' statement
1631 public class GotoCase : SwitchGoto
1635 public GotoCase (Expression e, Location l)
1641 public Expression Expr {
1647 public SwitchLabel Label { get; set; }
1649 public override bool Resolve (BlockContext ec)
1651 if (ec.Switch == null) {
1652 Error_GotoCaseRequiresSwitchBlock (ec);
1656 Constant c = expr.ResolveLabelConstant (ec);
1662 if (ec.Switch.IsNullable && c is NullLiteral) {
1665 TypeSpec type = ec.Switch.SwitchType;
1666 res = c.Reduce (ec, type);
1668 c.Error_ValueCannotBeConverted (ec, type, true);
1672 if (!Convert.ImplicitStandardConversionExists (c, type))
1673 ec.Report.Warning (469, 2, loc,
1674 "The `goto case' value is not implicitly convertible to type `{0}'",
1675 type.GetSignatureForError ());
1679 ec.Switch.RegisterGotoCase (this, res);
1686 protected override void DoEmit (EmitContext ec)
1688 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1691 protected override void CloneTo (CloneContext clonectx, Statement t)
1693 GotoCase target = (GotoCase) t;
1695 target.expr = expr.Clone (clonectx);
1698 public override Reachability MarkReachable (Reachability rc)
1700 if (!rc.IsUnreachable) {
1701 var label = switch_statement.FindLabel ((Constant) expr);
1702 if (label.IsUnreachable) {
1703 label.MarkReachable (rc);
1704 switch_statement.Block.ScanGotoJump (label);
1708 return base.MarkReachable (rc);
1711 public override object Accept (StructuralVisitor visitor)
1713 return visitor.Visit (this);
1717 public abstract class SwitchGoto : Statement
1719 protected bool unwind_protect;
1720 protected Switch switch_statement;
1722 protected SwitchGoto (Location loc)
1727 protected override void CloneTo (CloneContext clonectx, Statement target)
1732 public override bool Resolve (BlockContext bc)
1734 CheckExitBoundaries (bc, bc.Switch.Block);
1736 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1737 switch_statement = bc.Switch;
1742 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1747 public override Reachability MarkReachable (Reachability rc)
1749 base.MarkReachable (rc);
1750 return Reachability.CreateUnreachable ();
1753 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1755 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1759 public class Throw : Statement {
1762 public Throw (Expression expr, Location l)
1768 public Expression Expr {
1774 public override bool Resolve (BlockContext ec)
1777 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1778 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1779 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1780 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1781 if (b.IsFinallyBlock) {
1782 ec.Report.Error (724, loc,
1783 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1792 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1797 var et = ec.BuiltinTypes.Exception;
1798 if (Convert.ImplicitConversionExists (ec, expr, et))
1799 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1801 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1806 protected override void DoEmit (EmitContext ec)
1809 var atv = ec.AsyncThrowVariable;
1811 if (atv.HoistedVariant != null) {
1812 atv.HoistedVariant.Emit (ec);
1817 ec.Emit (OpCodes.Throw);
1819 ec.Emit (OpCodes.Rethrow);
1824 ec.Emit (OpCodes.Throw);
1828 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1831 expr.FlowAnalysis (fc);
1836 public override Reachability MarkReachable (Reachability rc)
1838 base.MarkReachable (rc);
1839 return Reachability.CreateUnreachable ();
1842 protected override void CloneTo (CloneContext clonectx, Statement t)
1844 Throw target = (Throw) t;
1847 target.expr = expr.Clone (clonectx);
1850 public override object Accept (StructuralVisitor visitor)
1852 return visitor.Visit (this);
1856 public class Break : LocalExitStatement
1858 public Break (Location l)
1863 public override object Accept (StructuralVisitor visitor)
1865 return visitor.Visit (this);
1868 protected override void DoEmit (EmitContext ec)
1872 if (ec.TryFinallyUnwind != null) {
1873 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1874 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1877 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1880 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1882 enclosing_loop.AddEndDefiniteAssignment (fc);
1886 protected override bool DoResolve (BlockContext bc)
1888 enclosing_loop = bc.EnclosingLoopOrSwitch;
1889 return base.DoResolve (bc);
1892 public override Reachability MarkReachable (Reachability rc)
1894 base.MarkReachable (rc);
1896 if (!rc.IsUnreachable)
1897 enclosing_loop.SetEndReachable ();
1899 return Reachability.CreateUnreachable ();
1903 public class Continue : LocalExitStatement
1905 public Continue (Location l)
1910 public override object Accept (StructuralVisitor visitor)
1912 return visitor.Visit (this);
1916 protected override void DoEmit (EmitContext ec)
1918 var l = ec.LoopBegin;
1920 if (ec.TryFinallyUnwind != null) {
1921 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1922 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1925 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1928 protected override bool DoResolve (BlockContext bc)
1930 enclosing_loop = bc.EnclosingLoop;
1931 return base.DoResolve (bc);
1934 public override Reachability MarkReachable (Reachability rc)
1936 base.MarkReachable (rc);
1938 if (!rc.IsUnreachable)
1939 enclosing_loop.SetIteratorReachable ();
1941 return Reachability.CreateUnreachable ();
1945 public abstract class LocalExitStatement : ExitStatement
1947 protected LoopStatement enclosing_loop;
1949 protected LocalExitStatement (Location loc)
1954 protected override bool IsLocalExit {
1960 protected override void CloneTo (CloneContext clonectx, Statement t)
1965 protected override bool DoResolve (BlockContext bc)
1967 if (enclosing_loop == null) {
1968 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1972 var block = enclosing_loop.Statement as Block;
1974 // Don't need to do extra checks for simple statements loops
1975 if (block != null) {
1976 CheckExitBoundaries (bc, block);
1983 public interface ILocalVariable
1985 void Emit (EmitContext ec);
1986 void EmitAssign (EmitContext ec);
1987 void EmitAddressOf (EmitContext ec);
1990 public interface INamedBlockVariable
1992 Block Block { get; }
1993 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1994 bool IsDeclared { get; }
1995 bool IsParameter { get; }
1996 Location Location { get; }
1999 public class BlockVariableDeclarator
2002 Expression initializer;
2004 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2006 if (li.Type != null)
2007 throw new ArgumentException ("Expected null variable type");
2010 this.initializer = initializer;
2015 public LocalVariable Variable {
2021 public Expression Initializer {
2026 initializer = value;
2032 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2034 var t = (BlockVariableDeclarator) MemberwiseClone ();
2035 if (initializer != null)
2036 t.initializer = initializer.Clone (cloneCtx);
2042 public class BlockVariable : Statement
2044 Expression initializer;
2045 protected FullNamedExpression type_expr;
2046 protected LocalVariable li;
2047 protected List<BlockVariableDeclarator> declarators;
2050 public BlockVariable (FullNamedExpression type, LocalVariable li)
2052 this.type_expr = type;
2054 this.loc = type_expr.Location;
2057 protected BlockVariable (LocalVariable li)
2064 public List<BlockVariableDeclarator> Declarators {
2070 public Expression Initializer {
2075 initializer = value;
2079 public FullNamedExpression TypeExpression {
2085 public LocalVariable Variable {
2093 public void AddDeclarator (BlockVariableDeclarator decl)
2095 if (declarators == null)
2096 declarators = new List<BlockVariableDeclarator> ();
2098 declarators.Add (decl);
2101 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2103 if (bc.Report.Errors != 0)
2106 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2108 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2109 new MemberName (li.Name, li.Location), null);
2111 container.AddField (f);
2114 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2118 public override bool Resolve (BlockContext bc)
2120 return Resolve (bc, true);
2123 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2125 if (type == null && !li.IsCompilerGenerated) {
2126 var vexpr = type_expr as VarExpr;
2129 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2130 // same name exists or as a keyword when no type was found
2132 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2133 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2134 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2137 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2141 if (li.IsConstant) {
2142 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2146 if (Initializer == null) {
2147 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2151 if (declarators != null) {
2152 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2156 Initializer = Initializer.Resolve (bc);
2157 if (Initializer != null) {
2158 ((VarExpr) type_expr).InferType (bc, Initializer);
2159 type = type_expr.Type;
2161 // Set error type to indicate the var was placed correctly but could
2164 // var a = missing ();
2166 type = InternalType.ErrorType;
2171 type = type_expr.ResolveAsType (bc);
2175 if (li.IsConstant && !type.IsConstantCompatible) {
2176 Const.Error_InvalidConstantType (type, loc, bc.Report);
2181 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2186 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2188 CreateEvaluatorVariable (bc, li);
2189 } else if (type != InternalType.ErrorType) {
2190 li.PrepareAssignmentAnalysis (bc);
2193 if (initializer != null) {
2194 initializer = ResolveInitializer (bc, li, initializer);
2195 // li.Variable.DefinitelyAssigned
2198 if (declarators != null) {
2199 foreach (var d in declarators) {
2200 d.Variable.Type = li.Type;
2202 CreateEvaluatorVariable (bc, d.Variable);
2203 } else if (type != InternalType.ErrorType) {
2204 d.Variable.PrepareAssignmentAnalysis (bc);
2207 if (d.Initializer != null && resolveDeclaratorInitializers) {
2208 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2209 // d.Variable.DefinitelyAssigned
2217 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2219 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2220 return a.ResolveStatement (bc);
2223 protected override void DoEmit (EmitContext ec)
2225 li.CreateBuilder (ec);
2227 if (Initializer != null && !IsUnreachable)
2228 ((ExpressionStatement) Initializer).EmitStatement (ec);
2230 if (declarators != null) {
2231 foreach (var d in declarators) {
2232 d.Variable.CreateBuilder (ec);
2233 if (d.Initializer != null && !IsUnreachable) {
2234 ec.Mark (d.Variable.Location);
2235 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2241 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2243 if (Initializer != null)
2244 Initializer.FlowAnalysis (fc);
2246 if (declarators != null) {
2247 foreach (var d in declarators) {
2248 if (d.Initializer != null)
2249 d.Initializer.FlowAnalysis (fc);
2256 public override Reachability MarkReachable (Reachability rc)
2258 var init = initializer as ExpressionStatement;
2260 init.MarkReachable (rc);
2262 return base.MarkReachable (rc);
2265 protected override void CloneTo (CloneContext clonectx, Statement target)
2267 BlockVariable t = (BlockVariable) target;
2269 if (type_expr != null)
2270 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2272 if (initializer != null)
2273 t.initializer = initializer.Clone (clonectx);
2275 if (declarators != null) {
2276 t.declarators = null;
2277 foreach (var d in declarators)
2278 t.AddDeclarator (d.Clone (clonectx));
2282 public override object Accept (StructuralVisitor visitor)
2284 return visitor.Visit (this);
2288 public class BlockConstant : BlockVariable
2290 public BlockConstant (FullNamedExpression type, LocalVariable li)
2295 public override void Emit (EmitContext ec)
2297 // Nothing to emit, not even sequence point
2300 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2302 initializer = initializer.Resolve (bc);
2303 if (initializer == null)
2306 var c = initializer as Constant;
2308 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2312 c = c.ConvertImplicitly (li.Type);
2314 if (TypeSpec.IsReferenceType (li.Type))
2315 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2317 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2322 li.ConstantValue = c;
2326 public override object Accept (StructuralVisitor visitor)
2328 return visitor.Visit (this);
2333 // The information about a user-perceived local variable
2335 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2342 AddressTaken = 1 << 2,
2343 CompilerGenerated = 1 << 3,
2345 ForeachVariable = 1 << 5,
2346 FixedVariable = 1 << 6,
2347 UsingVariable = 1 << 7,
2350 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2354 readonly string name;
2355 readonly Location loc;
2356 readonly Block block;
2358 Constant const_value;
2360 public VariableInfo VariableInfo;
2361 HoistedVariable hoisted_variant;
2363 LocalBuilder builder;
2365 public LocalVariable (Block block, string name, Location loc)
2372 public LocalVariable (Block block, string name, Flags flags, Location loc)
2373 : this (block, name, loc)
2379 // Used by variable declarators
2381 public LocalVariable (LocalVariable li, string name, Location loc)
2382 : this (li.block, name, li.flags, loc)
2388 public bool AddressTaken {
2390 return (flags & Flags.AddressTaken) != 0;
2394 public Block Block {
2400 public Constant ConstantValue {
2405 const_value = value;
2410 // Hoisted local variable variant
2412 public HoistedVariable HoistedVariant {
2414 return hoisted_variant;
2417 hoisted_variant = value;
2421 public bool IsDeclared {
2423 return type != null;
2427 public bool IsCompilerGenerated {
2429 return (flags & Flags.CompilerGenerated) != 0;
2433 public bool IsConstant {
2435 return (flags & Flags.Constant) != 0;
2439 public bool IsLocked {
2441 return (flags & Flags.IsLocked) != 0;
2444 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2448 public bool IsThis {
2450 return (flags & Flags.IsThis) != 0;
2454 public bool IsFixed {
2456 return (flags & Flags.FixedVariable) != 0;
2460 bool INamedBlockVariable.IsParameter {
2466 public bool IsReadonly {
2468 return (flags & Flags.ReadonlyMask) != 0;
2472 public Location Location {
2478 public string Name {
2484 public TypeSpec Type {
2495 public void CreateBuilder (EmitContext ec)
2497 if ((flags & Flags.Used) == 0) {
2498 if (VariableInfo == null) {
2499 // Missing flow analysis or wrong variable flags
2500 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2503 if (VariableInfo.IsEverAssigned)
2504 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2506 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2509 if (HoistedVariant != null)
2512 if (builder != null) {
2513 if ((flags & Flags.CompilerGenerated) != 0)
2516 // To avoid Used warning duplicates
2517 throw new InternalErrorException ("Already created variable `{0}'", name);
2521 // All fixed variabled are pinned, a slot has to be alocated
2523 builder = ec.DeclareLocal (Type, IsFixed);
2524 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2525 ec.DefineLocalVariable (name, builder);
2528 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2530 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2535 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2537 if (IsConstant && const_value != null)
2538 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2540 return new LocalVariableReference (this, loc);
2543 public void Emit (EmitContext ec)
2545 // TODO: Need something better for temporary variables
2546 if ((flags & Flags.CompilerGenerated) != 0)
2549 ec.Emit (OpCodes.Ldloc, builder);
2552 public void EmitAssign (EmitContext ec)
2554 // TODO: Need something better for temporary variables
2555 if ((flags & Flags.CompilerGenerated) != 0)
2558 ec.Emit (OpCodes.Stloc, builder);
2561 public void EmitAddressOf (EmitContext ec)
2563 // TODO: Need something better for temporary variables
2564 if ((flags & Flags.CompilerGenerated) != 0)
2567 ec.Emit (OpCodes.Ldloca, builder);
2570 public static string GetCompilerGeneratedName (Block block)
2572 // HACK: Debugger depends on the name semantics
2573 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2576 public string GetReadOnlyContext ()
2578 switch (flags & Flags.ReadonlyMask) {
2579 case Flags.FixedVariable:
2580 return "fixed variable";
2581 case Flags.ForeachVariable:
2582 return "foreach iteration variable";
2583 case Flags.UsingVariable:
2584 return "using variable";
2587 throw new InternalErrorException ("Variable is not readonly");
2590 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2592 if (VariableInfo == null)
2593 throw new Exception ();
2595 if (IsAssigned (fc))
2598 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2601 public bool IsAssigned (FlowAnalysisContext fc)
2603 return fc.IsDefinitelyAssigned (VariableInfo);
2606 public void PrepareAssignmentAnalysis (BlockContext bc)
2609 // No need to run assignment analysis for these guys
2611 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2614 VariableInfo = VariableInfo.Create (bc, this);
2618 // Mark the variables as referenced in the user code
2620 public void SetIsUsed ()
2622 flags |= Flags.Used;
2625 public void SetHasAddressTaken ()
2627 flags |= (Flags.AddressTaken | Flags.Used);
2630 public override string ToString ()
2632 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2637 /// Block represents a C# block.
2641 /// This class is used in a number of places: either to represent
2642 /// explicit blocks that the programmer places or implicit blocks.
2644 /// Implicit blocks are used as labels or to introduce variable
2647 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2648 /// they contain extra information that is not necessary on normal blocks.
2650 public class Block : Statement {
2657 HasCapturedVariable = 64,
2658 HasCapturedThis = 1 << 7,
2659 IsExpressionTree = 1 << 8,
2660 CompilerGenerated = 1 << 9,
2661 HasAsyncModifier = 1 << 10,
2663 YieldBlock = 1 << 12,
2664 AwaitBlock = 1 << 13,
2665 FinallyBlock = 1 << 14,
2666 CatchBlock = 1 << 15,
2668 NoFlowAnalysis = 1 << 21,
2669 InitializationEmitted = 1 << 22
2672 public Block Parent;
2673 public Location StartLocation;
2674 public Location EndLocation;
2676 public ExplicitBlock Explicit;
2677 public ParametersBlock ParametersBlock;
2679 protected Flags flags;
2682 // The statements in this block
2684 protected List<Statement> statements;
2686 protected List<Statement> scope_initializers;
2688 int? resolving_init_idx;
2694 public int ID = id++;
2696 static int clone_id_counter;
2700 // int assignable_slots;
2702 public Block (Block parent, Location start, Location end)
2703 : this (parent, 0, start, end)
2707 public Block (Block parent, Flags flags, Location start, Location end)
2709 if (parent != null) {
2710 // the appropriate constructors will fixup these fields
2711 ParametersBlock = parent.ParametersBlock;
2712 Explicit = parent.Explicit;
2715 this.Parent = parent;
2717 this.StartLocation = start;
2718 this.EndLocation = end;
2720 statements = new List<Statement> (4);
2722 this.original = this;
2727 public Block Original {
2736 public bool IsCompilerGenerated {
2737 get { return (flags & Flags.CompilerGenerated) != 0; }
2738 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2742 public bool IsCatchBlock {
2744 return (flags & Flags.CatchBlock) != 0;
2748 public bool IsFinallyBlock {
2750 return (flags & Flags.FinallyBlock) != 0;
2754 public bool Unchecked {
2755 get { return (flags & Flags.Unchecked) != 0; }
2756 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2759 public bool Unsafe {
2760 get { return (flags & Flags.Unsafe) != 0; }
2761 set { flags |= Flags.Unsafe; }
2764 public List<Statement> Statements {
2765 get { return statements; }
2770 public void SetEndLocation (Location loc)
2775 public void AddLabel (LabeledStatement target)
2777 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2780 public void AddLocalName (LocalVariable li)
2782 AddLocalName (li.Name, li);
2785 public void AddLocalName (string name, INamedBlockVariable li)
2787 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2790 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2792 if (reason == null) {
2793 Error_AlreadyDeclared (name, variable);
2797 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2798 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2799 "to `{0}', which is already used in a `{1}' scope to denote something else",
2803 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2805 var pi = variable as ParametersBlock.ParameterInfo;
2807 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2809 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2810 "A local variable named `{0}' is already defined in this scope", name);
2814 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2816 ParametersBlock.TopBlock.Report.Error (412, loc,
2817 "The type parameter name `{0}' is the same as local variable or parameter name",
2822 // It should be used by expressions which require to
2823 // register a statement during resolve process.
2825 public void AddScopeStatement (Statement s)
2827 if (scope_initializers == null)
2828 scope_initializers = new List<Statement> ();
2831 // Simple recursive helper, when resolve scope initializer another
2832 // new scope initializer can be added, this ensures it's initialized
2833 // before existing one. For now this can happen with expression trees
2834 // in base ctor initializer only
2836 if (resolving_init_idx.HasValue) {
2837 scope_initializers.Insert (resolving_init_idx.Value, s);
2838 ++resolving_init_idx;
2840 scope_initializers.Add (s);
2844 public void InsertStatement (int index, Statement s)
2846 statements.Insert (index, s);
2849 public void AddStatement (Statement s)
2854 public LabeledStatement LookupLabel (string name)
2856 return ParametersBlock.GetLabel (name, this);
2859 public override Reachability MarkReachable (Reachability rc)
2861 if (rc.IsUnreachable)
2864 MarkReachableScope (rc);
2866 foreach (var s in statements) {
2867 rc = s.MarkReachable (rc);
2868 if (rc.IsUnreachable) {
2869 if ((flags & Flags.ReachableEnd) != 0)
2870 return new Reachability ();
2876 flags |= Flags.ReachableEnd;
2881 public void MarkReachableScope (Reachability rc)
2883 base.MarkReachable (rc);
2885 if (scope_initializers != null) {
2886 foreach (var si in scope_initializers)
2887 si.MarkReachable (rc);
2891 public override bool Resolve (BlockContext bc)
2893 if ((flags & Flags.Resolved) != 0)
2896 Block prev_block = bc.CurrentBlock;
2897 bc.CurrentBlock = this;
2900 // Compiler generated scope statements
2902 if (scope_initializers != null) {
2903 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2904 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2907 resolving_init_idx = null;
2911 int statement_count = statements.Count;
2912 for (int ix = 0; ix < statement_count; ix++){
2913 Statement s = statements [ix];
2915 if (!s.Resolve (bc)) {
2917 statements [ix] = new EmptyStatement (s.loc);
2922 bc.CurrentBlock = prev_block;
2924 flags |= Flags.Resolved;
2928 protected override void DoEmit (EmitContext ec)
2930 for (int ix = 0; ix < statements.Count; ix++){
2931 statements [ix].Emit (ec);
2935 public override void Emit (EmitContext ec)
2937 if (scope_initializers != null)
2938 EmitScopeInitializers (ec);
2943 protected void EmitScopeInitializers (EmitContext ec)
2945 foreach (Statement s in scope_initializers)
2949 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2951 if (scope_initializers != null) {
2952 foreach (var si in scope_initializers)
2953 si.FlowAnalysis (fc);
2956 return DoFlowAnalysis (fc, 0);
2959 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2961 bool end_unreachable = !reachable;
2962 bool goto_flow_analysis = startIndex != 0;
2963 for (; startIndex < statements.Count; ++startIndex) {
2964 var s = statements[startIndex];
2966 end_unreachable = s.FlowAnalysis (fc);
2967 if (s.IsUnreachable) {
2968 statements [startIndex] = RewriteUnreachableStatement (s);
2973 // Statement end reachability is needed mostly due to goto support. Consider
2982 // X label is reachable only via goto not as another statement after if. We need
2983 // this for flow-analysis only to carry variable info correctly.
2985 if (end_unreachable) {
2986 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2988 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2989 s = statements[startIndex];
2990 if (s is SwitchLabel) {
2991 if (!after_goto_case)
2992 s.FlowAnalysis (fc);
2997 if (s.IsUnreachable) {
2998 s.FlowAnalysis (fc);
2999 statements [startIndex] = RewriteUnreachableStatement (s);
3004 // Idea is to stop after goto case because goto case will always have at least same
3005 // variable assigned as switch case label. This saves a lot for complex goto case tests
3007 if (after_goto_case)
3013 var lb = s as LabeledStatement;
3014 if (lb != null && fc.AddReachedLabel (lb))
3019 // The condition should be true unless there is forward jumping goto
3021 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3024 return !Explicit.HasReachableClosingBrace;
3027 static Statement RewriteUnreachableStatement (Statement s)
3029 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3030 // analysis. Even csc report unreachable warning for it but it's actually used hence
3031 // we try to emulate this behaviour
3039 if (s is BlockVariable || s is EmptyStatement)
3042 return new EmptyStatement (s.loc);
3045 public void ScanGotoJump (Statement label)
3048 for (i = 0; i < statements.Count; ++i) {
3049 if (statements[i] == label)
3053 var rc = new Reachability ();
3054 for (++i; i < statements.Count; ++i) {
3055 var s = statements[i];
3056 rc = s.MarkReachable (rc);
3057 if (rc.IsUnreachable)
3061 flags |= Flags.ReachableEnd;
3064 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3067 for (i = 0; i < statements.Count; ++i) {
3068 if (statements[i] == label)
3072 DoFlowAnalysis (fc, ++i);
3076 public override string ToString ()
3078 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3082 protected override void CloneTo (CloneContext clonectx, Statement t)
3084 Block target = (Block) t;
3086 target.clone_id = ++clone_id_counter;
3089 clonectx.AddBlockMap (this, target);
3090 if (original != this)
3091 clonectx.AddBlockMap (original, target);
3093 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3094 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3097 target.Parent = clonectx.RemapBlockCopy (Parent);
3099 target.statements = new List<Statement> (statements.Count);
3100 foreach (Statement s in statements)
3101 target.statements.Add (s.Clone (clonectx));
3104 public override object Accept (StructuralVisitor visitor)
3106 return visitor.Visit (this);
3110 public class ExplicitBlock : Block
3112 protected AnonymousMethodStorey am_storey;
3114 public ExplicitBlock (Block parent, Location start, Location end)
3115 : this (parent, (Flags) 0, start, end)
3119 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3120 : base (parent, flags, start, end)
3122 this.Explicit = this;
3127 public AnonymousMethodStorey AnonymousMethodStorey {
3133 public bool HasAwait {
3135 return (flags & Flags.AwaitBlock) != 0;
3139 public bool HasCapturedThis {
3141 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3144 return (flags & Flags.HasCapturedThis) != 0;
3149 // Used to indicate that the block has reference to parent
3150 // block and cannot be made static when defining anonymous method
3152 public bool HasCapturedVariable {
3154 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3157 return (flags & Flags.HasCapturedVariable) != 0;
3161 public bool HasReachableClosingBrace {
3163 return (flags & Flags.ReachableEnd) != 0;
3166 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3170 public bool HasYield {
3172 return (flags & Flags.YieldBlock) != 0;
3179 // Creates anonymous method storey in current block
3181 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3184 // Return same story for iterator and async blocks unless we are
3185 // in nested anonymous method
3187 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3188 return ec.CurrentAnonymousMethod.Storey;
3190 if (am_storey == null) {
3191 MemberBase mc = ec.MemberContext as MemberBase;
3194 // Creates anonymous method storey for this block
3196 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3202 public void EmitScopeInitialization (EmitContext ec)
3204 if ((flags & Flags.InitializationEmitted) != 0)
3207 if (am_storey != null) {
3208 DefineStoreyContainer (ec, am_storey);
3209 am_storey.EmitStoreyInstantiation (ec, this);
3212 if (scope_initializers != null)
3213 EmitScopeInitializers (ec);
3215 flags |= Flags.InitializationEmitted;
3218 public override void Emit (EmitContext ec)
3223 EmitScopeInitialization (ec);
3225 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3226 ec.Emit (OpCodes.Nop);
3234 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3235 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3236 ec.Emit (OpCodes.Nop);
3240 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3242 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3243 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3244 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3248 // Creates anonymous method storey
3250 storey.CreateContainer ();
3251 storey.DefineContainer ();
3253 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3256 // Only first storey in path will hold this reference. All children blocks will
3257 // reference it indirectly using $ref field
3259 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3260 if (b.Parent != null) {
3261 var s = b.Parent.Explicit.AnonymousMethodStorey;
3263 storey.HoistedThis = s.HoistedThis;
3268 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3269 if (storey.HoistedThis == null)
3270 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3272 if (storey.HoistedThis != null)
3278 // We are the first storey on path and 'this' has to be hoisted
3280 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3281 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3283 // ThisReferencesFromChildrenBlock holds all reference even if they
3284 // are not on this path. It saves some memory otherwise it'd have to
3285 // be in every explicit block. We run this check to see if the reference
3286 // is valid for this storey
3288 Block block_on_path = ref_block;
3289 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3291 if (block_on_path == null)
3294 if (storey.HoistedThis == null) {
3295 storey.AddCapturedThisField (ec, null);
3298 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3300 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3302 if (b_storey != null) {
3304 // Don't add storey cross reference for `this' when the storey ends up not
3305 // beeing attached to any parent
3307 if (b.ParametersBlock.StateMachine == null) {
3308 AnonymousMethodStorey s = null;
3309 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3310 s = ab.Explicit.AnonymousMethodStorey;
3315 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3317 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3318 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3325 // Stop propagation inside same top block
3327 if (b.ParametersBlock == ParametersBlock.Original) {
3328 b_storey.AddParentStoreyReference (ec, storey);
3329 // b_storey.HoistedThis = storey.HoistedThis;
3333 b = pb = b.ParametersBlock;
3335 pb = b as ParametersBlock;
3338 if (pb != null && pb.StateMachine != null) {
3339 if (pb.StateMachine == storey)
3343 // If we are state machine with no parent. We can hook into parent without additional
3344 // reference and capture this directly
3346 ExplicitBlock parent_storey_block = pb;
3347 while (parent_storey_block.Parent != null) {
3348 parent_storey_block = parent_storey_block.Parent.Explicit;
3349 if (parent_storey_block.AnonymousMethodStorey != null) {
3354 if (parent_storey_block.AnonymousMethodStorey == null) {
3355 if (pb.StateMachine.HoistedThis == null) {
3356 pb.StateMachine.AddCapturedThisField (ec, null);
3357 b.HasCapturedThis = true;
3363 var parent_this_block = pb;
3364 while (parent_this_block.Parent != null) {
3365 parent_this_block = parent_this_block.Parent.ParametersBlock;
3366 if (parent_this_block.StateMachine != null) {
3372 // Add reference to closest storey which holds captured this
3374 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3378 // Add parent storey reference only when this is not captured directly
3380 if (b_storey != null) {
3381 b_storey.AddParentStoreyReference (ec, storey);
3382 b_storey.HoistedThis = storey.HoistedThis;
3389 var ref_blocks = storey.ReferencesFromChildrenBlock;
3390 if (ref_blocks != null) {
3391 foreach (ExplicitBlock ref_block in ref_blocks) {
3392 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3393 if (b.AnonymousMethodStorey != null) {
3394 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3397 // Stop propagation inside same top block
3399 if (b.ParametersBlock == ParametersBlock.Original)
3402 b = b.ParametersBlock;
3405 var pb = b as ParametersBlock;
3406 if (pb != null && pb.StateMachine != null) {
3407 if (pb.StateMachine == storey)
3410 pb.StateMachine.AddParentStoreyReference (ec, storey);
3413 b.HasCapturedVariable = true;
3419 storey.PrepareEmit ();
3420 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3423 public void RegisterAsyncAwait ()
3426 while ((block.flags & Flags.AwaitBlock) == 0) {
3427 block.flags |= Flags.AwaitBlock;
3429 if (block is ParametersBlock)
3432 block = block.Parent.Explicit;
3436 public void RegisterIteratorYield ()
3438 ParametersBlock.TopBlock.IsIterator = true;
3441 while ((block.flags & Flags.YieldBlock) == 0) {
3442 block.flags |= Flags.YieldBlock;
3444 if (block.Parent == null)
3447 block = block.Parent.Explicit;
3451 public void SetCatchBlock ()
3453 flags |= Flags.CatchBlock;
3456 public void SetFinallyBlock ()
3458 flags |= Flags.FinallyBlock;
3461 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3463 tryBlock.statements = statements;
3464 statements = new List<Statement> (1);
3465 statements.Add (tf);
3470 // ParametersBlock was introduced to support anonymous methods
3471 // and lambda expressions
3473 public class ParametersBlock : ExplicitBlock
3475 public class ParameterInfo : INamedBlockVariable
3477 readonly ParametersBlock block;
3479 public VariableInfo VariableInfo;
3482 public ParameterInfo (ParametersBlock block, int index)
3490 public ParametersBlock Block {
3496 Block INamedBlockVariable.Block {
3502 public bool IsDeclared {
3508 public bool IsParameter {
3514 public bool IsLocked {
3523 public Location Location {
3525 return Parameter.Location;
3529 public Parameter Parameter {
3531 return block.Parameters [index];
3535 public TypeSpec ParameterType {
3537 return Parameter.Type;
3543 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3545 return new ParameterReference (this, loc);
3550 // Block is converted into an expression
3552 sealed class BlockScopeExpression : Expression
3555 readonly ParametersBlock block;
3557 public BlockScopeExpression (Expression child, ParametersBlock block)
3563 public override bool ContainsEmitWithAwait ()
3565 return child.ContainsEmitWithAwait ();
3568 public override Expression CreateExpressionTree (ResolveContext ec)
3570 throw new NotSupportedException ();
3573 protected override Expression DoResolve (ResolveContext ec)
3578 child = child.Resolve (ec);
3582 eclass = child.eclass;
3587 public override void Emit (EmitContext ec)
3589 block.EmitScopeInitializers (ec);
3594 protected ParametersCompiled parameters;
3595 protected ParameterInfo[] parameter_info;
3596 protected bool resolved;
3597 protected ToplevelBlock top_block;
3598 protected StateMachine state_machine;
3599 protected Dictionary<string, object> labels;
3601 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3602 : base (parent, 0, start, start)
3604 if (parameters == null)
3605 throw new ArgumentNullException ("parameters");
3607 this.parameters = parameters;
3608 ParametersBlock = this;
3610 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3612 this.top_block = parent.ParametersBlock.top_block;
3613 ProcessParameters ();
3616 protected ParametersBlock (ParametersCompiled parameters, Location start)
3617 : base (null, 0, start, start)
3619 if (parameters == null)
3620 throw new ArgumentNullException ("parameters");
3622 this.parameters = parameters;
3623 ParametersBlock = this;
3627 // It's supposed to be used by method body implementation of anonymous methods
3629 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3630 : base (null, 0, source.StartLocation, source.EndLocation)
3632 this.parameters = parameters;
3633 this.statements = source.statements;
3634 this.scope_initializers = source.scope_initializers;
3636 this.resolved = true;
3637 this.reachable = source.reachable;
3638 this.am_storey = source.am_storey;
3639 this.state_machine = source.state_machine;
3640 this.flags = source.flags & Flags.ReachableEnd;
3642 ParametersBlock = this;
3645 // Overwrite original for comparison purposes when linking cross references
3646 // between anonymous methods
3648 Original = source.Original;
3653 public bool IsAsync {
3655 return (flags & Flags.HasAsyncModifier) != 0;
3658 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3663 // Block has been converted to expression tree
3665 public bool IsExpressionTree {
3667 return (flags & Flags.IsExpressionTree) != 0;
3672 // The parameters for the block.
3674 public ParametersCompiled Parameters {
3680 public StateMachine StateMachine {
3682 return state_machine;
3686 public ToplevelBlock TopBlock {
3695 public bool Resolved {
3697 return (flags & Flags.Resolved) != 0;
3701 public int TemporaryLocalsCount { get; set; }
3706 // Checks whether all `out' parameters have been assigned.
3708 public void CheckControlExit (FlowAnalysisContext fc)
3710 CheckControlExit (fc, fc.DefiniteAssignment);
3713 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3715 if (parameter_info == null)
3718 foreach (var p in parameter_info) {
3719 if (p.VariableInfo == null)
3722 if (p.VariableInfo.IsAssigned (dat))
3725 fc.Report.Error (177, p.Location,
3726 "The out parameter `{0}' must be assigned to before control leaves the current method",
3731 protected override void CloneTo (CloneContext clonectx, Statement t)
3733 base.CloneTo (clonectx, t);
3735 var target = (ParametersBlock) t;
3738 // Clone label statements as well as they contain block reference
3742 if (pb.labels != null) {
3743 target.labels = new Dictionary<string, object> ();
3745 foreach (var entry in pb.labels) {
3746 var list = entry.Value as List<LabeledStatement>;
3749 var list_clone = new List<LabeledStatement> ();
3750 foreach (var lentry in list) {
3751 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3754 target.labels.Add (entry.Key, list_clone);
3756 var labeled = (LabeledStatement) entry.Value;
3757 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3764 if (pb.Parent == null)
3767 pb = pb.Parent.ParametersBlock;
3771 public override Expression CreateExpressionTree (ResolveContext ec)
3773 if (statements.Count == 1) {
3774 Expression expr = statements[0].CreateExpressionTree (ec);
3775 if (scope_initializers != null)
3776 expr = new BlockScopeExpression (expr, this);
3781 return base.CreateExpressionTree (ec);
3784 public override void Emit (EmitContext ec)
3786 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3787 DefineStoreyContainer (ec, state_machine);
3788 state_machine.EmitStoreyInstantiation (ec, this);
3794 public void EmitEmbedded (EmitContext ec)
3796 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3797 DefineStoreyContainer (ec, state_machine);
3798 state_machine.EmitStoreyInstantiation (ec, this);
3804 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3806 var res = base.DoFlowAnalysis (fc);
3808 if (HasReachableClosingBrace)
3809 CheckControlExit (fc);
3814 public LabeledStatement GetLabel (string name, Block block)
3817 // Cloned parameters blocks can have their own cloned version of top-level labels
3819 if (labels == null) {
3821 return Parent.ParametersBlock.GetLabel (name, block);
3827 if (!labels.TryGetValue (name, out value)) {
3831 var label = value as LabeledStatement;
3833 if (label != null) {
3834 if (IsLabelVisible (label, b))
3838 List<LabeledStatement> list = (List<LabeledStatement>) value;
3839 for (int i = 0; i < list.Count; ++i) {
3841 if (IsLabelVisible (label, b))
3849 static bool IsLabelVisible (LabeledStatement label, Block b)
3852 if (label.Block == b)
3855 } while (b != null);
3860 public ParameterInfo GetParameterInfo (Parameter p)
3862 for (int i = 0; i < parameters.Count; ++i) {
3863 if (parameters[i] == p)
3864 return parameter_info[i];
3867 throw new ArgumentException ("Invalid parameter");
3870 public ParameterReference GetParameterReference (int index, Location loc)
3872 return new ParameterReference (parameter_info[index], loc);
3875 public Statement PerformClone ()
3877 CloneContext clonectx = new CloneContext ();
3878 return Clone (clonectx);
3881 protected void ProcessParameters ()
3883 if (parameters.Count == 0)
3886 parameter_info = new ParameterInfo[parameters.Count];
3887 for (int i = 0; i < parameter_info.Length; ++i) {
3888 var p = parameters.FixedParameters[i];
3892 // TODO: Should use Parameter only and more block there
3893 parameter_info[i] = new ParameterInfo (this, i);
3895 AddLocalName (p.Name, parameter_info[i]);
3899 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3901 var src = stmt.Block;
3904 // Cannot remap label block if the label was not yet cloned which
3905 // can happen in case of anonymous method inside anoynymous method
3906 // with a label. But in this case we don't care because goto cannot
3907 // jump of out anonymous method
3909 if (src.ParametersBlock != this)
3912 var src_stmts = src.Statements;
3913 for (int i = 0; i < src_stmts.Count; ++i) {
3914 if (src_stmts[i] == stmt)
3915 return (LabeledStatement) dst.Statements[i];
3918 throw new InternalErrorException ("Should never be reached");
3921 public override bool Resolve (BlockContext bc)
3923 // TODO: if ((flags & Flags.Resolved) != 0)
3930 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3931 flags |= Flags.IsExpressionTree;
3934 PrepareAssignmentAnalysis (bc);
3936 if (!base.Resolve (bc))
3939 } catch (Exception e) {
3940 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3943 if (bc.CurrentBlock != null) {
3944 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3946 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3951 // If an asynchronous body of F is either an expression classified as nothing, or a
3952 // statement block where no return statements have expressions, the inferred return type is Task
3955 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3956 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3957 am.ReturnTypeInference = null;
3958 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3966 void PrepareAssignmentAnalysis (BlockContext bc)
3968 for (int i = 0; i < parameters.Count; ++i) {
3969 var par = parameters.FixedParameters[i];
3971 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3974 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3978 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3980 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3981 var stateMachine = new IteratorStorey (iterator);
3983 state_machine = stateMachine;
3984 iterator.SetStateMachine (stateMachine);
3986 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3987 tlb.Original = this;
3988 tlb.state_machine = stateMachine;
3989 tlb.AddStatement (new Return (iterator, iterator.Location));
3993 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3995 for (int i = 0; i < parameters.Count; i++) {
3996 Parameter p = parameters[i];
3997 Parameter.Modifier mod = p.ModFlags;
3998 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3999 host.Compiler.Report.Error (1988, p.Location,
4000 "Async methods cannot have ref or out parameters");
4004 if (p is ArglistParameter) {
4005 host.Compiler.Report.Error (4006, p.Location,
4006 "__arglist is not allowed in parameter list of async methods");
4010 if (parameters.Types[i].IsPointer) {
4011 host.Compiler.Report.Error (4005, p.Location,
4012 "Async methods cannot have unsafe parameters");
4018 host.Compiler.Report.Warning (1998, 1, loc,
4019 "Async block lacks `await' operator and will run synchronously");
4022 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4023 var initializer = new AsyncInitializer (this, host, block_type);
4024 initializer.Type = block_type;
4025 initializer.DelegateType = delegateType;
4027 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4029 state_machine = stateMachine;
4030 initializer.SetStateMachine (stateMachine);
4032 const Flags flags = Flags.CompilerGenerated;
4034 var b = this is ToplevelBlock ?
4035 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4036 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4039 b.state_machine = stateMachine;
4040 b.AddStatement (new AsyncInitializerStatement (initializer));
4048 public class ToplevelBlock : ParametersBlock
4050 LocalVariable this_variable;
4051 CompilerContext compiler;
4052 Dictionary<string, object> names;
4054 List<ExplicitBlock> this_references;
4056 public ToplevelBlock (CompilerContext ctx, Location loc)
4057 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4061 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4062 : base (parameters, start)
4064 this.compiler = ctx;
4068 ProcessParameters ();
4072 // Recreates a top level block from parameters block. Used for
4073 // compiler generated methods where the original block comes from
4074 // explicit child block. This works for already resolved blocks
4075 // only to ensure we resolve them in the correct flow order
4077 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4078 : base (source, parameters)
4080 this.compiler = source.TopBlock.compiler;
4084 public bool IsIterator {
4086 return (flags & Flags.Iterator) != 0;
4089 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4093 public Report Report {
4095 return compiler.Report;
4100 // Used by anonymous blocks to track references of `this' variable
4102 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4104 return this_references;
4109 // Returns the "this" instance variable of this block.
4110 // See AddThisVariable() for more information.
4112 public LocalVariable ThisVariable {
4114 return this_variable;
4118 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4121 names = new Dictionary<string, object> ();
4124 if (!names.TryGetValue (name, out value)) {
4125 names.Add (name, li);
4129 INamedBlockVariable existing = value as INamedBlockVariable;
4130 List<INamedBlockVariable> existing_list;
4131 if (existing != null) {
4132 existing_list = new List<INamedBlockVariable> ();
4133 existing_list.Add (existing);
4134 names[name] = existing_list;
4136 existing_list = (List<INamedBlockVariable>) value;
4140 // A collision checking between local names
4142 var variable_block = li.Block.Explicit;
4143 for (int i = 0; i < existing_list.Count; ++i) {
4144 existing = existing_list[i];
4145 Block b = existing.Block.Explicit;
4147 // Collision at same level
4148 if (variable_block == b) {
4149 li.Block.Error_AlreadyDeclared (name, li);
4153 // Collision with parent
4154 Block parent = variable_block;
4155 while ((parent = parent.Parent) != null) {
4157 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4158 i = existing_list.Count;
4163 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4164 // Collision with children
4165 while ((b = b.Parent) != null) {
4166 if (variable_block == b) {
4167 li.Block.Error_AlreadyDeclared (name, li, "child");
4168 i = existing_list.Count;
4175 existing_list.Add (li);
4178 public void AddLabel (string name, LabeledStatement label)
4181 labels = new Dictionary<string, object> ();
4184 if (!labels.TryGetValue (name, out value)) {
4185 labels.Add (name, label);
4189 LabeledStatement existing = value as LabeledStatement;
4190 List<LabeledStatement> existing_list;
4191 if (existing != null) {
4192 existing_list = new List<LabeledStatement> ();
4193 existing_list.Add (existing);
4194 labels[name] = existing_list;
4196 existing_list = (List<LabeledStatement>) value;
4200 // A collision checking between labels
4202 for (int i = 0; i < existing_list.Count; ++i) {
4203 existing = existing_list[i];
4204 Block b = existing.Block;
4206 // Collision at same level
4207 if (label.Block == b) {
4208 Report.SymbolRelatedToPreviousError (existing.loc, name);
4209 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4213 // Collision with parent
4215 while ((b = b.Parent) != null) {
4216 if (existing.Block == b) {
4217 Report.Error (158, label.loc,
4218 "The label `{0}' shadows another label by the same name in a contained scope", name);
4219 i = existing_list.Count;
4224 // Collision with with children
4226 while ((b = b.Parent) != null) {
4227 if (label.Block == b) {
4228 Report.Error (158, label.loc,
4229 "The label `{0}' shadows another label by the same name in a contained scope", name);
4230 i = existing_list.Count;
4236 existing_list.Add (label);
4239 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4241 if (this_references == null)
4242 this_references = new List<ExplicitBlock> ();
4244 if (!this_references.Contains (block))
4245 this_references.Add (block);
4248 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4250 this_references.Remove (block);
4254 // Creates an arguments set from all parameters, useful for method proxy calls
4256 public Arguments GetAllParametersArguments ()
4258 int count = parameters.Count;
4259 Arguments args = new Arguments (count);
4260 for (int i = 0; i < count; ++i) {
4261 var pi = parameter_info[i];
4262 var arg_expr = GetParameterReference (i, pi.Location);
4264 Argument.AType atype_modifier;
4265 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4266 case Parameter.Modifier.REF:
4267 atype_modifier = Argument.AType.Ref;
4269 case Parameter.Modifier.OUT:
4270 atype_modifier = Argument.AType.Out;
4277 args.Add (new Argument (arg_expr, atype_modifier));
4284 // Lookup inside a block, the returned value can represent 3 states
4286 // true+variable: A local name was found and it's valid
4287 // false+variable: A local name was found in a child block only
4288 // false+null: No local name was found
4290 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4296 if (!names.TryGetValue (name, out value))
4299 variable = value as INamedBlockVariable;
4301 if (variable != null) {
4303 if (variable.Block == b.Original)
4307 } while (b != null);
4315 } while (b != null);
4317 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4318 for (int i = 0; i < list.Count; ++i) {
4321 if (variable.Block == b.Original)
4325 } while (b != null);
4333 } while (b != null);
4343 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4345 if (block.names != null) {
4346 foreach (var n in block.names) {
4347 var variable = n.Value as INamedBlockVariable;
4348 if (variable != null) {
4349 if (variable.Block.ParametersBlock == pb)
4350 AddLocalName (n.Key, variable, false);
4354 foreach (var v in (List<INamedBlockVariable>) n.Value)
4355 if (v.Block.ParametersBlock == pb)
4356 AddLocalName (n.Key, v, false);
4362 // This is used by non-static `struct' constructors which do not have an
4363 // initializer - in this case, the constructor must initialize all of the
4364 // struct's fields. To do this, we add a "this" variable and use the flow
4365 // analysis code to ensure that it's been fully initialized before control
4366 // leaves the constructor.
4368 public void AddThisVariable (BlockContext bc)
4370 if (this_variable != null)
4371 throw new InternalErrorException (StartLocation.ToString ());
4373 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4374 this_variable.Type = bc.CurrentType;
4375 this_variable.PrepareAssignmentAnalysis (bc);
4378 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4381 // If we're a non-static struct constructor which doesn't have an
4382 // initializer, then we must initialize all of the struct's fields.
4384 if (this_variable != null)
4385 this_variable.IsThisAssigned (fc, this);
4387 base.CheckControlExit (fc, dat);
4390 public override void Emit (EmitContext ec)
4392 if (Report.Errors > 0)
4396 if (IsCompilerGenerated) {
4397 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4405 // If `HasReturnLabel' is set, then we already emitted a
4406 // jump to the end of the method, so we must emit a `ret'
4409 // Unfortunately, System.Reflection.Emit automatically emits
4410 // a leave to the end of a finally block. This is a problem
4411 // if no code is following the try/finally block since we may
4412 // jump to a point after the end of the method.
4413 // As a workaround, we're always creating a return label in
4416 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4417 if (ec.HasReturnLabel)
4418 ec.MarkLabel (ec.ReturnLabel);
4420 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4421 ec.Mark (EndLocation);
4423 if (ec.ReturnType.Kind != MemberKind.Void)
4424 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4426 ec.Emit (OpCodes.Ret);
4429 } catch (Exception e) {
4430 throw new InternalErrorException (e, StartLocation);
4434 public bool Resolve (BlockContext bc, IMethodData md)
4439 var errors = bc.Report.Errors;
4443 if (bc.Report.Errors > errors)
4446 MarkReachable (new Reachability ());
4448 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4449 // TODO: var md = bc.CurrentMemberDefinition;
4450 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4453 if ((flags & Flags.NoFlowAnalysis) != 0)
4456 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4459 } catch (Exception e) {
4460 throw new InternalErrorException (e, StartLocation);
4467 public class SwitchLabel : Statement
4475 // if expr == null, then it is the default case.
4477 public SwitchLabel (Expression expr, Location l)
4483 public bool IsDefault {
4485 return label == null;
4489 public Expression Label {
4495 public Location Location {
4501 public Constant Converted {
4510 public bool PatternMatching { get; set; }
4512 public bool SectionStart { get; set; }
4514 public Label GetILLabel (EmitContext ec)
4516 if (il_label == null){
4517 il_label = ec.DefineLabel ();
4520 return il_label.Value;
4523 protected override void DoEmit (EmitContext ec)
4525 ec.MarkLabel (GetILLabel (ec));
4528 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4533 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4537 public override bool Resolve (BlockContext bc)
4539 if (ResolveAndReduce (bc))
4540 bc.Switch.RegisterLabel (bc, this);
4546 // Resolves the expression, reduces it to a literal if possible
4547 // and then converts it to the requested type.
4549 bool ResolveAndReduce (BlockContext bc)
4554 var switch_statement = bc.Switch;
4556 if (PatternMatching) {
4557 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4558 return label != null;
4561 var c = label.ResolveLabelConstant (bc);
4565 if (switch_statement.IsNullable && c is NullLiteral) {
4570 if (switch_statement.IsPatternMatching) {
4571 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4575 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4576 return converted != null;
4579 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4581 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4582 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4585 protected override void CloneTo (CloneContext clonectx, Statement target)
4587 var t = (SwitchLabel) target;
4589 t.label = label.Clone (clonectx);
4592 public override object Accept (StructuralVisitor visitor)
4594 return visitor.Visit (this);
4597 public string GetSignatureForError ()
4600 if (converted == null)
4603 label = converted.GetValueAsLiteral ();
4605 return string.Format ("case {0}:", label);
4609 public class Switch : LoopStatement
4611 // structure used to hold blocks of keys while calculating table switch
4612 sealed class LabelsRange : IComparable<LabelsRange>
4614 public readonly long min;
4616 public readonly List<long> label_values;
4618 public LabelsRange (long value)
4621 label_values = new List<long> ();
4622 label_values.Add (value);
4625 public LabelsRange (long min, long max, ICollection<long> values)
4629 this.label_values = new List<long> (values);
4634 return max - min + 1;
4638 public bool AddValue (long value)
4640 var gap = value - min + 1;
4641 // Ensure the range has > 50% occupancy
4642 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4646 label_values.Add (value);
4650 public int CompareTo (LabelsRange other)
4652 int nLength = label_values.Count;
4653 int nLengthOther = other.label_values.Count;
4654 if (nLengthOther == nLength)
4655 return (int) (other.min - min);
4657 return nLength - nLengthOther;
4661 sealed class DispatchStatement : Statement
4663 readonly Switch body;
4665 public DispatchStatement (Switch body)
4670 protected override void CloneTo (CloneContext clonectx, Statement target)
4672 throw new NotImplementedException ();
4675 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4680 protected override void DoEmit (EmitContext ec)
4682 body.EmitDispatch (ec);
4686 class MissingBreak : Statement
4688 readonly SwitchLabel label;
4690 public MissingBreak (SwitchLabel sl)
4696 public bool FallOut { get; set; }
4698 protected override void DoEmit (EmitContext ec)
4702 protected override void CloneTo (CloneContext clonectx, Statement target)
4706 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4709 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4710 label.GetSignatureForError ());
4712 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4713 label.GetSignatureForError ());
4719 public Expression Expr;
4722 // Mapping of all labels to their SwitchLabels
4724 Dictionary<long, SwitchLabel> labels;
4725 Dictionary<string, SwitchLabel> string_labels;
4726 List<SwitchLabel> case_labels;
4728 List<Tuple<GotoCase, Constant>> goto_cases;
4729 List<DefiniteAssignmentBitSet> end_reachable_das;
4732 /// The governing switch type
4734 public TypeSpec SwitchType;
4736 Expression new_expr;
4738 SwitchLabel case_null;
4739 SwitchLabel case_default;
4741 Label defaultLabel, nullLabel;
4742 VariableReference value;
4743 ExpressionStatement string_dictionary;
4744 FieldExpr switch_cache_field;
4745 ExplicitBlock block;
4749 // Nullable Types support
4751 Nullable.Unwrap unwrap;
4753 public Switch (Expression e, ExplicitBlock block, Location l)
4761 public SwitchLabel ActiveLabel { get; set; }
4763 public ExplicitBlock Block {
4769 public SwitchLabel DefaultLabel {
4771 return case_default;
4775 public bool IsNullable {
4777 return unwrap != null;
4781 public bool IsPatternMatching {
4783 return new_expr == null && SwitchType != null;
4787 public List<SwitchLabel> RegisteredLabels {
4793 public VariableReference ExpressionValue {
4800 // Determines the governing type for a switch. The returned
4801 // expression might be the expression from the switch, or an
4802 // expression that includes any potential conversions to
4804 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4806 switch (expr.Type.BuiltinType) {
4807 case BuiltinTypeSpec.Type.Byte:
4808 case BuiltinTypeSpec.Type.SByte:
4809 case BuiltinTypeSpec.Type.UShort:
4810 case BuiltinTypeSpec.Type.Short:
4811 case BuiltinTypeSpec.Type.UInt:
4812 case BuiltinTypeSpec.Type.Int:
4813 case BuiltinTypeSpec.Type.ULong:
4814 case BuiltinTypeSpec.Type.Long:
4815 case BuiltinTypeSpec.Type.Char:
4816 case BuiltinTypeSpec.Type.String:
4817 case BuiltinTypeSpec.Type.Bool:
4821 if (expr.Type.IsEnum)
4825 // Try to find a *user* defined implicit conversion.
4827 // If there is no implicit conversion, or if there are multiple
4828 // conversions, we have to report an error
4830 Expression converted = null;
4831 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4833 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4836 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4837 Convert.UserConversionRestriction.ProbingOnly;
4840 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4842 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4847 // Ignore over-worked ImplicitUserConversions that do
4848 // an implicit conversion in addition to the user conversion.
4850 var uc = e as UserCast;
4854 if (converted != null){
4855 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4864 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4866 var types = module.Compiler.BuiltinTypes;
4868 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4869 TypeSpec[] stypes = new[] {
4882 if (nullable != null) {
4884 Array.Resize (ref stypes, stypes.Length + 9);
4886 for (int i = 0; i < 9; ++i) {
4887 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4894 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4896 case_labels.Add (sl);
4899 if (case_default != null) {
4900 sl.Error_AlreadyOccurs (rc, case_default);
4908 if (sl.Converted == null)
4912 if (string_labels != null) {
4913 string string_value = sl.Converted.GetValue () as string;
4914 if (string_value == null)
4917 string_labels.Add (string_value, sl);
4919 if (sl.Converted.IsNull) {
4922 labels.Add (sl.Converted.GetValueAsLong (), sl);
4925 } catch (ArgumentException) {
4926 if (string_labels != null)
4927 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4929 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4934 // This method emits code for a lookup-based switch statement (non-string)
4935 // Basically it groups the cases into blocks that are at least half full,
4936 // and then spits out individual lookup opcodes for each block.
4937 // It emits the longest blocks first, and short blocks are just
4938 // handled with direct compares.
4940 void EmitTableSwitch (EmitContext ec, Expression val)
4942 if (labels != null && labels.Count > 0) {
4943 List<LabelsRange> ranges;
4944 if (string_labels != null) {
4945 // We have done all hard work for string already
4946 // setup single range only
4947 ranges = new List<LabelsRange> (1);
4948 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4950 var element_keys = new long[labels.Count];
4951 labels.Keys.CopyTo (element_keys, 0);
4952 Array.Sort (element_keys);
4955 // Build possible ranges of switch labes to reduce number
4958 ranges = new List<LabelsRange> (element_keys.Length);
4959 var range = new LabelsRange (element_keys[0]);
4961 for (int i = 1; i < element_keys.Length; ++i) {
4962 var l = element_keys[i];
4963 if (range.AddValue (l))
4966 range = new LabelsRange (l);
4970 // sort the blocks so we can tackle the largest ones first
4974 Label lbl_default = defaultLabel;
4975 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4977 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4978 LabelsRange kb = ranges[range_index];
4979 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4981 // Optimize small ranges using simple equality check
4982 if (kb.Range <= 2) {
4983 foreach (var key in kb.label_values) {
4984 SwitchLabel sl = labels[key];
4985 if (sl == case_default || sl == case_null)
4988 if (sl.Converted.IsZeroInteger) {
4989 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4992 sl.Converted.Emit (ec);
4993 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4997 // TODO: if all the keys in the block are the same and there are
4998 // no gaps/defaults then just use a range-check.
4999 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5000 // TODO: optimize constant/I4 cases
5002 // check block range (could be > 2^31)
5004 ec.EmitLong (kb.min);
5005 ec.Emit (OpCodes.Blt, lbl_default);
5008 ec.EmitLong (kb.max);
5009 ec.Emit (OpCodes.Bgt, lbl_default);
5014 ec.EmitLong (kb.min);
5015 ec.Emit (OpCodes.Sub);
5018 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5022 int first = (int) kb.min;
5025 ec.Emit (OpCodes.Sub);
5026 } else if (first < 0) {
5027 ec.EmitInt (-first);
5028 ec.Emit (OpCodes.Add);
5032 // first, build the list of labels for the switch
5034 long cJumps = kb.Range;
5035 Label[] switch_labels = new Label[cJumps];
5036 for (int iJump = 0; iJump < cJumps; iJump++) {
5037 var key = kb.label_values[iKey];
5038 if (key == kb.min + iJump) {
5039 switch_labels[iJump] = labels[key].GetILLabel (ec);
5042 switch_labels[iJump] = lbl_default;
5046 // emit the switch opcode
5047 ec.Emit (OpCodes.Switch, switch_labels);
5050 // mark the default for this block
5051 if (range_index != 0)
5052 ec.MarkLabel (lbl_default);
5055 // the last default just goes to the end
5056 if (ranges.Count > 0)
5057 ec.Emit (OpCodes.Br, lbl_default);
5061 public SwitchLabel FindLabel (Constant value)
5063 SwitchLabel sl = null;
5065 if (string_labels != null) {
5066 string s = value.GetValue () as string;
5068 if (case_null != null)
5070 else if (case_default != null)
5073 string_labels.TryGetValue (s, out sl);
5076 if (value is NullLiteral) {
5079 labels.TryGetValue (value.GetValueAsLong (), out sl);
5083 if (sl == null || sl.SectionStart)
5087 // Always return section start, it simplifies handling of switch labels
5089 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5090 var cs = case_labels [idx];
5091 if (cs.SectionStart)
5096 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5098 Expr.FlowAnalysis (fc);
5100 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5101 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5102 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5104 block.FlowAnalysis (fc);
5106 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5108 if (end_reachable_das != null) {
5109 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5110 InitialDefinitiveAssignment |= sections_das;
5111 end_reachable_das = null;
5114 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5116 return case_default != null && !end_reachable;
5119 public override bool Resolve (BlockContext ec)
5121 Expr = Expr.Resolve (ec);
5126 // LAMESPEC: User conversion from non-nullable governing type has a priority
5128 new_expr = SwitchGoverningType (ec, Expr, false);
5130 if (new_expr == null) {
5131 if (Expr.Type.IsNullableType) {
5132 unwrap = Nullable.Unwrap.Create (Expr, false);
5137 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5138 // involving nullable Expr and nullable governing type is
5140 new_expr = SwitchGoverningType (ec, unwrap, true);
5144 Expression switch_expr;
5145 if (new_expr == null) {
5146 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5147 if (Expr.Type != InternalType.ErrorType) {
5148 ec.Report.Error (151, loc,
5149 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5150 Expr.Type.GetSignatureForError ());
5157 SwitchType = Expr.Type;
5159 switch_expr = new_expr;
5160 SwitchType = new_expr.Type;
5161 if (SwitchType.IsNullableType) {
5162 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5163 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5166 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5167 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5171 if (block.Statements.Count == 0)
5174 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5175 string_labels = new Dictionary<string, SwitchLabel> ();
5177 labels = new Dictionary<long, SwitchLabel> ();
5181 var constant = switch_expr as Constant;
5184 // Don't need extra variable for constant switch or switch with
5185 // only default case
5187 if (constant == null) {
5189 // Store switch expression for comparison purposes
5191 value = switch_expr as VariableReference;
5192 if (value == null && !HasOnlyDefaultSection ()) {
5193 var current_block = ec.CurrentBlock;
5194 ec.CurrentBlock = Block;
5195 // Create temporary variable inside switch scope
5196 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5198 ec.CurrentBlock = current_block;
5202 case_labels = new List<SwitchLabel> ();
5204 Switch old_switch = ec.Switch;
5206 var parent_los = ec.EnclosingLoopOrSwitch;
5207 ec.EnclosingLoopOrSwitch = this;
5209 var ok = Statement.Resolve (ec);
5211 ec.EnclosingLoopOrSwitch = parent_los;
5212 ec.Switch = old_switch;
5215 // Check if all goto cases are valid. Needs to be done after switch
5216 // is resolved because goto can jump forward in the scope.
5218 if (goto_cases != null) {
5219 foreach (var gc in goto_cases) {
5220 if (gc.Item1 == null) {
5221 if (DefaultLabel == null) {
5222 Goto.Error_UnknownLabel (ec, "default", loc);
5228 var sl = FindLabel (gc.Item2);
5230 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5232 gc.Item1.Label = sl;
5240 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5241 ResolveStringSwitchMap (ec);
5245 // Anonymous storey initialization has to happen before
5246 // any generated switch dispatch
5248 block.InsertStatement (0, new DispatchStatement (this));
5253 bool HasOnlyDefaultSection ()
5255 for (int i = 0; i < block.Statements.Count; ++i) {
5256 var s = block.Statements[i] as SwitchLabel;
5258 if (s == null || s.IsDefault)
5267 public override Reachability MarkReachable (Reachability rc)
5269 if (rc.IsUnreachable)
5272 base.MarkReachable (rc);
5274 block.MarkReachableScope (rc);
5276 if (block.Statements.Count == 0)
5279 SwitchLabel constant_label = null;
5280 var constant = new_expr as Constant;
5282 if (constant != null) {
5283 constant_label = FindLabel (constant) ?? case_default;
5284 if (constant_label == null) {
5285 block.Statements.RemoveAt (0);
5290 var section_rc = new Reachability ();
5291 SwitchLabel prev_label = null;
5293 for (int i = 0; i < block.Statements.Count; ++i) {
5294 var s = block.Statements[i];
5295 var sl = s as SwitchLabel;
5297 if (sl != null && sl.SectionStart) {
5299 // Section is marked already via goto case
5301 if (!sl.IsUnreachable) {
5302 section_rc = new Reachability ();
5306 if (constant_label != null && constant_label != sl)
5307 section_rc = Reachability.CreateUnreachable ();
5308 else if (section_rc.IsUnreachable) {
5309 section_rc = new Reachability ();
5311 if (prev_label != null) {
5312 sl.SectionStart = false;
5313 s = new MissingBreak (prev_label);
5314 s.MarkReachable (rc);
5315 block.Statements.Insert (i - 1, s);
5323 section_rc = s.MarkReachable (section_rc);
5326 if (!section_rc.IsUnreachable && prev_label != null) {
5327 prev_label.SectionStart = false;
5328 var s = new MissingBreak (prev_label) {
5332 s.MarkReachable (rc);
5333 block.Statements.Add (s);
5337 // Reachability can affect parent only when all possible paths are handled but
5338 // we still need to run reachability check on switch body to check for fall-through
5340 if (case_default == null && constant_label == null)
5344 // We have at least one local exit from the switch
5349 return Reachability.CreateUnreachable ();
5352 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5354 if (goto_cases == null)
5355 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5357 goto_cases.Add (Tuple.Create (gotoCase, value));
5361 // Converts string switch into string hashtable
5363 void ResolveStringSwitchMap (ResolveContext ec)
5365 FullNamedExpression string_dictionary_type;
5366 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5367 string_dictionary_type = new TypeExpression (
5368 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5369 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5371 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5372 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5374 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5378 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5379 Field field = new Field (ctype, string_dictionary_type,
5380 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5381 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5382 if (!field.Define ())
5384 ctype.AddField (field);
5386 var init = new List<Expression> ();
5388 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5389 string value = null;
5391 foreach (SwitchLabel sl in case_labels) {
5393 if (sl.SectionStart)
5394 labels.Add (++counter, sl);
5396 if (sl == case_default || sl == case_null)
5399 value = (string) sl.Converted.GetValue ();
5400 var init_args = new List<Expression> (2);
5401 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5403 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5404 init_args.Add (sl.Converted);
5406 init.Add (new CollectionElementInitializer (init_args, loc));
5409 Arguments args = new Arguments (1);
5410 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5411 Expression initializer = new NewInitialize (string_dictionary_type, args,
5412 new CollectionOrObjectInitializers (init, loc), loc);
5414 switch_cache_field = new FieldExpr (field, loc);
5415 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5418 void DoEmitStringSwitch (EmitContext ec)
5420 Label l_initialized = ec.DefineLabel ();
5423 // Skip initialization when value is null
5425 value.EmitBranchable (ec, nullLabel, false);
5428 // Check if string dictionary is initialized and initialize
5430 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5431 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5432 string_dictionary.EmitStatement (ec);
5434 ec.MarkLabel (l_initialized);
5436 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5438 ResolveContext rc = new ResolveContext (ec.MemberContext);
5440 if (switch_cache_field.Type.IsGeneric) {
5441 Arguments get_value_args = new Arguments (2);
5442 get_value_args.Add (new Argument (value));
5443 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5444 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5445 if (get_item == null)
5449 // A value was not found, go to default case
5451 get_item.EmitBranchable (ec, defaultLabel, false);
5453 Arguments get_value_args = new Arguments (1);
5454 get_value_args.Add (new Argument (value));
5456 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5457 if (get_item == null)
5460 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5461 get_item_object.EmitAssign (ec, get_item, true, false);
5462 ec.Emit (OpCodes.Brfalse, defaultLabel);
5464 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5465 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5467 get_item_int.EmitStatement (ec);
5468 get_item_object.Release (ec);
5471 EmitTableSwitch (ec, string_switch_variable);
5472 string_switch_variable.Release (ec);
5476 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5478 void EmitShortSwitch (EmitContext ec)
5480 MethodSpec equal_method = null;
5481 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5482 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5485 if (equal_method != null) {
5486 value.EmitBranchable (ec, nullLabel, false);
5489 for (int i = 0; i < case_labels.Count; ++i) {
5490 var label = case_labels [i];
5491 if (label == case_default || label == case_null)
5494 var constant = label.Converted;
5496 if (constant == null) {
5497 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5501 if (equal_method != null) {
5505 var call = new CallEmitter ();
5506 call.EmitPredefined (ec, equal_method, new Arguments (0));
5507 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5511 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5512 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5518 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5521 ec.Emit (OpCodes.Br, defaultLabel);
5524 void EmitDispatch (EmitContext ec)
5526 if (IsPatternMatching) {
5527 EmitShortSwitch (ec);
5531 if (value == null) {
5533 // Constant switch, we've already done the work if there is only 1 label
5537 foreach (var sl in case_labels) {
5538 if (sl.IsUnreachable)
5541 if (reachable++ > 0) {
5542 var constant = (Constant) new_expr;
5543 var constant_label = FindLabel (constant) ?? case_default;
5545 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5553 if (string_dictionary != null) {
5554 DoEmitStringSwitch (ec);
5555 } else if (case_labels.Count < 4 || string_labels != null) {
5556 EmitShortSwitch (ec);
5558 EmitTableSwitch (ec, value);
5562 protected override void DoEmit (EmitContext ec)
5565 // Setup the codegen context
5567 Label old_end = ec.LoopEnd;
5568 Switch old_switch = ec.Switch;
5570 ec.LoopEnd = ec.DefineLabel ();
5573 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5574 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5576 if (value != null) {
5579 var switch_expr = new_expr ?? Expr;
5581 unwrap.EmitCheck (ec);
5582 ec.Emit (OpCodes.Brfalse, nullLabel);
5583 value.EmitAssign (ec, switch_expr, false, false);
5584 } else if (switch_expr != value) {
5585 value.EmitAssign (ec, switch_expr, false, false);
5590 // Next statement is compiler generated we don't need extra
5591 // nop when we can use the statement for sequence point
5593 ec.Mark (block.StartLocation);
5594 block.IsCompilerGenerated = true;
5596 new_expr.EmitSideEffect (ec);
5601 // Restore context state.
5602 ec.MarkLabel (ec.LoopEnd);
5605 // Restore the previous context
5607 ec.LoopEnd = old_end;
5608 ec.Switch = old_switch;
5611 protected override void CloneTo (CloneContext clonectx, Statement t)
5613 Switch target = (Switch) t;
5615 target.Expr = Expr.Clone (clonectx);
5616 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5619 public override object Accept (StructuralVisitor visitor)
5621 return visitor.Visit (this);
5624 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5626 if (case_default == null && !(new_expr is Constant))
5629 if (end_reachable_das == null)
5630 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5632 end_reachable_das.Add (fc.DefiniteAssignment);
5635 public override void SetEndReachable ()
5637 end_reachable = true;
5641 // A place where execution can restart in a state machine
5642 public abstract class ResumableStatement : Statement
5645 protected Label resume_point;
5647 public Label PrepareForEmit (EmitContext ec)
5651 resume_point = ec.DefineLabel ();
5653 return resume_point;
5656 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5661 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5666 public abstract class TryFinallyBlock : ExceptionStatement
5668 protected Statement stmt;
5669 Label dispose_try_block;
5670 bool prepared_for_dispose, emitted_dispose;
5671 Method finally_host;
5673 protected TryFinallyBlock (Statement stmt, Location loc)
5681 public Statement Statement {
5689 protected abstract void EmitTryBody (EmitContext ec);
5690 public abstract void EmitFinallyBody (EmitContext ec);
5692 public override Label PrepareForDispose (EmitContext ec, Label end)
5694 if (!prepared_for_dispose) {
5695 prepared_for_dispose = true;
5696 dispose_try_block = ec.DefineLabel ();
5698 return dispose_try_block;
5701 protected sealed override void DoEmit (EmitContext ec)
5703 EmitTryBodyPrepare (ec);
5706 bool beginFinally = EmitBeginFinallyBlock (ec);
5708 Label start_finally = ec.DefineLabel ();
5709 if (resume_points != null && beginFinally) {
5710 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5712 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5713 ec.Emit (OpCodes.Brfalse_S, start_finally);
5714 ec.Emit (OpCodes.Endfinally);
5717 ec.MarkLabel (start_finally);
5719 if (finally_host != null) {
5720 finally_host.Define ();
5721 finally_host.PrepareEmit ();
5722 finally_host.Emit ();
5724 // Now it's safe to add, to close it properly and emit sequence points
5725 finally_host.Parent.AddMember (finally_host);
5727 var ce = new CallEmitter ();
5728 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5729 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5731 EmitFinallyBody (ec);
5735 ec.EndExceptionBlock ();
5738 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5740 if (emitted_dispose)
5743 emitted_dispose = true;
5745 Label end_of_try = ec.DefineLabel ();
5747 // Ensure that the only way we can get into this code is through a dispatcher
5748 if (have_dispatcher)
5749 ec.Emit (OpCodes.Br, end);
5751 ec.BeginExceptionBlock ();
5753 ec.MarkLabel (dispose_try_block);
5755 Label[] labels = null;
5756 for (int i = 0; i < resume_points.Count; ++i) {
5757 ResumableStatement s = resume_points[i];
5758 Label ret = s.PrepareForDispose (ec, end_of_try);
5759 if (ret.Equals (end_of_try) && labels == null)
5761 if (labels == null) {
5762 labels = new Label[resume_points.Count];
5763 for (int j = 0; j < i; ++j)
5764 labels[j] = end_of_try;
5769 if (labels != null) {
5771 for (j = 1; j < labels.Length; ++j)
5772 if (!labels[0].Equals (labels[j]))
5774 bool emit_dispatcher = j < labels.Length;
5776 if (emit_dispatcher) {
5777 ec.Emit (OpCodes.Ldloc, pc);
5778 ec.EmitInt (first_resume_pc);
5779 ec.Emit (OpCodes.Sub);
5780 ec.Emit (OpCodes.Switch, labels);
5783 foreach (ResumableStatement s in resume_points)
5784 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5787 ec.MarkLabel (end_of_try);
5789 ec.BeginFinallyBlock ();
5791 if (finally_host != null) {
5792 var ce = new CallEmitter ();
5793 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5794 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5796 EmitFinallyBody (ec);
5799 ec.EndExceptionBlock ();
5802 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5804 var res = stmt.FlowAnalysis (fc);
5809 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5811 ec.BeginFinallyBlock ();
5815 public override Reachability MarkReachable (Reachability rc)
5817 base.MarkReachable (rc);
5818 return Statement.MarkReachable (rc);
5821 public override bool Resolve (BlockContext bc)
5825 parent = bc.CurrentTryBlock;
5826 bc.CurrentTryBlock = this;
5828 using (bc.Set (ResolveContext.Options.TryScope)) {
5829 ok = stmt.Resolve (bc);
5832 bc.CurrentTryBlock = parent;
5835 // Finally block inside iterator is called from MoveNext and
5836 // Dispose methods that means we need to lift the block into
5837 // newly created host method to emit the body only once. The
5838 // original block then simply calls the newly generated method.
5840 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5841 var b = stmt as Block;
5842 if (b != null && b.Explicit.HasYield) {
5843 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5847 return base.Resolve (bc) && ok;
5852 // Base class for blocks using exception handling
5854 public abstract class ExceptionStatement : ResumableStatement
5856 protected List<ResumableStatement> resume_points;
5857 protected int first_resume_pc;
5858 protected ExceptionStatement parent;
5860 protected ExceptionStatement (Location loc)
5865 protected virtual void EmitBeginException (EmitContext ec)
5867 ec.BeginExceptionBlock ();
5870 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5872 StateMachineInitializer state_machine = null;
5873 if (resume_points != null) {
5874 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5876 ec.EmitInt ((int) IteratorStorey.State.Running);
5877 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5880 EmitBeginException (ec);
5882 if (resume_points != null) {
5883 ec.MarkLabel (resume_point);
5885 // For normal control flow, we want to fall-through the Switch
5886 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5887 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5888 ec.EmitInt (first_resume_pc);
5889 ec.Emit (OpCodes.Sub);
5891 Label[] labels = new Label[resume_points.Count];
5892 for (int i = 0; i < resume_points.Count; ++i)
5893 labels[i] = resume_points[i].PrepareForEmit (ec);
5894 ec.Emit (OpCodes.Switch, labels);
5898 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5900 if (parent != null) {
5901 // TODO: MOVE to virtual TryCatch
5902 var tc = this as TryCatch;
5903 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5905 pc = parent.AddResumePoint (s, pc, stateMachine);
5907 pc = stateMachine.AddResumePoint (this);
5910 if (resume_points == null) {
5911 resume_points = new List<ResumableStatement> ();
5912 first_resume_pc = pc;
5915 if (pc != first_resume_pc + resume_points.Count)
5916 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5918 resume_points.Add (stmt);
5923 public class Lock : TryFinallyBlock
5926 TemporaryVariableReference expr_copy;
5927 TemporaryVariableReference lock_taken;
5929 public Lock (Expression expr, Statement stmt, Location loc)
5935 public Expression Expr {
5941 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5943 expr.FlowAnalysis (fc);
5944 return base.DoFlowAnalysis (fc);
5947 public override bool Resolve (BlockContext ec)
5949 expr = expr.Resolve (ec);
5953 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
5954 ec.Report.Error (185, loc,
5955 "`{0}' is not a reference type as required by the lock statement",
5956 expr.Type.GetSignatureForError ());
5959 if (expr.Type.IsGenericParameter) {
5960 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5963 VariableReference lv = expr as VariableReference;
5966 locked = lv.IsLockedByStatement;
5967 lv.IsLockedByStatement = true;
5974 // Have to keep original lock value around to unlock same location
5975 // in the case of original value has changed or is null
5977 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5978 expr_copy.Resolve (ec);
5981 // Ensure Monitor methods are available
5983 if (ResolvePredefinedMethods (ec) > 1) {
5984 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5985 lock_taken.Resolve (ec);
5988 using (ec.Set (ResolveContext.Options.LockScope)) {
5993 lv.IsLockedByStatement = locked;
5999 protected override void EmitTryBodyPrepare (EmitContext ec)
6001 expr_copy.EmitAssign (ec, expr);
6003 if (lock_taken != null) {
6005 // Initialize ref variable
6007 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6010 // Monitor.Enter (expr_copy)
6012 expr_copy.Emit (ec);
6013 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6016 base.EmitTryBodyPrepare (ec);
6019 protected override void EmitTryBody (EmitContext ec)
6022 // Monitor.Enter (expr_copy, ref lock_taken)
6024 if (lock_taken != null) {
6025 expr_copy.Emit (ec);
6026 lock_taken.LocalInfo.CreateBuilder (ec);
6027 lock_taken.AddressOf (ec, AddressOp.Load);
6028 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6031 Statement.Emit (ec);
6034 public override void EmitFinallyBody (EmitContext ec)
6037 // if (lock_taken) Monitor.Exit (expr_copy)
6039 Label skip = ec.DefineLabel ();
6041 if (lock_taken != null) {
6042 lock_taken.Emit (ec);
6043 ec.Emit (OpCodes.Brfalse_S, skip);
6046 expr_copy.Emit (ec);
6047 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6049 ec.Emit (OpCodes.Call, m);
6051 ec.MarkLabel (skip);
6054 int ResolvePredefinedMethods (ResolveContext rc)
6056 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6057 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6061 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6065 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6069 protected override void CloneTo (CloneContext clonectx, Statement t)
6071 Lock target = (Lock) t;
6073 target.expr = expr.Clone (clonectx);
6074 target.stmt = Statement.Clone (clonectx);
6077 public override object Accept (StructuralVisitor visitor)
6079 return visitor.Visit (this);
6084 public class Unchecked : Statement {
6087 public Unchecked (Block b, Location loc)
6094 public override bool Resolve (BlockContext ec)
6096 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6097 return Block.Resolve (ec);
6100 protected override void DoEmit (EmitContext ec)
6102 using (ec.With (EmitContext.Options.CheckedScope, false))
6106 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6108 return Block.FlowAnalysis (fc);
6111 public override Reachability MarkReachable (Reachability rc)
6113 base.MarkReachable (rc);
6114 return Block.MarkReachable (rc);
6117 protected override void CloneTo (CloneContext clonectx, Statement t)
6119 Unchecked target = (Unchecked) t;
6121 target.Block = clonectx.LookupBlock (Block);
6124 public override object Accept (StructuralVisitor visitor)
6126 return visitor.Visit (this);
6130 public class Checked : Statement {
6133 public Checked (Block b, Location loc)
6136 b.Unchecked = false;
6140 public override bool Resolve (BlockContext ec)
6142 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6143 return Block.Resolve (ec);
6146 protected override void DoEmit (EmitContext ec)
6148 using (ec.With (EmitContext.Options.CheckedScope, true))
6152 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6154 return Block.FlowAnalysis (fc);
6157 public override Reachability MarkReachable (Reachability rc)
6159 base.MarkReachable (rc);
6160 return Block.MarkReachable (rc);
6163 protected override void CloneTo (CloneContext clonectx, Statement t)
6165 Checked target = (Checked) t;
6167 target.Block = clonectx.LookupBlock (Block);
6170 public override object Accept (StructuralVisitor visitor)
6172 return visitor.Visit (this);
6176 public class Unsafe : Statement {
6179 public Unsafe (Block b, Location loc)
6182 Block.Unsafe = true;
6186 public override bool Resolve (BlockContext ec)
6188 if (ec.CurrentIterator != null)
6189 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6191 using (ec.Set (ResolveContext.Options.UnsafeScope))
6192 return Block.Resolve (ec);
6195 protected override void DoEmit (EmitContext ec)
6200 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6202 return Block.FlowAnalysis (fc);
6205 public override Reachability MarkReachable (Reachability rc)
6207 base.MarkReachable (rc);
6208 return Block.MarkReachable (rc);
6211 protected override void CloneTo (CloneContext clonectx, Statement t)
6213 Unsafe target = (Unsafe) t;
6215 target.Block = clonectx.LookupBlock (Block);
6218 public override object Accept (StructuralVisitor visitor)
6220 return visitor.Visit (this);
6227 public class Fixed : Statement
6229 abstract class Emitter : ShimExpression
6231 protected LocalVariable vi;
6233 protected Emitter (Expression expr, LocalVariable li)
6239 public abstract void EmitExit (EmitContext ec);
6241 public override void FlowAnalysis (FlowAnalysisContext fc)
6243 expr.FlowAnalysis (fc);
6247 class ExpressionEmitter : Emitter {
6248 public ExpressionEmitter (Expression converted, LocalVariable li) :
6249 base (converted, li)
6253 protected override Expression DoResolve (ResolveContext rc)
6255 throw new NotImplementedException ();
6258 public override void Emit (EmitContext ec) {
6260 // Store pointer in pinned location
6266 public override void EmitExit (EmitContext ec)
6269 ec.Emit (OpCodes.Conv_U);
6274 class StringEmitter : Emitter
6276 LocalVariable pinned_string;
6278 public StringEmitter (Expression expr, LocalVariable li)
6283 protected override Expression DoResolve (ResolveContext rc)
6285 pinned_string = new LocalVariable (vi.Block, "$pinned",
6286 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6288 pinned_string.Type = rc.BuiltinTypes.String;
6290 eclass = ExprClass.Variable;
6291 type = rc.BuiltinTypes.Int;
6295 public override void Emit (EmitContext ec)
6297 pinned_string.CreateBuilder (ec);
6300 pinned_string.EmitAssign (ec);
6302 // TODO: Should use Binary::Add
6303 pinned_string.Emit (ec);
6304 ec.Emit (OpCodes.Conv_I);
6306 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6310 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6311 //pe.InstanceExpression = pinned_string;
6312 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6314 ec.Emit (OpCodes.Add);
6318 public override void EmitExit (EmitContext ec)
6321 pinned_string.EmitAssign (ec);
6325 public class VariableDeclaration : BlockVariable
6327 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6332 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6334 if (!Variable.Type.IsPointer && li == Variable) {
6335 bc.Report.Error (209, TypeExpression.Location,
6336 "The type of locals declared in a fixed statement must be a pointer type");
6340 var res = initializer.Resolve (bc);
6347 if (res.Type.IsArray) {
6348 TypeSpec array_type = TypeManager.GetElementType (res.Type);
6351 // Provided that array_type is unmanaged,
6353 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6357 // and T* is implicitly convertible to the
6358 // pointer type given in the fixed statement.
6360 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6362 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6363 if (converted == null)
6367 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6369 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6370 new Binary (Binary.Operator.Equality, res, new NullLiteral (loc)),
6371 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6372 new NullLiteral (loc),
6375 converted = converted.Resolve (bc);
6377 return new ExpressionEmitter (converted, li);
6383 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6384 return new StringEmitter (res, li).Resolve (bc);
6387 // Case 3: fixed buffer
6388 if (res is FixedBufferPtr) {
6389 return new ExpressionEmitter (res, li);
6392 bool already_fixed = true;
6395 // Case 4: & object.
6397 Unary u = res as Unary;
6399 if (u.Oper == Unary.Operator.AddressOf) {
6400 IVariableReference vr = u.Expr as IVariableReference;
6401 if (vr == null || !vr.IsFixed) {
6402 already_fixed = false;
6405 } else if (initializer is Cast) {
6406 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6410 if (already_fixed) {
6411 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6414 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6415 return new ExpressionEmitter (res, li);
6420 VariableDeclaration decl;
6421 Statement statement;
6424 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6433 public Statement Statement {
6439 public BlockVariable Variables {
6447 public override bool Resolve (BlockContext bc)
6449 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6450 if (!decl.Resolve (bc))
6454 return statement.Resolve (bc);
6457 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6459 decl.FlowAnalysis (fc);
6460 return statement.FlowAnalysis (fc);
6463 protected override void DoEmit (EmitContext ec)
6465 decl.Variable.CreateBuilder (ec);
6466 decl.Initializer.Emit (ec);
6467 if (decl.Declarators != null) {
6468 foreach (var d in decl.Declarators) {
6469 d.Variable.CreateBuilder (ec);
6470 d.Initializer.Emit (ec);
6474 statement.Emit (ec);
6480 // Clear the pinned variable
6482 ((Emitter) decl.Initializer).EmitExit (ec);
6483 if (decl.Declarators != null) {
6484 foreach (var d in decl.Declarators) {
6485 ((Emitter)d.Initializer).EmitExit (ec);
6490 public override Reachability MarkReachable (Reachability rc)
6492 base.MarkReachable (rc);
6494 decl.MarkReachable (rc);
6496 rc = statement.MarkReachable (rc);
6498 // TODO: What if there is local exit?
6499 has_ret = rc.IsUnreachable;
6503 protected override void CloneTo (CloneContext clonectx, Statement t)
6505 Fixed target = (Fixed) t;
6507 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6508 target.statement = statement.Clone (clonectx);
6511 public override object Accept (StructuralVisitor visitor)
6513 return visitor.Visit (this);
6517 public class Catch : Statement
6519 class CatchVariableStore : Statement
6521 readonly Catch ctch;
6523 public CatchVariableStore (Catch ctch)
6528 protected override void CloneTo (CloneContext clonectx, Statement target)
6532 protected override void DoEmit (EmitContext ec)
6534 // Emits catch variable debug information inside correct block
6535 ctch.EmitCatchVariableStore (ec);
6538 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6544 class FilterStatement : Statement
6546 readonly Catch ctch;
6548 public FilterStatement (Catch ctch)
6553 protected override void CloneTo (CloneContext clonectx, Statement target)
6557 protected override void DoEmit (EmitContext ec)
6559 if (ctch.li != null) {
6560 if (ctch.hoisted_temp != null)
6561 ctch.hoisted_temp.Emit (ec);
6565 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6566 ec.Emit (OpCodes.Box, ctch.type);
6569 var expr_start = ec.DefineLabel ();
6570 var end = ec.DefineLabel ();
6572 ec.Emit (OpCodes.Brtrue_S, expr_start);
6574 ec.Emit (OpCodes.Br, end);
6575 ec.MarkLabel (expr_start);
6577 ctch.Filter.Emit (ec);
6580 ec.Emit (OpCodes.Endfilter);
6581 ec.BeginFilterHandler ();
6582 ec.Emit (OpCodes.Pop);
6585 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6587 ctch.Filter.FlowAnalysis (fc);
6591 public override bool Resolve (BlockContext bc)
6593 ctch.Filter = ctch.Filter.Resolve (bc);
6595 if (ctch.Filter != null) {
6596 if (ctch.Filter.ContainsEmitWithAwait ()) {
6597 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6600 var c = ctch.Filter as Constant;
6601 if (c != null && !c.IsDefaultValue) {
6602 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6610 ExplicitBlock block;
6612 FullNamedExpression type_expr;
6613 CompilerAssign assign;
6615 LocalTemporary hoisted_temp;
6617 public Catch (ExplicitBlock block, Location loc)
6625 public ExplicitBlock Block {
6631 public TypeSpec CatchType {
6637 public Expression Filter {
6641 public bool IsGeneral {
6643 return type_expr == null;
6647 public FullNamedExpression TypeExpression {
6656 public LocalVariable Variable {
6667 protected override void DoEmit (EmitContext ec)
6669 if (Filter != null) {
6670 ec.BeginExceptionFilterBlock ();
6671 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6673 if (Block.HasAwait) {
6674 Block.EmitScopeInitialization (ec);
6683 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6685 ec.BeginCatchBlock (CatchType);
6688 ec.Emit (OpCodes.Pop);
6690 if (Block.HasAwait) {
6692 EmitCatchVariableStore (ec);
6698 void EmitCatchVariableStore (EmitContext ec)
6700 li.CreateBuilder (ec);
6703 // For hoisted catch variable we have to use a temporary local variable
6704 // for captured variable initialization during storey setup because variable
6705 // needs to be on the stack after storey instance for stfld operation
6707 if (li.HoistedVariant != null) {
6708 hoisted_temp = new LocalTemporary (li.Type);
6709 hoisted_temp.Store (ec);
6711 // switch to assignment from temporary variable and not from top of the stack
6712 assign.UpdateSource (hoisted_temp);
6716 public override bool Resolve (BlockContext bc)
6718 using (bc.Set (ResolveContext.Options.CatchScope)) {
6719 if (type_expr == null) {
6720 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6721 if (!block.HasAwait || Filter != null)
6722 block.AddScopeStatement (new CatchVariableStore (this));
6724 Expression source = new EmptyExpression (li.Type);
6725 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6726 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6729 type = type_expr.ResolveAsType (bc);
6734 CreateExceptionVariable (type);
6736 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6737 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6738 } else if (li != null) {
6740 li.PrepareAssignmentAnalysis (bc);
6742 // source variable is at the top of the stack
6743 Expression source = new EmptyExpression (li.Type);
6744 if (li.Type.IsGenericParameter)
6745 source = new UnboxCast (source, li.Type);
6747 if (!block.HasAwait || Filter != null)
6748 block.AddScopeStatement (new CatchVariableStore (this));
6751 // Uses Location.Null to hide from symbol file
6753 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6754 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6758 if (Filter != null) {
6759 Block.AddScopeStatement (new FilterStatement (this));
6762 Block.SetCatchBlock ();
6763 return Block.Resolve (bc);
6767 bool CreateExceptionVariable (TypeSpec type)
6769 if (!Block.HasAwait)
6772 // TODO: Scan the block for rethrow expression
6773 //if (!Block.HasRethrow)
6776 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6780 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6782 if (li != null && !li.IsCompilerGenerated) {
6783 fc.SetVariableAssigned (li.VariableInfo, true);
6786 return block.FlowAnalysis (fc);
6789 public override Reachability MarkReachable (Reachability rc)
6791 base.MarkReachable (rc);
6793 var c = Filter as Constant;
6794 if (c != null && c.IsDefaultValue)
6795 return Reachability.CreateUnreachable ();
6797 return block.MarkReachable (rc);
6800 protected override void CloneTo (CloneContext clonectx, Statement t)
6802 Catch target = (Catch) t;
6804 if (type_expr != null)
6805 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6808 target.Filter = Filter.Clone (clonectx);
6810 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6814 public class TryFinally : TryFinallyBlock
6817 List<DefiniteAssignmentBitSet> try_exit_dat;
6818 List<Label> redirected_jumps;
6819 Label? start_fin_label;
6821 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6827 public ExplicitBlock FinallyBlock {
6833 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6835 if (try_exit_dat == null)
6836 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6838 try_exit_dat.Add (vector);
6841 public override bool Resolve (BlockContext bc)
6843 bool ok = base.Resolve (bc);
6845 fini.SetFinallyBlock ();
6846 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6847 ok &= fini.Resolve (bc);
6853 protected override void EmitBeginException (EmitContext ec)
6855 if (fini.HasAwait && stmt is TryCatch)
6856 ec.BeginExceptionBlock ();
6858 base.EmitBeginException (ec);
6861 protected override void EmitTryBody (EmitContext ec)
6863 if (fini.HasAwait) {
6864 if (ec.TryFinallyUnwind == null)
6865 ec.TryFinallyUnwind = new List<TryFinally> ();
6867 ec.TryFinallyUnwind.Add (this);
6870 if (stmt is TryCatch)
6871 ec.EndExceptionBlock ();
6873 ec.TryFinallyUnwind.Remove (this);
6875 if (start_fin_label != null)
6876 ec.MarkLabel (start_fin_label.Value);
6884 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6889 return base.EmitBeginFinallyBlock (ec);
6892 public override void EmitFinallyBody (EmitContext ec)
6894 if (!fini.HasAwait) {
6900 // Emits catch block like
6902 // catch (object temp) {
6903 // this.exception_field = temp;
6906 var type = ec.BuiltinTypes.Object;
6907 ec.BeginCatchBlock (type);
6909 var temp = ec.GetTemporaryLocal (type);
6910 ec.Emit (OpCodes.Stloc, temp);
6912 var exception_field = ec.GetTemporaryField (type);
6914 ec.Emit (OpCodes.Ldloc, temp);
6915 exception_field.EmitAssignFromStack (ec);
6917 ec.EndExceptionBlock ();
6919 ec.FreeTemporaryLocal (temp, type);
6924 // Emits exception rethrow
6926 // if (this.exception_field != null)
6927 // throw this.exception_field;
6929 exception_field.Emit (ec);
6930 var skip_throw = ec.DefineLabel ();
6931 ec.Emit (OpCodes.Brfalse_S, skip_throw);
6932 exception_field.Emit (ec);
6933 ec.Emit (OpCodes.Throw);
6934 ec.MarkLabel (skip_throw);
6936 exception_field.IsAvailableForReuse = true;
6938 EmitUnwindFinallyTable (ec);
6941 bool IsParentBlock (Block block)
6943 for (Block b = fini; b != null; b = b.Parent) {
6951 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
6954 if (labelBlock != null) {
6955 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
6956 var fin = ec.TryFinallyUnwind [idx - 1];
6957 if (!fin.IsParentBlock (labelBlock))
6964 bool set_return_state = true;
6966 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
6967 var fin = ec.TryFinallyUnwind [idx];
6968 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
6971 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
6972 set_return_state = false;
6974 if (fin.start_fin_label == null) {
6975 fin.start_fin_label = ec.DefineLabel ();
6978 label = fin.start_fin_label.Value;
6984 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
6986 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
6989 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
6991 if (redirected_jumps == null) {
6992 redirected_jumps = new List<Label> ();
6994 // Add fallthrough label
6995 redirected_jumps.Add (ec.DefineLabel ());
6998 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7001 int index = redirected_jumps.IndexOf (label);
7003 redirected_jumps.Add (label);
7004 index = redirected_jumps.Count - 1;
7008 // Indicates we have captured exit jump
7010 if (setReturnState) {
7011 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7012 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7017 // Emits state table of jumps outside of try block and reload of return
7018 // value when try block returns value
7020 void EmitUnwindFinallyTable (EmitContext ec)
7022 if (redirected_jumps == null)
7025 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7026 initializer.HoistedReturnState.EmitLoad (ec);
7027 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7029 // Mark fallthrough label
7030 ec.MarkLabel (redirected_jumps [0]);
7033 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7035 var da = fc.BranchDefiniteAssignment ();
7037 var tf = fc.TryFinally;
7038 fc.TryFinally = this;
7040 var res_stmt = Statement.FlowAnalysis (fc);
7044 var try_da = fc.DefiniteAssignment;
7045 fc.DefiniteAssignment = da;
7047 var res_fin = fini.FlowAnalysis (fc);
7049 if (try_exit_dat != null) {
7051 // try block has global exit but we need to run definite assignment check
7052 // for parameter block out parameter after finally block because it's always
7053 // executed before exit
7055 foreach (var try_da_part in try_exit_dat)
7056 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7058 try_exit_dat = null;
7061 fc.DefiniteAssignment |= try_da;
7062 return res_stmt | res_fin;
7065 public override Reachability MarkReachable (Reachability rc)
7068 // Mark finally block first for any exit statement in try block
7069 // to know whether the code which follows finally is reachable
7071 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7074 protected override void CloneTo (CloneContext clonectx, Statement t)
7076 TryFinally target = (TryFinally) t;
7078 target.stmt = stmt.Clone (clonectx);
7080 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7083 public override object Accept (StructuralVisitor visitor)
7085 return visitor.Visit (this);
7089 public class TryCatch : ExceptionStatement
7092 List<Catch> clauses;
7093 readonly bool inside_try_finally;
7094 List<Catch> catch_sm;
7096 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7100 this.clauses = catch_clauses;
7101 this.inside_try_finally = inside_try_finally;
7104 public List<Catch> Clauses {
7110 public bool IsTryCatchFinally {
7112 return inside_try_finally;
7116 public override bool Resolve (BlockContext bc)
7120 using (bc.Set (ResolveContext.Options.TryScope)) {
7121 parent = bc.CurrentTryBlock;
7123 if (IsTryCatchFinally) {
7124 ok = Block.Resolve (bc);
7126 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7127 bc.CurrentTryBlock = this;
7128 ok = Block.Resolve (bc);
7129 bc.CurrentTryBlock = parent;
7134 for (int i = 0; i < clauses.Count; ++i) {
7137 ok &= c.Resolve (bc);
7139 if (c.Block.HasAwait) {
7140 if (catch_sm == null)
7141 catch_sm = new List<Catch> ();
7146 if (c.Filter != null)
7149 TypeSpec resolved_type = c.CatchType;
7150 if (resolved_type == null)
7153 for (int ii = 0; ii < clauses.Count; ++ii) {
7157 if (clauses[ii].Filter != null)
7160 if (clauses[ii].IsGeneral) {
7161 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7164 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7167 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7170 bc.Report.Warning (1058, 1, c.loc,
7171 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7179 var ct = clauses[ii].CatchType;
7183 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7184 bc.Report.Error (160, c.loc,
7185 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7186 ct.GetSignatureForError ());
7192 return base.Resolve (bc) && ok;
7195 protected sealed override void DoEmit (EmitContext ec)
7197 if (!inside_try_finally)
7198 EmitTryBodyPrepare (ec);
7202 LocalBuilder state_variable = null;
7203 foreach (Catch c in clauses) {
7206 if (catch_sm != null) {
7207 if (state_variable == null) {
7209 // Cannot reuse temp variable because non-catch path assumes the value is 0
7210 // which may not be true for reused local variable
7212 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7215 var index = catch_sm.IndexOf (c);
7219 ec.EmitInt (index + 1);
7220 ec.Emit (OpCodes.Stloc, state_variable);
7224 if (!inside_try_finally)
7225 ec.EndExceptionBlock ();
7227 if (state_variable != null) {
7228 ec.Emit (OpCodes.Ldloc, state_variable);
7230 var labels = new Label [catch_sm.Count + 1];
7231 for (int i = 0; i < labels.Length; ++i) {
7232 labels [i] = ec.DefineLabel ();
7235 var end = ec.DefineLabel ();
7236 ec.Emit (OpCodes.Switch, labels);
7238 // 0 value is default label
7239 ec.MarkLabel (labels [0]);
7240 ec.Emit (OpCodes.Br, end);
7242 var atv = ec.AsyncThrowVariable;
7244 for (int i = 0; i < catch_sm.Count; ++i) {
7245 if (c != null && c.Block.HasReachableClosingBrace)
7246 ec.Emit (OpCodes.Br, end);
7248 ec.MarkLabel (labels [i + 1]);
7250 ec.AsyncThrowVariable = c.Variable;
7253 ec.AsyncThrowVariable = atv;
7259 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7261 var start_fc = fc.BranchDefiniteAssignment ();
7262 var res = Block.FlowAnalysis (fc);
7264 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7266 foreach (var c in clauses) {
7267 fc.BranchDefiniteAssignment (start_fc);
7268 if (!c.FlowAnalysis (fc)) {
7270 try_fc = fc.DefiniteAssignment;
7272 try_fc &= fc.DefiniteAssignment;
7278 fc.DefiniteAssignment = try_fc ?? start_fc;
7283 public override Reachability MarkReachable (Reachability rc)
7285 if (rc.IsUnreachable)
7288 base.MarkReachable (rc);
7290 var tc_rc = Block.MarkReachable (rc);
7292 foreach (var c in clauses)
7293 tc_rc &= c.MarkReachable (rc);
7298 protected override void CloneTo (CloneContext clonectx, Statement t)
7300 TryCatch target = (TryCatch) t;
7302 target.Block = clonectx.LookupBlock (Block);
7303 if (clauses != null){
7304 target.clauses = new List<Catch> ();
7305 foreach (Catch c in clauses)
7306 target.clauses.Add ((Catch) c.Clone (clonectx));
7310 public override object Accept (StructuralVisitor visitor)
7312 return visitor.Visit (this);
7316 public class Using : TryFinallyBlock
7318 public class VariableDeclaration : BlockVariable
7320 Statement dispose_call;
7322 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7327 public VariableDeclaration (LocalVariable li, Location loc)
7334 public VariableDeclaration (Expression expr)
7337 loc = expr.Location;
7343 public bool IsNested { get; private set; }
7347 public void EmitDispose (EmitContext ec)
7349 dispose_call.Emit (ec);
7352 public override bool Resolve (BlockContext bc)
7357 return base.Resolve (bc, false);
7360 public Expression ResolveExpression (BlockContext bc)
7362 var e = Initializer.Resolve (bc);
7366 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7367 Initializer = ResolveInitializer (bc, Variable, e);
7371 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7373 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7374 initializer = initializer.Resolve (bc);
7375 if (initializer == null)
7378 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7379 Arguments args = new Arguments (1);
7380 args.Add (new Argument (initializer));
7381 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7382 if (initializer == null)
7385 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7386 dispose_call = CreateDisposeCall (bc, var);
7387 dispose_call.Resolve (bc);
7389 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7392 if (li == Variable) {
7393 CheckIDiposableConversion (bc, li, initializer);
7394 dispose_call = CreateDisposeCall (bc, li);
7395 dispose_call.Resolve (bc);
7398 return base.ResolveInitializer (bc, li, initializer);
7401 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7405 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7406 if (type.IsNullableType) {
7407 // it's handled in CreateDisposeCall
7411 if (type != InternalType.ErrorType) {
7412 bc.Report.SymbolRelatedToPreviousError (type);
7413 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7414 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7415 type.GetSignatureForError ());
7422 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7424 var target = bc.BuiltinTypes.IDisposable;
7425 var tp = type as TypeParameterSpec;
7427 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7429 return type.ImplementsInterface (target, false);
7432 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7434 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7436 var loc = lv.Location;
7438 var idt = bc.BuiltinTypes.IDisposable;
7439 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7441 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7442 dispose_mg.InstanceExpression = type.IsNullableType ?
7443 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7447 // Hide it from symbol file via null location
7449 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7451 // Add conditional call when disposing possible null variable
7452 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7453 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7458 public void ResolveDeclaratorInitializer (BlockContext bc)
7460 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7463 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7465 for (int i = declarators.Count - 1; i >= 0; --i) {
7466 var d = declarators [i];
7467 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7468 vd.Initializer = d.Initializer;
7470 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7471 vd.dispose_call.Resolve (bc);
7473 stmt = new Using (vd, stmt, d.Variable.Location);
7480 public override object Accept (StructuralVisitor visitor)
7482 return visitor.Visit (this);
7486 VariableDeclaration decl;
7488 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7494 public Using (Expression expr, Statement stmt, Location loc)
7497 this.decl = new VariableDeclaration (expr);
7502 public Expression Expr {
7504 return decl.Variable == null ? decl.Initializer : null;
7508 public BlockVariable Variables {
7516 public override void Emit (EmitContext ec)
7519 // Don't emit sequence point it will be set on variable declaration
7524 protected override void EmitTryBodyPrepare (EmitContext ec)
7527 base.EmitTryBodyPrepare (ec);
7530 protected override void EmitTryBody (EmitContext ec)
7535 public override void EmitFinallyBody (EmitContext ec)
7537 decl.EmitDispose (ec);
7540 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7542 decl.FlowAnalysis (fc);
7543 return stmt.FlowAnalysis (fc);
7546 public override Reachability MarkReachable (Reachability rc)
7548 decl.MarkReachable (rc);
7549 return base.MarkReachable (rc);
7552 public override bool Resolve (BlockContext ec)
7554 VariableReference vr;
7555 bool vr_locked = false;
7557 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7558 if (decl.Variable == null) {
7559 vr = decl.ResolveExpression (ec) as VariableReference;
7561 vr_locked = vr.IsLockedByStatement;
7562 vr.IsLockedByStatement = true;
7565 if (decl.IsNested) {
7566 decl.ResolveDeclaratorInitializer (ec);
7568 if (!decl.Resolve (ec))
7571 if (decl.Declarators != null) {
7572 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7580 var ok = base.Resolve (ec);
7583 vr.IsLockedByStatement = vr_locked;
7588 protected override void CloneTo (CloneContext clonectx, Statement t)
7590 Using target = (Using) t;
7592 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7593 target.stmt = stmt.Clone (clonectx);
7596 public override object Accept (StructuralVisitor visitor)
7598 return visitor.Visit (this);
7603 /// Implementation of the foreach C# statement
7605 public class Foreach : LoopStatement
7607 abstract class IteratorStatement : Statement
7609 protected readonly Foreach for_each;
7611 protected IteratorStatement (Foreach @foreach)
7613 this.for_each = @foreach;
7614 this.loc = @foreach.expr.Location;
7617 protected override void CloneTo (CloneContext clonectx, Statement target)
7619 throw new NotImplementedException ();
7622 public override void Emit (EmitContext ec)
7624 if (ec.EmitAccurateDebugInfo) {
7625 ec.Emit (OpCodes.Nop);
7631 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7633 throw new NotImplementedException ();
7637 sealed class ArrayForeach : IteratorStatement
7639 TemporaryVariableReference[] lengths;
7640 Expression [] length_exprs;
7641 StatementExpression[] counter;
7642 TemporaryVariableReference[] variables;
7644 TemporaryVariableReference copy;
7646 public ArrayForeach (Foreach @foreach, int rank)
7649 counter = new StatementExpression[rank];
7650 variables = new TemporaryVariableReference[rank];
7651 length_exprs = new Expression [rank];
7654 // Only use temporary length variables when dealing with
7655 // multi-dimensional arrays
7658 lengths = new TemporaryVariableReference [rank];
7661 public override bool Resolve (BlockContext ec)
7663 Block variables_block = for_each.variable.Block;
7664 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7667 int rank = length_exprs.Length;
7668 Arguments list = new Arguments (rank);
7669 for (int i = 0; i < rank; i++) {
7670 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7672 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7673 counter[i].Resolve (ec);
7676 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7678 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7679 lengths[i].Resolve (ec);
7681 Arguments args = new Arguments (1);
7682 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7683 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7686 list.Add (new Argument (v));
7689 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7694 if (for_each.type is VarExpr) {
7695 // Infer implicitly typed local variable from foreach array type
7696 var_type = access.Type;
7698 var_type = for_each.type.ResolveAsType (ec);
7700 if (var_type == null)
7703 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7708 for_each.variable.Type = var_type;
7710 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7711 if (variable_ref == null)
7714 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7716 return for_each.body.Resolve (ec);
7719 protected override void DoEmit (EmitContext ec)
7721 copy.EmitAssign (ec, for_each.expr);
7723 int rank = length_exprs.Length;
7724 Label[] test = new Label [rank];
7725 Label[] loop = new Label [rank];
7727 for (int i = 0; i < rank; i++) {
7728 test [i] = ec.DefineLabel ();
7729 loop [i] = ec.DefineLabel ();
7731 if (lengths != null)
7732 lengths [i].EmitAssign (ec, length_exprs [i]);
7735 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7736 for (int i = 0; i < rank; i++) {
7737 variables [i].EmitAssign (ec, zero);
7739 ec.Emit (OpCodes.Br, test [i]);
7740 ec.MarkLabel (loop [i]);
7743 for_each.body.Emit (ec);
7745 ec.MarkLabel (ec.LoopBegin);
7746 ec.Mark (for_each.expr.Location);
7748 for (int i = rank - 1; i >= 0; i--){
7749 counter [i].Emit (ec);
7751 ec.MarkLabel (test [i]);
7752 variables [i].Emit (ec);
7754 if (lengths != null)
7755 lengths [i].Emit (ec);
7757 length_exprs [i].Emit (ec);
7759 ec.Emit (OpCodes.Blt, loop [i]);
7762 ec.MarkLabel (ec.LoopEnd);
7766 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7768 class RuntimeDispose : Using.VariableDeclaration
7770 public RuntimeDispose (LocalVariable lv, Location loc)
7776 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7778 // Defered to runtime check
7781 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7783 var idt = bc.BuiltinTypes.IDisposable;
7786 // Fabricates code like
7788 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7791 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7793 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7794 dispose_variable.CreateReferenceExpression (bc, loc),
7795 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7796 loc), new NullLiteral (loc));
7798 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7800 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7801 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7803 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7804 return new If (idisaposable_test, dispose, loc);
7808 LocalVariable variable;
7810 Statement statement;
7811 ExpressionStatement init;
7812 TemporaryVariableReference enumerator_variable;
7813 bool ambiguous_getenumerator_name;
7815 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7818 this.variable = var;
7822 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7824 rc.Report.SymbolRelatedToPreviousError (enumerator);
7825 rc.Report.Error (202, loc,
7826 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7827 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7830 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7833 // Option 1: Try to match by name GetEnumerator first
7835 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7836 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7838 var mg = mexpr as MethodGroupExpr;
7840 mg.InstanceExpression = expr;
7841 Arguments args = new Arguments (0);
7842 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7844 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7845 if (ambiguous_getenumerator_name)
7848 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7854 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7857 PredefinedMember<MethodSpec> iface_candidate = null;
7858 var ptypes = rc.Module.PredefinedTypes;
7859 var gen_ienumerable = ptypes.IEnumerableGeneric;
7860 if (!gen_ienumerable.Define ())
7861 gen_ienumerable = null;
7863 var ifaces = t.Interfaces;
7864 if (ifaces != null) {
7865 foreach (var iface in ifaces) {
7866 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7867 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7868 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7869 rc.Report.Error (1640, loc,
7870 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7871 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7876 // TODO: Cache this somehow
7877 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7878 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7883 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7884 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7889 if (iface_candidate == null) {
7890 if (expr.Type != InternalType.ErrorType) {
7891 rc.Report.Error (1579, loc,
7892 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7893 expr.Type.GetSignatureForError (), "GetEnumerator");
7899 var method = iface_candidate.Resolve (loc);
7903 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7904 mg.InstanceExpression = expr;
7908 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7910 var ms = MemberCache.FindMember (enumerator.ReturnType,
7911 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7912 BindingRestriction.InstanceOnly) as MethodSpec;
7914 if (ms == null || !ms.IsPublic) {
7915 Error_WrongEnumerator (rc, enumerator);
7919 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7922 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7924 var ps = MemberCache.FindMember (enumerator.ReturnType,
7925 MemberFilter.Property ("Current", null),
7926 BindingRestriction.InstanceOnly) as PropertySpec;
7928 if (ps == null || !ps.IsPublic) {
7929 Error_WrongEnumerator (rc, enumerator);
7936 public override bool Resolve (BlockContext ec)
7938 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7941 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7942 } else if (expr.Type.IsNullableType) {
7943 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7946 var get_enumerator_mg = ResolveGetEnumerator (ec);
7947 if (get_enumerator_mg == null) {
7951 var get_enumerator = get_enumerator_mg.BestCandidate;
7952 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7953 enumerator_variable.Resolve (ec);
7955 // Prepare bool MoveNext ()
7956 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7957 if (move_next_mg == null) {
7961 move_next_mg.InstanceExpression = enumerator_variable;
7963 // Prepare ~T~ Current { get; }
7964 var current_prop = ResolveCurrent (ec, get_enumerator);
7965 if (current_prop == null) {
7969 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7970 if (current_pe == null)
7973 VarExpr ve = for_each.type as VarExpr;
7977 // Source type is dynamic, set element type to dynamic too
7978 variable.Type = ec.BuiltinTypes.Dynamic;
7980 // Infer implicitly typed local variable from foreach enumerable type
7981 variable.Type = current_pe.Type;
7985 // Explicit cast of dynamic collection elements has to be done at runtime
7986 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7989 variable.Type = for_each.type.ResolveAsType (ec);
7991 if (variable.Type == null)
7994 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7995 if (current_pe == null)
7999 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8000 if (variable_ref == null)
8003 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8005 var init = new Invocation.Predefined (get_enumerator_mg, null);
8007 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8008 for_each.body, Location.Null);
8010 var enum_type = enumerator_variable.Type;
8013 // Add Dispose method call when enumerator can be IDisposable
8015 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8016 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8018 // Runtime Dispose check
8020 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8021 vd.Initializer = init;
8022 statement = new Using (vd, statement, Location.Null);
8025 // No Dispose call needed
8027 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8028 this.init.Resolve (ec);
8032 // Static Dispose check
8034 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8035 vd.Initializer = init;
8036 statement = new Using (vd, statement, Location.Null);
8039 return statement.Resolve (ec);
8042 protected override void DoEmit (EmitContext ec)
8044 enumerator_variable.LocalInfo.CreateBuilder (ec);
8047 init.EmitStatement (ec);
8049 statement.Emit (ec);
8052 #region IErrorHandler Members
8054 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8056 ec.Report.SymbolRelatedToPreviousError (best);
8057 ec.Report.Warning (278, 2, expr.Location,
8058 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8059 expr.Type.GetSignatureForError (), "enumerable",
8060 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8062 ambiguous_getenumerator_name = true;
8066 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8071 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8076 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8085 LocalVariable variable;
8089 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8093 this.variable = var;
8099 public Expression Expr {
8100 get { return expr; }
8103 public Expression TypeExpression {
8104 get { return type; }
8107 public LocalVariable Variable {
8108 get { return variable; }
8111 public override Reachability MarkReachable (Reachability rc)
8113 base.MarkReachable (rc);
8115 body.MarkReachable (rc);
8120 public override bool Resolve (BlockContext ec)
8122 expr = expr.Resolve (ec);
8127 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8131 body.AddStatement (Statement);
8133 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8134 Statement = new ArrayForeach (this, 1);
8135 } else if (expr.Type is ArrayContainer) {
8136 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8138 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8139 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8140 expr.ExprClassName);
8144 Statement = new CollectionForeach (this, variable, expr);
8147 return base.Resolve (ec);
8150 protected override void DoEmit (EmitContext ec)
8152 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8153 ec.LoopBegin = ec.DefineLabel ();
8154 ec.LoopEnd = ec.DefineLabel ();
8156 if (!(Statement is Block))
8157 ec.BeginCompilerScope ();
8159 variable.CreateBuilder (ec);
8161 Statement.Emit (ec);
8163 if (!(Statement is Block))
8166 ec.LoopBegin = old_begin;
8167 ec.LoopEnd = old_end;
8170 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8172 expr.FlowAnalysis (fc);
8174 var da = fc.BranchDefiniteAssignment ();
8175 body.FlowAnalysis (fc);
8176 fc.DefiniteAssignment = da;
8180 protected override void CloneTo (CloneContext clonectx, Statement t)
8182 Foreach target = (Foreach) t;
8184 target.type = type.Clone (clonectx);
8185 target.expr = expr.Clone (clonectx);
8186 target.body = (Block) body.Clone (clonectx);
8187 target.Statement = Statement.Clone (clonectx);
8190 public override object Accept (StructuralVisitor visitor)
8192 return visitor.Visit (this);