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;
2460 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2464 bool INamedBlockVariable.IsParameter {
2470 public bool IsReadonly {
2472 return (flags & Flags.ReadonlyMask) != 0;
2476 public Location Location {
2482 public string Name {
2488 public TypeSpec Type {
2499 public void CreateBuilder (EmitContext ec)
2501 if ((flags & Flags.Used) == 0) {
2502 if (VariableInfo == null) {
2503 // Missing flow analysis or wrong variable flags
2504 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2507 if (VariableInfo.IsEverAssigned)
2508 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2510 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2513 if (HoistedVariant != null)
2516 if (builder != null) {
2517 if ((flags & Flags.CompilerGenerated) != 0)
2520 // To avoid Used warning duplicates
2521 throw new InternalErrorException ("Already created variable `{0}'", name);
2525 // All fixed variabled are pinned, a slot has to be alocated
2527 builder = ec.DeclareLocal (Type, IsFixed);
2528 if ((flags & Flags.SymbolFileHidden) == 0)
2529 ec.DefineLocalVariable (name, builder);
2532 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2534 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2535 if (!writeToSymbolFile)
2536 li.flags |= Flags.SymbolFileHidden;
2542 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2544 if (IsConstant && const_value != null)
2545 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2547 return new LocalVariableReference (this, loc);
2550 public void Emit (EmitContext ec)
2552 // TODO: Need something better for temporary variables
2553 if ((flags & Flags.CompilerGenerated) != 0)
2556 ec.Emit (OpCodes.Ldloc, builder);
2559 public void EmitAssign (EmitContext ec)
2561 // TODO: Need something better for temporary variables
2562 if ((flags & Flags.CompilerGenerated) != 0)
2565 ec.Emit (OpCodes.Stloc, builder);
2568 public void EmitAddressOf (EmitContext ec)
2570 // TODO: Need something better for temporary variables
2571 if ((flags & Flags.CompilerGenerated) != 0)
2574 ec.Emit (OpCodes.Ldloca, builder);
2577 public static string GetCompilerGeneratedName (Block block)
2579 // HACK: Debugger depends on the name semantics
2580 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2583 public string GetReadOnlyContext ()
2585 switch (flags & Flags.ReadonlyMask) {
2586 case Flags.FixedVariable:
2587 return "fixed variable";
2588 case Flags.ForeachVariable:
2589 return "foreach iteration variable";
2590 case Flags.UsingVariable:
2591 return "using variable";
2594 throw new InternalErrorException ("Variable is not readonly");
2597 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2599 if (VariableInfo == null)
2600 throw new Exception ();
2602 if (IsAssigned (fc))
2605 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2608 public bool IsAssigned (FlowAnalysisContext fc)
2610 return fc.IsDefinitelyAssigned (VariableInfo);
2613 public void PrepareAssignmentAnalysis (BlockContext bc)
2616 // No need to run assignment analysis for these guys
2618 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2621 VariableInfo = VariableInfo.Create (bc, this);
2625 // Mark the variables as referenced in the user code
2627 public void SetIsUsed ()
2629 flags |= Flags.Used;
2632 public void SetHasAddressTaken ()
2634 flags |= (Flags.AddressTaken | Flags.Used);
2637 public override string ToString ()
2639 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2644 /// Block represents a C# block.
2648 /// This class is used in a number of places: either to represent
2649 /// explicit blocks that the programmer places or implicit blocks.
2651 /// Implicit blocks are used as labels or to introduce variable
2654 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2655 /// they contain extra information that is not necessary on normal blocks.
2657 public class Block : Statement {
2664 HasCapturedVariable = 64,
2665 HasCapturedThis = 1 << 7,
2666 IsExpressionTree = 1 << 8,
2667 CompilerGenerated = 1 << 9,
2668 HasAsyncModifier = 1 << 10,
2670 YieldBlock = 1 << 12,
2671 AwaitBlock = 1 << 13,
2672 FinallyBlock = 1 << 14,
2673 CatchBlock = 1 << 15,
2675 NoFlowAnalysis = 1 << 21,
2676 InitializationEmitted = 1 << 22
2679 public Block Parent;
2680 public Location StartLocation;
2681 public Location EndLocation;
2683 public ExplicitBlock Explicit;
2684 public ParametersBlock ParametersBlock;
2686 protected Flags flags;
2689 // The statements in this block
2691 protected List<Statement> statements;
2693 protected List<Statement> scope_initializers;
2695 int? resolving_init_idx;
2701 public int ID = id++;
2703 static int clone_id_counter;
2707 // int assignable_slots;
2709 public Block (Block parent, Location start, Location end)
2710 : this (parent, 0, start, end)
2714 public Block (Block parent, Flags flags, Location start, Location end)
2716 if (parent != null) {
2717 // the appropriate constructors will fixup these fields
2718 ParametersBlock = parent.ParametersBlock;
2719 Explicit = parent.Explicit;
2722 this.Parent = parent;
2724 this.StartLocation = start;
2725 this.EndLocation = end;
2727 statements = new List<Statement> (4);
2729 this.original = this;
2734 public Block Original {
2743 public bool IsCompilerGenerated {
2744 get { return (flags & Flags.CompilerGenerated) != 0; }
2745 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2749 public bool IsCatchBlock {
2751 return (flags & Flags.CatchBlock) != 0;
2755 public bool IsFinallyBlock {
2757 return (flags & Flags.FinallyBlock) != 0;
2761 public bool Unchecked {
2762 get { return (flags & Flags.Unchecked) != 0; }
2763 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2766 public bool Unsafe {
2767 get { return (flags & Flags.Unsafe) != 0; }
2768 set { flags |= Flags.Unsafe; }
2771 public List<Statement> Statements {
2772 get { return statements; }
2777 public void SetEndLocation (Location loc)
2782 public void AddLabel (LabeledStatement target)
2784 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2787 public void AddLocalName (LocalVariable li)
2789 AddLocalName (li.Name, li);
2792 public void AddLocalName (string name, INamedBlockVariable li)
2794 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2797 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2799 if (reason == null) {
2800 Error_AlreadyDeclared (name, variable);
2804 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2805 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2806 "to `{0}', which is already used in a `{1}' scope to denote something else",
2810 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2812 var pi = variable as ParametersBlock.ParameterInfo;
2814 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2816 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2817 "A local variable named `{0}' is already defined in this scope", name);
2821 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2823 ParametersBlock.TopBlock.Report.Error (412, loc,
2824 "The type parameter name `{0}' is the same as local variable or parameter name",
2829 // It should be used by expressions which require to
2830 // register a statement during resolve process.
2832 public void AddScopeStatement (Statement s)
2834 if (scope_initializers == null)
2835 scope_initializers = new List<Statement> ();
2838 // Simple recursive helper, when resolve scope initializer another
2839 // new scope initializer can be added, this ensures it's initialized
2840 // before existing one. For now this can happen with expression trees
2841 // in base ctor initializer only
2843 if (resolving_init_idx.HasValue) {
2844 scope_initializers.Insert (resolving_init_idx.Value, s);
2845 ++resolving_init_idx;
2847 scope_initializers.Add (s);
2851 public void InsertStatement (int index, Statement s)
2853 statements.Insert (index, s);
2856 public void AddStatement (Statement s)
2861 public LabeledStatement LookupLabel (string name)
2863 return ParametersBlock.GetLabel (name, this);
2866 public override Reachability MarkReachable (Reachability rc)
2868 if (rc.IsUnreachable)
2871 MarkReachableScope (rc);
2873 foreach (var s in statements) {
2874 rc = s.MarkReachable (rc);
2875 if (rc.IsUnreachable) {
2876 if ((flags & Flags.ReachableEnd) != 0)
2877 return new Reachability ();
2883 flags |= Flags.ReachableEnd;
2888 public void MarkReachableScope (Reachability rc)
2890 base.MarkReachable (rc);
2892 if (scope_initializers != null) {
2893 foreach (var si in scope_initializers)
2894 si.MarkReachable (rc);
2898 public override bool Resolve (BlockContext bc)
2900 if ((flags & Flags.Resolved) != 0)
2903 Block prev_block = bc.CurrentBlock;
2904 bc.CurrentBlock = this;
2907 // Compiler generated scope statements
2909 if (scope_initializers != null) {
2910 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2911 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2914 resolving_init_idx = null;
2918 int statement_count = statements.Count;
2919 for (int ix = 0; ix < statement_count; ix++){
2920 Statement s = statements [ix];
2922 if (!s.Resolve (bc)) {
2924 statements [ix] = new EmptyStatement (s.loc);
2929 bc.CurrentBlock = prev_block;
2931 flags |= Flags.Resolved;
2935 protected override void DoEmit (EmitContext ec)
2937 for (int ix = 0; ix < statements.Count; ix++){
2938 statements [ix].Emit (ec);
2942 public override void Emit (EmitContext ec)
2944 if (scope_initializers != null)
2945 EmitScopeInitializers (ec);
2950 protected void EmitScopeInitializers (EmitContext ec)
2952 foreach (Statement s in scope_initializers)
2956 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2958 if (scope_initializers != null) {
2959 foreach (var si in scope_initializers)
2960 si.FlowAnalysis (fc);
2963 return DoFlowAnalysis (fc, 0);
2966 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2968 bool end_unreachable = !reachable;
2969 bool goto_flow_analysis = startIndex != 0;
2970 for (; startIndex < statements.Count; ++startIndex) {
2971 var s = statements[startIndex];
2973 end_unreachable = s.FlowAnalysis (fc);
2974 if (s.IsUnreachable) {
2975 statements [startIndex] = RewriteUnreachableStatement (s);
2980 // Statement end reachability is needed mostly due to goto support. Consider
2989 // X label is reachable only via goto not as another statement after if. We need
2990 // this for flow-analysis only to carry variable info correctly.
2992 if (end_unreachable) {
2993 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2995 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2996 s = statements[startIndex];
2997 if (s is SwitchLabel) {
2998 if (!after_goto_case)
2999 s.FlowAnalysis (fc);
3004 if (s.IsUnreachable) {
3005 s.FlowAnalysis (fc);
3006 statements [startIndex] = RewriteUnreachableStatement (s);
3011 // Idea is to stop after goto case because goto case will always have at least same
3012 // variable assigned as switch case label. This saves a lot for complex goto case tests
3014 if (after_goto_case)
3020 var lb = s as LabeledStatement;
3021 if (lb != null && fc.AddReachedLabel (lb))
3026 // The condition should be true unless there is forward jumping goto
3028 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3031 return !Explicit.HasReachableClosingBrace;
3034 static Statement RewriteUnreachableStatement (Statement s)
3036 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3037 // analysis. Even csc report unreachable warning for it but it's actually used hence
3038 // we try to emulate this behaviour
3046 if (s is BlockVariable || s is EmptyStatement)
3049 return new EmptyStatement (s.loc);
3052 public void ScanGotoJump (Statement label)
3055 for (i = 0; i < statements.Count; ++i) {
3056 if (statements[i] == label)
3060 var rc = new Reachability ();
3061 for (++i; i < statements.Count; ++i) {
3062 var s = statements[i];
3063 rc = s.MarkReachable (rc);
3064 if (rc.IsUnreachable)
3068 flags |= Flags.ReachableEnd;
3071 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3074 for (i = 0; i < statements.Count; ++i) {
3075 if (statements[i] == label)
3079 DoFlowAnalysis (fc, ++i);
3083 public override string ToString ()
3085 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3089 protected override void CloneTo (CloneContext clonectx, Statement t)
3091 Block target = (Block) t;
3093 target.clone_id = ++clone_id_counter;
3096 clonectx.AddBlockMap (this, target);
3097 if (original != this)
3098 clonectx.AddBlockMap (original, target);
3100 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3101 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3104 target.Parent = clonectx.RemapBlockCopy (Parent);
3106 target.statements = new List<Statement> (statements.Count);
3107 foreach (Statement s in statements)
3108 target.statements.Add (s.Clone (clonectx));
3111 public override object Accept (StructuralVisitor visitor)
3113 return visitor.Visit (this);
3117 public class ExplicitBlock : Block
3119 protected AnonymousMethodStorey am_storey;
3121 public ExplicitBlock (Block parent, Location start, Location end)
3122 : this (parent, (Flags) 0, start, end)
3126 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3127 : base (parent, flags, start, end)
3129 this.Explicit = this;
3134 public AnonymousMethodStorey AnonymousMethodStorey {
3140 public bool HasAwait {
3142 return (flags & Flags.AwaitBlock) != 0;
3146 public bool HasCapturedThis {
3148 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3151 return (flags & Flags.HasCapturedThis) != 0;
3156 // Used to indicate that the block has reference to parent
3157 // block and cannot be made static when defining anonymous method
3159 public bool HasCapturedVariable {
3161 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3164 return (flags & Flags.HasCapturedVariable) != 0;
3168 public bool HasReachableClosingBrace {
3170 return (flags & Flags.ReachableEnd) != 0;
3173 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3177 public bool HasYield {
3179 return (flags & Flags.YieldBlock) != 0;
3186 // Creates anonymous method storey in current block
3188 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3191 // Return same story for iterator and async blocks unless we are
3192 // in nested anonymous method
3194 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3195 return ec.CurrentAnonymousMethod.Storey;
3197 if (am_storey == null) {
3198 MemberBase mc = ec.MemberContext as MemberBase;
3201 // Creates anonymous method storey for this block
3203 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3209 public void EmitScopeInitialization (EmitContext ec)
3211 if ((flags & Flags.InitializationEmitted) != 0)
3214 if (am_storey != null) {
3215 DefineStoreyContainer (ec, am_storey);
3216 am_storey.EmitStoreyInstantiation (ec, this);
3219 if (scope_initializers != null)
3220 EmitScopeInitializers (ec);
3222 flags |= Flags.InitializationEmitted;
3225 public override void Emit (EmitContext ec)
3230 EmitScopeInitialization (ec);
3232 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3233 ec.Emit (OpCodes.Nop);
3241 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3242 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3243 ec.Emit (OpCodes.Nop);
3247 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3249 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3250 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3251 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3255 // Creates anonymous method storey
3257 storey.CreateContainer ();
3258 storey.DefineContainer ();
3260 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3263 // Only first storey in path will hold this reference. All children blocks will
3264 // reference it indirectly using $ref field
3266 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3267 if (b.Parent != null) {
3268 var s = b.Parent.Explicit.AnonymousMethodStorey;
3270 storey.HoistedThis = s.HoistedThis;
3275 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3276 if (storey.HoistedThis == null)
3277 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3279 if (storey.HoistedThis != null)
3285 // We are the first storey on path and 'this' has to be hoisted
3287 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3288 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3290 // ThisReferencesFromChildrenBlock holds all reference even if they
3291 // are not on this path. It saves some memory otherwise it'd have to
3292 // be in every explicit block. We run this check to see if the reference
3293 // is valid for this storey
3295 Block block_on_path = ref_block;
3296 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3298 if (block_on_path == null)
3301 if (storey.HoistedThis == null) {
3302 storey.AddCapturedThisField (ec, null);
3305 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3307 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3309 if (b_storey != null) {
3311 // Don't add storey cross reference for `this' when the storey ends up not
3312 // beeing attached to any parent
3314 if (b.ParametersBlock.StateMachine == null) {
3315 AnonymousMethodStorey s = null;
3316 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3317 s = ab.Explicit.AnonymousMethodStorey;
3322 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3324 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3325 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3332 // Stop propagation inside same top block
3334 if (b.ParametersBlock == ParametersBlock.Original) {
3335 b_storey.AddParentStoreyReference (ec, storey);
3336 // b_storey.HoistedThis = storey.HoistedThis;
3340 b = pb = b.ParametersBlock;
3342 pb = b as ParametersBlock;
3345 if (pb != null && pb.StateMachine != null) {
3346 if (pb.StateMachine == storey)
3350 // If we are state machine with no parent. We can hook into parent without additional
3351 // reference and capture this directly
3353 ExplicitBlock parent_storey_block = pb;
3354 while (parent_storey_block.Parent != null) {
3355 parent_storey_block = parent_storey_block.Parent.Explicit;
3356 if (parent_storey_block.AnonymousMethodStorey != null) {
3361 if (parent_storey_block.AnonymousMethodStorey == null) {
3362 if (pb.StateMachine.HoistedThis == null) {
3363 pb.StateMachine.AddCapturedThisField (ec, null);
3364 b.HasCapturedThis = true;
3370 var parent_this_block = pb;
3371 while (parent_this_block.Parent != null) {
3372 parent_this_block = parent_this_block.Parent.ParametersBlock;
3373 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3379 // Add reference to closest storey which holds captured this
3381 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3385 // Add parent storey reference only when this is not captured directly
3387 if (b_storey != null) {
3388 b_storey.AddParentStoreyReference (ec, storey);
3389 b_storey.HoistedThis = storey.HoistedThis;
3396 var ref_blocks = storey.ReferencesFromChildrenBlock;
3397 if (ref_blocks != null) {
3398 foreach (ExplicitBlock ref_block in ref_blocks) {
3399 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3400 if (b.AnonymousMethodStorey != null) {
3401 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3404 // Stop propagation inside same top block
3406 if (b.ParametersBlock == ParametersBlock.Original)
3409 b = b.ParametersBlock;
3412 var pb = b as ParametersBlock;
3413 if (pb != null && pb.StateMachine != null) {
3414 if (pb.StateMachine == storey)
3417 pb.StateMachine.AddParentStoreyReference (ec, storey);
3420 b.HasCapturedVariable = true;
3426 storey.PrepareEmit ();
3427 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3430 public void RegisterAsyncAwait ()
3433 while ((block.flags & Flags.AwaitBlock) == 0) {
3434 block.flags |= Flags.AwaitBlock;
3436 if (block is ParametersBlock)
3439 block = block.Parent.Explicit;
3443 public void RegisterIteratorYield ()
3445 ParametersBlock.TopBlock.IsIterator = true;
3448 while ((block.flags & Flags.YieldBlock) == 0) {
3449 block.flags |= Flags.YieldBlock;
3451 if (block.Parent == null)
3454 block = block.Parent.Explicit;
3458 public void SetCatchBlock ()
3460 flags |= Flags.CatchBlock;
3463 public void SetFinallyBlock ()
3465 flags |= Flags.FinallyBlock;
3468 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3470 tryBlock.statements = statements;
3471 statements = new List<Statement> (1);
3472 statements.Add (tf);
3477 // ParametersBlock was introduced to support anonymous methods
3478 // and lambda expressions
3480 public class ParametersBlock : ExplicitBlock
3482 public class ParameterInfo : INamedBlockVariable
3484 readonly ParametersBlock block;
3486 public VariableInfo VariableInfo;
3489 public ParameterInfo (ParametersBlock block, int index)
3497 public ParametersBlock Block {
3503 Block INamedBlockVariable.Block {
3509 public bool IsDeclared {
3515 public bool IsParameter {
3521 public bool IsLocked {
3530 public Location Location {
3532 return Parameter.Location;
3536 public Parameter Parameter {
3538 return block.Parameters [index];
3542 public TypeSpec ParameterType {
3544 return Parameter.Type;
3550 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3552 return new ParameterReference (this, loc);
3557 // Block is converted into an expression
3559 sealed class BlockScopeExpression : Expression
3562 readonly ParametersBlock block;
3564 public BlockScopeExpression (Expression child, ParametersBlock block)
3570 public override bool ContainsEmitWithAwait ()
3572 return child.ContainsEmitWithAwait ();
3575 public override Expression CreateExpressionTree (ResolveContext ec)
3577 throw new NotSupportedException ();
3580 protected override Expression DoResolve (ResolveContext ec)
3585 child = child.Resolve (ec);
3589 eclass = child.eclass;
3594 public override void Emit (EmitContext ec)
3596 block.EmitScopeInitializers (ec);
3601 protected ParametersCompiled parameters;
3602 protected ParameterInfo[] parameter_info;
3603 protected bool resolved;
3604 protected ToplevelBlock top_block;
3605 protected StateMachine state_machine;
3606 protected Dictionary<string, object> labels;
3608 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3609 : base (parent, 0, start, start)
3611 if (parameters == null)
3612 throw new ArgumentNullException ("parameters");
3614 this.parameters = parameters;
3615 ParametersBlock = this;
3617 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3619 this.top_block = parent.ParametersBlock.top_block;
3620 ProcessParameters ();
3623 protected ParametersBlock (ParametersCompiled parameters, Location start)
3624 : base (null, 0, start, start)
3626 if (parameters == null)
3627 throw new ArgumentNullException ("parameters");
3629 this.parameters = parameters;
3630 ParametersBlock = this;
3634 // It's supposed to be used by method body implementation of anonymous methods
3636 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3637 : base (null, 0, source.StartLocation, source.EndLocation)
3639 this.parameters = parameters;
3640 this.statements = source.statements;
3641 this.scope_initializers = source.scope_initializers;
3643 this.resolved = true;
3644 this.reachable = source.reachable;
3645 this.am_storey = source.am_storey;
3646 this.state_machine = source.state_machine;
3647 this.flags = source.flags & Flags.ReachableEnd;
3649 ParametersBlock = this;
3652 // Overwrite original for comparison purposes when linking cross references
3653 // between anonymous methods
3655 Original = source.Original;
3660 public bool IsAsync {
3662 return (flags & Flags.HasAsyncModifier) != 0;
3665 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3670 // Block has been converted to expression tree
3672 public bool IsExpressionTree {
3674 return (flags & Flags.IsExpressionTree) != 0;
3679 // The parameters for the block.
3681 public ParametersCompiled Parameters {
3687 public StateMachine StateMachine {
3689 return state_machine;
3693 public ToplevelBlock TopBlock {
3702 public bool Resolved {
3704 return (flags & Flags.Resolved) != 0;
3708 public int TemporaryLocalsCount { get; set; }
3713 // Checks whether all `out' parameters have been assigned.
3715 public void CheckControlExit (FlowAnalysisContext fc)
3717 CheckControlExit (fc, fc.DefiniteAssignment);
3720 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3722 if (parameter_info == null)
3725 foreach (var p in parameter_info) {
3726 if (p.VariableInfo == null)
3729 if (p.VariableInfo.IsAssigned (dat))
3732 fc.Report.Error (177, p.Location,
3733 "The out parameter `{0}' must be assigned to before control leaves the current method",
3738 protected override void CloneTo (CloneContext clonectx, Statement t)
3740 base.CloneTo (clonectx, t);
3742 var target = (ParametersBlock) t;
3745 // Clone label statements as well as they contain block reference
3749 if (pb.labels != null) {
3750 target.labels = new Dictionary<string, object> ();
3752 foreach (var entry in pb.labels) {
3753 var list = entry.Value as List<LabeledStatement>;
3756 var list_clone = new List<LabeledStatement> ();
3757 foreach (var lentry in list) {
3758 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3761 target.labels.Add (entry.Key, list_clone);
3763 var labeled = (LabeledStatement) entry.Value;
3764 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3771 if (pb.Parent == null)
3774 pb = pb.Parent.ParametersBlock;
3778 public override Expression CreateExpressionTree (ResolveContext ec)
3780 if (statements.Count == 1) {
3781 Expression expr = statements[0].CreateExpressionTree (ec);
3782 if (scope_initializers != null)
3783 expr = new BlockScopeExpression (expr, this);
3788 return base.CreateExpressionTree (ec);
3791 public override void Emit (EmitContext ec)
3793 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3794 DefineStoreyContainer (ec, state_machine);
3795 state_machine.EmitStoreyInstantiation (ec, this);
3801 public void EmitEmbedded (EmitContext ec)
3803 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3804 DefineStoreyContainer (ec, state_machine);
3805 state_machine.EmitStoreyInstantiation (ec, this);
3811 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3813 var res = base.DoFlowAnalysis (fc);
3815 if (HasReachableClosingBrace)
3816 CheckControlExit (fc);
3821 public LabeledStatement GetLabel (string name, Block block)
3824 // Cloned parameters blocks can have their own cloned version of top-level labels
3826 if (labels == null) {
3828 return Parent.ParametersBlock.GetLabel (name, block);
3834 if (!labels.TryGetValue (name, out value)) {
3838 var label = value as LabeledStatement;
3840 if (label != null) {
3841 if (IsLabelVisible (label, b))
3845 List<LabeledStatement> list = (List<LabeledStatement>) value;
3846 for (int i = 0; i < list.Count; ++i) {
3848 if (IsLabelVisible (label, b))
3856 static bool IsLabelVisible (LabeledStatement label, Block b)
3859 if (label.Block == b)
3862 } while (b != null);
3867 public ParameterInfo GetParameterInfo (Parameter p)
3869 for (int i = 0; i < parameters.Count; ++i) {
3870 if (parameters[i] == p)
3871 return parameter_info[i];
3874 throw new ArgumentException ("Invalid parameter");
3877 public ParameterReference GetParameterReference (int index, Location loc)
3879 return new ParameterReference (parameter_info[index], loc);
3882 public Statement PerformClone ()
3884 CloneContext clonectx = new CloneContext ();
3885 return Clone (clonectx);
3888 protected void ProcessParameters ()
3890 if (parameters.Count == 0)
3893 parameter_info = new ParameterInfo[parameters.Count];
3894 for (int i = 0; i < parameter_info.Length; ++i) {
3895 var p = parameters.FixedParameters[i];
3899 // TODO: Should use Parameter only and more block there
3900 parameter_info[i] = new ParameterInfo (this, i);
3902 AddLocalName (p.Name, parameter_info[i]);
3906 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3908 var src = stmt.Block;
3911 // Cannot remap label block if the label was not yet cloned which
3912 // can happen in case of anonymous method inside anoynymous method
3913 // with a label. But in this case we don't care because goto cannot
3914 // jump of out anonymous method
3916 if (src.ParametersBlock != this)
3919 var src_stmts = src.Statements;
3920 for (int i = 0; i < src_stmts.Count; ++i) {
3921 if (src_stmts[i] == stmt)
3922 return (LabeledStatement) dst.Statements[i];
3925 throw new InternalErrorException ("Should never be reached");
3928 public override bool Resolve (BlockContext bc)
3930 // TODO: if ((flags & Flags.Resolved) != 0)
3937 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3938 flags |= Flags.IsExpressionTree;
3941 PrepareAssignmentAnalysis (bc);
3943 if (!base.Resolve (bc))
3946 } catch (Exception e) {
3947 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3950 if (bc.CurrentBlock != null) {
3951 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3953 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3958 // If an asynchronous body of F is either an expression classified as nothing, or a
3959 // statement block where no return statements have expressions, the inferred return type is Task
3962 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3963 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3964 am.ReturnTypeInference = null;
3965 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3973 void PrepareAssignmentAnalysis (BlockContext bc)
3975 for (int i = 0; i < parameters.Count; ++i) {
3976 var par = parameters.FixedParameters[i];
3978 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3981 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3985 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3987 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3988 var stateMachine = new IteratorStorey (iterator);
3990 state_machine = stateMachine;
3991 iterator.SetStateMachine (stateMachine);
3993 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3994 tlb.Original = this;
3995 tlb.state_machine = stateMachine;
3996 tlb.AddStatement (new Return (iterator, iterator.Location));
4000 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4002 for (int i = 0; i < parameters.Count; i++) {
4003 Parameter p = parameters[i];
4004 Parameter.Modifier mod = p.ModFlags;
4005 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4006 host.Compiler.Report.Error (1988, p.Location,
4007 "Async methods cannot have ref or out parameters");
4011 if (p is ArglistParameter) {
4012 host.Compiler.Report.Error (4006, p.Location,
4013 "__arglist is not allowed in parameter list of async methods");
4017 if (parameters.Types[i].IsPointer) {
4018 host.Compiler.Report.Error (4005, p.Location,
4019 "Async methods cannot have unsafe parameters");
4025 host.Compiler.Report.Warning (1998, 1, loc,
4026 "Async block lacks `await' operator and will run synchronously");
4029 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4030 var initializer = new AsyncInitializer (this, host, block_type);
4031 initializer.Type = block_type;
4032 initializer.DelegateType = delegateType;
4034 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4036 state_machine = stateMachine;
4037 initializer.SetStateMachine (stateMachine);
4039 const Flags flags = Flags.CompilerGenerated;
4041 var b = this is ToplevelBlock ?
4042 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4043 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4046 b.state_machine = stateMachine;
4047 b.AddStatement (new AsyncInitializerStatement (initializer));
4055 public class ToplevelBlock : ParametersBlock
4057 LocalVariable this_variable;
4058 CompilerContext compiler;
4059 Dictionary<string, object> names;
4061 List<ExplicitBlock> this_references;
4063 public ToplevelBlock (CompilerContext ctx, Location loc)
4064 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4068 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4069 : base (parameters, start)
4071 this.compiler = ctx;
4075 ProcessParameters ();
4079 // Recreates a top level block from parameters block. Used for
4080 // compiler generated methods where the original block comes from
4081 // explicit child block. This works for already resolved blocks
4082 // only to ensure we resolve them in the correct flow order
4084 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4085 : base (source, parameters)
4087 this.compiler = source.TopBlock.compiler;
4091 public bool IsIterator {
4093 return (flags & Flags.Iterator) != 0;
4096 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4100 public Report Report {
4102 return compiler.Report;
4107 // Used by anonymous blocks to track references of `this' variable
4109 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4111 return this_references;
4116 // Returns the "this" instance variable of this block.
4117 // See AddThisVariable() for more information.
4119 public LocalVariable ThisVariable {
4121 return this_variable;
4125 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4128 names = new Dictionary<string, object> ();
4131 if (!names.TryGetValue (name, out value)) {
4132 names.Add (name, li);
4136 INamedBlockVariable existing = value as INamedBlockVariable;
4137 List<INamedBlockVariable> existing_list;
4138 if (existing != null) {
4139 existing_list = new List<INamedBlockVariable> ();
4140 existing_list.Add (existing);
4141 names[name] = existing_list;
4143 existing_list = (List<INamedBlockVariable>) value;
4147 // A collision checking between local names
4149 var variable_block = li.Block.Explicit;
4150 for (int i = 0; i < existing_list.Count; ++i) {
4151 existing = existing_list[i];
4152 Block b = existing.Block.Explicit;
4154 // Collision at same level
4155 if (variable_block == b) {
4156 li.Block.Error_AlreadyDeclared (name, li);
4160 // Collision with parent
4161 Block parent = variable_block;
4162 while ((parent = parent.Parent) != null) {
4164 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4165 i = existing_list.Count;
4170 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4171 // Collision with children
4172 while ((b = b.Parent) != null) {
4173 if (variable_block == b) {
4174 li.Block.Error_AlreadyDeclared (name, li, "child");
4175 i = existing_list.Count;
4182 existing_list.Add (li);
4185 public void AddLabel (string name, LabeledStatement label)
4188 labels = new Dictionary<string, object> ();
4191 if (!labels.TryGetValue (name, out value)) {
4192 labels.Add (name, label);
4196 LabeledStatement existing = value as LabeledStatement;
4197 List<LabeledStatement> existing_list;
4198 if (existing != null) {
4199 existing_list = new List<LabeledStatement> ();
4200 existing_list.Add (existing);
4201 labels[name] = existing_list;
4203 existing_list = (List<LabeledStatement>) value;
4207 // A collision checking between labels
4209 for (int i = 0; i < existing_list.Count; ++i) {
4210 existing = existing_list[i];
4211 Block b = existing.Block;
4213 // Collision at same level
4214 if (label.Block == b) {
4215 Report.SymbolRelatedToPreviousError (existing.loc, name);
4216 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4220 // Collision with parent
4222 while ((b = b.Parent) != null) {
4223 if (existing.Block == b) {
4224 Report.Error (158, label.loc,
4225 "The label `{0}' shadows another label by the same name in a contained scope", name);
4226 i = existing_list.Count;
4231 // Collision with with children
4233 while ((b = b.Parent) != null) {
4234 if (label.Block == b) {
4235 Report.Error (158, label.loc,
4236 "The label `{0}' shadows another label by the same name in a contained scope", name);
4237 i = existing_list.Count;
4243 existing_list.Add (label);
4246 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4248 if (this_references == null)
4249 this_references = new List<ExplicitBlock> ();
4251 if (!this_references.Contains (block))
4252 this_references.Add (block);
4255 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4257 this_references.Remove (block);
4261 // Creates an arguments set from all parameters, useful for method proxy calls
4263 public Arguments GetAllParametersArguments ()
4265 int count = parameters.Count;
4266 Arguments args = new Arguments (count);
4267 for (int i = 0; i < count; ++i) {
4268 var pi = parameter_info[i];
4269 var arg_expr = GetParameterReference (i, pi.Location);
4271 Argument.AType atype_modifier;
4272 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4273 case Parameter.Modifier.REF:
4274 atype_modifier = Argument.AType.Ref;
4276 case Parameter.Modifier.OUT:
4277 atype_modifier = Argument.AType.Out;
4284 args.Add (new Argument (arg_expr, atype_modifier));
4291 // Lookup inside a block, the returned value can represent 3 states
4293 // true+variable: A local name was found and it's valid
4294 // false+variable: A local name was found in a child block only
4295 // false+null: No local name was found
4297 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4303 if (!names.TryGetValue (name, out value))
4306 variable = value as INamedBlockVariable;
4308 if (variable != null) {
4310 if (variable.Block == b.Original)
4314 } while (b != null);
4322 } while (b != null);
4324 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4325 for (int i = 0; i < list.Count; ++i) {
4328 if (variable.Block == b.Original)
4332 } while (b != null);
4340 } while (b != null);
4350 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4352 if (block.names != null) {
4353 foreach (var n in block.names) {
4354 var variable = n.Value as INamedBlockVariable;
4355 if (variable != null) {
4356 if (variable.Block.ParametersBlock == pb)
4357 AddLocalName (n.Key, variable, false);
4361 foreach (var v in (List<INamedBlockVariable>) n.Value)
4362 if (v.Block.ParametersBlock == pb)
4363 AddLocalName (n.Key, v, false);
4369 // This is used by non-static `struct' constructors which do not have an
4370 // initializer - in this case, the constructor must initialize all of the
4371 // struct's fields. To do this, we add a "this" variable and use the flow
4372 // analysis code to ensure that it's been fully initialized before control
4373 // leaves the constructor.
4375 public void AddThisVariable (BlockContext bc)
4377 if (this_variable != null)
4378 throw new InternalErrorException (StartLocation.ToString ());
4380 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4381 this_variable.Type = bc.CurrentType;
4382 this_variable.PrepareAssignmentAnalysis (bc);
4385 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4388 // If we're a non-static struct constructor which doesn't have an
4389 // initializer, then we must initialize all of the struct's fields.
4391 if (this_variable != null)
4392 this_variable.IsThisAssigned (fc, this);
4394 base.CheckControlExit (fc, dat);
4397 public override void Emit (EmitContext ec)
4399 if (Report.Errors > 0)
4403 if (IsCompilerGenerated) {
4404 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4412 // If `HasReturnLabel' is set, then we already emitted a
4413 // jump to the end of the method, so we must emit a `ret'
4416 // Unfortunately, System.Reflection.Emit automatically emits
4417 // a leave to the end of a finally block. This is a problem
4418 // if no code is following the try/finally block since we may
4419 // jump to a point after the end of the method.
4420 // As a workaround, we're always creating a return label in
4423 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4424 if (ec.HasReturnLabel)
4425 ec.MarkLabel (ec.ReturnLabel);
4427 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4428 ec.Mark (EndLocation);
4430 if (ec.ReturnType.Kind != MemberKind.Void)
4431 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4433 ec.Emit (OpCodes.Ret);
4436 } catch (Exception e) {
4437 throw new InternalErrorException (e, StartLocation);
4441 public bool Resolve (BlockContext bc, IMethodData md)
4446 var errors = bc.Report.Errors;
4450 if (bc.Report.Errors > errors)
4453 MarkReachable (new Reachability ());
4455 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4456 // TODO: var md = bc.CurrentMemberDefinition;
4457 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4460 if ((flags & Flags.NoFlowAnalysis) != 0)
4463 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4466 } catch (Exception e) {
4467 throw new InternalErrorException (e, StartLocation);
4474 public class SwitchLabel : Statement
4482 // if expr == null, then it is the default case.
4484 public SwitchLabel (Expression expr, Location l)
4490 public bool IsDefault {
4492 return label == null;
4496 public Expression Label {
4502 public Location Location {
4508 public Constant Converted {
4517 public bool PatternMatching { get; set; }
4519 public bool SectionStart { get; set; }
4521 public Label GetILLabel (EmitContext ec)
4523 if (il_label == null){
4524 il_label = ec.DefineLabel ();
4527 return il_label.Value;
4530 protected override void DoEmit (EmitContext ec)
4532 ec.MarkLabel (GetILLabel (ec));
4535 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4540 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4544 public override bool Resolve (BlockContext bc)
4546 if (ResolveAndReduce (bc))
4547 bc.Switch.RegisterLabel (bc, this);
4553 // Resolves the expression, reduces it to a literal if possible
4554 // and then converts it to the requested type.
4556 bool ResolveAndReduce (BlockContext bc)
4561 var switch_statement = bc.Switch;
4563 if (PatternMatching) {
4564 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4565 return label != null;
4568 var c = label.ResolveLabelConstant (bc);
4572 if (switch_statement.IsNullable && c is NullLiteral) {
4577 if (switch_statement.IsPatternMatching) {
4578 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4582 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4583 return converted != null;
4586 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4588 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4589 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4592 protected override void CloneTo (CloneContext clonectx, Statement target)
4594 var t = (SwitchLabel) target;
4596 t.label = label.Clone (clonectx);
4599 public override object Accept (StructuralVisitor visitor)
4601 return visitor.Visit (this);
4604 public string GetSignatureForError ()
4607 if (converted == null)
4610 label = converted.GetValueAsLiteral ();
4612 return string.Format ("case {0}:", label);
4616 public class Switch : LoopStatement
4618 // structure used to hold blocks of keys while calculating table switch
4619 sealed class LabelsRange : IComparable<LabelsRange>
4621 public readonly long min;
4623 public readonly List<long> label_values;
4625 public LabelsRange (long value)
4628 label_values = new List<long> ();
4629 label_values.Add (value);
4632 public LabelsRange (long min, long max, ICollection<long> values)
4636 this.label_values = new List<long> (values);
4641 return max - min + 1;
4645 public bool AddValue (long value)
4647 var gap = value - min + 1;
4648 // Ensure the range has > 50% occupancy
4649 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4653 label_values.Add (value);
4657 public int CompareTo (LabelsRange other)
4659 int nLength = label_values.Count;
4660 int nLengthOther = other.label_values.Count;
4661 if (nLengthOther == nLength)
4662 return (int) (other.min - min);
4664 return nLength - nLengthOther;
4668 sealed class DispatchStatement : Statement
4670 readonly Switch body;
4672 public DispatchStatement (Switch body)
4677 protected override void CloneTo (CloneContext clonectx, Statement target)
4679 throw new NotImplementedException ();
4682 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4687 protected override void DoEmit (EmitContext ec)
4689 body.EmitDispatch (ec);
4693 class MissingBreak : Statement
4695 readonly SwitchLabel label;
4697 public MissingBreak (SwitchLabel sl)
4703 public bool FallOut { get; set; }
4705 protected override void DoEmit (EmitContext ec)
4709 protected override void CloneTo (CloneContext clonectx, Statement target)
4713 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4716 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4717 label.GetSignatureForError ());
4719 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4720 label.GetSignatureForError ());
4726 public Expression Expr;
4729 // Mapping of all labels to their SwitchLabels
4731 Dictionary<long, SwitchLabel> labels;
4732 Dictionary<string, SwitchLabel> string_labels;
4733 List<SwitchLabel> case_labels;
4735 List<Tuple<GotoCase, Constant>> goto_cases;
4736 List<DefiniteAssignmentBitSet> end_reachable_das;
4739 /// The governing switch type
4741 public TypeSpec SwitchType;
4743 Expression new_expr;
4745 SwitchLabel case_null;
4746 SwitchLabel case_default;
4748 Label defaultLabel, nullLabel;
4749 VariableReference value;
4750 ExpressionStatement string_dictionary;
4751 FieldExpr switch_cache_field;
4752 ExplicitBlock block;
4756 // Nullable Types support
4758 Nullable.Unwrap unwrap;
4760 public Switch (Expression e, ExplicitBlock block, Location l)
4768 public SwitchLabel ActiveLabel { get; set; }
4770 public ExplicitBlock Block {
4776 public SwitchLabel DefaultLabel {
4778 return case_default;
4782 public bool IsNullable {
4784 return unwrap != null;
4788 public bool IsPatternMatching {
4790 return new_expr == null && SwitchType != null;
4794 public List<SwitchLabel> RegisteredLabels {
4800 public VariableReference ExpressionValue {
4807 // Determines the governing type for a switch. The returned
4808 // expression might be the expression from the switch, or an
4809 // expression that includes any potential conversions to
4811 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4813 switch (expr.Type.BuiltinType) {
4814 case BuiltinTypeSpec.Type.Byte:
4815 case BuiltinTypeSpec.Type.SByte:
4816 case BuiltinTypeSpec.Type.UShort:
4817 case BuiltinTypeSpec.Type.Short:
4818 case BuiltinTypeSpec.Type.UInt:
4819 case BuiltinTypeSpec.Type.Int:
4820 case BuiltinTypeSpec.Type.ULong:
4821 case BuiltinTypeSpec.Type.Long:
4822 case BuiltinTypeSpec.Type.Char:
4823 case BuiltinTypeSpec.Type.String:
4824 case BuiltinTypeSpec.Type.Bool:
4828 if (expr.Type.IsEnum)
4832 // Try to find a *user* defined implicit conversion.
4834 // If there is no implicit conversion, or if there are multiple
4835 // conversions, we have to report an error
4837 Expression converted = null;
4838 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4840 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4843 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4844 Convert.UserConversionRestriction.ProbingOnly;
4847 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4849 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4854 // Ignore over-worked ImplicitUserConversions that do
4855 // an implicit conversion in addition to the user conversion.
4857 var uc = e as UserCast;
4861 if (converted != null){
4862 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4871 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4873 var types = module.Compiler.BuiltinTypes;
4875 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4876 TypeSpec[] stypes = new[] {
4889 if (nullable != null) {
4891 Array.Resize (ref stypes, stypes.Length + 9);
4893 for (int i = 0; i < 9; ++i) {
4894 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4901 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4903 case_labels.Add (sl);
4906 if (case_default != null) {
4907 sl.Error_AlreadyOccurs (rc, case_default);
4915 if (sl.Converted == null)
4919 if (string_labels != null) {
4920 string string_value = sl.Converted.GetValue () as string;
4921 if (string_value == null)
4924 string_labels.Add (string_value, sl);
4926 if (sl.Converted.IsNull) {
4929 labels.Add (sl.Converted.GetValueAsLong (), sl);
4932 } catch (ArgumentException) {
4933 if (string_labels != null)
4934 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4936 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4941 // This method emits code for a lookup-based switch statement (non-string)
4942 // Basically it groups the cases into blocks that are at least half full,
4943 // and then spits out individual lookup opcodes for each block.
4944 // It emits the longest blocks first, and short blocks are just
4945 // handled with direct compares.
4947 void EmitTableSwitch (EmitContext ec, Expression val)
4949 if (labels != null && labels.Count > 0) {
4950 List<LabelsRange> ranges;
4951 if (string_labels != null) {
4952 // We have done all hard work for string already
4953 // setup single range only
4954 ranges = new List<LabelsRange> (1);
4955 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4957 var element_keys = new long[labels.Count];
4958 labels.Keys.CopyTo (element_keys, 0);
4959 Array.Sort (element_keys);
4962 // Build possible ranges of switch labes to reduce number
4965 ranges = new List<LabelsRange> (element_keys.Length);
4966 var range = new LabelsRange (element_keys[0]);
4968 for (int i = 1; i < element_keys.Length; ++i) {
4969 var l = element_keys[i];
4970 if (range.AddValue (l))
4973 range = new LabelsRange (l);
4977 // sort the blocks so we can tackle the largest ones first
4981 Label lbl_default = defaultLabel;
4982 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4984 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4985 LabelsRange kb = ranges[range_index];
4986 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4988 // Optimize small ranges using simple equality check
4989 if (kb.Range <= 2) {
4990 foreach (var key in kb.label_values) {
4991 SwitchLabel sl = labels[key];
4992 if (sl == case_default || sl == case_null)
4995 if (sl.Converted.IsZeroInteger) {
4996 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4999 sl.Converted.Emit (ec);
5000 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5004 // TODO: if all the keys in the block are the same and there are
5005 // no gaps/defaults then just use a range-check.
5006 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5007 // TODO: optimize constant/I4 cases
5009 // check block range (could be > 2^31)
5011 ec.EmitLong (kb.min);
5012 ec.Emit (OpCodes.Blt, lbl_default);
5015 ec.EmitLong (kb.max);
5016 ec.Emit (OpCodes.Bgt, lbl_default);
5021 ec.EmitLong (kb.min);
5022 ec.Emit (OpCodes.Sub);
5025 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5029 int first = (int) kb.min;
5032 ec.Emit (OpCodes.Sub);
5033 } else if (first < 0) {
5034 ec.EmitInt (-first);
5035 ec.Emit (OpCodes.Add);
5039 // first, build the list of labels for the switch
5041 long cJumps = kb.Range;
5042 Label[] switch_labels = new Label[cJumps];
5043 for (int iJump = 0; iJump < cJumps; iJump++) {
5044 var key = kb.label_values[iKey];
5045 if (key == kb.min + iJump) {
5046 switch_labels[iJump] = labels[key].GetILLabel (ec);
5049 switch_labels[iJump] = lbl_default;
5053 // emit the switch opcode
5054 ec.Emit (OpCodes.Switch, switch_labels);
5057 // mark the default for this block
5058 if (range_index != 0)
5059 ec.MarkLabel (lbl_default);
5062 // the last default just goes to the end
5063 if (ranges.Count > 0)
5064 ec.Emit (OpCodes.Br, lbl_default);
5068 public SwitchLabel FindLabel (Constant value)
5070 SwitchLabel sl = null;
5072 if (string_labels != null) {
5073 string s = value.GetValue () as string;
5075 if (case_null != null)
5077 else if (case_default != null)
5080 string_labels.TryGetValue (s, out sl);
5083 if (value is NullLiteral) {
5086 labels.TryGetValue (value.GetValueAsLong (), out sl);
5090 if (sl == null || sl.SectionStart)
5094 // Always return section start, it simplifies handling of switch labels
5096 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5097 var cs = case_labels [idx];
5098 if (cs.SectionStart)
5103 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5105 Expr.FlowAnalysis (fc);
5107 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5108 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5109 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5111 block.FlowAnalysis (fc);
5113 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5115 if (end_reachable_das != null) {
5116 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5117 InitialDefinitiveAssignment |= sections_das;
5118 end_reachable_das = null;
5121 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5123 return case_default != null && !end_reachable;
5126 public override bool Resolve (BlockContext ec)
5128 Expr = Expr.Resolve (ec);
5133 // LAMESPEC: User conversion from non-nullable governing type has a priority
5135 new_expr = SwitchGoverningType (ec, Expr, false);
5137 if (new_expr == null) {
5138 if (Expr.Type.IsNullableType) {
5139 unwrap = Nullable.Unwrap.Create (Expr, false);
5144 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5145 // involving nullable Expr and nullable governing type is
5147 new_expr = SwitchGoverningType (ec, unwrap, true);
5151 Expression switch_expr;
5152 if (new_expr == null) {
5153 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5154 if (Expr.Type != InternalType.ErrorType) {
5155 ec.Report.Error (151, loc,
5156 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5157 Expr.Type.GetSignatureForError ());
5164 SwitchType = Expr.Type;
5166 switch_expr = new_expr;
5167 SwitchType = new_expr.Type;
5168 if (SwitchType.IsNullableType) {
5169 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5170 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5173 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5174 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5178 if (block.Statements.Count == 0)
5181 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5182 string_labels = new Dictionary<string, SwitchLabel> ();
5184 labels = new Dictionary<long, SwitchLabel> ();
5188 var constant = switch_expr as Constant;
5191 // Don't need extra variable for constant switch or switch with
5192 // only default case
5194 if (constant == null) {
5196 // Store switch expression for comparison purposes
5198 value = switch_expr as VariableReference;
5199 if (value == null && !HasOnlyDefaultSection ()) {
5200 var current_block = ec.CurrentBlock;
5201 ec.CurrentBlock = Block;
5202 // Create temporary variable inside switch scope
5203 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5205 ec.CurrentBlock = current_block;
5209 case_labels = new List<SwitchLabel> ();
5211 Switch old_switch = ec.Switch;
5213 var parent_los = ec.EnclosingLoopOrSwitch;
5214 ec.EnclosingLoopOrSwitch = this;
5216 var ok = Statement.Resolve (ec);
5218 ec.EnclosingLoopOrSwitch = parent_los;
5219 ec.Switch = old_switch;
5222 // Check if all goto cases are valid. Needs to be done after switch
5223 // is resolved because goto can jump forward in the scope.
5225 if (goto_cases != null) {
5226 foreach (var gc in goto_cases) {
5227 if (gc.Item1 == null) {
5228 if (DefaultLabel == null) {
5229 Goto.Error_UnknownLabel (ec, "default", loc);
5235 var sl = FindLabel (gc.Item2);
5237 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5239 gc.Item1.Label = sl;
5247 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5248 ResolveStringSwitchMap (ec);
5252 // Anonymous storey initialization has to happen before
5253 // any generated switch dispatch
5255 block.InsertStatement (0, new DispatchStatement (this));
5260 bool HasOnlyDefaultSection ()
5262 for (int i = 0; i < block.Statements.Count; ++i) {
5263 var s = block.Statements[i] as SwitchLabel;
5265 if (s == null || s.IsDefault)
5274 public override Reachability MarkReachable (Reachability rc)
5276 if (rc.IsUnreachable)
5279 base.MarkReachable (rc);
5281 block.MarkReachableScope (rc);
5283 if (block.Statements.Count == 0)
5286 SwitchLabel constant_label = null;
5287 var constant = new_expr as Constant;
5289 if (constant != null) {
5290 constant_label = FindLabel (constant) ?? case_default;
5291 if (constant_label == null) {
5292 block.Statements.RemoveAt (0);
5297 var section_rc = new Reachability ();
5298 SwitchLabel prev_label = null;
5300 for (int i = 0; i < block.Statements.Count; ++i) {
5301 var s = block.Statements[i];
5302 var sl = s as SwitchLabel;
5304 if (sl != null && sl.SectionStart) {
5306 // Section is marked already via goto case
5308 if (!sl.IsUnreachable) {
5309 section_rc = new Reachability ();
5313 if (constant_label != null && constant_label != sl)
5314 section_rc = Reachability.CreateUnreachable ();
5315 else if (section_rc.IsUnreachable) {
5316 section_rc = new Reachability ();
5318 if (prev_label != null) {
5319 sl.SectionStart = false;
5320 s = new MissingBreak (prev_label);
5321 s.MarkReachable (rc);
5322 block.Statements.Insert (i - 1, s);
5330 section_rc = s.MarkReachable (section_rc);
5333 if (!section_rc.IsUnreachable && prev_label != null) {
5334 prev_label.SectionStart = false;
5335 var s = new MissingBreak (prev_label) {
5339 s.MarkReachable (rc);
5340 block.Statements.Add (s);
5344 // Reachability can affect parent only when all possible paths are handled but
5345 // we still need to run reachability check on switch body to check for fall-through
5347 if (case_default == null && constant_label == null)
5351 // We have at least one local exit from the switch
5356 return Reachability.CreateUnreachable ();
5359 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5361 if (goto_cases == null)
5362 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5364 goto_cases.Add (Tuple.Create (gotoCase, value));
5368 // Converts string switch into string hashtable
5370 void ResolveStringSwitchMap (ResolveContext ec)
5372 FullNamedExpression string_dictionary_type;
5373 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5374 string_dictionary_type = new TypeExpression (
5375 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5376 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5378 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5379 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5381 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5385 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5386 Field field = new Field (ctype, string_dictionary_type,
5387 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5388 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5389 if (!field.Define ())
5391 ctype.AddField (field);
5393 var init = new List<Expression> ();
5395 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5396 string value = null;
5398 foreach (SwitchLabel sl in case_labels) {
5400 if (sl.SectionStart)
5401 labels.Add (++counter, sl);
5403 if (sl == case_default || sl == case_null)
5406 value = (string) sl.Converted.GetValue ();
5407 var init_args = new List<Expression> (2);
5408 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5410 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5411 init_args.Add (sl.Converted);
5413 init.Add (new CollectionElementInitializer (init_args, loc));
5416 Arguments args = new Arguments (1);
5417 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5418 Expression initializer = new NewInitialize (string_dictionary_type, args,
5419 new CollectionOrObjectInitializers (init, loc), loc);
5421 switch_cache_field = new FieldExpr (field, loc);
5422 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5425 void DoEmitStringSwitch (EmitContext ec)
5427 Label l_initialized = ec.DefineLabel ();
5430 // Skip initialization when value is null
5432 value.EmitBranchable (ec, nullLabel, false);
5435 // Check if string dictionary is initialized and initialize
5437 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5438 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5439 string_dictionary.EmitStatement (ec);
5441 ec.MarkLabel (l_initialized);
5443 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5445 ResolveContext rc = new ResolveContext (ec.MemberContext);
5447 if (switch_cache_field.Type.IsGeneric) {
5448 Arguments get_value_args = new Arguments (2);
5449 get_value_args.Add (new Argument (value));
5450 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5451 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5452 if (get_item == null)
5456 // A value was not found, go to default case
5458 get_item.EmitBranchable (ec, defaultLabel, false);
5460 Arguments get_value_args = new Arguments (1);
5461 get_value_args.Add (new Argument (value));
5463 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5464 if (get_item == null)
5467 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5468 get_item_object.EmitAssign (ec, get_item, true, false);
5469 ec.Emit (OpCodes.Brfalse, defaultLabel);
5471 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5472 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5474 get_item_int.EmitStatement (ec);
5475 get_item_object.Release (ec);
5478 EmitTableSwitch (ec, string_switch_variable);
5479 string_switch_variable.Release (ec);
5483 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5485 void EmitShortSwitch (EmitContext ec)
5487 MethodSpec equal_method = null;
5488 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5489 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5492 if (equal_method != null) {
5493 value.EmitBranchable (ec, nullLabel, false);
5496 for (int i = 0; i < case_labels.Count; ++i) {
5497 var label = case_labels [i];
5498 if (label == case_default || label == case_null)
5501 var constant = label.Converted;
5503 if (constant == null) {
5504 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5508 if (equal_method != null) {
5512 var call = new CallEmitter ();
5513 call.EmitPredefined (ec, equal_method, new Arguments (0));
5514 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5518 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5519 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5525 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5528 ec.Emit (OpCodes.Br, defaultLabel);
5531 void EmitDispatch (EmitContext ec)
5533 if (IsPatternMatching) {
5534 EmitShortSwitch (ec);
5538 if (value == null) {
5540 // Constant switch, we've already done the work if there is only 1 label
5544 foreach (var sl in case_labels) {
5545 if (sl.IsUnreachable)
5548 if (reachable++ > 0) {
5549 var constant = (Constant) new_expr;
5550 var constant_label = FindLabel (constant) ?? case_default;
5552 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5560 if (string_dictionary != null) {
5561 DoEmitStringSwitch (ec);
5562 } else if (case_labels.Count < 4 || string_labels != null) {
5563 EmitShortSwitch (ec);
5565 EmitTableSwitch (ec, value);
5569 protected override void DoEmit (EmitContext ec)
5572 // Setup the codegen context
5574 Label old_end = ec.LoopEnd;
5575 Switch old_switch = ec.Switch;
5577 ec.LoopEnd = ec.DefineLabel ();
5580 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5581 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5583 if (value != null) {
5586 var switch_expr = new_expr ?? Expr;
5588 unwrap.EmitCheck (ec);
5589 ec.Emit (OpCodes.Brfalse, nullLabel);
5590 value.EmitAssign (ec, switch_expr, false, false);
5591 } else if (switch_expr != value) {
5592 value.EmitAssign (ec, switch_expr, false, false);
5597 // Next statement is compiler generated we don't need extra
5598 // nop when we can use the statement for sequence point
5600 ec.Mark (block.StartLocation);
5601 block.IsCompilerGenerated = true;
5603 new_expr.EmitSideEffect (ec);
5608 // Restore context state.
5609 ec.MarkLabel (ec.LoopEnd);
5612 // Restore the previous context
5614 ec.LoopEnd = old_end;
5615 ec.Switch = old_switch;
5618 protected override void CloneTo (CloneContext clonectx, Statement t)
5620 Switch target = (Switch) t;
5622 target.Expr = Expr.Clone (clonectx);
5623 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5626 public override object Accept (StructuralVisitor visitor)
5628 return visitor.Visit (this);
5631 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5633 if (case_default == null && !(new_expr is Constant))
5636 if (end_reachable_das == null)
5637 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5639 end_reachable_das.Add (fc.DefiniteAssignment);
5642 public override void SetEndReachable ()
5644 end_reachable = true;
5648 // A place where execution can restart in a state machine
5649 public abstract class ResumableStatement : Statement
5652 protected Label resume_point;
5654 public Label PrepareForEmit (EmitContext ec)
5658 resume_point = ec.DefineLabel ();
5660 return resume_point;
5663 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5668 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5673 public abstract class TryFinallyBlock : ExceptionStatement
5675 protected Statement stmt;
5676 Label dispose_try_block;
5677 bool prepared_for_dispose, emitted_dispose;
5678 Method finally_host;
5680 protected TryFinallyBlock (Statement stmt, Location loc)
5688 public Statement Statement {
5696 protected abstract void EmitTryBody (EmitContext ec);
5697 public abstract void EmitFinallyBody (EmitContext ec);
5699 public override Label PrepareForDispose (EmitContext ec, Label end)
5701 if (!prepared_for_dispose) {
5702 prepared_for_dispose = true;
5703 dispose_try_block = ec.DefineLabel ();
5705 return dispose_try_block;
5708 protected sealed override void DoEmit (EmitContext ec)
5710 EmitTryBodyPrepare (ec);
5713 bool beginFinally = EmitBeginFinallyBlock (ec);
5715 Label start_finally = ec.DefineLabel ();
5716 if (resume_points != null && beginFinally) {
5717 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5719 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5720 ec.Emit (OpCodes.Brfalse_S, start_finally);
5721 ec.Emit (OpCodes.Endfinally);
5724 ec.MarkLabel (start_finally);
5726 if (finally_host != null) {
5727 finally_host.Define ();
5728 finally_host.PrepareEmit ();
5729 finally_host.Emit ();
5731 // Now it's safe to add, to close it properly and emit sequence points
5732 finally_host.Parent.AddMember (finally_host);
5734 var ce = new CallEmitter ();
5735 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5736 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5738 EmitFinallyBody (ec);
5742 ec.EndExceptionBlock ();
5745 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5747 if (emitted_dispose)
5750 emitted_dispose = true;
5752 Label end_of_try = ec.DefineLabel ();
5754 // Ensure that the only way we can get into this code is through a dispatcher
5755 if (have_dispatcher)
5756 ec.Emit (OpCodes.Br, end);
5758 ec.BeginExceptionBlock ();
5760 ec.MarkLabel (dispose_try_block);
5762 Label[] labels = null;
5763 for (int i = 0; i < resume_points.Count; ++i) {
5764 ResumableStatement s = resume_points[i];
5765 Label ret = s.PrepareForDispose (ec, end_of_try);
5766 if (ret.Equals (end_of_try) && labels == null)
5768 if (labels == null) {
5769 labels = new Label[resume_points.Count];
5770 for (int j = 0; j < i; ++j)
5771 labels[j] = end_of_try;
5776 if (labels != null) {
5778 for (j = 1; j < labels.Length; ++j)
5779 if (!labels[0].Equals (labels[j]))
5781 bool emit_dispatcher = j < labels.Length;
5783 if (emit_dispatcher) {
5784 ec.Emit (OpCodes.Ldloc, pc);
5785 ec.EmitInt (first_resume_pc);
5786 ec.Emit (OpCodes.Sub);
5787 ec.Emit (OpCodes.Switch, labels);
5790 foreach (ResumableStatement s in resume_points)
5791 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5794 ec.MarkLabel (end_of_try);
5796 ec.BeginFinallyBlock ();
5798 if (finally_host != null) {
5799 var ce = new CallEmitter ();
5800 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5801 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5803 EmitFinallyBody (ec);
5806 ec.EndExceptionBlock ();
5809 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5811 var res = stmt.FlowAnalysis (fc);
5816 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5818 ec.BeginFinallyBlock ();
5822 public override Reachability MarkReachable (Reachability rc)
5824 base.MarkReachable (rc);
5825 return Statement.MarkReachable (rc);
5828 public override bool Resolve (BlockContext bc)
5832 parent = bc.CurrentTryBlock;
5833 bc.CurrentTryBlock = this;
5835 using (bc.Set (ResolveContext.Options.TryScope)) {
5836 ok = stmt.Resolve (bc);
5839 bc.CurrentTryBlock = parent;
5842 // Finally block inside iterator is called from MoveNext and
5843 // Dispose methods that means we need to lift the block into
5844 // newly created host method to emit the body only once. The
5845 // original block then simply calls the newly generated method.
5847 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5848 var b = stmt as Block;
5849 if (b != null && b.Explicit.HasYield) {
5850 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5854 return base.Resolve (bc) && ok;
5859 // Base class for blocks using exception handling
5861 public abstract class ExceptionStatement : ResumableStatement
5863 protected List<ResumableStatement> resume_points;
5864 protected int first_resume_pc;
5865 protected ExceptionStatement parent;
5867 protected ExceptionStatement (Location loc)
5872 protected virtual void EmitBeginException (EmitContext ec)
5874 ec.BeginExceptionBlock ();
5877 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5879 StateMachineInitializer state_machine = null;
5880 if (resume_points != null) {
5881 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5883 ec.EmitInt ((int) IteratorStorey.State.Running);
5884 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5887 EmitBeginException (ec);
5889 if (resume_points != null) {
5890 ec.MarkLabel (resume_point);
5892 // For normal control flow, we want to fall-through the Switch
5893 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5894 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5895 ec.EmitInt (first_resume_pc);
5896 ec.Emit (OpCodes.Sub);
5898 Label[] labels = new Label[resume_points.Count];
5899 for (int i = 0; i < resume_points.Count; ++i)
5900 labels[i] = resume_points[i].PrepareForEmit (ec);
5901 ec.Emit (OpCodes.Switch, labels);
5905 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5907 if (parent != null) {
5908 // TODO: MOVE to virtual TryCatch
5909 var tc = this as TryCatch;
5910 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5912 pc = parent.AddResumePoint (s, pc, stateMachine);
5914 pc = stateMachine.AddResumePoint (this);
5917 if (resume_points == null) {
5918 resume_points = new List<ResumableStatement> ();
5919 first_resume_pc = pc;
5922 if (pc != first_resume_pc + resume_points.Count)
5923 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5925 resume_points.Add (stmt);
5930 public class Lock : TryFinallyBlock
5933 TemporaryVariableReference expr_copy;
5934 TemporaryVariableReference lock_taken;
5936 public Lock (Expression expr, Statement stmt, Location loc)
5942 public Expression Expr {
5948 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5950 expr.FlowAnalysis (fc);
5951 return base.DoFlowAnalysis (fc);
5954 public override bool Resolve (BlockContext ec)
5956 expr = expr.Resolve (ec);
5960 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
5961 ec.Report.Error (185, loc,
5962 "`{0}' is not a reference type as required by the lock statement",
5963 expr.Type.GetSignatureForError ());
5966 if (expr.Type.IsGenericParameter) {
5967 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5970 VariableReference lv = expr as VariableReference;
5973 locked = lv.IsLockedByStatement;
5974 lv.IsLockedByStatement = true;
5981 // Have to keep original lock value around to unlock same location
5982 // in the case of original value has changed or is null
5984 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5985 expr_copy.Resolve (ec);
5988 // Ensure Monitor methods are available
5990 if (ResolvePredefinedMethods (ec) > 1) {
5991 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5992 lock_taken.Resolve (ec);
5995 using (ec.Set (ResolveContext.Options.LockScope)) {
6000 lv.IsLockedByStatement = locked;
6006 protected override void EmitTryBodyPrepare (EmitContext ec)
6008 expr_copy.EmitAssign (ec, expr);
6010 if (lock_taken != null) {
6012 // Initialize ref variable
6014 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6017 // Monitor.Enter (expr_copy)
6019 expr_copy.Emit (ec);
6020 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6023 base.EmitTryBodyPrepare (ec);
6026 protected override void EmitTryBody (EmitContext ec)
6029 // Monitor.Enter (expr_copy, ref lock_taken)
6031 if (lock_taken != null) {
6032 expr_copy.Emit (ec);
6033 lock_taken.LocalInfo.CreateBuilder (ec);
6034 lock_taken.AddressOf (ec, AddressOp.Load);
6035 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6038 Statement.Emit (ec);
6041 public override void EmitFinallyBody (EmitContext ec)
6044 // if (lock_taken) Monitor.Exit (expr_copy)
6046 Label skip = ec.DefineLabel ();
6048 if (lock_taken != null) {
6049 lock_taken.Emit (ec);
6050 ec.Emit (OpCodes.Brfalse_S, skip);
6053 expr_copy.Emit (ec);
6054 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6056 ec.Emit (OpCodes.Call, m);
6058 ec.MarkLabel (skip);
6061 int ResolvePredefinedMethods (ResolveContext rc)
6063 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6064 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6068 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6072 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6076 protected override void CloneTo (CloneContext clonectx, Statement t)
6078 Lock target = (Lock) t;
6080 target.expr = expr.Clone (clonectx);
6081 target.stmt = Statement.Clone (clonectx);
6084 public override object Accept (StructuralVisitor visitor)
6086 return visitor.Visit (this);
6091 public class Unchecked : Statement {
6094 public Unchecked (Block b, Location loc)
6101 public override bool Resolve (BlockContext ec)
6103 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6104 return Block.Resolve (ec);
6107 protected override void DoEmit (EmitContext ec)
6109 using (ec.With (EmitContext.Options.CheckedScope, false))
6113 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6115 return Block.FlowAnalysis (fc);
6118 public override Reachability MarkReachable (Reachability rc)
6120 base.MarkReachable (rc);
6121 return Block.MarkReachable (rc);
6124 protected override void CloneTo (CloneContext clonectx, Statement t)
6126 Unchecked target = (Unchecked) t;
6128 target.Block = clonectx.LookupBlock (Block);
6131 public override object Accept (StructuralVisitor visitor)
6133 return visitor.Visit (this);
6137 public class Checked : Statement {
6140 public Checked (Block b, Location loc)
6143 b.Unchecked = false;
6147 public override bool Resolve (BlockContext ec)
6149 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6150 return Block.Resolve (ec);
6153 protected override void DoEmit (EmitContext ec)
6155 using (ec.With (EmitContext.Options.CheckedScope, true))
6159 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6161 return Block.FlowAnalysis (fc);
6164 public override Reachability MarkReachable (Reachability rc)
6166 base.MarkReachable (rc);
6167 return Block.MarkReachable (rc);
6170 protected override void CloneTo (CloneContext clonectx, Statement t)
6172 Checked target = (Checked) t;
6174 target.Block = clonectx.LookupBlock (Block);
6177 public override object Accept (StructuralVisitor visitor)
6179 return visitor.Visit (this);
6183 public class Unsafe : Statement {
6186 public Unsafe (Block b, Location loc)
6189 Block.Unsafe = true;
6193 public override bool Resolve (BlockContext ec)
6195 if (ec.CurrentIterator != null)
6196 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6198 using (ec.Set (ResolveContext.Options.UnsafeScope))
6199 return Block.Resolve (ec);
6202 protected override void DoEmit (EmitContext ec)
6207 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6209 return Block.FlowAnalysis (fc);
6212 public override Reachability MarkReachable (Reachability rc)
6214 base.MarkReachable (rc);
6215 return Block.MarkReachable (rc);
6218 protected override void CloneTo (CloneContext clonectx, Statement t)
6220 Unsafe target = (Unsafe) t;
6222 target.Block = clonectx.LookupBlock (Block);
6225 public override object Accept (StructuralVisitor visitor)
6227 return visitor.Visit (this);
6234 public class Fixed : Statement
6236 abstract class Emitter : ShimExpression
6238 protected LocalVariable vi;
6240 protected Emitter (Expression expr, LocalVariable li)
6246 public abstract void EmitExit (EmitContext ec);
6248 public override void FlowAnalysis (FlowAnalysisContext fc)
6250 expr.FlowAnalysis (fc);
6254 sealed class ExpressionEmitter : Emitter {
6255 public ExpressionEmitter (Expression converted, LocalVariable li)
6256 : base (converted, li)
6260 protected override Expression DoResolve (ResolveContext rc)
6262 throw new NotImplementedException ();
6265 public override void Emit (EmitContext ec) {
6267 // Store pointer in pinned location
6273 public override void EmitExit (EmitContext ec)
6276 ec.Emit (OpCodes.Conv_U);
6281 class StringEmitter : Emitter
6283 LocalVariable pinned_string;
6285 public StringEmitter (Expression expr, LocalVariable li)
6290 protected override Expression DoResolve (ResolveContext rc)
6292 pinned_string = new LocalVariable (vi.Block, "$pinned",
6293 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6295 pinned_string.Type = rc.BuiltinTypes.String;
6298 eclass = ExprClass.Variable;
6299 type = rc.BuiltinTypes.Int;
6303 public override void Emit (EmitContext ec)
6305 pinned_string.CreateBuilder (ec);
6308 pinned_string.EmitAssign (ec);
6310 // TODO: Should use Binary::Add
6311 pinned_string.Emit (ec);
6312 ec.Emit (OpCodes.Conv_I);
6314 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6318 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6319 //pe.InstanceExpression = pinned_string;
6320 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6322 ec.Emit (OpCodes.Add);
6326 public override void EmitExit (EmitContext ec)
6329 pinned_string.EmitAssign (ec);
6333 public class VariableDeclaration : BlockVariable
6335 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6340 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6342 if (!Variable.Type.IsPointer && li == Variable) {
6343 bc.Report.Error (209, TypeExpression.Location,
6344 "The type of locals declared in a fixed statement must be a pointer type");
6348 var res = initializer.Resolve (bc);
6355 var ac = res.Type as ArrayContainer;
6357 TypeSpec array_type = ac.Element;
6360 // Provided that array_type is unmanaged,
6362 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6365 Expression res_init;
6366 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6369 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6370 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6371 res = expr_variable.CreateReferenceExpression (bc, loc);
6375 // and T* is implicitly convertible to the
6376 // pointer type given in the fixed statement.
6378 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6380 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6381 if (converted == null)
6385 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6387 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6388 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6389 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6390 new NullLiteral (loc),
6393 converted = converted.Resolve (bc);
6395 return new ExpressionEmitter (converted, li);
6401 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6402 return new StringEmitter (res, li).Resolve (bc);
6405 // Case 3: fixed buffer
6406 if (res is FixedBufferPtr) {
6407 return new ExpressionEmitter (res, li);
6410 bool already_fixed = true;
6413 // Case 4: & object.
6415 Unary u = res as Unary;
6417 if (u.Oper == Unary.Operator.AddressOf) {
6418 IVariableReference vr = u.Expr as IVariableReference;
6419 if (vr == null || !vr.IsFixed) {
6420 already_fixed = false;
6423 } else if (initializer is Cast) {
6424 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6428 if (already_fixed) {
6429 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6432 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6433 return new ExpressionEmitter (res, li);
6438 VariableDeclaration decl;
6439 Statement statement;
6442 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6451 public Statement Statement {
6457 public BlockVariable Variables {
6465 public override bool Resolve (BlockContext bc)
6467 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6468 if (!decl.Resolve (bc))
6472 return statement.Resolve (bc);
6475 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6477 decl.FlowAnalysis (fc);
6478 return statement.FlowAnalysis (fc);
6481 protected override void DoEmit (EmitContext ec)
6483 decl.Variable.CreateBuilder (ec);
6484 decl.Initializer.Emit (ec);
6485 if (decl.Declarators != null) {
6486 foreach (var d in decl.Declarators) {
6487 d.Variable.CreateBuilder (ec);
6488 d.Initializer.Emit (ec);
6492 statement.Emit (ec);
6498 // Clear the pinned variable
6500 ((Emitter) decl.Initializer).EmitExit (ec);
6501 if (decl.Declarators != null) {
6502 foreach (var d in decl.Declarators) {
6503 ((Emitter)d.Initializer).EmitExit (ec);
6508 public override Reachability MarkReachable (Reachability rc)
6510 base.MarkReachable (rc);
6512 decl.MarkReachable (rc);
6514 rc = statement.MarkReachable (rc);
6516 // TODO: What if there is local exit?
6517 has_ret = rc.IsUnreachable;
6521 protected override void CloneTo (CloneContext clonectx, Statement t)
6523 Fixed target = (Fixed) t;
6525 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6526 target.statement = statement.Clone (clonectx);
6529 public override object Accept (StructuralVisitor visitor)
6531 return visitor.Visit (this);
6535 public class Catch : Statement
6537 class CatchVariableStore : Statement
6539 readonly Catch ctch;
6541 public CatchVariableStore (Catch ctch)
6546 protected override void CloneTo (CloneContext clonectx, Statement target)
6550 protected override void DoEmit (EmitContext ec)
6552 // Emits catch variable debug information inside correct block
6553 ctch.EmitCatchVariableStore (ec);
6556 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6562 class FilterStatement : Statement
6564 readonly Catch ctch;
6566 public FilterStatement (Catch ctch)
6571 protected override void CloneTo (CloneContext clonectx, Statement target)
6575 protected override void DoEmit (EmitContext ec)
6577 if (ctch.li != null) {
6578 if (ctch.hoisted_temp != null)
6579 ctch.hoisted_temp.Emit (ec);
6583 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6584 ec.Emit (OpCodes.Box, ctch.type);
6587 var expr_start = ec.DefineLabel ();
6588 var end = ec.DefineLabel ();
6590 ec.Emit (OpCodes.Brtrue_S, expr_start);
6592 ec.Emit (OpCodes.Br, end);
6593 ec.MarkLabel (expr_start);
6595 ctch.Filter.Emit (ec);
6598 ec.Emit (OpCodes.Endfilter);
6599 ec.BeginFilterHandler ();
6600 ec.Emit (OpCodes.Pop);
6603 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6605 ctch.Filter.FlowAnalysis (fc);
6609 public override bool Resolve (BlockContext bc)
6611 ctch.Filter = ctch.Filter.Resolve (bc);
6613 if (ctch.Filter != null) {
6614 if (ctch.Filter.ContainsEmitWithAwait ()) {
6615 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6618 var c = ctch.Filter as Constant;
6619 if (c != null && !c.IsDefaultValue) {
6620 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6628 ExplicitBlock block;
6630 FullNamedExpression type_expr;
6631 CompilerAssign assign;
6633 LocalTemporary hoisted_temp;
6635 public Catch (ExplicitBlock block, Location loc)
6643 public ExplicitBlock Block {
6649 public TypeSpec CatchType {
6655 public Expression Filter {
6659 public bool IsGeneral {
6661 return type_expr == null;
6665 public FullNamedExpression TypeExpression {
6674 public LocalVariable Variable {
6685 protected override void DoEmit (EmitContext ec)
6687 if (Filter != null) {
6688 ec.BeginExceptionFilterBlock ();
6689 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6691 if (Block.HasAwait) {
6692 Block.EmitScopeInitialization (ec);
6701 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6703 ec.BeginCatchBlock (CatchType);
6706 ec.Emit (OpCodes.Pop);
6708 if (Block.HasAwait) {
6710 EmitCatchVariableStore (ec);
6716 void EmitCatchVariableStore (EmitContext ec)
6718 li.CreateBuilder (ec);
6721 // For hoisted catch variable we have to use a temporary local variable
6722 // for captured variable initialization during storey setup because variable
6723 // needs to be on the stack after storey instance for stfld operation
6725 if (li.HoistedVariant != null) {
6726 hoisted_temp = new LocalTemporary (li.Type);
6727 hoisted_temp.Store (ec);
6729 // switch to assignment from temporary variable and not from top of the stack
6730 assign.UpdateSource (hoisted_temp);
6734 public override bool Resolve (BlockContext bc)
6736 using (bc.Set (ResolveContext.Options.CatchScope)) {
6737 if (type_expr == null) {
6738 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6739 if (!block.HasAwait || Filter != null)
6740 block.AddScopeStatement (new CatchVariableStore (this));
6742 Expression source = new EmptyExpression (li.Type);
6743 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6744 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6747 type = type_expr.ResolveAsType (bc);
6752 CreateExceptionVariable (type);
6754 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6755 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6756 } else if (li != null) {
6758 li.PrepareAssignmentAnalysis (bc);
6760 // source variable is at the top of the stack
6761 Expression source = new EmptyExpression (li.Type);
6762 if (li.Type.IsGenericParameter)
6763 source = new UnboxCast (source, li.Type);
6765 if (!block.HasAwait || Filter != null)
6766 block.AddScopeStatement (new CatchVariableStore (this));
6769 // Uses Location.Null to hide from symbol file
6771 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6772 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6776 if (Filter != null) {
6777 Block.AddScopeStatement (new FilterStatement (this));
6780 Block.SetCatchBlock ();
6781 return Block.Resolve (bc);
6785 bool CreateExceptionVariable (TypeSpec type)
6787 if (!Block.HasAwait)
6790 // TODO: Scan the block for rethrow expression
6791 //if (!Block.HasRethrow)
6794 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6798 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6800 if (li != null && !li.IsCompilerGenerated) {
6801 fc.SetVariableAssigned (li.VariableInfo, true);
6804 return block.FlowAnalysis (fc);
6807 public override Reachability MarkReachable (Reachability rc)
6809 base.MarkReachable (rc);
6811 var c = Filter as Constant;
6812 if (c != null && c.IsDefaultValue)
6813 return Reachability.CreateUnreachable ();
6815 return block.MarkReachable (rc);
6818 protected override void CloneTo (CloneContext clonectx, Statement t)
6820 Catch target = (Catch) t;
6822 if (type_expr != null)
6823 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6826 target.Filter = Filter.Clone (clonectx);
6828 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6832 public class TryFinally : TryFinallyBlock
6835 List<DefiniteAssignmentBitSet> try_exit_dat;
6836 List<Label> redirected_jumps;
6837 Label? start_fin_label;
6839 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6845 public ExplicitBlock FinallyBlock {
6851 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6853 if (try_exit_dat == null)
6854 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6856 try_exit_dat.Add (vector);
6859 public override bool Resolve (BlockContext bc)
6861 bool ok = base.Resolve (bc);
6863 fini.SetFinallyBlock ();
6864 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6865 ok &= fini.Resolve (bc);
6871 protected override void EmitBeginException (EmitContext ec)
6873 if (fini.HasAwait && stmt is TryCatch)
6874 ec.BeginExceptionBlock ();
6876 base.EmitBeginException (ec);
6879 protected override void EmitTryBody (EmitContext ec)
6881 if (fini.HasAwait) {
6882 if (ec.TryFinallyUnwind == null)
6883 ec.TryFinallyUnwind = new List<TryFinally> ();
6885 ec.TryFinallyUnwind.Add (this);
6888 if (stmt is TryCatch)
6889 ec.EndExceptionBlock ();
6891 ec.TryFinallyUnwind.Remove (this);
6893 if (start_fin_label != null)
6894 ec.MarkLabel (start_fin_label.Value);
6902 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6907 return base.EmitBeginFinallyBlock (ec);
6910 public override void EmitFinallyBody (EmitContext ec)
6912 if (!fini.HasAwait) {
6918 // Emits catch block like
6920 // catch (object temp) {
6921 // this.exception_field = temp;
6924 var type = ec.BuiltinTypes.Object;
6925 ec.BeginCatchBlock (type);
6927 var temp = ec.GetTemporaryLocal (type);
6928 ec.Emit (OpCodes.Stloc, temp);
6930 var exception_field = ec.GetTemporaryField (type);
6932 ec.Emit (OpCodes.Ldloc, temp);
6933 exception_field.EmitAssignFromStack (ec);
6935 ec.EndExceptionBlock ();
6937 ec.FreeTemporaryLocal (temp, type);
6942 // Emits exception rethrow
6944 // if (this.exception_field != null)
6945 // throw this.exception_field;
6947 exception_field.Emit (ec);
6948 var skip_throw = ec.DefineLabel ();
6949 ec.Emit (OpCodes.Brfalse_S, skip_throw);
6950 exception_field.Emit (ec);
6951 ec.Emit (OpCodes.Throw);
6952 ec.MarkLabel (skip_throw);
6954 exception_field.IsAvailableForReuse = true;
6956 EmitUnwindFinallyTable (ec);
6959 bool IsParentBlock (Block block)
6961 for (Block b = fini; b != null; b = b.Parent) {
6969 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
6972 if (labelBlock != null) {
6973 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
6974 var fin = ec.TryFinallyUnwind [idx - 1];
6975 if (!fin.IsParentBlock (labelBlock))
6982 bool set_return_state = true;
6984 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
6985 var fin = ec.TryFinallyUnwind [idx];
6986 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
6989 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
6990 set_return_state = false;
6992 if (fin.start_fin_label == null) {
6993 fin.start_fin_label = ec.DefineLabel ();
6996 label = fin.start_fin_label.Value;
7002 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7004 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7007 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7009 if (redirected_jumps == null) {
7010 redirected_jumps = new List<Label> ();
7012 // Add fallthrough label
7013 redirected_jumps.Add (ec.DefineLabel ());
7016 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7019 int index = redirected_jumps.IndexOf (label);
7021 redirected_jumps.Add (label);
7022 index = redirected_jumps.Count - 1;
7026 // Indicates we have captured exit jump
7028 if (setReturnState) {
7029 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7030 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7035 // Emits state table of jumps outside of try block and reload of return
7036 // value when try block returns value
7038 void EmitUnwindFinallyTable (EmitContext ec)
7040 if (redirected_jumps == null)
7043 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7044 initializer.HoistedReturnState.EmitLoad (ec);
7045 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7047 // Mark fallthrough label
7048 ec.MarkLabel (redirected_jumps [0]);
7051 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7053 var da = fc.BranchDefiniteAssignment ();
7055 var tf = fc.TryFinally;
7056 fc.TryFinally = this;
7058 var res_stmt = Statement.FlowAnalysis (fc);
7062 var try_da = fc.DefiniteAssignment;
7063 fc.DefiniteAssignment = da;
7065 var res_fin = fini.FlowAnalysis (fc);
7067 if (try_exit_dat != null) {
7069 // try block has global exit but we need to run definite assignment check
7070 // for parameter block out parameter after finally block because it's always
7071 // executed before exit
7073 foreach (var try_da_part in try_exit_dat)
7074 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7076 try_exit_dat = null;
7079 fc.DefiniteAssignment |= try_da;
7080 return res_stmt | res_fin;
7083 public override Reachability MarkReachable (Reachability rc)
7086 // Mark finally block first for any exit statement in try block
7087 // to know whether the code which follows finally is reachable
7089 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7092 protected override void CloneTo (CloneContext clonectx, Statement t)
7094 TryFinally target = (TryFinally) t;
7096 target.stmt = stmt.Clone (clonectx);
7098 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7101 public override object Accept (StructuralVisitor visitor)
7103 return visitor.Visit (this);
7107 public class TryCatch : ExceptionStatement
7110 List<Catch> clauses;
7111 readonly bool inside_try_finally;
7112 List<Catch> catch_sm;
7114 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7118 this.clauses = catch_clauses;
7119 this.inside_try_finally = inside_try_finally;
7122 public List<Catch> Clauses {
7128 public bool IsTryCatchFinally {
7130 return inside_try_finally;
7134 public override bool Resolve (BlockContext bc)
7138 using (bc.Set (ResolveContext.Options.TryScope)) {
7139 parent = bc.CurrentTryBlock;
7141 if (IsTryCatchFinally) {
7142 ok = Block.Resolve (bc);
7144 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7145 bc.CurrentTryBlock = this;
7146 ok = Block.Resolve (bc);
7147 bc.CurrentTryBlock = parent;
7152 for (int i = 0; i < clauses.Count; ++i) {
7155 ok &= c.Resolve (bc);
7157 if (c.Block.HasAwait) {
7158 if (catch_sm == null)
7159 catch_sm = new List<Catch> ();
7164 if (c.Filter != null)
7167 TypeSpec resolved_type = c.CatchType;
7168 if (resolved_type == null)
7171 for (int ii = 0; ii < clauses.Count; ++ii) {
7175 if (clauses[ii].Filter != null)
7178 if (clauses[ii].IsGeneral) {
7179 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7182 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7185 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7188 bc.Report.Warning (1058, 1, c.loc,
7189 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7197 var ct = clauses[ii].CatchType;
7201 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7202 bc.Report.Error (160, c.loc,
7203 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7204 ct.GetSignatureForError ());
7210 return base.Resolve (bc) && ok;
7213 protected sealed override void DoEmit (EmitContext ec)
7215 if (!inside_try_finally)
7216 EmitTryBodyPrepare (ec);
7220 LocalBuilder state_variable = null;
7221 foreach (Catch c in clauses) {
7224 if (catch_sm != null) {
7225 if (state_variable == null) {
7227 // Cannot reuse temp variable because non-catch path assumes the value is 0
7228 // which may not be true for reused local variable
7230 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7233 var index = catch_sm.IndexOf (c);
7237 ec.EmitInt (index + 1);
7238 ec.Emit (OpCodes.Stloc, state_variable);
7242 if (!inside_try_finally)
7243 ec.EndExceptionBlock ();
7245 if (state_variable != null) {
7246 ec.Emit (OpCodes.Ldloc, state_variable);
7248 var labels = new Label [catch_sm.Count + 1];
7249 for (int i = 0; i < labels.Length; ++i) {
7250 labels [i] = ec.DefineLabel ();
7253 var end = ec.DefineLabel ();
7254 ec.Emit (OpCodes.Switch, labels);
7256 // 0 value is default label
7257 ec.MarkLabel (labels [0]);
7258 ec.Emit (OpCodes.Br, end);
7260 var atv = ec.AsyncThrowVariable;
7262 for (int i = 0; i < catch_sm.Count; ++i) {
7263 if (c != null && c.Block.HasReachableClosingBrace)
7264 ec.Emit (OpCodes.Br, end);
7266 ec.MarkLabel (labels [i + 1]);
7268 ec.AsyncThrowVariable = c.Variable;
7271 ec.AsyncThrowVariable = atv;
7277 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7279 var start_fc = fc.BranchDefiniteAssignment ();
7280 var res = Block.FlowAnalysis (fc);
7282 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7284 foreach (var c in clauses) {
7285 fc.BranchDefiniteAssignment (start_fc);
7286 if (!c.FlowAnalysis (fc)) {
7288 try_fc = fc.DefiniteAssignment;
7290 try_fc &= fc.DefiniteAssignment;
7296 fc.DefiniteAssignment = try_fc ?? start_fc;
7301 public override Reachability MarkReachable (Reachability rc)
7303 if (rc.IsUnreachable)
7306 base.MarkReachable (rc);
7308 var tc_rc = Block.MarkReachable (rc);
7310 foreach (var c in clauses)
7311 tc_rc &= c.MarkReachable (rc);
7316 protected override void CloneTo (CloneContext clonectx, Statement t)
7318 TryCatch target = (TryCatch) t;
7320 target.Block = clonectx.LookupBlock (Block);
7321 if (clauses != null){
7322 target.clauses = new List<Catch> ();
7323 foreach (Catch c in clauses)
7324 target.clauses.Add ((Catch) c.Clone (clonectx));
7328 public override object Accept (StructuralVisitor visitor)
7330 return visitor.Visit (this);
7334 public class Using : TryFinallyBlock
7336 public class VariableDeclaration : BlockVariable
7338 Statement dispose_call;
7340 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7345 public VariableDeclaration (LocalVariable li, Location loc)
7352 public VariableDeclaration (Expression expr)
7355 loc = expr.Location;
7361 public bool IsNested { get; private set; }
7365 public void EmitDispose (EmitContext ec)
7367 dispose_call.Emit (ec);
7370 public override bool Resolve (BlockContext bc)
7375 return base.Resolve (bc, false);
7378 public Expression ResolveExpression (BlockContext bc)
7380 var e = Initializer.Resolve (bc);
7384 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7385 Initializer = ResolveInitializer (bc, Variable, e);
7389 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7391 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7392 initializer = initializer.Resolve (bc);
7393 if (initializer == null)
7396 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7397 Arguments args = new Arguments (1);
7398 args.Add (new Argument (initializer));
7399 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7400 if (initializer == null)
7403 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7404 dispose_call = CreateDisposeCall (bc, var);
7405 dispose_call.Resolve (bc);
7407 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7410 if (li == Variable) {
7411 CheckIDiposableConversion (bc, li, initializer);
7412 dispose_call = CreateDisposeCall (bc, li);
7413 dispose_call.Resolve (bc);
7416 return base.ResolveInitializer (bc, li, initializer);
7419 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7423 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7424 if (type.IsNullableType) {
7425 // it's handled in CreateDisposeCall
7429 if (type != InternalType.ErrorType) {
7430 bc.Report.SymbolRelatedToPreviousError (type);
7431 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7432 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7433 type.GetSignatureForError ());
7440 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7442 var target = bc.BuiltinTypes.IDisposable;
7443 var tp = type as TypeParameterSpec;
7445 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7447 return type.ImplementsInterface (target, false);
7450 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7452 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7454 var loc = lv.Location;
7456 var idt = bc.BuiltinTypes.IDisposable;
7457 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7459 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7460 dispose_mg.InstanceExpression = type.IsNullableType ?
7461 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7465 // Hide it from symbol file via null location
7467 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7469 // Add conditional call when disposing possible null variable
7470 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7471 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7476 public void ResolveDeclaratorInitializer (BlockContext bc)
7478 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7481 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7483 for (int i = declarators.Count - 1; i >= 0; --i) {
7484 var d = declarators [i];
7485 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7486 vd.Initializer = d.Initializer;
7488 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7489 vd.dispose_call.Resolve (bc);
7491 stmt = new Using (vd, stmt, d.Variable.Location);
7498 public override object Accept (StructuralVisitor visitor)
7500 return visitor.Visit (this);
7504 VariableDeclaration decl;
7506 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7512 public Using (Expression expr, Statement stmt, Location loc)
7515 this.decl = new VariableDeclaration (expr);
7520 public Expression Expr {
7522 return decl.Variable == null ? decl.Initializer : null;
7526 public BlockVariable Variables {
7534 public override void Emit (EmitContext ec)
7537 // Don't emit sequence point it will be set on variable declaration
7542 protected override void EmitTryBodyPrepare (EmitContext ec)
7545 base.EmitTryBodyPrepare (ec);
7548 protected override void EmitTryBody (EmitContext ec)
7553 public override void EmitFinallyBody (EmitContext ec)
7555 decl.EmitDispose (ec);
7558 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7560 decl.FlowAnalysis (fc);
7561 return stmt.FlowAnalysis (fc);
7564 public override Reachability MarkReachable (Reachability rc)
7566 decl.MarkReachable (rc);
7567 return base.MarkReachable (rc);
7570 public override bool Resolve (BlockContext ec)
7572 VariableReference vr;
7573 bool vr_locked = false;
7575 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7576 if (decl.Variable == null) {
7577 vr = decl.ResolveExpression (ec) as VariableReference;
7579 vr_locked = vr.IsLockedByStatement;
7580 vr.IsLockedByStatement = true;
7583 if (decl.IsNested) {
7584 decl.ResolveDeclaratorInitializer (ec);
7586 if (!decl.Resolve (ec))
7589 if (decl.Declarators != null) {
7590 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7598 var ok = base.Resolve (ec);
7601 vr.IsLockedByStatement = vr_locked;
7606 protected override void CloneTo (CloneContext clonectx, Statement t)
7608 Using target = (Using) t;
7610 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7611 target.stmt = stmt.Clone (clonectx);
7614 public override object Accept (StructuralVisitor visitor)
7616 return visitor.Visit (this);
7621 /// Implementation of the foreach C# statement
7623 public class Foreach : LoopStatement
7625 abstract class IteratorStatement : Statement
7627 protected readonly Foreach for_each;
7629 protected IteratorStatement (Foreach @foreach)
7631 this.for_each = @foreach;
7632 this.loc = @foreach.expr.Location;
7635 protected override void CloneTo (CloneContext clonectx, Statement target)
7637 throw new NotImplementedException ();
7640 public override void Emit (EmitContext ec)
7642 if (ec.EmitAccurateDebugInfo) {
7643 ec.Emit (OpCodes.Nop);
7649 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7651 throw new NotImplementedException ();
7655 sealed class ArrayForeach : IteratorStatement
7657 TemporaryVariableReference[] lengths;
7658 Expression [] length_exprs;
7659 StatementExpression[] counter;
7660 TemporaryVariableReference[] variables;
7662 TemporaryVariableReference copy;
7664 public ArrayForeach (Foreach @foreach, int rank)
7667 counter = new StatementExpression[rank];
7668 variables = new TemporaryVariableReference[rank];
7669 length_exprs = new Expression [rank];
7672 // Only use temporary length variables when dealing with
7673 // multi-dimensional arrays
7676 lengths = new TemporaryVariableReference [rank];
7679 public override bool Resolve (BlockContext ec)
7681 Block variables_block = for_each.variable.Block;
7682 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7685 int rank = length_exprs.Length;
7686 Arguments list = new Arguments (rank);
7687 for (int i = 0; i < rank; i++) {
7688 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7690 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7691 counter[i].Resolve (ec);
7694 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7696 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7697 lengths[i].Resolve (ec);
7699 Arguments args = new Arguments (1);
7700 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7701 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7704 list.Add (new Argument (v));
7707 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7712 if (for_each.type is VarExpr) {
7713 // Infer implicitly typed local variable from foreach array type
7714 var_type = access.Type;
7716 var_type = for_each.type.ResolveAsType (ec);
7718 if (var_type == null)
7721 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7726 for_each.variable.Type = var_type;
7728 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7729 if (variable_ref == null)
7732 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7734 return for_each.body.Resolve (ec);
7737 protected override void DoEmit (EmitContext ec)
7739 copy.EmitAssign (ec, for_each.expr);
7741 int rank = length_exprs.Length;
7742 Label[] test = new Label [rank];
7743 Label[] loop = new Label [rank];
7745 for (int i = 0; i < rank; i++) {
7746 test [i] = ec.DefineLabel ();
7747 loop [i] = ec.DefineLabel ();
7749 if (lengths != null)
7750 lengths [i].EmitAssign (ec, length_exprs [i]);
7753 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7754 for (int i = 0; i < rank; i++) {
7755 variables [i].EmitAssign (ec, zero);
7757 ec.Emit (OpCodes.Br, test [i]);
7758 ec.MarkLabel (loop [i]);
7761 for_each.body.Emit (ec);
7763 ec.MarkLabel (ec.LoopBegin);
7764 ec.Mark (for_each.expr.Location);
7766 for (int i = rank - 1; i >= 0; i--){
7767 counter [i].Emit (ec);
7769 ec.MarkLabel (test [i]);
7770 variables [i].Emit (ec);
7772 if (lengths != null)
7773 lengths [i].Emit (ec);
7775 length_exprs [i].Emit (ec);
7777 ec.Emit (OpCodes.Blt, loop [i]);
7780 ec.MarkLabel (ec.LoopEnd);
7784 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7786 class RuntimeDispose : Using.VariableDeclaration
7788 public RuntimeDispose (LocalVariable lv, Location loc)
7794 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7796 // Defered to runtime check
7799 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7801 var idt = bc.BuiltinTypes.IDisposable;
7804 // Fabricates code like
7806 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7809 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7811 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7812 dispose_variable.CreateReferenceExpression (bc, loc),
7813 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7814 loc), new NullLiteral (loc));
7816 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7818 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7819 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7821 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7822 return new If (idisaposable_test, dispose, loc);
7826 LocalVariable variable;
7828 Statement statement;
7829 ExpressionStatement init;
7830 TemporaryVariableReference enumerator_variable;
7831 bool ambiguous_getenumerator_name;
7833 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7836 this.variable = var;
7840 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7842 rc.Report.SymbolRelatedToPreviousError (enumerator);
7843 rc.Report.Error (202, loc,
7844 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7845 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7848 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7851 // Option 1: Try to match by name GetEnumerator first
7853 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7854 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7856 var mg = mexpr as MethodGroupExpr;
7858 mg.InstanceExpression = expr;
7859 Arguments args = new Arguments (0);
7860 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7862 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7863 if (ambiguous_getenumerator_name)
7866 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7872 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7875 PredefinedMember<MethodSpec> iface_candidate = null;
7876 var ptypes = rc.Module.PredefinedTypes;
7877 var gen_ienumerable = ptypes.IEnumerableGeneric;
7878 if (!gen_ienumerable.Define ())
7879 gen_ienumerable = null;
7881 var ifaces = t.Interfaces;
7882 if (ifaces != null) {
7883 foreach (var iface in ifaces) {
7884 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7885 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7886 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7887 rc.Report.Error (1640, loc,
7888 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7889 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7894 // TODO: Cache this somehow
7895 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7896 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7901 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7902 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7907 if (iface_candidate == null) {
7908 if (expr.Type != InternalType.ErrorType) {
7909 rc.Report.Error (1579, loc,
7910 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7911 expr.Type.GetSignatureForError (), "GetEnumerator");
7917 var method = iface_candidate.Resolve (loc);
7921 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7922 mg.InstanceExpression = expr;
7926 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7928 var ms = MemberCache.FindMember (enumerator.ReturnType,
7929 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7930 BindingRestriction.InstanceOnly) as MethodSpec;
7932 if (ms == null || !ms.IsPublic) {
7933 Error_WrongEnumerator (rc, enumerator);
7937 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7940 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7942 var ps = MemberCache.FindMember (enumerator.ReturnType,
7943 MemberFilter.Property ("Current", null),
7944 BindingRestriction.InstanceOnly) as PropertySpec;
7946 if (ps == null || !ps.IsPublic) {
7947 Error_WrongEnumerator (rc, enumerator);
7954 public override bool Resolve (BlockContext ec)
7956 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7959 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7960 } else if (expr.Type.IsNullableType) {
7961 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7964 var get_enumerator_mg = ResolveGetEnumerator (ec);
7965 if (get_enumerator_mg == null) {
7969 var get_enumerator = get_enumerator_mg.BestCandidate;
7970 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7971 enumerator_variable.Resolve (ec);
7973 // Prepare bool MoveNext ()
7974 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7975 if (move_next_mg == null) {
7979 move_next_mg.InstanceExpression = enumerator_variable;
7981 // Prepare ~T~ Current { get; }
7982 var current_prop = ResolveCurrent (ec, get_enumerator);
7983 if (current_prop == null) {
7987 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7988 if (current_pe == null)
7991 VarExpr ve = for_each.type as VarExpr;
7995 // Source type is dynamic, set element type to dynamic too
7996 variable.Type = ec.BuiltinTypes.Dynamic;
7998 // Infer implicitly typed local variable from foreach enumerable type
7999 variable.Type = current_pe.Type;
8003 // Explicit cast of dynamic collection elements has to be done at runtime
8004 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8007 variable.Type = for_each.type.ResolveAsType (ec);
8009 if (variable.Type == null)
8012 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8013 if (current_pe == null)
8017 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8018 if (variable_ref == null)
8021 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8023 var init = new Invocation.Predefined (get_enumerator_mg, null);
8025 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8026 for_each.body, Location.Null);
8028 var enum_type = enumerator_variable.Type;
8031 // Add Dispose method call when enumerator can be IDisposable
8033 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8034 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8036 // Runtime Dispose check
8038 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8039 vd.Initializer = init;
8040 statement = new Using (vd, statement, Location.Null);
8043 // No Dispose call needed
8045 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8046 this.init.Resolve (ec);
8050 // Static Dispose check
8052 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8053 vd.Initializer = init;
8054 statement = new Using (vd, statement, Location.Null);
8057 return statement.Resolve (ec);
8060 protected override void DoEmit (EmitContext ec)
8062 enumerator_variable.LocalInfo.CreateBuilder (ec);
8065 init.EmitStatement (ec);
8067 statement.Emit (ec);
8070 #region IErrorHandler Members
8072 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8074 ec.Report.SymbolRelatedToPreviousError (best);
8075 ec.Report.Warning (278, 2, expr.Location,
8076 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8077 expr.Type.GetSignatureForError (), "enumerable",
8078 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8080 ambiguous_getenumerator_name = true;
8084 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8089 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8094 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8103 LocalVariable variable;
8107 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8111 this.variable = var;
8117 public Expression Expr {
8118 get { return expr; }
8121 public Expression TypeExpression {
8122 get { return type; }
8125 public LocalVariable Variable {
8126 get { return variable; }
8129 public override Reachability MarkReachable (Reachability rc)
8131 base.MarkReachable (rc);
8133 body.MarkReachable (rc);
8138 public override bool Resolve (BlockContext ec)
8140 expr = expr.Resolve (ec);
8145 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8149 body.AddStatement (Statement);
8151 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8152 Statement = new ArrayForeach (this, 1);
8153 } else if (expr.Type is ArrayContainer) {
8154 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8156 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8157 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8158 expr.ExprClassName);
8162 Statement = new CollectionForeach (this, variable, expr);
8165 return base.Resolve (ec);
8168 protected override void DoEmit (EmitContext ec)
8170 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8171 ec.LoopBegin = ec.DefineLabel ();
8172 ec.LoopEnd = ec.DefineLabel ();
8174 if (!(Statement is Block))
8175 ec.BeginCompilerScope ();
8177 variable.CreateBuilder (ec);
8179 Statement.Emit (ec);
8181 if (!(Statement is Block))
8184 ec.LoopBegin = old_begin;
8185 ec.LoopEnd = old_end;
8188 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8190 expr.FlowAnalysis (fc);
8192 var da = fc.BranchDefiniteAssignment ();
8193 body.FlowAnalysis (fc);
8194 fc.DefiniteAssignment = da;
8198 protected override void CloneTo (CloneContext clonectx, Statement t)
8200 Foreach target = (Foreach) t;
8202 target.type = type.Clone (clonectx);
8203 target.expr = expr.Clone (clonectx);
8204 target.body = (Block) body.Clone (clonectx);
8205 target.Statement = Statement.Clone (clonectx);
8208 public override object Accept (StructuralVisitor visitor)
8210 return visitor.Visit (this);