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 && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
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,
2349 SymbolFileHidden = 1 << 9,
2351 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2355 readonly string name;
2356 readonly Location loc;
2357 readonly Block block;
2359 Constant const_value;
2361 public VariableInfo VariableInfo;
2362 HoistedVariable hoisted_variant;
2364 LocalBuilder builder;
2366 public LocalVariable (Block block, string name, Location loc)
2373 public LocalVariable (Block block, string name, Flags flags, Location loc)
2374 : this (block, name, loc)
2380 // Used by variable declarators
2382 public LocalVariable (LocalVariable li, string name, Location loc)
2383 : this (li.block, name, li.flags, loc)
2389 public bool AddressTaken {
2391 return (flags & Flags.AddressTaken) != 0;
2395 public Block Block {
2401 public Constant ConstantValue {
2406 const_value = value;
2411 // Hoisted local variable variant
2413 public HoistedVariable HoistedVariant {
2415 return hoisted_variant;
2418 hoisted_variant = value;
2422 public bool IsDeclared {
2424 return type != null;
2428 public bool IsCompilerGenerated {
2430 return (flags & Flags.CompilerGenerated) != 0;
2434 public bool IsConstant {
2436 return (flags & Flags.Constant) != 0;
2440 public bool IsLocked {
2442 return (flags & Flags.IsLocked) != 0;
2445 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2449 public bool IsThis {
2451 return (flags & Flags.IsThis) != 0;
2455 public bool IsFixed {
2457 return (flags & Flags.FixedVariable) != 0;
2461 bool INamedBlockVariable.IsParameter {
2467 public bool IsReadonly {
2469 return (flags & Flags.ReadonlyMask) != 0;
2473 public Location Location {
2479 public string Name {
2485 public TypeSpec Type {
2496 public void CreateBuilder (EmitContext ec)
2498 if ((flags & Flags.Used) == 0) {
2499 if (VariableInfo == null) {
2500 // Missing flow analysis or wrong variable flags
2501 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2504 if (VariableInfo.IsEverAssigned)
2505 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2507 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2510 if (HoistedVariant != null)
2513 if (builder != null) {
2514 if ((flags & Flags.CompilerGenerated) != 0)
2517 // To avoid Used warning duplicates
2518 throw new InternalErrorException ("Already created variable `{0}'", name);
2522 // All fixed variabled are pinned, a slot has to be alocated
2524 builder = ec.DeclareLocal (Type, IsFixed);
2525 if ((flags & Flags.SymbolFileHidden) == 0)
2526 ec.DefineLocalVariable (name, builder);
2529 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2531 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2532 if (!writeToSymbolFile)
2533 li.flags |= Flags.SymbolFileHidden;
2539 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2541 if (IsConstant && const_value != null)
2542 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2544 return new LocalVariableReference (this, loc);
2547 public void Emit (EmitContext ec)
2549 // TODO: Need something better for temporary variables
2550 if ((flags & Flags.CompilerGenerated) != 0)
2553 ec.Emit (OpCodes.Ldloc, builder);
2556 public void EmitAssign (EmitContext ec)
2558 // TODO: Need something better for temporary variables
2559 if ((flags & Flags.CompilerGenerated) != 0)
2562 ec.Emit (OpCodes.Stloc, builder);
2565 public void EmitAddressOf (EmitContext ec)
2567 // TODO: Need something better for temporary variables
2568 if ((flags & Flags.CompilerGenerated) != 0)
2571 ec.Emit (OpCodes.Ldloca, builder);
2574 public static string GetCompilerGeneratedName (Block block)
2576 // HACK: Debugger depends on the name semantics
2577 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2580 public string GetReadOnlyContext ()
2582 switch (flags & Flags.ReadonlyMask) {
2583 case Flags.FixedVariable:
2584 return "fixed variable";
2585 case Flags.ForeachVariable:
2586 return "foreach iteration variable";
2587 case Flags.UsingVariable:
2588 return "using variable";
2591 throw new InternalErrorException ("Variable is not readonly");
2594 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2596 if (VariableInfo == null)
2597 throw new Exception ();
2599 if (IsAssigned (fc))
2602 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2605 public bool IsAssigned (FlowAnalysisContext fc)
2607 return fc.IsDefinitelyAssigned (VariableInfo);
2610 public void PrepareAssignmentAnalysis (BlockContext bc)
2613 // No need to run assignment analysis for these guys
2615 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2618 VariableInfo = VariableInfo.Create (bc, this);
2622 // Mark the variables as referenced in the user code
2624 public void SetIsUsed ()
2626 flags |= Flags.Used;
2629 public void SetHasAddressTaken ()
2631 flags |= (Flags.AddressTaken | Flags.Used);
2634 public override string ToString ()
2636 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2641 /// Block represents a C# block.
2645 /// This class is used in a number of places: either to represent
2646 /// explicit blocks that the programmer places or implicit blocks.
2648 /// Implicit blocks are used as labels or to introduce variable
2651 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2652 /// they contain extra information that is not necessary on normal blocks.
2654 public class Block : Statement {
2661 HasCapturedVariable = 64,
2662 HasCapturedThis = 1 << 7,
2663 IsExpressionTree = 1 << 8,
2664 CompilerGenerated = 1 << 9,
2665 HasAsyncModifier = 1 << 10,
2667 YieldBlock = 1 << 12,
2668 AwaitBlock = 1 << 13,
2669 FinallyBlock = 1 << 14,
2670 CatchBlock = 1 << 15,
2672 NoFlowAnalysis = 1 << 21,
2673 InitializationEmitted = 1 << 22
2676 public Block Parent;
2677 public Location StartLocation;
2678 public Location EndLocation;
2680 public ExplicitBlock Explicit;
2681 public ParametersBlock ParametersBlock;
2683 protected Flags flags;
2686 // The statements in this block
2688 protected List<Statement> statements;
2690 protected List<Statement> scope_initializers;
2692 int? resolving_init_idx;
2698 public int ID = id++;
2700 static int clone_id_counter;
2704 // int assignable_slots;
2706 public Block (Block parent, Location start, Location end)
2707 : this (parent, 0, start, end)
2711 public Block (Block parent, Flags flags, Location start, Location end)
2713 if (parent != null) {
2714 // the appropriate constructors will fixup these fields
2715 ParametersBlock = parent.ParametersBlock;
2716 Explicit = parent.Explicit;
2719 this.Parent = parent;
2721 this.StartLocation = start;
2722 this.EndLocation = end;
2724 statements = new List<Statement> (4);
2726 this.original = this;
2731 public Block Original {
2740 public bool IsCompilerGenerated {
2741 get { return (flags & Flags.CompilerGenerated) != 0; }
2742 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2746 public bool IsCatchBlock {
2748 return (flags & Flags.CatchBlock) != 0;
2752 public bool IsFinallyBlock {
2754 return (flags & Flags.FinallyBlock) != 0;
2758 public bool Unchecked {
2759 get { return (flags & Flags.Unchecked) != 0; }
2760 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2763 public bool Unsafe {
2764 get { return (flags & Flags.Unsafe) != 0; }
2765 set { flags |= Flags.Unsafe; }
2768 public List<Statement> Statements {
2769 get { return statements; }
2774 public void SetEndLocation (Location loc)
2779 public void AddLabel (LabeledStatement target)
2781 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2784 public void AddLocalName (LocalVariable li)
2786 AddLocalName (li.Name, li);
2789 public void AddLocalName (string name, INamedBlockVariable li)
2791 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2794 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2796 if (reason == null) {
2797 Error_AlreadyDeclared (name, variable);
2801 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2802 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2803 "to `{0}', which is already used in a `{1}' scope to denote something else",
2807 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2809 var pi = variable as ParametersBlock.ParameterInfo;
2811 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2813 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2814 "A local variable named `{0}' is already defined in this scope", name);
2818 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2820 ParametersBlock.TopBlock.Report.Error (412, loc,
2821 "The type parameter name `{0}' is the same as local variable or parameter name",
2826 // It should be used by expressions which require to
2827 // register a statement during resolve process.
2829 public void AddScopeStatement (Statement s)
2831 if (scope_initializers == null)
2832 scope_initializers = new List<Statement> ();
2835 // Simple recursive helper, when resolve scope initializer another
2836 // new scope initializer can be added, this ensures it's initialized
2837 // before existing one. For now this can happen with expression trees
2838 // in base ctor initializer only
2840 if (resolving_init_idx.HasValue) {
2841 scope_initializers.Insert (resolving_init_idx.Value, s);
2842 ++resolving_init_idx;
2844 scope_initializers.Add (s);
2848 public void InsertStatement (int index, Statement s)
2850 statements.Insert (index, s);
2853 public void AddStatement (Statement s)
2858 public LabeledStatement LookupLabel (string name)
2860 return ParametersBlock.GetLabel (name, this);
2863 public override Reachability MarkReachable (Reachability rc)
2865 if (rc.IsUnreachable)
2868 MarkReachableScope (rc);
2870 foreach (var s in statements) {
2871 rc = s.MarkReachable (rc);
2872 if (rc.IsUnreachable) {
2873 if ((flags & Flags.ReachableEnd) != 0)
2874 return new Reachability ();
2880 flags |= Flags.ReachableEnd;
2885 public void MarkReachableScope (Reachability rc)
2887 base.MarkReachable (rc);
2889 if (scope_initializers != null) {
2890 foreach (var si in scope_initializers)
2891 si.MarkReachable (rc);
2895 public override bool Resolve (BlockContext bc)
2897 if ((flags & Flags.Resolved) != 0)
2900 Block prev_block = bc.CurrentBlock;
2901 bc.CurrentBlock = this;
2904 // Compiler generated scope statements
2906 if (scope_initializers != null) {
2907 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2908 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2911 resolving_init_idx = null;
2915 int statement_count = statements.Count;
2916 for (int ix = 0; ix < statement_count; ix++){
2917 Statement s = statements [ix];
2919 if (!s.Resolve (bc)) {
2921 statements [ix] = new EmptyStatement (s.loc);
2926 bc.CurrentBlock = prev_block;
2928 flags |= Flags.Resolved;
2932 protected override void DoEmit (EmitContext ec)
2934 for (int ix = 0; ix < statements.Count; ix++){
2935 statements [ix].Emit (ec);
2939 public override void Emit (EmitContext ec)
2941 if (scope_initializers != null)
2942 EmitScopeInitializers (ec);
2947 protected void EmitScopeInitializers (EmitContext ec)
2949 foreach (Statement s in scope_initializers)
2953 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2955 if (scope_initializers != null) {
2956 foreach (var si in scope_initializers)
2957 si.FlowAnalysis (fc);
2960 return DoFlowAnalysis (fc, 0);
2963 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2965 bool end_unreachable = !reachable;
2966 bool goto_flow_analysis = startIndex != 0;
2967 for (; startIndex < statements.Count; ++startIndex) {
2968 var s = statements[startIndex];
2970 end_unreachable = s.FlowAnalysis (fc);
2971 if (s.IsUnreachable) {
2972 statements [startIndex] = RewriteUnreachableStatement (s);
2977 // Statement end reachability is needed mostly due to goto support. Consider
2986 // X label is reachable only via goto not as another statement after if. We need
2987 // this for flow-analysis only to carry variable info correctly.
2989 if (end_unreachable) {
2990 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2992 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2993 s = statements[startIndex];
2994 if (s is SwitchLabel) {
2995 if (!after_goto_case)
2996 s.FlowAnalysis (fc);
3001 if (s.IsUnreachable) {
3002 s.FlowAnalysis (fc);
3003 statements [startIndex] = RewriteUnreachableStatement (s);
3008 // Idea is to stop after goto case because goto case will always have at least same
3009 // variable assigned as switch case label. This saves a lot for complex goto case tests
3011 if (after_goto_case)
3017 var lb = s as LabeledStatement;
3018 if (lb != null && fc.AddReachedLabel (lb))
3023 // The condition should be true unless there is forward jumping goto
3025 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3028 return !Explicit.HasReachableClosingBrace;
3031 static Statement RewriteUnreachableStatement (Statement s)
3033 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3034 // analysis. Even csc report unreachable warning for it but it's actually used hence
3035 // we try to emulate this behaviour
3043 if (s is BlockVariable || s is EmptyStatement)
3046 return new EmptyStatement (s.loc);
3049 public void ScanGotoJump (Statement label)
3052 for (i = 0; i < statements.Count; ++i) {
3053 if (statements[i] == label)
3057 var rc = new Reachability ();
3058 for (++i; i < statements.Count; ++i) {
3059 var s = statements[i];
3060 rc = s.MarkReachable (rc);
3061 if (rc.IsUnreachable)
3065 flags |= Flags.ReachableEnd;
3068 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3071 for (i = 0; i < statements.Count; ++i) {
3072 if (statements[i] == label)
3076 DoFlowAnalysis (fc, ++i);
3080 public override string ToString ()
3082 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3086 protected override void CloneTo (CloneContext clonectx, Statement t)
3088 Block target = (Block) t;
3090 target.clone_id = ++clone_id_counter;
3093 clonectx.AddBlockMap (this, target);
3094 if (original != this)
3095 clonectx.AddBlockMap (original, target);
3097 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3098 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3101 target.Parent = clonectx.RemapBlockCopy (Parent);
3103 target.statements = new List<Statement> (statements.Count);
3104 foreach (Statement s in statements)
3105 target.statements.Add (s.Clone (clonectx));
3108 public override object Accept (StructuralVisitor visitor)
3110 return visitor.Visit (this);
3114 public class ExplicitBlock : Block
3116 protected AnonymousMethodStorey am_storey;
3118 public ExplicitBlock (Block parent, Location start, Location end)
3119 : this (parent, (Flags) 0, start, end)
3123 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3124 : base (parent, flags, start, end)
3126 this.Explicit = this;
3131 public AnonymousMethodStorey AnonymousMethodStorey {
3137 public bool HasAwait {
3139 return (flags & Flags.AwaitBlock) != 0;
3143 public bool HasCapturedThis {
3145 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3148 return (flags & Flags.HasCapturedThis) != 0;
3153 // Used to indicate that the block has reference to parent
3154 // block and cannot be made static when defining anonymous method
3156 public bool HasCapturedVariable {
3158 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3161 return (flags & Flags.HasCapturedVariable) != 0;
3165 public bool HasReachableClosingBrace {
3167 return (flags & Flags.ReachableEnd) != 0;
3170 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3174 public bool HasYield {
3176 return (flags & Flags.YieldBlock) != 0;
3183 // Creates anonymous method storey in current block
3185 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3188 // Return same story for iterator and async blocks unless we are
3189 // in nested anonymous method
3191 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3192 return ec.CurrentAnonymousMethod.Storey;
3194 if (am_storey == null) {
3195 MemberBase mc = ec.MemberContext as MemberBase;
3198 // Creates anonymous method storey for this block
3200 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3206 public void EmitScopeInitialization (EmitContext ec)
3208 if ((flags & Flags.InitializationEmitted) != 0)
3211 if (am_storey != null) {
3212 DefineStoreyContainer (ec, am_storey);
3213 am_storey.EmitStoreyInstantiation (ec, this);
3216 if (scope_initializers != null)
3217 EmitScopeInitializers (ec);
3219 flags |= Flags.InitializationEmitted;
3222 public override void Emit (EmitContext ec)
3227 EmitScopeInitialization (ec);
3229 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3230 ec.Emit (OpCodes.Nop);
3238 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3239 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3240 ec.Emit (OpCodes.Nop);
3244 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3246 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3247 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3248 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3252 // Creates anonymous method storey
3254 storey.CreateContainer ();
3255 storey.DefineContainer ();
3257 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3260 // Only first storey in path will hold this reference. All children blocks will
3261 // reference it indirectly using $ref field
3263 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3264 if (b.Parent != null) {
3265 var s = b.Parent.Explicit.AnonymousMethodStorey;
3267 storey.HoistedThis = s.HoistedThis;
3272 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3273 if (storey.HoistedThis == null)
3274 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3276 if (storey.HoistedThis != null)
3282 // We are the first storey on path and 'this' has to be hoisted
3284 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3285 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3287 // ThisReferencesFromChildrenBlock holds all reference even if they
3288 // are not on this path. It saves some memory otherwise it'd have to
3289 // be in every explicit block. We run this check to see if the reference
3290 // is valid for this storey
3292 Block block_on_path = ref_block;
3293 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3295 if (block_on_path == null)
3298 if (storey.HoistedThis == null) {
3299 storey.AddCapturedThisField (ec, null);
3302 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3304 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3306 if (b_storey != null) {
3308 // Don't add storey cross reference for `this' when the storey ends up not
3309 // beeing attached to any parent
3311 if (b.ParametersBlock.StateMachine == null) {
3312 AnonymousMethodStorey s = null;
3313 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3314 s = ab.Explicit.AnonymousMethodStorey;
3319 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3321 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3322 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3329 // Stop propagation inside same top block
3331 if (b.ParametersBlock == ParametersBlock.Original) {
3332 b_storey.AddParentStoreyReference (ec, storey);
3333 // b_storey.HoistedThis = storey.HoistedThis;
3337 b = pb = b.ParametersBlock;
3339 pb = b as ParametersBlock;
3342 if (pb != null && pb.StateMachine != null) {
3343 if (pb.StateMachine == storey)
3347 // If we are state machine with no parent. We can hook into parent without additional
3348 // reference and capture this directly
3350 ExplicitBlock parent_storey_block = pb;
3351 while (parent_storey_block.Parent != null) {
3352 parent_storey_block = parent_storey_block.Parent.Explicit;
3353 if (parent_storey_block.AnonymousMethodStorey != null) {
3358 if (parent_storey_block.AnonymousMethodStorey == null) {
3359 if (pb.StateMachine.HoistedThis == null) {
3360 pb.StateMachine.AddCapturedThisField (ec, null);
3361 b.HasCapturedThis = true;
3367 var parent_this_block = pb;
3368 while (parent_this_block.Parent != null) {
3369 parent_this_block = parent_this_block.Parent.ParametersBlock;
3370 if (parent_this_block.StateMachine != null) {
3376 // Add reference to closest storey which holds captured this
3378 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3382 // Add parent storey reference only when this is not captured directly
3384 if (b_storey != null) {
3385 b_storey.AddParentStoreyReference (ec, storey);
3386 b_storey.HoistedThis = storey.HoistedThis;
3393 var ref_blocks = storey.ReferencesFromChildrenBlock;
3394 if (ref_blocks != null) {
3395 foreach (ExplicitBlock ref_block in ref_blocks) {
3396 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3397 if (b.AnonymousMethodStorey != null) {
3398 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3401 // Stop propagation inside same top block
3403 if (b.ParametersBlock == ParametersBlock.Original)
3406 b = b.ParametersBlock;
3409 var pb = b as ParametersBlock;
3410 if (pb != null && pb.StateMachine != null) {
3411 if (pb.StateMachine == storey)
3414 pb.StateMachine.AddParentStoreyReference (ec, storey);
3417 b.HasCapturedVariable = true;
3423 storey.PrepareEmit ();
3424 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3427 public void RegisterAsyncAwait ()
3430 while ((block.flags & Flags.AwaitBlock) == 0) {
3431 block.flags |= Flags.AwaitBlock;
3433 if (block is ParametersBlock)
3436 block = block.Parent.Explicit;
3440 public void RegisterIteratorYield ()
3442 ParametersBlock.TopBlock.IsIterator = true;
3445 while ((block.flags & Flags.YieldBlock) == 0) {
3446 block.flags |= Flags.YieldBlock;
3448 if (block.Parent == null)
3451 block = block.Parent.Explicit;
3455 public void SetCatchBlock ()
3457 flags |= Flags.CatchBlock;
3460 public void SetFinallyBlock ()
3462 flags |= Flags.FinallyBlock;
3465 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3467 tryBlock.statements = statements;
3468 statements = new List<Statement> (1);
3469 statements.Add (tf);
3474 // ParametersBlock was introduced to support anonymous methods
3475 // and lambda expressions
3477 public class ParametersBlock : ExplicitBlock
3479 public class ParameterInfo : INamedBlockVariable
3481 readonly ParametersBlock block;
3483 public VariableInfo VariableInfo;
3486 public ParameterInfo (ParametersBlock block, int index)
3494 public ParametersBlock Block {
3500 Block INamedBlockVariable.Block {
3506 public bool IsDeclared {
3512 public bool IsParameter {
3518 public bool IsLocked {
3527 public Location Location {
3529 return Parameter.Location;
3533 public Parameter Parameter {
3535 return block.Parameters [index];
3539 public TypeSpec ParameterType {
3541 return Parameter.Type;
3547 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3549 return new ParameterReference (this, loc);
3554 // Block is converted into an expression
3556 sealed class BlockScopeExpression : Expression
3559 readonly ParametersBlock block;
3561 public BlockScopeExpression (Expression child, ParametersBlock block)
3567 public override bool ContainsEmitWithAwait ()
3569 return child.ContainsEmitWithAwait ();
3572 public override Expression CreateExpressionTree (ResolveContext ec)
3574 throw new NotSupportedException ();
3577 protected override Expression DoResolve (ResolveContext ec)
3582 child = child.Resolve (ec);
3586 eclass = child.eclass;
3591 public override void Emit (EmitContext ec)
3593 block.EmitScopeInitializers (ec);
3598 protected ParametersCompiled parameters;
3599 protected ParameterInfo[] parameter_info;
3600 protected bool resolved;
3601 protected ToplevelBlock top_block;
3602 protected StateMachine state_machine;
3603 protected Dictionary<string, object> labels;
3605 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3606 : base (parent, 0, start, start)
3608 if (parameters == null)
3609 throw new ArgumentNullException ("parameters");
3611 this.parameters = parameters;
3612 ParametersBlock = this;
3614 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3616 this.top_block = parent.ParametersBlock.top_block;
3617 ProcessParameters ();
3620 protected ParametersBlock (ParametersCompiled parameters, Location start)
3621 : base (null, 0, start, start)
3623 if (parameters == null)
3624 throw new ArgumentNullException ("parameters");
3626 this.parameters = parameters;
3627 ParametersBlock = this;
3631 // It's supposed to be used by method body implementation of anonymous methods
3633 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3634 : base (null, 0, source.StartLocation, source.EndLocation)
3636 this.parameters = parameters;
3637 this.statements = source.statements;
3638 this.scope_initializers = source.scope_initializers;
3640 this.resolved = true;
3641 this.reachable = source.reachable;
3642 this.am_storey = source.am_storey;
3643 this.state_machine = source.state_machine;
3644 this.flags = source.flags & Flags.ReachableEnd;
3646 ParametersBlock = this;
3649 // Overwrite original for comparison purposes when linking cross references
3650 // between anonymous methods
3652 Original = source.Original;
3657 public bool IsAsync {
3659 return (flags & Flags.HasAsyncModifier) != 0;
3662 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3667 // Block has been converted to expression tree
3669 public bool IsExpressionTree {
3671 return (flags & Flags.IsExpressionTree) != 0;
3676 // The parameters for the block.
3678 public ParametersCompiled Parameters {
3684 public StateMachine StateMachine {
3686 return state_machine;
3690 public ToplevelBlock TopBlock {
3699 public bool Resolved {
3701 return (flags & Flags.Resolved) != 0;
3705 public int TemporaryLocalsCount { get; set; }
3710 // Checks whether all `out' parameters have been assigned.
3712 public void CheckControlExit (FlowAnalysisContext fc)
3714 CheckControlExit (fc, fc.DefiniteAssignment);
3717 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3719 if (parameter_info == null)
3722 foreach (var p in parameter_info) {
3723 if (p.VariableInfo == null)
3726 if (p.VariableInfo.IsAssigned (dat))
3729 fc.Report.Error (177, p.Location,
3730 "The out parameter `{0}' must be assigned to before control leaves the current method",
3735 protected override void CloneTo (CloneContext clonectx, Statement t)
3737 base.CloneTo (clonectx, t);
3739 var target = (ParametersBlock) t;
3742 // Clone label statements as well as they contain block reference
3746 if (pb.labels != null) {
3747 target.labels = new Dictionary<string, object> ();
3749 foreach (var entry in pb.labels) {
3750 var list = entry.Value as List<LabeledStatement>;
3753 var list_clone = new List<LabeledStatement> ();
3754 foreach (var lentry in list) {
3755 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3758 target.labels.Add (entry.Key, list_clone);
3760 var labeled = (LabeledStatement) entry.Value;
3761 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3768 if (pb.Parent == null)
3771 pb = pb.Parent.ParametersBlock;
3775 public override Expression CreateExpressionTree (ResolveContext ec)
3777 if (statements.Count == 1) {
3778 Expression expr = statements[0].CreateExpressionTree (ec);
3779 if (scope_initializers != null)
3780 expr = new BlockScopeExpression (expr, this);
3785 return base.CreateExpressionTree (ec);
3788 public override void Emit (EmitContext ec)
3790 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3791 DefineStoreyContainer (ec, state_machine);
3792 state_machine.EmitStoreyInstantiation (ec, this);
3798 public void EmitEmbedded (EmitContext ec)
3800 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3801 DefineStoreyContainer (ec, state_machine);
3802 state_machine.EmitStoreyInstantiation (ec, this);
3808 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3810 var res = base.DoFlowAnalysis (fc);
3812 if (HasReachableClosingBrace)
3813 CheckControlExit (fc);
3818 public LabeledStatement GetLabel (string name, Block block)
3821 // Cloned parameters blocks can have their own cloned version of top-level labels
3823 if (labels == null) {
3825 return Parent.ParametersBlock.GetLabel (name, block);
3831 if (!labels.TryGetValue (name, out value)) {
3835 var label = value as LabeledStatement;
3837 if (label != null) {
3838 if (IsLabelVisible (label, b))
3842 List<LabeledStatement> list = (List<LabeledStatement>) value;
3843 for (int i = 0; i < list.Count; ++i) {
3845 if (IsLabelVisible (label, b))
3853 static bool IsLabelVisible (LabeledStatement label, Block b)
3856 if (label.Block == b)
3859 } while (b != null);
3864 public ParameterInfo GetParameterInfo (Parameter p)
3866 for (int i = 0; i < parameters.Count; ++i) {
3867 if (parameters[i] == p)
3868 return parameter_info[i];
3871 throw new ArgumentException ("Invalid parameter");
3874 public ParameterReference GetParameterReference (int index, Location loc)
3876 return new ParameterReference (parameter_info[index], loc);
3879 public Statement PerformClone ()
3881 CloneContext clonectx = new CloneContext ();
3882 return Clone (clonectx);
3885 protected void ProcessParameters ()
3887 if (parameters.Count == 0)
3890 parameter_info = new ParameterInfo[parameters.Count];
3891 for (int i = 0; i < parameter_info.Length; ++i) {
3892 var p = parameters.FixedParameters[i];
3896 // TODO: Should use Parameter only and more block there
3897 parameter_info[i] = new ParameterInfo (this, i);
3899 AddLocalName (p.Name, parameter_info[i]);
3903 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3905 var src = stmt.Block;
3908 // Cannot remap label block if the label was not yet cloned which
3909 // can happen in case of anonymous method inside anoynymous method
3910 // with a label. But in this case we don't care because goto cannot
3911 // jump of out anonymous method
3913 if (src.ParametersBlock != this)
3916 var src_stmts = src.Statements;
3917 for (int i = 0; i < src_stmts.Count; ++i) {
3918 if (src_stmts[i] == stmt)
3919 return (LabeledStatement) dst.Statements[i];
3922 throw new InternalErrorException ("Should never be reached");
3925 public override bool Resolve (BlockContext bc)
3927 // TODO: if ((flags & Flags.Resolved) != 0)
3934 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3935 flags |= Flags.IsExpressionTree;
3938 PrepareAssignmentAnalysis (bc);
3940 if (!base.Resolve (bc))
3943 } catch (Exception e) {
3944 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3947 if (bc.CurrentBlock != null) {
3948 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3950 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3955 // If an asynchronous body of F is either an expression classified as nothing, or a
3956 // statement block where no return statements have expressions, the inferred return type is Task
3959 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3960 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3961 am.ReturnTypeInference = null;
3962 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3970 void PrepareAssignmentAnalysis (BlockContext bc)
3972 for (int i = 0; i < parameters.Count; ++i) {
3973 var par = parameters.FixedParameters[i];
3975 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3978 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3982 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3984 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3985 var stateMachine = new IteratorStorey (iterator);
3987 state_machine = stateMachine;
3988 iterator.SetStateMachine (stateMachine);
3990 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3991 tlb.Original = this;
3992 tlb.state_machine = stateMachine;
3993 tlb.AddStatement (new Return (iterator, iterator.Location));
3997 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3999 for (int i = 0; i < parameters.Count; i++) {
4000 Parameter p = parameters[i];
4001 Parameter.Modifier mod = p.ModFlags;
4002 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4003 host.Compiler.Report.Error (1988, p.Location,
4004 "Async methods cannot have ref or out parameters");
4008 if (p is ArglistParameter) {
4009 host.Compiler.Report.Error (4006, p.Location,
4010 "__arglist is not allowed in parameter list of async methods");
4014 if (parameters.Types[i].IsPointer) {
4015 host.Compiler.Report.Error (4005, p.Location,
4016 "Async methods cannot have unsafe parameters");
4022 host.Compiler.Report.Warning (1998, 1, loc,
4023 "Async block lacks `await' operator and will run synchronously");
4026 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4027 var initializer = new AsyncInitializer (this, host, block_type);
4028 initializer.Type = block_type;
4029 initializer.DelegateType = delegateType;
4031 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4033 state_machine = stateMachine;
4034 initializer.SetStateMachine (stateMachine);
4036 const Flags flags = Flags.CompilerGenerated;
4038 var b = this is ToplevelBlock ?
4039 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4040 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4043 b.state_machine = stateMachine;
4044 b.AddStatement (new AsyncInitializerStatement (initializer));
4052 public class ToplevelBlock : ParametersBlock
4054 LocalVariable this_variable;
4055 CompilerContext compiler;
4056 Dictionary<string, object> names;
4058 List<ExplicitBlock> this_references;
4060 public ToplevelBlock (CompilerContext ctx, Location loc)
4061 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4065 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4066 : base (parameters, start)
4068 this.compiler = ctx;
4072 ProcessParameters ();
4076 // Recreates a top level block from parameters block. Used for
4077 // compiler generated methods where the original block comes from
4078 // explicit child block. This works for already resolved blocks
4079 // only to ensure we resolve them in the correct flow order
4081 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4082 : base (source, parameters)
4084 this.compiler = source.TopBlock.compiler;
4088 public bool IsIterator {
4090 return (flags & Flags.Iterator) != 0;
4093 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4097 public Report Report {
4099 return compiler.Report;
4104 // Used by anonymous blocks to track references of `this' variable
4106 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4108 return this_references;
4113 // Returns the "this" instance variable of this block.
4114 // See AddThisVariable() for more information.
4116 public LocalVariable ThisVariable {
4118 return this_variable;
4122 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4125 names = new Dictionary<string, object> ();
4128 if (!names.TryGetValue (name, out value)) {
4129 names.Add (name, li);
4133 INamedBlockVariable existing = value as INamedBlockVariable;
4134 List<INamedBlockVariable> existing_list;
4135 if (existing != null) {
4136 existing_list = new List<INamedBlockVariable> ();
4137 existing_list.Add (existing);
4138 names[name] = existing_list;
4140 existing_list = (List<INamedBlockVariable>) value;
4144 // A collision checking between local names
4146 var variable_block = li.Block.Explicit;
4147 for (int i = 0; i < existing_list.Count; ++i) {
4148 existing = existing_list[i];
4149 Block b = existing.Block.Explicit;
4151 // Collision at same level
4152 if (variable_block == b) {
4153 li.Block.Error_AlreadyDeclared (name, li);
4157 // Collision with parent
4158 Block parent = variable_block;
4159 while ((parent = parent.Parent) != null) {
4161 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4162 i = existing_list.Count;
4167 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4168 // Collision with children
4169 while ((b = b.Parent) != null) {
4170 if (variable_block == b) {
4171 li.Block.Error_AlreadyDeclared (name, li, "child");
4172 i = existing_list.Count;
4179 existing_list.Add (li);
4182 public void AddLabel (string name, LabeledStatement label)
4185 labels = new Dictionary<string, object> ();
4188 if (!labels.TryGetValue (name, out value)) {
4189 labels.Add (name, label);
4193 LabeledStatement existing = value as LabeledStatement;
4194 List<LabeledStatement> existing_list;
4195 if (existing != null) {
4196 existing_list = new List<LabeledStatement> ();
4197 existing_list.Add (existing);
4198 labels[name] = existing_list;
4200 existing_list = (List<LabeledStatement>) value;
4204 // A collision checking between labels
4206 for (int i = 0; i < existing_list.Count; ++i) {
4207 existing = existing_list[i];
4208 Block b = existing.Block;
4210 // Collision at same level
4211 if (label.Block == b) {
4212 Report.SymbolRelatedToPreviousError (existing.loc, name);
4213 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4217 // Collision with parent
4219 while ((b = b.Parent) != null) {
4220 if (existing.Block == b) {
4221 Report.Error (158, label.loc,
4222 "The label `{0}' shadows another label by the same name in a contained scope", name);
4223 i = existing_list.Count;
4228 // Collision with with children
4230 while ((b = b.Parent) != null) {
4231 if (label.Block == b) {
4232 Report.Error (158, label.loc,
4233 "The label `{0}' shadows another label by the same name in a contained scope", name);
4234 i = existing_list.Count;
4240 existing_list.Add (label);
4243 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4245 if (this_references == null)
4246 this_references = new List<ExplicitBlock> ();
4248 if (!this_references.Contains (block))
4249 this_references.Add (block);
4252 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4254 this_references.Remove (block);
4258 // Creates an arguments set from all parameters, useful for method proxy calls
4260 public Arguments GetAllParametersArguments ()
4262 int count = parameters.Count;
4263 Arguments args = new Arguments (count);
4264 for (int i = 0; i < count; ++i) {
4265 var pi = parameter_info[i];
4266 var arg_expr = GetParameterReference (i, pi.Location);
4268 Argument.AType atype_modifier;
4269 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4270 case Parameter.Modifier.REF:
4271 atype_modifier = Argument.AType.Ref;
4273 case Parameter.Modifier.OUT:
4274 atype_modifier = Argument.AType.Out;
4281 args.Add (new Argument (arg_expr, atype_modifier));
4288 // Lookup inside a block, the returned value can represent 3 states
4290 // true+variable: A local name was found and it's valid
4291 // false+variable: A local name was found in a child block only
4292 // false+null: No local name was found
4294 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4300 if (!names.TryGetValue (name, out value))
4303 variable = value as INamedBlockVariable;
4305 if (variable != null) {
4307 if (variable.Block == b.Original)
4311 } while (b != null);
4319 } while (b != null);
4321 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4322 for (int i = 0; i < list.Count; ++i) {
4325 if (variable.Block == b.Original)
4329 } while (b != null);
4337 } while (b != null);
4347 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4349 if (block.names != null) {
4350 foreach (var n in block.names) {
4351 var variable = n.Value as INamedBlockVariable;
4352 if (variable != null) {
4353 if (variable.Block.ParametersBlock == pb)
4354 AddLocalName (n.Key, variable, false);
4358 foreach (var v in (List<INamedBlockVariable>) n.Value)
4359 if (v.Block.ParametersBlock == pb)
4360 AddLocalName (n.Key, v, false);
4366 // This is used by non-static `struct' constructors which do not have an
4367 // initializer - in this case, the constructor must initialize all of the
4368 // struct's fields. To do this, we add a "this" variable and use the flow
4369 // analysis code to ensure that it's been fully initialized before control
4370 // leaves the constructor.
4372 public void AddThisVariable (BlockContext bc)
4374 if (this_variable != null)
4375 throw new InternalErrorException (StartLocation.ToString ());
4377 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4378 this_variable.Type = bc.CurrentType;
4379 this_variable.PrepareAssignmentAnalysis (bc);
4382 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4385 // If we're a non-static struct constructor which doesn't have an
4386 // initializer, then we must initialize all of the struct's fields.
4388 if (this_variable != null)
4389 this_variable.IsThisAssigned (fc, this);
4391 base.CheckControlExit (fc, dat);
4394 public override void Emit (EmitContext ec)
4396 if (Report.Errors > 0)
4400 if (IsCompilerGenerated) {
4401 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4409 // If `HasReturnLabel' is set, then we already emitted a
4410 // jump to the end of the method, so we must emit a `ret'
4413 // Unfortunately, System.Reflection.Emit automatically emits
4414 // a leave to the end of a finally block. This is a problem
4415 // if no code is following the try/finally block since we may
4416 // jump to a point after the end of the method.
4417 // As a workaround, we're always creating a return label in
4420 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4421 if (ec.HasReturnLabel)
4422 ec.MarkLabel (ec.ReturnLabel);
4424 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4425 ec.Mark (EndLocation);
4427 if (ec.ReturnType.Kind != MemberKind.Void)
4428 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4430 ec.Emit (OpCodes.Ret);
4433 } catch (Exception e) {
4434 throw new InternalErrorException (e, StartLocation);
4438 public bool Resolve (BlockContext bc, IMethodData md)
4443 var errors = bc.Report.Errors;
4447 if (bc.Report.Errors > errors)
4450 MarkReachable (new Reachability ());
4452 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4453 // TODO: var md = bc.CurrentMemberDefinition;
4454 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4457 if ((flags & Flags.NoFlowAnalysis) != 0)
4460 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4463 } catch (Exception e) {
4464 throw new InternalErrorException (e, StartLocation);
4471 public class SwitchLabel : Statement
4479 // if expr == null, then it is the default case.
4481 public SwitchLabel (Expression expr, Location l)
4487 public bool IsDefault {
4489 return label == null;
4493 public Expression Label {
4499 public Location Location {
4505 public Constant Converted {
4514 public bool PatternMatching { get; set; }
4516 public bool SectionStart { get; set; }
4518 public Label GetILLabel (EmitContext ec)
4520 if (il_label == null){
4521 il_label = ec.DefineLabel ();
4524 return il_label.Value;
4527 protected override void DoEmit (EmitContext ec)
4529 ec.MarkLabel (GetILLabel (ec));
4532 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4537 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4541 public override bool Resolve (BlockContext bc)
4543 if (ResolveAndReduce (bc))
4544 bc.Switch.RegisterLabel (bc, this);
4550 // Resolves the expression, reduces it to a literal if possible
4551 // and then converts it to the requested type.
4553 bool ResolveAndReduce (BlockContext bc)
4558 var switch_statement = bc.Switch;
4560 if (PatternMatching) {
4561 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4562 return label != null;
4565 var c = label.ResolveLabelConstant (bc);
4569 if (switch_statement.IsNullable && c is NullLiteral) {
4574 if (switch_statement.IsPatternMatching) {
4575 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4579 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4580 return converted != null;
4583 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4585 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4586 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4589 protected override void CloneTo (CloneContext clonectx, Statement target)
4591 var t = (SwitchLabel) target;
4593 t.label = label.Clone (clonectx);
4596 public override object Accept (StructuralVisitor visitor)
4598 return visitor.Visit (this);
4601 public string GetSignatureForError ()
4604 if (converted == null)
4607 label = converted.GetValueAsLiteral ();
4609 return string.Format ("case {0}:", label);
4613 public class Switch : LoopStatement
4615 // structure used to hold blocks of keys while calculating table switch
4616 sealed class LabelsRange : IComparable<LabelsRange>
4618 public readonly long min;
4620 public readonly List<long> label_values;
4622 public LabelsRange (long value)
4625 label_values = new List<long> ();
4626 label_values.Add (value);
4629 public LabelsRange (long min, long max, ICollection<long> values)
4633 this.label_values = new List<long> (values);
4638 return max - min + 1;
4642 public bool AddValue (long value)
4644 var gap = value - min + 1;
4645 // Ensure the range has > 50% occupancy
4646 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4650 label_values.Add (value);
4654 public int CompareTo (LabelsRange other)
4656 int nLength = label_values.Count;
4657 int nLengthOther = other.label_values.Count;
4658 if (nLengthOther == nLength)
4659 return (int) (other.min - min);
4661 return nLength - nLengthOther;
4665 sealed class DispatchStatement : Statement
4667 readonly Switch body;
4669 public DispatchStatement (Switch body)
4674 protected override void CloneTo (CloneContext clonectx, Statement target)
4676 throw new NotImplementedException ();
4679 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4684 protected override void DoEmit (EmitContext ec)
4686 body.EmitDispatch (ec);
4690 class MissingBreak : Statement
4692 readonly SwitchLabel label;
4694 public MissingBreak (SwitchLabel sl)
4700 public bool FallOut { get; set; }
4702 protected override void DoEmit (EmitContext ec)
4706 protected override void CloneTo (CloneContext clonectx, Statement target)
4710 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4713 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4714 label.GetSignatureForError ());
4716 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4717 label.GetSignatureForError ());
4723 public Expression Expr;
4726 // Mapping of all labels to their SwitchLabels
4728 Dictionary<long, SwitchLabel> labels;
4729 Dictionary<string, SwitchLabel> string_labels;
4730 List<SwitchLabel> case_labels;
4732 List<Tuple<GotoCase, Constant>> goto_cases;
4733 List<DefiniteAssignmentBitSet> end_reachable_das;
4736 /// The governing switch type
4738 public TypeSpec SwitchType;
4740 Expression new_expr;
4742 SwitchLabel case_null;
4743 SwitchLabel case_default;
4745 Label defaultLabel, nullLabel;
4746 VariableReference value;
4747 ExpressionStatement string_dictionary;
4748 FieldExpr switch_cache_field;
4749 ExplicitBlock block;
4753 // Nullable Types support
4755 Nullable.Unwrap unwrap;
4757 public Switch (Expression e, ExplicitBlock block, Location l)
4765 public SwitchLabel ActiveLabel { get; set; }
4767 public ExplicitBlock Block {
4773 public SwitchLabel DefaultLabel {
4775 return case_default;
4779 public bool IsNullable {
4781 return unwrap != null;
4785 public bool IsPatternMatching {
4787 return new_expr == null && SwitchType != null;
4791 public List<SwitchLabel> RegisteredLabels {
4797 public VariableReference ExpressionValue {
4804 // Determines the governing type for a switch. The returned
4805 // expression might be the expression from the switch, or an
4806 // expression that includes any potential conversions to
4808 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4810 switch (expr.Type.BuiltinType) {
4811 case BuiltinTypeSpec.Type.Byte:
4812 case BuiltinTypeSpec.Type.SByte:
4813 case BuiltinTypeSpec.Type.UShort:
4814 case BuiltinTypeSpec.Type.Short:
4815 case BuiltinTypeSpec.Type.UInt:
4816 case BuiltinTypeSpec.Type.Int:
4817 case BuiltinTypeSpec.Type.ULong:
4818 case BuiltinTypeSpec.Type.Long:
4819 case BuiltinTypeSpec.Type.Char:
4820 case BuiltinTypeSpec.Type.String:
4821 case BuiltinTypeSpec.Type.Bool:
4825 if (expr.Type.IsEnum)
4829 // Try to find a *user* defined implicit conversion.
4831 // If there is no implicit conversion, or if there are multiple
4832 // conversions, we have to report an error
4834 Expression converted = null;
4835 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4837 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4840 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4841 Convert.UserConversionRestriction.ProbingOnly;
4844 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4846 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4851 // Ignore over-worked ImplicitUserConversions that do
4852 // an implicit conversion in addition to the user conversion.
4854 var uc = e as UserCast;
4858 if (converted != null){
4859 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4868 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4870 var types = module.Compiler.BuiltinTypes;
4872 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4873 TypeSpec[] stypes = new[] {
4886 if (nullable != null) {
4888 Array.Resize (ref stypes, stypes.Length + 9);
4890 for (int i = 0; i < 9; ++i) {
4891 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4898 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4900 case_labels.Add (sl);
4903 if (case_default != null) {
4904 sl.Error_AlreadyOccurs (rc, case_default);
4912 if (sl.Converted == null)
4916 if (string_labels != null) {
4917 string string_value = sl.Converted.GetValue () as string;
4918 if (string_value == null)
4921 string_labels.Add (string_value, sl);
4923 if (sl.Converted.IsNull) {
4926 labels.Add (sl.Converted.GetValueAsLong (), sl);
4929 } catch (ArgumentException) {
4930 if (string_labels != null)
4931 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4933 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4938 // This method emits code for a lookup-based switch statement (non-string)
4939 // Basically it groups the cases into blocks that are at least half full,
4940 // and then spits out individual lookup opcodes for each block.
4941 // It emits the longest blocks first, and short blocks are just
4942 // handled with direct compares.
4944 void EmitTableSwitch (EmitContext ec, Expression val)
4946 if (labels != null && labels.Count > 0) {
4947 List<LabelsRange> ranges;
4948 if (string_labels != null) {
4949 // We have done all hard work for string already
4950 // setup single range only
4951 ranges = new List<LabelsRange> (1);
4952 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4954 var element_keys = new long[labels.Count];
4955 labels.Keys.CopyTo (element_keys, 0);
4956 Array.Sort (element_keys);
4959 // Build possible ranges of switch labes to reduce number
4962 ranges = new List<LabelsRange> (element_keys.Length);
4963 var range = new LabelsRange (element_keys[0]);
4965 for (int i = 1; i < element_keys.Length; ++i) {
4966 var l = element_keys[i];
4967 if (range.AddValue (l))
4970 range = new LabelsRange (l);
4974 // sort the blocks so we can tackle the largest ones first
4978 Label lbl_default = defaultLabel;
4979 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4981 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4982 LabelsRange kb = ranges[range_index];
4983 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4985 // Optimize small ranges using simple equality check
4986 if (kb.Range <= 2) {
4987 foreach (var key in kb.label_values) {
4988 SwitchLabel sl = labels[key];
4989 if (sl == case_default || sl == case_null)
4992 if (sl.Converted.IsZeroInteger) {
4993 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4996 sl.Converted.Emit (ec);
4997 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5001 // TODO: if all the keys in the block are the same and there are
5002 // no gaps/defaults then just use a range-check.
5003 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5004 // TODO: optimize constant/I4 cases
5006 // check block range (could be > 2^31)
5008 ec.EmitLong (kb.min);
5009 ec.Emit (OpCodes.Blt, lbl_default);
5012 ec.EmitLong (kb.max);
5013 ec.Emit (OpCodes.Bgt, lbl_default);
5018 ec.EmitLong (kb.min);
5019 ec.Emit (OpCodes.Sub);
5022 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5026 int first = (int) kb.min;
5029 ec.Emit (OpCodes.Sub);
5030 } else if (first < 0) {
5031 ec.EmitInt (-first);
5032 ec.Emit (OpCodes.Add);
5036 // first, build the list of labels for the switch
5038 long cJumps = kb.Range;
5039 Label[] switch_labels = new Label[cJumps];
5040 for (int iJump = 0; iJump < cJumps; iJump++) {
5041 var key = kb.label_values[iKey];
5042 if (key == kb.min + iJump) {
5043 switch_labels[iJump] = labels[key].GetILLabel (ec);
5046 switch_labels[iJump] = lbl_default;
5050 // emit the switch opcode
5051 ec.Emit (OpCodes.Switch, switch_labels);
5054 // mark the default for this block
5055 if (range_index != 0)
5056 ec.MarkLabel (lbl_default);
5059 // the last default just goes to the end
5060 if (ranges.Count > 0)
5061 ec.Emit (OpCodes.Br, lbl_default);
5065 public SwitchLabel FindLabel (Constant value)
5067 SwitchLabel sl = null;
5069 if (string_labels != null) {
5070 string s = value.GetValue () as string;
5072 if (case_null != null)
5074 else if (case_default != null)
5077 string_labels.TryGetValue (s, out sl);
5080 if (value is NullLiteral) {
5083 labels.TryGetValue (value.GetValueAsLong (), out sl);
5087 if (sl == null || sl.SectionStart)
5091 // Always return section start, it simplifies handling of switch labels
5093 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5094 var cs = case_labels [idx];
5095 if (cs.SectionStart)
5100 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5102 Expr.FlowAnalysis (fc);
5104 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5105 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5106 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5108 block.FlowAnalysis (fc);
5110 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5112 if (end_reachable_das != null) {
5113 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5114 InitialDefinitiveAssignment |= sections_das;
5115 end_reachable_das = null;
5118 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5120 return case_default != null && !end_reachable;
5123 public override bool Resolve (BlockContext ec)
5125 Expr = Expr.Resolve (ec);
5130 // LAMESPEC: User conversion from non-nullable governing type has a priority
5132 new_expr = SwitchGoverningType (ec, Expr, false);
5134 if (new_expr == null) {
5135 if (Expr.Type.IsNullableType) {
5136 unwrap = Nullable.Unwrap.Create (Expr, false);
5141 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5142 // involving nullable Expr and nullable governing type is
5144 new_expr = SwitchGoverningType (ec, unwrap, true);
5148 Expression switch_expr;
5149 if (new_expr == null) {
5150 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5151 if (Expr.Type != InternalType.ErrorType) {
5152 ec.Report.Error (151, loc,
5153 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5154 Expr.Type.GetSignatureForError ());
5161 SwitchType = Expr.Type;
5163 switch_expr = new_expr;
5164 SwitchType = new_expr.Type;
5165 if (SwitchType.IsNullableType) {
5166 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5167 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5170 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5171 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5175 if (block.Statements.Count == 0)
5178 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5179 string_labels = new Dictionary<string, SwitchLabel> ();
5181 labels = new Dictionary<long, SwitchLabel> ();
5185 var constant = switch_expr as Constant;
5188 // Don't need extra variable for constant switch or switch with
5189 // only default case
5191 if (constant == null) {
5193 // Store switch expression for comparison purposes
5195 value = switch_expr as VariableReference;
5196 if (value == null && !HasOnlyDefaultSection ()) {
5197 var current_block = ec.CurrentBlock;
5198 ec.CurrentBlock = Block;
5199 // Create temporary variable inside switch scope
5200 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5202 ec.CurrentBlock = current_block;
5206 case_labels = new List<SwitchLabel> ();
5208 Switch old_switch = ec.Switch;
5210 var parent_los = ec.EnclosingLoopOrSwitch;
5211 ec.EnclosingLoopOrSwitch = this;
5213 var ok = Statement.Resolve (ec);
5215 ec.EnclosingLoopOrSwitch = parent_los;
5216 ec.Switch = old_switch;
5219 // Check if all goto cases are valid. Needs to be done after switch
5220 // is resolved because goto can jump forward in the scope.
5222 if (goto_cases != null) {
5223 foreach (var gc in goto_cases) {
5224 if (gc.Item1 == null) {
5225 if (DefaultLabel == null) {
5226 Goto.Error_UnknownLabel (ec, "default", loc);
5232 var sl = FindLabel (gc.Item2);
5234 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5236 gc.Item1.Label = sl;
5244 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5245 ResolveStringSwitchMap (ec);
5249 // Anonymous storey initialization has to happen before
5250 // any generated switch dispatch
5252 block.InsertStatement (0, new DispatchStatement (this));
5257 bool HasOnlyDefaultSection ()
5259 for (int i = 0; i < block.Statements.Count; ++i) {
5260 var s = block.Statements[i] as SwitchLabel;
5262 if (s == null || s.IsDefault)
5271 public override Reachability MarkReachable (Reachability rc)
5273 if (rc.IsUnreachable)
5276 base.MarkReachable (rc);
5278 block.MarkReachableScope (rc);
5280 if (block.Statements.Count == 0)
5283 SwitchLabel constant_label = null;
5284 var constant = new_expr as Constant;
5286 if (constant != null) {
5287 constant_label = FindLabel (constant) ?? case_default;
5288 if (constant_label == null) {
5289 block.Statements.RemoveAt (0);
5294 var section_rc = new Reachability ();
5295 SwitchLabel prev_label = null;
5297 for (int i = 0; i < block.Statements.Count; ++i) {
5298 var s = block.Statements[i];
5299 var sl = s as SwitchLabel;
5301 if (sl != null && sl.SectionStart) {
5303 // Section is marked already via goto case
5305 if (!sl.IsUnreachable) {
5306 section_rc = new Reachability ();
5310 if (constant_label != null && constant_label != sl)
5311 section_rc = Reachability.CreateUnreachable ();
5312 else if (section_rc.IsUnreachable) {
5313 section_rc = new Reachability ();
5315 if (prev_label != null) {
5316 sl.SectionStart = false;
5317 s = new MissingBreak (prev_label);
5318 s.MarkReachable (rc);
5319 block.Statements.Insert (i - 1, s);
5327 section_rc = s.MarkReachable (section_rc);
5330 if (!section_rc.IsUnreachable && prev_label != null) {
5331 prev_label.SectionStart = false;
5332 var s = new MissingBreak (prev_label) {
5336 s.MarkReachable (rc);
5337 block.Statements.Add (s);
5341 // Reachability can affect parent only when all possible paths are handled but
5342 // we still need to run reachability check on switch body to check for fall-through
5344 if (case_default == null && constant_label == null)
5348 // We have at least one local exit from the switch
5353 return Reachability.CreateUnreachable ();
5356 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5358 if (goto_cases == null)
5359 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5361 goto_cases.Add (Tuple.Create (gotoCase, value));
5365 // Converts string switch into string hashtable
5367 void ResolveStringSwitchMap (ResolveContext ec)
5369 FullNamedExpression string_dictionary_type;
5370 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5371 string_dictionary_type = new TypeExpression (
5372 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5373 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5375 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5376 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5378 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5382 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5383 Field field = new Field (ctype, string_dictionary_type,
5384 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5385 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5386 if (!field.Define ())
5388 ctype.AddField (field);
5390 var init = new List<Expression> ();
5392 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5393 string value = null;
5395 foreach (SwitchLabel sl in case_labels) {
5397 if (sl.SectionStart)
5398 labels.Add (++counter, sl);
5400 if (sl == case_default || sl == case_null)
5403 value = (string) sl.Converted.GetValue ();
5404 var init_args = new List<Expression> (2);
5405 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5407 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5408 init_args.Add (sl.Converted);
5410 init.Add (new CollectionElementInitializer (init_args, loc));
5413 Arguments args = new Arguments (1);
5414 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5415 Expression initializer = new NewInitialize (string_dictionary_type, args,
5416 new CollectionOrObjectInitializers (init, loc), loc);
5418 switch_cache_field = new FieldExpr (field, loc);
5419 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5422 void DoEmitStringSwitch (EmitContext ec)
5424 Label l_initialized = ec.DefineLabel ();
5427 // Skip initialization when value is null
5429 value.EmitBranchable (ec, nullLabel, false);
5432 // Check if string dictionary is initialized and initialize
5434 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5435 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5436 string_dictionary.EmitStatement (ec);
5438 ec.MarkLabel (l_initialized);
5440 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5442 ResolveContext rc = new ResolveContext (ec.MemberContext);
5444 if (switch_cache_field.Type.IsGeneric) {
5445 Arguments get_value_args = new Arguments (2);
5446 get_value_args.Add (new Argument (value));
5447 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5448 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5449 if (get_item == null)
5453 // A value was not found, go to default case
5455 get_item.EmitBranchable (ec, defaultLabel, false);
5457 Arguments get_value_args = new Arguments (1);
5458 get_value_args.Add (new Argument (value));
5460 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5461 if (get_item == null)
5464 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5465 get_item_object.EmitAssign (ec, get_item, true, false);
5466 ec.Emit (OpCodes.Brfalse, defaultLabel);
5468 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5469 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5471 get_item_int.EmitStatement (ec);
5472 get_item_object.Release (ec);
5475 EmitTableSwitch (ec, string_switch_variable);
5476 string_switch_variable.Release (ec);
5480 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5482 void EmitShortSwitch (EmitContext ec)
5484 MethodSpec equal_method = null;
5485 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5486 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5489 if (equal_method != null) {
5490 value.EmitBranchable (ec, nullLabel, false);
5493 for (int i = 0; i < case_labels.Count; ++i) {
5494 var label = case_labels [i];
5495 if (label == case_default || label == case_null)
5498 var constant = label.Converted;
5500 if (constant == null) {
5501 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5505 if (equal_method != null) {
5509 var call = new CallEmitter ();
5510 call.EmitPredefined (ec, equal_method, new Arguments (0));
5511 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5515 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5516 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5522 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5525 ec.Emit (OpCodes.Br, defaultLabel);
5528 void EmitDispatch (EmitContext ec)
5530 if (IsPatternMatching) {
5531 EmitShortSwitch (ec);
5535 if (value == null) {
5537 // Constant switch, we've already done the work if there is only 1 label
5541 foreach (var sl in case_labels) {
5542 if (sl.IsUnreachable)
5545 if (reachable++ > 0) {
5546 var constant = (Constant) new_expr;
5547 var constant_label = FindLabel (constant) ?? case_default;
5549 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5557 if (string_dictionary != null) {
5558 DoEmitStringSwitch (ec);
5559 } else if (case_labels.Count < 4 || string_labels != null) {
5560 EmitShortSwitch (ec);
5562 EmitTableSwitch (ec, value);
5566 protected override void DoEmit (EmitContext ec)
5569 // Setup the codegen context
5571 Label old_end = ec.LoopEnd;
5572 Switch old_switch = ec.Switch;
5574 ec.LoopEnd = ec.DefineLabel ();
5577 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5578 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5580 if (value != null) {
5583 var switch_expr = new_expr ?? Expr;
5585 unwrap.EmitCheck (ec);
5586 ec.Emit (OpCodes.Brfalse, nullLabel);
5587 value.EmitAssign (ec, switch_expr, false, false);
5588 } else if (switch_expr != value) {
5589 value.EmitAssign (ec, switch_expr, false, false);
5594 // Next statement is compiler generated we don't need extra
5595 // nop when we can use the statement for sequence point
5597 ec.Mark (block.StartLocation);
5598 block.IsCompilerGenerated = true;
5600 new_expr.EmitSideEffect (ec);
5605 // Restore context state.
5606 ec.MarkLabel (ec.LoopEnd);
5609 // Restore the previous context
5611 ec.LoopEnd = old_end;
5612 ec.Switch = old_switch;
5615 protected override void CloneTo (CloneContext clonectx, Statement t)
5617 Switch target = (Switch) t;
5619 target.Expr = Expr.Clone (clonectx);
5620 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5623 public override object Accept (StructuralVisitor visitor)
5625 return visitor.Visit (this);
5628 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5630 if (case_default == null && !(new_expr is Constant))
5633 if (end_reachable_das == null)
5634 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5636 end_reachable_das.Add (fc.DefiniteAssignment);
5639 public override void SetEndReachable ()
5641 end_reachable = true;
5645 // A place where execution can restart in a state machine
5646 public abstract class ResumableStatement : Statement
5649 protected Label resume_point;
5651 public Label PrepareForEmit (EmitContext ec)
5655 resume_point = ec.DefineLabel ();
5657 return resume_point;
5660 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5665 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5670 public abstract class TryFinallyBlock : ExceptionStatement
5672 protected Statement stmt;
5673 Label dispose_try_block;
5674 bool prepared_for_dispose, emitted_dispose;
5675 Method finally_host;
5677 protected TryFinallyBlock (Statement stmt, Location loc)
5685 public Statement Statement {
5693 protected abstract void EmitTryBody (EmitContext ec);
5694 public abstract void EmitFinallyBody (EmitContext ec);
5696 public override Label PrepareForDispose (EmitContext ec, Label end)
5698 if (!prepared_for_dispose) {
5699 prepared_for_dispose = true;
5700 dispose_try_block = ec.DefineLabel ();
5702 return dispose_try_block;
5705 protected sealed override void DoEmit (EmitContext ec)
5707 EmitTryBodyPrepare (ec);
5710 bool beginFinally = EmitBeginFinallyBlock (ec);
5712 Label start_finally = ec.DefineLabel ();
5713 if (resume_points != null && beginFinally) {
5714 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5716 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5717 ec.Emit (OpCodes.Brfalse_S, start_finally);
5718 ec.Emit (OpCodes.Endfinally);
5721 ec.MarkLabel (start_finally);
5723 if (finally_host != null) {
5724 finally_host.Define ();
5725 finally_host.PrepareEmit ();
5726 finally_host.Emit ();
5728 // Now it's safe to add, to close it properly and emit sequence points
5729 finally_host.Parent.AddMember (finally_host);
5731 var ce = new CallEmitter ();
5732 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5733 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5735 EmitFinallyBody (ec);
5739 ec.EndExceptionBlock ();
5742 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5744 if (emitted_dispose)
5747 emitted_dispose = true;
5749 Label end_of_try = ec.DefineLabel ();
5751 // Ensure that the only way we can get into this code is through a dispatcher
5752 if (have_dispatcher)
5753 ec.Emit (OpCodes.Br, end);
5755 ec.BeginExceptionBlock ();
5757 ec.MarkLabel (dispose_try_block);
5759 Label[] labels = null;
5760 for (int i = 0; i < resume_points.Count; ++i) {
5761 ResumableStatement s = resume_points[i];
5762 Label ret = s.PrepareForDispose (ec, end_of_try);
5763 if (ret.Equals (end_of_try) && labels == null)
5765 if (labels == null) {
5766 labels = new Label[resume_points.Count];
5767 for (int j = 0; j < i; ++j)
5768 labels[j] = end_of_try;
5773 if (labels != null) {
5775 for (j = 1; j < labels.Length; ++j)
5776 if (!labels[0].Equals (labels[j]))
5778 bool emit_dispatcher = j < labels.Length;
5780 if (emit_dispatcher) {
5781 ec.Emit (OpCodes.Ldloc, pc);
5782 ec.EmitInt (first_resume_pc);
5783 ec.Emit (OpCodes.Sub);
5784 ec.Emit (OpCodes.Switch, labels);
5787 foreach (ResumableStatement s in resume_points)
5788 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5791 ec.MarkLabel (end_of_try);
5793 ec.BeginFinallyBlock ();
5795 if (finally_host != null) {
5796 var ce = new CallEmitter ();
5797 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5798 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5800 EmitFinallyBody (ec);
5803 ec.EndExceptionBlock ();
5806 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5808 var res = stmt.FlowAnalysis (fc);
5813 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5815 ec.BeginFinallyBlock ();
5819 public override Reachability MarkReachable (Reachability rc)
5821 base.MarkReachable (rc);
5822 return Statement.MarkReachable (rc);
5825 public override bool Resolve (BlockContext bc)
5829 parent = bc.CurrentTryBlock;
5830 bc.CurrentTryBlock = this;
5832 using (bc.Set (ResolveContext.Options.TryScope)) {
5833 ok = stmt.Resolve (bc);
5836 bc.CurrentTryBlock = parent;
5839 // Finally block inside iterator is called from MoveNext and
5840 // Dispose methods that means we need to lift the block into
5841 // newly created host method to emit the body only once. The
5842 // original block then simply calls the newly generated method.
5844 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5845 var b = stmt as Block;
5846 if (b != null && b.Explicit.HasYield) {
5847 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5851 return base.Resolve (bc) && ok;
5856 // Base class for blocks using exception handling
5858 public abstract class ExceptionStatement : ResumableStatement
5860 protected List<ResumableStatement> resume_points;
5861 protected int first_resume_pc;
5862 protected ExceptionStatement parent;
5864 protected ExceptionStatement (Location loc)
5869 protected virtual void EmitBeginException (EmitContext ec)
5871 ec.BeginExceptionBlock ();
5874 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5876 StateMachineInitializer state_machine = null;
5877 if (resume_points != null) {
5878 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5880 ec.EmitInt ((int) IteratorStorey.State.Running);
5881 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5884 EmitBeginException (ec);
5886 if (resume_points != null) {
5887 ec.MarkLabel (resume_point);
5889 // For normal control flow, we want to fall-through the Switch
5890 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5891 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5892 ec.EmitInt (first_resume_pc);
5893 ec.Emit (OpCodes.Sub);
5895 Label[] labels = new Label[resume_points.Count];
5896 for (int i = 0; i < resume_points.Count; ++i)
5897 labels[i] = resume_points[i].PrepareForEmit (ec);
5898 ec.Emit (OpCodes.Switch, labels);
5902 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5904 if (parent != null) {
5905 // TODO: MOVE to virtual TryCatch
5906 var tc = this as TryCatch;
5907 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5909 pc = parent.AddResumePoint (s, pc, stateMachine);
5911 pc = stateMachine.AddResumePoint (this);
5914 if (resume_points == null) {
5915 resume_points = new List<ResumableStatement> ();
5916 first_resume_pc = pc;
5919 if (pc != first_resume_pc + resume_points.Count)
5920 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5922 resume_points.Add (stmt);
5927 public class Lock : TryFinallyBlock
5930 TemporaryVariableReference expr_copy;
5931 TemporaryVariableReference lock_taken;
5933 public Lock (Expression expr, Statement stmt, Location loc)
5939 public Expression Expr {
5945 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5947 expr.FlowAnalysis (fc);
5948 return base.DoFlowAnalysis (fc);
5951 public override bool Resolve (BlockContext ec)
5953 expr = expr.Resolve (ec);
5957 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
5958 ec.Report.Error (185, loc,
5959 "`{0}' is not a reference type as required by the lock statement",
5960 expr.Type.GetSignatureForError ());
5963 if (expr.Type.IsGenericParameter) {
5964 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5967 VariableReference lv = expr as VariableReference;
5970 locked = lv.IsLockedByStatement;
5971 lv.IsLockedByStatement = true;
5978 // Have to keep original lock value around to unlock same location
5979 // in the case of original value has changed or is null
5981 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5982 expr_copy.Resolve (ec);
5985 // Ensure Monitor methods are available
5987 if (ResolvePredefinedMethods (ec) > 1) {
5988 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5989 lock_taken.Resolve (ec);
5992 using (ec.Set (ResolveContext.Options.LockScope)) {
5997 lv.IsLockedByStatement = locked;
6003 protected override void EmitTryBodyPrepare (EmitContext ec)
6005 expr_copy.EmitAssign (ec, expr);
6007 if (lock_taken != null) {
6009 // Initialize ref variable
6011 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6014 // Monitor.Enter (expr_copy)
6016 expr_copy.Emit (ec);
6017 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6020 base.EmitTryBodyPrepare (ec);
6023 protected override void EmitTryBody (EmitContext ec)
6026 // Monitor.Enter (expr_copy, ref lock_taken)
6028 if (lock_taken != null) {
6029 expr_copy.Emit (ec);
6030 lock_taken.LocalInfo.CreateBuilder (ec);
6031 lock_taken.AddressOf (ec, AddressOp.Load);
6032 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6035 Statement.Emit (ec);
6038 public override void EmitFinallyBody (EmitContext ec)
6041 // if (lock_taken) Monitor.Exit (expr_copy)
6043 Label skip = ec.DefineLabel ();
6045 if (lock_taken != null) {
6046 lock_taken.Emit (ec);
6047 ec.Emit (OpCodes.Brfalse_S, skip);
6050 expr_copy.Emit (ec);
6051 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6053 ec.Emit (OpCodes.Call, m);
6055 ec.MarkLabel (skip);
6058 int ResolvePredefinedMethods (ResolveContext rc)
6060 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6061 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6065 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6069 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6073 protected override void CloneTo (CloneContext clonectx, Statement t)
6075 Lock target = (Lock) t;
6077 target.expr = expr.Clone (clonectx);
6078 target.stmt = Statement.Clone (clonectx);
6081 public override object Accept (StructuralVisitor visitor)
6083 return visitor.Visit (this);
6088 public class Unchecked : Statement {
6091 public Unchecked (Block b, Location loc)
6098 public override bool Resolve (BlockContext ec)
6100 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6101 return Block.Resolve (ec);
6104 protected override void DoEmit (EmitContext ec)
6106 using (ec.With (EmitContext.Options.CheckedScope, false))
6110 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6112 return Block.FlowAnalysis (fc);
6115 public override Reachability MarkReachable (Reachability rc)
6117 base.MarkReachable (rc);
6118 return Block.MarkReachable (rc);
6121 protected override void CloneTo (CloneContext clonectx, Statement t)
6123 Unchecked target = (Unchecked) t;
6125 target.Block = clonectx.LookupBlock (Block);
6128 public override object Accept (StructuralVisitor visitor)
6130 return visitor.Visit (this);
6134 public class Checked : Statement {
6137 public Checked (Block b, Location loc)
6140 b.Unchecked = false;
6144 public override bool Resolve (BlockContext ec)
6146 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6147 return Block.Resolve (ec);
6150 protected override void DoEmit (EmitContext ec)
6152 using (ec.With (EmitContext.Options.CheckedScope, true))
6156 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6158 return Block.FlowAnalysis (fc);
6161 public override Reachability MarkReachable (Reachability rc)
6163 base.MarkReachable (rc);
6164 return Block.MarkReachable (rc);
6167 protected override void CloneTo (CloneContext clonectx, Statement t)
6169 Checked target = (Checked) t;
6171 target.Block = clonectx.LookupBlock (Block);
6174 public override object Accept (StructuralVisitor visitor)
6176 return visitor.Visit (this);
6180 public class Unsafe : Statement {
6183 public Unsafe (Block b, Location loc)
6186 Block.Unsafe = true;
6190 public override bool Resolve (BlockContext ec)
6192 if (ec.CurrentIterator != null)
6193 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6195 using (ec.Set (ResolveContext.Options.UnsafeScope))
6196 return Block.Resolve (ec);
6199 protected override void DoEmit (EmitContext ec)
6204 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6206 return Block.FlowAnalysis (fc);
6209 public override Reachability MarkReachable (Reachability rc)
6211 base.MarkReachable (rc);
6212 return Block.MarkReachable (rc);
6215 protected override void CloneTo (CloneContext clonectx, Statement t)
6217 Unsafe target = (Unsafe) t;
6219 target.Block = clonectx.LookupBlock (Block);
6222 public override object Accept (StructuralVisitor visitor)
6224 return visitor.Visit (this);
6231 public class Fixed : Statement
6233 abstract class Emitter : ShimExpression
6235 protected LocalVariable vi;
6237 protected Emitter (Expression expr, LocalVariable li)
6243 public abstract void EmitExit (EmitContext ec);
6245 public override void FlowAnalysis (FlowAnalysisContext fc)
6247 expr.FlowAnalysis (fc);
6251 class ExpressionEmitter : Emitter {
6252 public ExpressionEmitter (Expression converted, LocalVariable li) :
6253 base (converted, li)
6257 protected override Expression DoResolve (ResolveContext rc)
6259 throw new NotImplementedException ();
6262 public override void Emit (EmitContext ec) {
6264 // Store pointer in pinned location
6270 public override void EmitExit (EmitContext ec)
6273 ec.Emit (OpCodes.Conv_U);
6278 class StringEmitter : Emitter
6280 LocalVariable pinned_string;
6282 public StringEmitter (Expression expr, LocalVariable li)
6287 protected override Expression DoResolve (ResolveContext rc)
6289 pinned_string = new LocalVariable (vi.Block, "$pinned",
6290 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6292 pinned_string.Type = rc.BuiltinTypes.String;
6294 eclass = ExprClass.Variable;
6295 type = rc.BuiltinTypes.Int;
6299 public override void Emit (EmitContext ec)
6301 pinned_string.CreateBuilder (ec);
6304 pinned_string.EmitAssign (ec);
6306 // TODO: Should use Binary::Add
6307 pinned_string.Emit (ec);
6308 ec.Emit (OpCodes.Conv_I);
6310 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6314 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6315 //pe.InstanceExpression = pinned_string;
6316 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6318 ec.Emit (OpCodes.Add);
6322 public override void EmitExit (EmitContext ec)
6325 pinned_string.EmitAssign (ec);
6329 public class VariableDeclaration : BlockVariable
6331 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6336 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6338 if (!Variable.Type.IsPointer && li == Variable) {
6339 bc.Report.Error (209, TypeExpression.Location,
6340 "The type of locals declared in a fixed statement must be a pointer type");
6344 var res = initializer.Resolve (bc);
6351 if (res.Type.IsArray) {
6352 TypeSpec array_type = TypeManager.GetElementType (res.Type);
6355 // Provided that array_type is unmanaged,
6357 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6361 // and T* is implicitly convertible to the
6362 // pointer type given in the fixed statement.
6364 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6366 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6367 if (converted == null)
6371 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6373 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6374 new Binary (Binary.Operator.Equality, res, new NullLiteral (loc)),
6375 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6376 new NullLiteral (loc),
6379 converted = converted.Resolve (bc);
6381 return new ExpressionEmitter (converted, li);
6387 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6388 return new StringEmitter (res, li).Resolve (bc);
6391 // Case 3: fixed buffer
6392 if (res is FixedBufferPtr) {
6393 return new ExpressionEmitter (res, li);
6396 bool already_fixed = true;
6399 // Case 4: & object.
6401 Unary u = res as Unary;
6403 if (u.Oper == Unary.Operator.AddressOf) {
6404 IVariableReference vr = u.Expr as IVariableReference;
6405 if (vr == null || !vr.IsFixed) {
6406 already_fixed = false;
6409 } else if (initializer is Cast) {
6410 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6414 if (already_fixed) {
6415 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6418 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6419 return new ExpressionEmitter (res, li);
6424 VariableDeclaration decl;
6425 Statement statement;
6428 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6437 public Statement Statement {
6443 public BlockVariable Variables {
6451 public override bool Resolve (BlockContext bc)
6453 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6454 if (!decl.Resolve (bc))
6458 return statement.Resolve (bc);
6461 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6463 decl.FlowAnalysis (fc);
6464 return statement.FlowAnalysis (fc);
6467 protected override void DoEmit (EmitContext ec)
6469 decl.Variable.CreateBuilder (ec);
6470 decl.Initializer.Emit (ec);
6471 if (decl.Declarators != null) {
6472 foreach (var d in decl.Declarators) {
6473 d.Variable.CreateBuilder (ec);
6474 d.Initializer.Emit (ec);
6478 statement.Emit (ec);
6484 // Clear the pinned variable
6486 ((Emitter) decl.Initializer).EmitExit (ec);
6487 if (decl.Declarators != null) {
6488 foreach (var d in decl.Declarators) {
6489 ((Emitter)d.Initializer).EmitExit (ec);
6494 public override Reachability MarkReachable (Reachability rc)
6496 base.MarkReachable (rc);
6498 decl.MarkReachable (rc);
6500 rc = statement.MarkReachable (rc);
6502 // TODO: What if there is local exit?
6503 has_ret = rc.IsUnreachable;
6507 protected override void CloneTo (CloneContext clonectx, Statement t)
6509 Fixed target = (Fixed) t;
6511 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6512 target.statement = statement.Clone (clonectx);
6515 public override object Accept (StructuralVisitor visitor)
6517 return visitor.Visit (this);
6521 public class Catch : Statement
6523 class CatchVariableStore : Statement
6525 readonly Catch ctch;
6527 public CatchVariableStore (Catch ctch)
6532 protected override void CloneTo (CloneContext clonectx, Statement target)
6536 protected override void DoEmit (EmitContext ec)
6538 // Emits catch variable debug information inside correct block
6539 ctch.EmitCatchVariableStore (ec);
6542 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6548 class FilterStatement : Statement
6550 readonly Catch ctch;
6552 public FilterStatement (Catch ctch)
6557 protected override void CloneTo (CloneContext clonectx, Statement target)
6561 protected override void DoEmit (EmitContext ec)
6563 if (ctch.li != null) {
6564 if (ctch.hoisted_temp != null)
6565 ctch.hoisted_temp.Emit (ec);
6569 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6570 ec.Emit (OpCodes.Box, ctch.type);
6573 var expr_start = ec.DefineLabel ();
6574 var end = ec.DefineLabel ();
6576 ec.Emit (OpCodes.Brtrue_S, expr_start);
6578 ec.Emit (OpCodes.Br, end);
6579 ec.MarkLabel (expr_start);
6581 ctch.Filter.Emit (ec);
6584 ec.Emit (OpCodes.Endfilter);
6585 ec.BeginFilterHandler ();
6586 ec.Emit (OpCodes.Pop);
6589 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6591 ctch.Filter.FlowAnalysis (fc);
6595 public override bool Resolve (BlockContext bc)
6597 ctch.Filter = ctch.Filter.Resolve (bc);
6599 if (ctch.Filter != null) {
6600 if (ctch.Filter.ContainsEmitWithAwait ()) {
6601 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6604 var c = ctch.Filter as Constant;
6605 if (c != null && !c.IsDefaultValue) {
6606 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6614 ExplicitBlock block;
6616 FullNamedExpression type_expr;
6617 CompilerAssign assign;
6619 LocalTemporary hoisted_temp;
6621 public Catch (ExplicitBlock block, Location loc)
6629 public ExplicitBlock Block {
6635 public TypeSpec CatchType {
6641 public Expression Filter {
6645 public bool IsGeneral {
6647 return type_expr == null;
6651 public FullNamedExpression TypeExpression {
6660 public LocalVariable Variable {
6671 protected override void DoEmit (EmitContext ec)
6673 if (Filter != null) {
6674 ec.BeginExceptionFilterBlock ();
6675 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6677 if (Block.HasAwait) {
6678 Block.EmitScopeInitialization (ec);
6687 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6689 ec.BeginCatchBlock (CatchType);
6692 ec.Emit (OpCodes.Pop);
6694 if (Block.HasAwait) {
6696 EmitCatchVariableStore (ec);
6702 void EmitCatchVariableStore (EmitContext ec)
6704 li.CreateBuilder (ec);
6707 // For hoisted catch variable we have to use a temporary local variable
6708 // for captured variable initialization during storey setup because variable
6709 // needs to be on the stack after storey instance for stfld operation
6711 if (li.HoistedVariant != null) {
6712 hoisted_temp = new LocalTemporary (li.Type);
6713 hoisted_temp.Store (ec);
6715 // switch to assignment from temporary variable and not from top of the stack
6716 assign.UpdateSource (hoisted_temp);
6720 public override bool Resolve (BlockContext bc)
6722 using (bc.Set (ResolveContext.Options.CatchScope)) {
6723 if (type_expr == null) {
6724 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6725 if (!block.HasAwait || Filter != null)
6726 block.AddScopeStatement (new CatchVariableStore (this));
6728 Expression source = new EmptyExpression (li.Type);
6729 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6730 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6733 type = type_expr.ResolveAsType (bc);
6738 CreateExceptionVariable (type);
6740 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6741 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6742 } else if (li != null) {
6744 li.PrepareAssignmentAnalysis (bc);
6746 // source variable is at the top of the stack
6747 Expression source = new EmptyExpression (li.Type);
6748 if (li.Type.IsGenericParameter)
6749 source = new UnboxCast (source, li.Type);
6751 if (!block.HasAwait || Filter != null)
6752 block.AddScopeStatement (new CatchVariableStore (this));
6755 // Uses Location.Null to hide from symbol file
6757 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6758 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6762 if (Filter != null) {
6763 Block.AddScopeStatement (new FilterStatement (this));
6766 Block.SetCatchBlock ();
6767 return Block.Resolve (bc);
6771 bool CreateExceptionVariable (TypeSpec type)
6773 if (!Block.HasAwait)
6776 // TODO: Scan the block for rethrow expression
6777 //if (!Block.HasRethrow)
6780 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6784 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6786 if (li != null && !li.IsCompilerGenerated) {
6787 fc.SetVariableAssigned (li.VariableInfo, true);
6790 return block.FlowAnalysis (fc);
6793 public override Reachability MarkReachable (Reachability rc)
6795 base.MarkReachable (rc);
6797 var c = Filter as Constant;
6798 if (c != null && c.IsDefaultValue)
6799 return Reachability.CreateUnreachable ();
6801 return block.MarkReachable (rc);
6804 protected override void CloneTo (CloneContext clonectx, Statement t)
6806 Catch target = (Catch) t;
6808 if (type_expr != null)
6809 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6812 target.Filter = Filter.Clone (clonectx);
6814 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6818 public class TryFinally : TryFinallyBlock
6821 List<DefiniteAssignmentBitSet> try_exit_dat;
6822 List<Label> redirected_jumps;
6823 Label? start_fin_label;
6825 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6831 public ExplicitBlock FinallyBlock {
6837 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6839 if (try_exit_dat == null)
6840 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6842 try_exit_dat.Add (vector);
6845 public override bool Resolve (BlockContext bc)
6847 bool ok = base.Resolve (bc);
6849 fini.SetFinallyBlock ();
6850 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6851 ok &= fini.Resolve (bc);
6857 protected override void EmitBeginException (EmitContext ec)
6859 if (fini.HasAwait && stmt is TryCatch)
6860 ec.BeginExceptionBlock ();
6862 base.EmitBeginException (ec);
6865 protected override void EmitTryBody (EmitContext ec)
6867 if (fini.HasAwait) {
6868 if (ec.TryFinallyUnwind == null)
6869 ec.TryFinallyUnwind = new List<TryFinally> ();
6871 ec.TryFinallyUnwind.Add (this);
6874 if (stmt is TryCatch)
6875 ec.EndExceptionBlock ();
6877 ec.TryFinallyUnwind.Remove (this);
6879 if (start_fin_label != null)
6880 ec.MarkLabel (start_fin_label.Value);
6888 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6893 return base.EmitBeginFinallyBlock (ec);
6896 public override void EmitFinallyBody (EmitContext ec)
6898 if (!fini.HasAwait) {
6904 // Emits catch block like
6906 // catch (object temp) {
6907 // this.exception_field = temp;
6910 var type = ec.BuiltinTypes.Object;
6911 ec.BeginCatchBlock (type);
6913 var temp = ec.GetTemporaryLocal (type);
6914 ec.Emit (OpCodes.Stloc, temp);
6916 var exception_field = ec.GetTemporaryField (type);
6918 ec.Emit (OpCodes.Ldloc, temp);
6919 exception_field.EmitAssignFromStack (ec);
6921 ec.EndExceptionBlock ();
6923 ec.FreeTemporaryLocal (temp, type);
6928 // Emits exception rethrow
6930 // if (this.exception_field != null)
6931 // throw this.exception_field;
6933 exception_field.Emit (ec);
6934 var skip_throw = ec.DefineLabel ();
6935 ec.Emit (OpCodes.Brfalse_S, skip_throw);
6936 exception_field.Emit (ec);
6937 ec.Emit (OpCodes.Throw);
6938 ec.MarkLabel (skip_throw);
6940 exception_field.IsAvailableForReuse = true;
6942 EmitUnwindFinallyTable (ec);
6945 bool IsParentBlock (Block block)
6947 for (Block b = fini; b != null; b = b.Parent) {
6955 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
6958 if (labelBlock != null) {
6959 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
6960 var fin = ec.TryFinallyUnwind [idx - 1];
6961 if (!fin.IsParentBlock (labelBlock))
6968 bool set_return_state = true;
6970 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
6971 var fin = ec.TryFinallyUnwind [idx];
6972 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
6975 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
6976 set_return_state = false;
6978 if (fin.start_fin_label == null) {
6979 fin.start_fin_label = ec.DefineLabel ();
6982 label = fin.start_fin_label.Value;
6988 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
6990 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
6993 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
6995 if (redirected_jumps == null) {
6996 redirected_jumps = new List<Label> ();
6998 // Add fallthrough label
6999 redirected_jumps.Add (ec.DefineLabel ());
7002 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7005 int index = redirected_jumps.IndexOf (label);
7007 redirected_jumps.Add (label);
7008 index = redirected_jumps.Count - 1;
7012 // Indicates we have captured exit jump
7014 if (setReturnState) {
7015 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7016 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7021 // Emits state table of jumps outside of try block and reload of return
7022 // value when try block returns value
7024 void EmitUnwindFinallyTable (EmitContext ec)
7026 if (redirected_jumps == null)
7029 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7030 initializer.HoistedReturnState.EmitLoad (ec);
7031 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7033 // Mark fallthrough label
7034 ec.MarkLabel (redirected_jumps [0]);
7037 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7039 var da = fc.BranchDefiniteAssignment ();
7041 var tf = fc.TryFinally;
7042 fc.TryFinally = this;
7044 var res_stmt = Statement.FlowAnalysis (fc);
7048 var try_da = fc.DefiniteAssignment;
7049 fc.DefiniteAssignment = da;
7051 var res_fin = fini.FlowAnalysis (fc);
7053 if (try_exit_dat != null) {
7055 // try block has global exit but we need to run definite assignment check
7056 // for parameter block out parameter after finally block because it's always
7057 // executed before exit
7059 foreach (var try_da_part in try_exit_dat)
7060 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7062 try_exit_dat = null;
7065 fc.DefiniteAssignment |= try_da;
7066 return res_stmt | res_fin;
7069 public override Reachability MarkReachable (Reachability rc)
7072 // Mark finally block first for any exit statement in try block
7073 // to know whether the code which follows finally is reachable
7075 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7078 protected override void CloneTo (CloneContext clonectx, Statement t)
7080 TryFinally target = (TryFinally) t;
7082 target.stmt = stmt.Clone (clonectx);
7084 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7087 public override object Accept (StructuralVisitor visitor)
7089 return visitor.Visit (this);
7093 public class TryCatch : ExceptionStatement
7096 List<Catch> clauses;
7097 readonly bool inside_try_finally;
7098 List<Catch> catch_sm;
7100 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7104 this.clauses = catch_clauses;
7105 this.inside_try_finally = inside_try_finally;
7108 public List<Catch> Clauses {
7114 public bool IsTryCatchFinally {
7116 return inside_try_finally;
7120 public override bool Resolve (BlockContext bc)
7124 using (bc.Set (ResolveContext.Options.TryScope)) {
7125 parent = bc.CurrentTryBlock;
7127 if (IsTryCatchFinally) {
7128 ok = Block.Resolve (bc);
7130 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7131 bc.CurrentTryBlock = this;
7132 ok = Block.Resolve (bc);
7133 bc.CurrentTryBlock = parent;
7138 for (int i = 0; i < clauses.Count; ++i) {
7141 ok &= c.Resolve (bc);
7143 if (c.Block.HasAwait) {
7144 if (catch_sm == null)
7145 catch_sm = new List<Catch> ();
7150 if (c.Filter != null)
7153 TypeSpec resolved_type = c.CatchType;
7154 if (resolved_type == null)
7157 for (int ii = 0; ii < clauses.Count; ++ii) {
7161 if (clauses[ii].Filter != null)
7164 if (clauses[ii].IsGeneral) {
7165 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7168 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7171 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7174 bc.Report.Warning (1058, 1, c.loc,
7175 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7183 var ct = clauses[ii].CatchType;
7187 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7188 bc.Report.Error (160, c.loc,
7189 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7190 ct.GetSignatureForError ());
7196 return base.Resolve (bc) && ok;
7199 protected sealed override void DoEmit (EmitContext ec)
7201 if (!inside_try_finally)
7202 EmitTryBodyPrepare (ec);
7206 LocalBuilder state_variable = null;
7207 foreach (Catch c in clauses) {
7210 if (catch_sm != null) {
7211 if (state_variable == null) {
7213 // Cannot reuse temp variable because non-catch path assumes the value is 0
7214 // which may not be true for reused local variable
7216 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7219 var index = catch_sm.IndexOf (c);
7223 ec.EmitInt (index + 1);
7224 ec.Emit (OpCodes.Stloc, state_variable);
7228 if (!inside_try_finally)
7229 ec.EndExceptionBlock ();
7231 if (state_variable != null) {
7232 ec.Emit (OpCodes.Ldloc, state_variable);
7234 var labels = new Label [catch_sm.Count + 1];
7235 for (int i = 0; i < labels.Length; ++i) {
7236 labels [i] = ec.DefineLabel ();
7239 var end = ec.DefineLabel ();
7240 ec.Emit (OpCodes.Switch, labels);
7242 // 0 value is default label
7243 ec.MarkLabel (labels [0]);
7244 ec.Emit (OpCodes.Br, end);
7246 var atv = ec.AsyncThrowVariable;
7248 for (int i = 0; i < catch_sm.Count; ++i) {
7249 if (c != null && c.Block.HasReachableClosingBrace)
7250 ec.Emit (OpCodes.Br, end);
7252 ec.MarkLabel (labels [i + 1]);
7254 ec.AsyncThrowVariable = c.Variable;
7257 ec.AsyncThrowVariable = atv;
7263 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7265 var start_fc = fc.BranchDefiniteAssignment ();
7266 var res = Block.FlowAnalysis (fc);
7268 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7270 foreach (var c in clauses) {
7271 fc.BranchDefiniteAssignment (start_fc);
7272 if (!c.FlowAnalysis (fc)) {
7274 try_fc = fc.DefiniteAssignment;
7276 try_fc &= fc.DefiniteAssignment;
7282 fc.DefiniteAssignment = try_fc ?? start_fc;
7287 public override Reachability MarkReachable (Reachability rc)
7289 if (rc.IsUnreachable)
7292 base.MarkReachable (rc);
7294 var tc_rc = Block.MarkReachable (rc);
7296 foreach (var c in clauses)
7297 tc_rc &= c.MarkReachable (rc);
7302 protected override void CloneTo (CloneContext clonectx, Statement t)
7304 TryCatch target = (TryCatch) t;
7306 target.Block = clonectx.LookupBlock (Block);
7307 if (clauses != null){
7308 target.clauses = new List<Catch> ();
7309 foreach (Catch c in clauses)
7310 target.clauses.Add ((Catch) c.Clone (clonectx));
7314 public override object Accept (StructuralVisitor visitor)
7316 return visitor.Visit (this);
7320 public class Using : TryFinallyBlock
7322 public class VariableDeclaration : BlockVariable
7324 Statement dispose_call;
7326 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7331 public VariableDeclaration (LocalVariable li, Location loc)
7338 public VariableDeclaration (Expression expr)
7341 loc = expr.Location;
7347 public bool IsNested { get; private set; }
7351 public void EmitDispose (EmitContext ec)
7353 dispose_call.Emit (ec);
7356 public override bool Resolve (BlockContext bc)
7361 return base.Resolve (bc, false);
7364 public Expression ResolveExpression (BlockContext bc)
7366 var e = Initializer.Resolve (bc);
7370 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7371 Initializer = ResolveInitializer (bc, Variable, e);
7375 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7377 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7378 initializer = initializer.Resolve (bc);
7379 if (initializer == null)
7382 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7383 Arguments args = new Arguments (1);
7384 args.Add (new Argument (initializer));
7385 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7386 if (initializer == null)
7389 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7390 dispose_call = CreateDisposeCall (bc, var);
7391 dispose_call.Resolve (bc);
7393 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7396 if (li == Variable) {
7397 CheckIDiposableConversion (bc, li, initializer);
7398 dispose_call = CreateDisposeCall (bc, li);
7399 dispose_call.Resolve (bc);
7402 return base.ResolveInitializer (bc, li, initializer);
7405 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7409 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7410 if (type.IsNullableType) {
7411 // it's handled in CreateDisposeCall
7415 if (type != InternalType.ErrorType) {
7416 bc.Report.SymbolRelatedToPreviousError (type);
7417 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7418 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7419 type.GetSignatureForError ());
7426 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7428 var target = bc.BuiltinTypes.IDisposable;
7429 var tp = type as TypeParameterSpec;
7431 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7433 return type.ImplementsInterface (target, false);
7436 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7438 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7440 var loc = lv.Location;
7442 var idt = bc.BuiltinTypes.IDisposable;
7443 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7445 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7446 dispose_mg.InstanceExpression = type.IsNullableType ?
7447 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7451 // Hide it from symbol file via null location
7453 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7455 // Add conditional call when disposing possible null variable
7456 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7457 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7462 public void ResolveDeclaratorInitializer (BlockContext bc)
7464 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7467 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7469 for (int i = declarators.Count - 1; i >= 0; --i) {
7470 var d = declarators [i];
7471 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7472 vd.Initializer = d.Initializer;
7474 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7475 vd.dispose_call.Resolve (bc);
7477 stmt = new Using (vd, stmt, d.Variable.Location);
7484 public override object Accept (StructuralVisitor visitor)
7486 return visitor.Visit (this);
7490 VariableDeclaration decl;
7492 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7498 public Using (Expression expr, Statement stmt, Location loc)
7501 this.decl = new VariableDeclaration (expr);
7506 public Expression Expr {
7508 return decl.Variable == null ? decl.Initializer : null;
7512 public BlockVariable Variables {
7520 public override void Emit (EmitContext ec)
7523 // Don't emit sequence point it will be set on variable declaration
7528 protected override void EmitTryBodyPrepare (EmitContext ec)
7531 base.EmitTryBodyPrepare (ec);
7534 protected override void EmitTryBody (EmitContext ec)
7539 public override void EmitFinallyBody (EmitContext ec)
7541 decl.EmitDispose (ec);
7544 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7546 decl.FlowAnalysis (fc);
7547 return stmt.FlowAnalysis (fc);
7550 public override Reachability MarkReachable (Reachability rc)
7552 decl.MarkReachable (rc);
7553 return base.MarkReachable (rc);
7556 public override bool Resolve (BlockContext ec)
7558 VariableReference vr;
7559 bool vr_locked = false;
7561 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7562 if (decl.Variable == null) {
7563 vr = decl.ResolveExpression (ec) as VariableReference;
7565 vr_locked = vr.IsLockedByStatement;
7566 vr.IsLockedByStatement = true;
7569 if (decl.IsNested) {
7570 decl.ResolveDeclaratorInitializer (ec);
7572 if (!decl.Resolve (ec))
7575 if (decl.Declarators != null) {
7576 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7584 var ok = base.Resolve (ec);
7587 vr.IsLockedByStatement = vr_locked;
7592 protected override void CloneTo (CloneContext clonectx, Statement t)
7594 Using target = (Using) t;
7596 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7597 target.stmt = stmt.Clone (clonectx);
7600 public override object Accept (StructuralVisitor visitor)
7602 return visitor.Visit (this);
7607 /// Implementation of the foreach C# statement
7609 public class Foreach : LoopStatement
7611 abstract class IteratorStatement : Statement
7613 protected readonly Foreach for_each;
7615 protected IteratorStatement (Foreach @foreach)
7617 this.for_each = @foreach;
7618 this.loc = @foreach.expr.Location;
7621 protected override void CloneTo (CloneContext clonectx, Statement target)
7623 throw new NotImplementedException ();
7626 public override void Emit (EmitContext ec)
7628 if (ec.EmitAccurateDebugInfo) {
7629 ec.Emit (OpCodes.Nop);
7635 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7637 throw new NotImplementedException ();
7641 sealed class ArrayForeach : IteratorStatement
7643 TemporaryVariableReference[] lengths;
7644 Expression [] length_exprs;
7645 StatementExpression[] counter;
7646 TemporaryVariableReference[] variables;
7648 TemporaryVariableReference copy;
7650 public ArrayForeach (Foreach @foreach, int rank)
7653 counter = new StatementExpression[rank];
7654 variables = new TemporaryVariableReference[rank];
7655 length_exprs = new Expression [rank];
7658 // Only use temporary length variables when dealing with
7659 // multi-dimensional arrays
7662 lengths = new TemporaryVariableReference [rank];
7665 public override bool Resolve (BlockContext ec)
7667 Block variables_block = for_each.variable.Block;
7668 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7671 int rank = length_exprs.Length;
7672 Arguments list = new Arguments (rank);
7673 for (int i = 0; i < rank; i++) {
7674 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7676 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7677 counter[i].Resolve (ec);
7680 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7682 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7683 lengths[i].Resolve (ec);
7685 Arguments args = new Arguments (1);
7686 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7687 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7690 list.Add (new Argument (v));
7693 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7698 if (for_each.type is VarExpr) {
7699 // Infer implicitly typed local variable from foreach array type
7700 var_type = access.Type;
7702 var_type = for_each.type.ResolveAsType (ec);
7704 if (var_type == null)
7707 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7712 for_each.variable.Type = var_type;
7714 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7715 if (variable_ref == null)
7718 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7720 return for_each.body.Resolve (ec);
7723 protected override void DoEmit (EmitContext ec)
7725 copy.EmitAssign (ec, for_each.expr);
7727 int rank = length_exprs.Length;
7728 Label[] test = new Label [rank];
7729 Label[] loop = new Label [rank];
7731 for (int i = 0; i < rank; i++) {
7732 test [i] = ec.DefineLabel ();
7733 loop [i] = ec.DefineLabel ();
7735 if (lengths != null)
7736 lengths [i].EmitAssign (ec, length_exprs [i]);
7739 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7740 for (int i = 0; i < rank; i++) {
7741 variables [i].EmitAssign (ec, zero);
7743 ec.Emit (OpCodes.Br, test [i]);
7744 ec.MarkLabel (loop [i]);
7747 for_each.body.Emit (ec);
7749 ec.MarkLabel (ec.LoopBegin);
7750 ec.Mark (for_each.expr.Location);
7752 for (int i = rank - 1; i >= 0; i--){
7753 counter [i].Emit (ec);
7755 ec.MarkLabel (test [i]);
7756 variables [i].Emit (ec);
7758 if (lengths != null)
7759 lengths [i].Emit (ec);
7761 length_exprs [i].Emit (ec);
7763 ec.Emit (OpCodes.Blt, loop [i]);
7766 ec.MarkLabel (ec.LoopEnd);
7770 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7772 class RuntimeDispose : Using.VariableDeclaration
7774 public RuntimeDispose (LocalVariable lv, Location loc)
7780 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7782 // Defered to runtime check
7785 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7787 var idt = bc.BuiltinTypes.IDisposable;
7790 // Fabricates code like
7792 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7795 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7797 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7798 dispose_variable.CreateReferenceExpression (bc, loc),
7799 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7800 loc), new NullLiteral (loc));
7802 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7804 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7805 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7807 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7808 return new If (idisaposable_test, dispose, loc);
7812 LocalVariable variable;
7814 Statement statement;
7815 ExpressionStatement init;
7816 TemporaryVariableReference enumerator_variable;
7817 bool ambiguous_getenumerator_name;
7819 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7822 this.variable = var;
7826 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7828 rc.Report.SymbolRelatedToPreviousError (enumerator);
7829 rc.Report.Error (202, loc,
7830 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7831 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7834 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7837 // Option 1: Try to match by name GetEnumerator first
7839 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7840 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7842 var mg = mexpr as MethodGroupExpr;
7844 mg.InstanceExpression = expr;
7845 Arguments args = new Arguments (0);
7846 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7848 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7849 if (ambiguous_getenumerator_name)
7852 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7858 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7861 PredefinedMember<MethodSpec> iface_candidate = null;
7862 var ptypes = rc.Module.PredefinedTypes;
7863 var gen_ienumerable = ptypes.IEnumerableGeneric;
7864 if (!gen_ienumerable.Define ())
7865 gen_ienumerable = null;
7867 var ifaces = t.Interfaces;
7868 if (ifaces != null) {
7869 foreach (var iface in ifaces) {
7870 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7871 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7872 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7873 rc.Report.Error (1640, loc,
7874 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7875 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7880 // TODO: Cache this somehow
7881 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7882 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7887 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7888 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7893 if (iface_candidate == null) {
7894 if (expr.Type != InternalType.ErrorType) {
7895 rc.Report.Error (1579, loc,
7896 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7897 expr.Type.GetSignatureForError (), "GetEnumerator");
7903 var method = iface_candidate.Resolve (loc);
7907 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7908 mg.InstanceExpression = expr;
7912 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7914 var ms = MemberCache.FindMember (enumerator.ReturnType,
7915 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7916 BindingRestriction.InstanceOnly) as MethodSpec;
7918 if (ms == null || !ms.IsPublic) {
7919 Error_WrongEnumerator (rc, enumerator);
7923 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7926 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7928 var ps = MemberCache.FindMember (enumerator.ReturnType,
7929 MemberFilter.Property ("Current", null),
7930 BindingRestriction.InstanceOnly) as PropertySpec;
7932 if (ps == null || !ps.IsPublic) {
7933 Error_WrongEnumerator (rc, enumerator);
7940 public override bool Resolve (BlockContext ec)
7942 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7945 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7946 } else if (expr.Type.IsNullableType) {
7947 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7950 var get_enumerator_mg = ResolveGetEnumerator (ec);
7951 if (get_enumerator_mg == null) {
7955 var get_enumerator = get_enumerator_mg.BestCandidate;
7956 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7957 enumerator_variable.Resolve (ec);
7959 // Prepare bool MoveNext ()
7960 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7961 if (move_next_mg == null) {
7965 move_next_mg.InstanceExpression = enumerator_variable;
7967 // Prepare ~T~ Current { get; }
7968 var current_prop = ResolveCurrent (ec, get_enumerator);
7969 if (current_prop == null) {
7973 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7974 if (current_pe == null)
7977 VarExpr ve = for_each.type as VarExpr;
7981 // Source type is dynamic, set element type to dynamic too
7982 variable.Type = ec.BuiltinTypes.Dynamic;
7984 // Infer implicitly typed local variable from foreach enumerable type
7985 variable.Type = current_pe.Type;
7989 // Explicit cast of dynamic collection elements has to be done at runtime
7990 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7993 variable.Type = for_each.type.ResolveAsType (ec);
7995 if (variable.Type == null)
7998 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7999 if (current_pe == null)
8003 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8004 if (variable_ref == null)
8007 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8009 var init = new Invocation.Predefined (get_enumerator_mg, null);
8011 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8012 for_each.body, Location.Null);
8014 var enum_type = enumerator_variable.Type;
8017 // Add Dispose method call when enumerator can be IDisposable
8019 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8020 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8022 // Runtime Dispose check
8024 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8025 vd.Initializer = init;
8026 statement = new Using (vd, statement, Location.Null);
8029 // No Dispose call needed
8031 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8032 this.init.Resolve (ec);
8036 // Static Dispose check
8038 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8039 vd.Initializer = init;
8040 statement = new Using (vd, statement, Location.Null);
8043 return statement.Resolve (ec);
8046 protected override void DoEmit (EmitContext ec)
8048 enumerator_variable.LocalInfo.CreateBuilder (ec);
8051 init.EmitStatement (ec);
8053 statement.Emit (ec);
8056 #region IErrorHandler Members
8058 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8060 ec.Report.SymbolRelatedToPreviousError (best);
8061 ec.Report.Warning (278, 2, expr.Location,
8062 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8063 expr.Type.GetSignatureForError (), "enumerable",
8064 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8066 ambiguous_getenumerator_name = true;
8070 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8075 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8080 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8089 LocalVariable variable;
8093 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8097 this.variable = var;
8103 public Expression Expr {
8104 get { return expr; }
8107 public Expression TypeExpression {
8108 get { return type; }
8111 public LocalVariable Variable {
8112 get { return variable; }
8115 public override Reachability MarkReachable (Reachability rc)
8117 base.MarkReachable (rc);
8119 body.MarkReachable (rc);
8124 public override bool Resolve (BlockContext ec)
8126 expr = expr.Resolve (ec);
8131 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8135 body.AddStatement (Statement);
8137 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8138 Statement = new ArrayForeach (this, 1);
8139 } else if (expr.Type is ArrayContainer) {
8140 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8142 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8143 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8144 expr.ExprClassName);
8148 Statement = new CollectionForeach (this, variable, expr);
8151 return base.Resolve (ec);
8154 protected override void DoEmit (EmitContext ec)
8156 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8157 ec.LoopBegin = ec.DefineLabel ();
8158 ec.LoopEnd = ec.DefineLabel ();
8160 if (!(Statement is Block))
8161 ec.BeginCompilerScope ();
8163 variable.CreateBuilder (ec);
8165 Statement.Emit (ec);
8167 if (!(Statement is Block))
8170 ec.LoopBegin = old_begin;
8171 ec.LoopEnd = old_end;
8174 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8176 expr.FlowAnalysis (fc);
8178 var da = fc.BranchDefiniteAssignment ();
8179 body.FlowAnalysis (fc);
8180 fc.DefiniteAssignment = da;
8184 protected override void CloneTo (CloneContext clonectx, Statement t)
8186 Foreach target = (Foreach) t;
8188 target.type = type.Clone (clonectx);
8189 target.expr = expr.Clone (clonectx);
8190 target.body = (Block) body.Clone (clonectx);
8191 target.Statement = Statement.Clone (clonectx);
8194 public override object Accept (StructuralVisitor visitor)
8196 return visitor.Visit (this);