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 // Goto to unreachable label
1415 if (fc.AddReachedLabel (label))
1418 label.Block.ScanGotoJump (label, fc);
1422 public override Reachability MarkReachable (Reachability rc)
1424 if (rc.IsUnreachable)
1427 base.MarkReachable (rc);
1429 if (try_finally != null) {
1430 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1431 label.AddGotoReference (rc);
1436 label.AddGotoReference (rc);
1439 return Reachability.CreateUnreachable ();
1442 protected override void CloneTo (CloneContext clonectx, Statement target)
1447 protected override void DoEmit (EmitContext ec)
1449 // This should only happen for goto from try block to unrechable label
1453 Label l = label.LabelTarget (ec);
1455 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1456 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1457 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1460 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1463 bool IsLeavingFinally (Block labelBlock)
1465 var b = try_finally.Statement as Block;
1467 if (b == labelBlock)
1476 public override object Accept (StructuralVisitor visitor)
1478 return visitor.Visit (this);
1482 public class LabeledStatement : Statement {
1489 public LabeledStatement (string name, Block block, Location l)
1496 public Label LabelTarget (EmitContext ec)
1501 label = ec.DefineLabel ();
1506 public Block Block {
1512 public string Name {
1513 get { return name; }
1516 protected override void CloneTo (CloneContext clonectx, Statement target)
1518 var t = (LabeledStatement) target;
1520 t.block = clonectx.RemapBlockCopy (block);
1523 public override bool Resolve (BlockContext bc)
1528 protected override void DoEmit (EmitContext ec)
1531 ec.MarkLabel (label);
1534 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1537 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1543 public override Reachability MarkReachable (Reachability rc)
1545 base.MarkReachable (rc);
1548 rc = new Reachability ();
1553 public void AddGotoReference (Reachability rc)
1561 block.ScanGotoJump (this);
1564 public override object Accept (StructuralVisitor visitor)
1566 return visitor.Visit (this);
1572 /// `goto default' statement
1574 public class GotoDefault : SwitchGoto
1576 public GotoDefault (Location l)
1581 public override bool Resolve (BlockContext bc)
1583 if (bc.Switch == null) {
1584 Error_GotoCaseRequiresSwitchBlock (bc);
1588 bc.Switch.RegisterGotoCase (null, null);
1594 protected override void DoEmit (EmitContext ec)
1596 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1599 public override Reachability MarkReachable (Reachability rc)
1601 if (!rc.IsUnreachable) {
1602 var label = switch_statement.DefaultLabel;
1603 if (label.IsUnreachable) {
1604 label.MarkReachable (rc);
1605 switch_statement.Block.ScanGotoJump (label);
1609 return base.MarkReachable (rc);
1612 public override object Accept (StructuralVisitor visitor)
1614 return visitor.Visit (this);
1619 /// `goto case' statement
1621 public class GotoCase : SwitchGoto
1625 public GotoCase (Expression e, Location l)
1631 public Expression Expr {
1637 public SwitchLabel Label { get; set; }
1639 public override bool Resolve (BlockContext ec)
1641 if (ec.Switch == null) {
1642 Error_GotoCaseRequiresSwitchBlock (ec);
1646 Constant c = expr.ResolveLabelConstant (ec);
1652 if (ec.Switch.IsNullable && c is NullLiteral) {
1655 TypeSpec type = ec.Switch.SwitchType;
1656 res = c.Reduce (ec, type);
1658 c.Error_ValueCannotBeConverted (ec, type, true);
1662 if (!Convert.ImplicitStandardConversionExists (c, type))
1663 ec.Report.Warning (469, 2, loc,
1664 "The `goto case' value is not implicitly convertible to type `{0}'",
1665 type.GetSignatureForError ());
1669 ec.Switch.RegisterGotoCase (this, res);
1676 protected override void DoEmit (EmitContext ec)
1678 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1681 protected override void CloneTo (CloneContext clonectx, Statement t)
1683 GotoCase target = (GotoCase) t;
1685 target.expr = expr.Clone (clonectx);
1688 public override Reachability MarkReachable (Reachability rc)
1690 if (!rc.IsUnreachable) {
1691 var label = switch_statement.FindLabel ((Constant) expr);
1692 if (label.IsUnreachable) {
1693 label.MarkReachable (rc);
1694 switch_statement.Block.ScanGotoJump (label);
1698 return base.MarkReachable (rc);
1701 public override object Accept (StructuralVisitor visitor)
1703 return visitor.Visit (this);
1707 public abstract class SwitchGoto : Statement
1709 protected bool unwind_protect;
1710 protected Switch switch_statement;
1712 protected SwitchGoto (Location loc)
1717 protected override void CloneTo (CloneContext clonectx, Statement target)
1722 public override bool Resolve (BlockContext bc)
1724 CheckExitBoundaries (bc, bc.Switch.Block);
1726 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1727 switch_statement = bc.Switch;
1732 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1737 public override Reachability MarkReachable (Reachability rc)
1739 base.MarkReachable (rc);
1740 return Reachability.CreateUnreachable ();
1743 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1745 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1749 public class Throw : Statement {
1752 public Throw (Expression expr, Location l)
1758 public Expression Expr {
1764 public override bool Resolve (BlockContext ec)
1767 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1768 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1769 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1770 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1771 if (b.IsFinallyBlock) {
1772 ec.Report.Error (724, loc,
1773 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1782 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1787 var et = ec.BuiltinTypes.Exception;
1788 if (Convert.ImplicitConversionExists (ec, expr, et))
1789 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1791 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1796 protected override void DoEmit (EmitContext ec)
1799 var atv = ec.AsyncThrowVariable;
1801 if (atv.HoistedVariant != null) {
1802 atv.HoistedVariant.Emit (ec);
1807 ec.Emit (OpCodes.Throw);
1809 ec.Emit (OpCodes.Rethrow);
1814 ec.Emit (OpCodes.Throw);
1818 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1821 expr.FlowAnalysis (fc);
1826 public override Reachability MarkReachable (Reachability rc)
1828 base.MarkReachable (rc);
1829 return Reachability.CreateUnreachable ();
1832 protected override void CloneTo (CloneContext clonectx, Statement t)
1834 Throw target = (Throw) t;
1837 target.expr = expr.Clone (clonectx);
1840 public override object Accept (StructuralVisitor visitor)
1842 return visitor.Visit (this);
1846 public class Break : LocalExitStatement
1848 public Break (Location l)
1853 public override object Accept (StructuralVisitor visitor)
1855 return visitor.Visit (this);
1858 protected override void DoEmit (EmitContext ec)
1862 if (ec.TryFinallyUnwind != null) {
1863 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1864 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1867 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1870 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1872 enclosing_loop.AddEndDefiniteAssignment (fc);
1876 protected override bool DoResolve (BlockContext bc)
1878 enclosing_loop = bc.EnclosingLoopOrSwitch;
1879 return base.DoResolve (bc);
1882 public override Reachability MarkReachable (Reachability rc)
1884 base.MarkReachable (rc);
1886 if (!rc.IsUnreachable)
1887 enclosing_loop.SetEndReachable ();
1889 return Reachability.CreateUnreachable ();
1893 public class Continue : LocalExitStatement
1895 public Continue (Location l)
1900 public override object Accept (StructuralVisitor visitor)
1902 return visitor.Visit (this);
1906 protected override void DoEmit (EmitContext ec)
1908 var l = ec.LoopBegin;
1910 if (ec.TryFinallyUnwind != null) {
1911 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1912 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1915 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1918 protected override bool DoResolve (BlockContext bc)
1920 enclosing_loop = bc.EnclosingLoop;
1921 return base.DoResolve (bc);
1924 public override Reachability MarkReachable (Reachability rc)
1926 base.MarkReachable (rc);
1928 if (!rc.IsUnreachable)
1929 enclosing_loop.SetIteratorReachable ();
1931 return Reachability.CreateUnreachable ();
1935 public abstract class LocalExitStatement : ExitStatement
1937 protected LoopStatement enclosing_loop;
1939 protected LocalExitStatement (Location loc)
1944 protected override bool IsLocalExit {
1950 protected override void CloneTo (CloneContext clonectx, Statement t)
1955 protected override bool DoResolve (BlockContext bc)
1957 if (enclosing_loop == null) {
1958 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1962 var block = enclosing_loop.Statement as Block;
1964 // Don't need to do extra checks for simple statements loops
1965 if (block != null) {
1966 CheckExitBoundaries (bc, block);
1973 public interface ILocalVariable
1975 void Emit (EmitContext ec);
1976 void EmitAssign (EmitContext ec);
1977 void EmitAddressOf (EmitContext ec);
1980 public interface INamedBlockVariable
1982 Block Block { get; }
1983 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1984 bool IsDeclared { get; }
1985 bool IsParameter { get; }
1986 Location Location { get; }
1989 public class BlockVariableDeclarator
1992 Expression initializer;
1994 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1996 if (li.Type != null)
1997 throw new ArgumentException ("Expected null variable type");
2000 this.initializer = initializer;
2005 public LocalVariable Variable {
2011 public Expression Initializer {
2016 initializer = value;
2022 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2024 var t = (BlockVariableDeclarator) MemberwiseClone ();
2025 if (initializer != null)
2026 t.initializer = initializer.Clone (cloneCtx);
2032 public class BlockVariable : Statement
2034 Expression initializer;
2035 protected FullNamedExpression type_expr;
2036 protected LocalVariable li;
2037 protected List<BlockVariableDeclarator> declarators;
2040 public BlockVariable (FullNamedExpression type, LocalVariable li)
2042 this.type_expr = type;
2044 this.loc = type_expr.Location;
2047 protected BlockVariable (LocalVariable li)
2054 public List<BlockVariableDeclarator> Declarators {
2060 public Expression Initializer {
2065 initializer = value;
2069 public FullNamedExpression TypeExpression {
2075 public LocalVariable Variable {
2083 public void AddDeclarator (BlockVariableDeclarator decl)
2085 if (declarators == null)
2086 declarators = new List<BlockVariableDeclarator> ();
2088 declarators.Add (decl);
2091 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2093 if (bc.Report.Errors != 0)
2096 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2098 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2099 new MemberName (li.Name, li.Location), null);
2101 container.AddField (f);
2104 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2108 public override bool Resolve (BlockContext bc)
2110 return Resolve (bc, true);
2113 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2115 if (type == null && !li.IsCompilerGenerated) {
2116 var vexpr = type_expr as VarExpr;
2119 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2120 // same name exists or as a keyword when no type was found
2122 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2123 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2124 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2127 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2131 if (li.IsConstant) {
2132 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2136 if (Initializer == null) {
2137 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2141 if (declarators != null) {
2142 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2146 Initializer = Initializer.Resolve (bc);
2147 if (Initializer != null) {
2148 ((VarExpr) type_expr).InferType (bc, Initializer);
2149 type = type_expr.Type;
2151 // Set error type to indicate the var was placed correctly but could
2154 // var a = missing ();
2156 type = InternalType.ErrorType;
2161 type = type_expr.ResolveAsType (bc);
2165 if (li.IsConstant && !type.IsConstantCompatible) {
2166 Const.Error_InvalidConstantType (type, loc, bc.Report);
2171 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2176 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2178 CreateEvaluatorVariable (bc, li);
2179 } else if (type != InternalType.ErrorType) {
2180 li.PrepareAssignmentAnalysis (bc);
2183 if (initializer != null) {
2184 initializer = ResolveInitializer (bc, li, initializer);
2185 // li.Variable.DefinitelyAssigned
2188 if (declarators != null) {
2189 foreach (var d in declarators) {
2190 d.Variable.Type = li.Type;
2192 CreateEvaluatorVariable (bc, d.Variable);
2193 } else if (type != InternalType.ErrorType) {
2194 d.Variable.PrepareAssignmentAnalysis (bc);
2197 if (d.Initializer != null && resolveDeclaratorInitializers) {
2198 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2199 // d.Variable.DefinitelyAssigned
2207 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2209 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2210 return a.ResolveStatement (bc);
2213 protected override void DoEmit (EmitContext ec)
2215 li.CreateBuilder (ec);
2217 if (Initializer != null && !IsUnreachable)
2218 ((ExpressionStatement) Initializer).EmitStatement (ec);
2220 if (declarators != null) {
2221 foreach (var d in declarators) {
2222 d.Variable.CreateBuilder (ec);
2223 if (d.Initializer != null && !IsUnreachable) {
2224 ec.Mark (d.Variable.Location);
2225 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2231 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2233 if (Initializer != null)
2234 Initializer.FlowAnalysis (fc);
2236 if (declarators != null) {
2237 foreach (var d in declarators) {
2238 if (d.Initializer != null)
2239 d.Initializer.FlowAnalysis (fc);
2246 public override Reachability MarkReachable (Reachability rc)
2248 var init = initializer as ExpressionStatement;
2250 init.MarkReachable (rc);
2252 return base.MarkReachable (rc);
2255 protected override void CloneTo (CloneContext clonectx, Statement target)
2257 BlockVariable t = (BlockVariable) target;
2259 if (type_expr != null)
2260 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2262 if (initializer != null)
2263 t.initializer = initializer.Clone (clonectx);
2265 if (declarators != null) {
2266 t.declarators = null;
2267 foreach (var d in declarators)
2268 t.AddDeclarator (d.Clone (clonectx));
2272 public override object Accept (StructuralVisitor visitor)
2274 return visitor.Visit (this);
2278 public class BlockConstant : BlockVariable
2280 public BlockConstant (FullNamedExpression type, LocalVariable li)
2285 public override void Emit (EmitContext ec)
2287 // Nothing to emit, not even sequence point
2290 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2292 initializer = initializer.Resolve (bc);
2293 if (initializer == null)
2296 var c = initializer as Constant;
2298 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2302 c = c.ConvertImplicitly (li.Type);
2304 if (TypeSpec.IsReferenceType (li.Type))
2305 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2307 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2312 li.ConstantValue = c;
2316 public override object Accept (StructuralVisitor visitor)
2318 return visitor.Visit (this);
2323 // The information about a user-perceived local variable
2325 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2332 AddressTaken = 1 << 2,
2333 CompilerGenerated = 1 << 3,
2335 ForeachVariable = 1 << 5,
2336 FixedVariable = 1 << 6,
2337 UsingVariable = 1 << 7,
2339 SymbolFileHidden = 1 << 9,
2341 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2345 readonly string name;
2346 readonly Location loc;
2347 readonly Block block;
2349 Constant const_value;
2351 public VariableInfo VariableInfo;
2352 HoistedVariable hoisted_variant;
2354 LocalBuilder builder;
2356 public LocalVariable (Block block, string name, Location loc)
2363 public LocalVariable (Block block, string name, Flags flags, Location loc)
2364 : this (block, name, loc)
2370 // Used by variable declarators
2372 public LocalVariable (LocalVariable li, string name, Location loc)
2373 : this (li.block, name, li.flags, loc)
2379 public bool AddressTaken {
2381 return (flags & Flags.AddressTaken) != 0;
2385 public Block Block {
2391 public Constant ConstantValue {
2396 const_value = value;
2401 // Hoisted local variable variant
2403 public HoistedVariable HoistedVariant {
2405 return hoisted_variant;
2408 hoisted_variant = value;
2412 public bool IsDeclared {
2414 return type != null;
2418 public bool IsCompilerGenerated {
2420 return (flags & Flags.CompilerGenerated) != 0;
2424 public bool IsConstant {
2426 return (flags & Flags.Constant) != 0;
2430 public bool IsLocked {
2432 return (flags & Flags.IsLocked) != 0;
2435 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2439 public bool IsThis {
2441 return (flags & Flags.IsThis) != 0;
2445 public bool IsFixed {
2447 return (flags & Flags.FixedVariable) != 0;
2450 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2454 bool INamedBlockVariable.IsParameter {
2460 public bool IsReadonly {
2462 return (flags & Flags.ReadonlyMask) != 0;
2466 public Location Location {
2472 public string Name {
2478 public TypeSpec Type {
2489 public void CreateBuilder (EmitContext ec)
2491 if ((flags & Flags.Used) == 0) {
2492 if (VariableInfo == null) {
2493 // Missing flow analysis or wrong variable flags
2494 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2497 if (VariableInfo.IsEverAssigned)
2498 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2500 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2503 if (HoistedVariant != null)
2506 if (builder != null) {
2507 if ((flags & Flags.CompilerGenerated) != 0)
2510 // To avoid Used warning duplicates
2511 throw new InternalErrorException ("Already created variable `{0}'", name);
2515 // All fixed variabled are pinned, a slot has to be alocated
2517 builder = ec.DeclareLocal (Type, IsFixed);
2518 if ((flags & Flags.SymbolFileHidden) == 0)
2519 ec.DefineLocalVariable (name, builder);
2522 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2524 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2525 if (!writeToSymbolFile)
2526 li.flags |= Flags.SymbolFileHidden;
2532 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2534 if (IsConstant && const_value != null)
2535 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2537 return new LocalVariableReference (this, loc);
2540 public void Emit (EmitContext ec)
2542 // TODO: Need something better for temporary variables
2543 if ((flags & Flags.CompilerGenerated) != 0)
2546 ec.Emit (OpCodes.Ldloc, builder);
2549 public void EmitAssign (EmitContext ec)
2551 // TODO: Need something better for temporary variables
2552 if ((flags & Flags.CompilerGenerated) != 0)
2555 ec.Emit (OpCodes.Stloc, builder);
2558 public void EmitAddressOf (EmitContext ec)
2560 // TODO: Need something better for temporary variables
2561 if ((flags & Flags.CompilerGenerated) != 0)
2564 ec.Emit (OpCodes.Ldloca, builder);
2567 public static string GetCompilerGeneratedName (Block block)
2569 // HACK: Debugger depends on the name semantics
2570 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2573 public string GetReadOnlyContext ()
2575 switch (flags & Flags.ReadonlyMask) {
2576 case Flags.FixedVariable:
2577 return "fixed variable";
2578 case Flags.ForeachVariable:
2579 return "foreach iteration variable";
2580 case Flags.UsingVariable:
2581 return "using variable";
2584 throw new InternalErrorException ("Variable is not readonly");
2587 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2589 if (VariableInfo == null)
2590 throw new Exception ();
2592 if (IsAssigned (fc))
2595 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2598 public bool IsAssigned (FlowAnalysisContext fc)
2600 return fc.IsDefinitelyAssigned (VariableInfo);
2603 public void PrepareAssignmentAnalysis (BlockContext bc)
2606 // No need to run assignment analysis for these guys
2608 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2611 VariableInfo = VariableInfo.Create (bc, this);
2615 // Mark the variables as referenced in the user code
2617 public void SetIsUsed ()
2619 flags |= Flags.Used;
2622 public void SetHasAddressTaken ()
2624 flags |= (Flags.AddressTaken | Flags.Used);
2627 public override string ToString ()
2629 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2634 /// Block represents a C# block.
2638 /// This class is used in a number of places: either to represent
2639 /// explicit blocks that the programmer places or implicit blocks.
2641 /// Implicit blocks are used as labels or to introduce variable
2644 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2645 /// they contain extra information that is not necessary on normal blocks.
2647 public class Block : Statement {
2654 HasCapturedVariable = 64,
2655 HasCapturedThis = 1 << 7,
2656 IsExpressionTree = 1 << 8,
2657 CompilerGenerated = 1 << 9,
2658 HasAsyncModifier = 1 << 10,
2660 YieldBlock = 1 << 12,
2661 AwaitBlock = 1 << 13,
2662 FinallyBlock = 1 << 14,
2663 CatchBlock = 1 << 15,
2665 NoFlowAnalysis = 1 << 21,
2666 InitializationEmitted = 1 << 22
2669 public Block Parent;
2670 public Location StartLocation;
2671 public Location EndLocation;
2673 public ExplicitBlock Explicit;
2674 public ParametersBlock ParametersBlock;
2676 protected Flags flags;
2679 // The statements in this block
2681 protected List<Statement> statements;
2683 protected List<Statement> scope_initializers;
2685 int? resolving_init_idx;
2691 public int ID = id++;
2693 static int clone_id_counter;
2697 // int assignable_slots;
2699 public Block (Block parent, Location start, Location end)
2700 : this (parent, 0, start, end)
2704 public Block (Block parent, Flags flags, Location start, Location end)
2706 if (parent != null) {
2707 // the appropriate constructors will fixup these fields
2708 ParametersBlock = parent.ParametersBlock;
2709 Explicit = parent.Explicit;
2712 this.Parent = parent;
2714 this.StartLocation = start;
2715 this.EndLocation = end;
2717 statements = new List<Statement> (4);
2719 this.original = this;
2724 public Block Original {
2733 public bool IsCompilerGenerated {
2734 get { return (flags & Flags.CompilerGenerated) != 0; }
2735 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2739 public bool IsCatchBlock {
2741 return (flags & Flags.CatchBlock) != 0;
2745 public bool IsFinallyBlock {
2747 return (flags & Flags.FinallyBlock) != 0;
2751 public bool Unchecked {
2752 get { return (flags & Flags.Unchecked) != 0; }
2753 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2756 public bool Unsafe {
2757 get { return (flags & Flags.Unsafe) != 0; }
2758 set { flags |= Flags.Unsafe; }
2761 public List<Statement> Statements {
2762 get { return statements; }
2767 public void SetEndLocation (Location loc)
2772 public void AddLabel (LabeledStatement target)
2774 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2777 public void AddLocalName (LocalVariable li)
2779 AddLocalName (li.Name, li);
2782 public void AddLocalName (string name, INamedBlockVariable li)
2784 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2787 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2789 if (reason == null) {
2790 Error_AlreadyDeclared (name, variable);
2794 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2795 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2796 "to `{0}', which is already used in a `{1}' scope to denote something else",
2800 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2802 var pi = variable as ParametersBlock.ParameterInfo;
2804 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2806 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2807 "A local variable named `{0}' is already defined in this scope", name);
2811 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2813 ParametersBlock.TopBlock.Report.Error (412, loc,
2814 "The type parameter name `{0}' is the same as local variable or parameter name",
2819 // It should be used by expressions which require to
2820 // register a statement during resolve process.
2822 public void AddScopeStatement (Statement s)
2824 if (scope_initializers == null)
2825 scope_initializers = new List<Statement> ();
2828 // Simple recursive helper, when resolve scope initializer another
2829 // new scope initializer can be added, this ensures it's initialized
2830 // before existing one. For now this can happen with expression trees
2831 // in base ctor initializer only
2833 if (resolving_init_idx.HasValue) {
2834 scope_initializers.Insert (resolving_init_idx.Value, s);
2835 ++resolving_init_idx;
2837 scope_initializers.Add (s);
2841 public void InsertStatement (int index, Statement s)
2843 statements.Insert (index, s);
2846 public void AddStatement (Statement s)
2851 public LabeledStatement LookupLabel (string name)
2853 return ParametersBlock.GetLabel (name, this);
2856 public override Reachability MarkReachable (Reachability rc)
2858 if (rc.IsUnreachable)
2861 MarkReachableScope (rc);
2863 foreach (var s in statements) {
2864 rc = s.MarkReachable (rc);
2865 if (rc.IsUnreachable) {
2866 if ((flags & Flags.ReachableEnd) != 0)
2867 return new Reachability ();
2873 flags |= Flags.ReachableEnd;
2878 public void MarkReachableScope (Reachability rc)
2880 base.MarkReachable (rc);
2882 if (scope_initializers != null) {
2883 foreach (var si in scope_initializers)
2884 si.MarkReachable (rc);
2888 public override bool Resolve (BlockContext bc)
2890 if ((flags & Flags.Resolved) != 0)
2893 Block prev_block = bc.CurrentBlock;
2894 bc.CurrentBlock = this;
2897 // Compiler generated scope statements
2899 if (scope_initializers != null) {
2900 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2901 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2904 resolving_init_idx = null;
2908 int statement_count = statements.Count;
2909 for (int ix = 0; ix < statement_count; ix++){
2910 Statement s = statements [ix];
2912 if (!s.Resolve (bc)) {
2914 statements [ix] = new EmptyStatement (s.loc);
2919 bc.CurrentBlock = prev_block;
2921 flags |= Flags.Resolved;
2925 protected override void DoEmit (EmitContext ec)
2927 for (int ix = 0; ix < statements.Count; ix++){
2928 statements [ix].Emit (ec);
2932 public override void Emit (EmitContext ec)
2934 if (scope_initializers != null)
2935 EmitScopeInitializers (ec);
2940 protected void EmitScopeInitializers (EmitContext ec)
2942 foreach (Statement s in scope_initializers)
2946 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2948 if (scope_initializers != null) {
2949 foreach (var si in scope_initializers)
2950 si.FlowAnalysis (fc);
2953 return DoFlowAnalysis (fc, 0);
2956 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2958 bool end_unreachable = !reachable;
2959 bool goto_flow_analysis = startIndex != 0;
2960 for (; startIndex < statements.Count; ++startIndex) {
2961 var s = statements[startIndex];
2963 end_unreachable = s.FlowAnalysis (fc);
2964 if (s.IsUnreachable) {
2965 statements [startIndex] = RewriteUnreachableStatement (s);
2970 // Statement end reachability is needed mostly due to goto support. Consider
2979 // X label is reachable only via goto not as another statement after if. We need
2980 // this for flow-analysis only to carry variable info correctly.
2982 if (end_unreachable) {
2983 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2985 var f = s as TryFinally;
2986 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
2988 // Special case for try-finally with unreachable code after
2989 // finally block. Try block has to include leave opcode but there is
2990 // no label to leave to after unreachable finally block closing
2991 // brace. This sentinel ensures there is always IL instruction to
2992 // leave to even if we know it'll never be reached.
2994 statements.Insert (startIndex + 1, new SentinelStatement ());
2996 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2997 s = statements [startIndex];
2998 if (s is SwitchLabel) {
2999 if (!after_goto_case)
3000 s.FlowAnalysis (fc);
3005 if (s.IsUnreachable) {
3006 s.FlowAnalysis (fc);
3007 statements [startIndex] = RewriteUnreachableStatement (s);
3013 // Idea is to stop after goto case because goto case will always have at least same
3014 // variable assigned as switch case label. This saves a lot for complex goto case tests
3016 if (after_goto_case)
3022 var lb = s as LabeledStatement;
3023 if (lb != null && fc.AddReachedLabel (lb))
3028 // The condition should be true unless there is forward jumping goto
3030 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3033 return !Explicit.HasReachableClosingBrace;
3036 static Statement RewriteUnreachableStatement (Statement s)
3038 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3039 // analysis. Even csc report unreachable warning for it but it's actually used hence
3040 // we try to emulate this behaviour
3048 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3051 return new EmptyStatement (s.loc);
3054 public void ScanGotoJump (Statement label)
3057 for (i = 0; i < statements.Count; ++i) {
3058 if (statements[i] == label)
3062 var rc = new Reachability ();
3063 for (++i; i < statements.Count; ++i) {
3064 var s = statements[i];
3065 rc = s.MarkReachable (rc);
3066 if (rc.IsUnreachable)
3070 flags |= Flags.ReachableEnd;
3073 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3076 for (i = 0; i < statements.Count; ++i) {
3077 if (statements[i] == label)
3081 DoFlowAnalysis (fc, ++i);
3085 public override string ToString ()
3087 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3091 protected override void CloneTo (CloneContext clonectx, Statement t)
3093 Block target = (Block) t;
3095 target.clone_id = ++clone_id_counter;
3098 clonectx.AddBlockMap (this, target);
3099 if (original != this)
3100 clonectx.AddBlockMap (original, target);
3102 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3103 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3106 target.Parent = clonectx.RemapBlockCopy (Parent);
3108 target.statements = new List<Statement> (statements.Count);
3109 foreach (Statement s in statements)
3110 target.statements.Add (s.Clone (clonectx));
3113 public override object Accept (StructuralVisitor visitor)
3115 return visitor.Visit (this);
3119 public class ExplicitBlock : Block
3121 protected AnonymousMethodStorey am_storey;
3123 public ExplicitBlock (Block parent, Location start, Location end)
3124 : this (parent, (Flags) 0, start, end)
3128 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3129 : base (parent, flags, start, end)
3131 this.Explicit = this;
3136 public AnonymousMethodStorey AnonymousMethodStorey {
3142 public bool HasAwait {
3144 return (flags & Flags.AwaitBlock) != 0;
3148 public bool HasCapturedThis {
3150 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3153 return (flags & Flags.HasCapturedThis) != 0;
3158 // Used to indicate that the block has reference to parent
3159 // block and cannot be made static when defining anonymous method
3161 public bool HasCapturedVariable {
3163 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3166 return (flags & Flags.HasCapturedVariable) != 0;
3170 public bool HasReachableClosingBrace {
3172 return (flags & Flags.ReachableEnd) != 0;
3175 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3179 public bool HasYield {
3181 return (flags & Flags.YieldBlock) != 0;
3188 // Creates anonymous method storey in current block
3190 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3193 // Return same story for iterator and async blocks unless we are
3194 // in nested anonymous method
3196 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3197 return ec.CurrentAnonymousMethod.Storey;
3199 if (am_storey == null) {
3200 MemberBase mc = ec.MemberContext as MemberBase;
3203 // Creates anonymous method storey for this block
3205 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3211 public void EmitScopeInitialization (EmitContext ec)
3213 if ((flags & Flags.InitializationEmitted) != 0)
3216 if (am_storey != null) {
3217 DefineStoreyContainer (ec, am_storey);
3218 am_storey.EmitStoreyInstantiation (ec, this);
3221 if (scope_initializers != null)
3222 EmitScopeInitializers (ec);
3224 flags |= Flags.InitializationEmitted;
3227 public override void Emit (EmitContext ec)
3232 EmitScopeInitialization (ec);
3234 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3235 ec.Emit (OpCodes.Nop);
3243 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3244 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3245 ec.Emit (OpCodes.Nop);
3249 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3251 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3252 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3253 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3257 // Creates anonymous method storey
3259 storey.CreateContainer ();
3260 storey.DefineContainer ();
3262 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3265 // Only first storey in path will hold this reference. All children blocks will
3266 // reference it indirectly using $ref field
3268 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3269 if (b.Parent != null) {
3270 var s = b.Parent.Explicit.AnonymousMethodStorey;
3272 storey.HoistedThis = s.HoistedThis;
3277 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3278 if (storey.HoistedThis == null)
3279 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3281 if (storey.HoistedThis != null)
3287 // We are the first storey on path and 'this' has to be hoisted
3289 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3290 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3292 // ThisReferencesFromChildrenBlock holds all reference even if they
3293 // are not on this path. It saves some memory otherwise it'd have to
3294 // be in every explicit block. We run this check to see if the reference
3295 // is valid for this storey
3297 Block block_on_path = ref_block;
3298 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3300 if (block_on_path == null)
3303 if (storey.HoistedThis == null) {
3304 storey.AddCapturedThisField (ec, null);
3307 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3309 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3311 if (b_storey != null) {
3313 // Don't add storey cross reference for `this' when the storey ends up not
3314 // beeing attached to any parent
3316 if (b.ParametersBlock.StateMachine == null) {
3317 AnonymousMethodStorey s = null;
3318 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3319 s = ab.Explicit.AnonymousMethodStorey;
3324 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3326 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3327 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3334 // Stop propagation inside same top block
3336 if (b.ParametersBlock == ParametersBlock.Original) {
3337 b_storey.AddParentStoreyReference (ec, storey);
3338 // b_storey.HoistedThis = storey.HoistedThis;
3342 b = pb = b.ParametersBlock;
3344 pb = b as ParametersBlock;
3347 if (pb != null && pb.StateMachine != null) {
3348 if (pb.StateMachine == storey)
3352 // If we are state machine with no parent. We can hook into parent without additional
3353 // reference and capture this directly
3355 ExplicitBlock parent_storey_block = pb;
3356 while (parent_storey_block.Parent != null) {
3357 parent_storey_block = parent_storey_block.Parent.Explicit;
3358 if (parent_storey_block.AnonymousMethodStorey != null) {
3363 if (parent_storey_block.AnonymousMethodStorey == null) {
3364 if (pb.StateMachine.HoistedThis == null) {
3365 pb.StateMachine.AddCapturedThisField (ec, null);
3366 b.HasCapturedThis = true;
3372 var parent_this_block = pb;
3373 while (parent_this_block.Parent != null) {
3374 parent_this_block = parent_this_block.Parent.ParametersBlock;
3375 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3381 // Add reference to closest storey which holds captured this
3383 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3387 // Add parent storey reference only when this is not captured directly
3389 if (b_storey != null) {
3390 b_storey.AddParentStoreyReference (ec, storey);
3391 b_storey.HoistedThis = storey.HoistedThis;
3398 var ref_blocks = storey.ReferencesFromChildrenBlock;
3399 if (ref_blocks != null) {
3400 foreach (ExplicitBlock ref_block in ref_blocks) {
3401 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3402 if (b.AnonymousMethodStorey != null) {
3403 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3406 // Stop propagation inside same top block
3408 if (b.ParametersBlock == ParametersBlock.Original)
3411 b = b.ParametersBlock;
3414 var pb = b as ParametersBlock;
3415 if (pb != null && pb.StateMachine != null) {
3416 if (pb.StateMachine == storey)
3419 pb.StateMachine.AddParentStoreyReference (ec, storey);
3422 b.HasCapturedVariable = true;
3428 storey.PrepareEmit ();
3429 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3432 public void RegisterAsyncAwait ()
3435 while ((block.flags & Flags.AwaitBlock) == 0) {
3436 block.flags |= Flags.AwaitBlock;
3438 if (block is ParametersBlock)
3441 block = block.Parent.Explicit;
3445 public void RegisterIteratorYield ()
3447 ParametersBlock.TopBlock.IsIterator = true;
3450 while ((block.flags & Flags.YieldBlock) == 0) {
3451 block.flags |= Flags.YieldBlock;
3453 if (block.Parent == null)
3456 block = block.Parent.Explicit;
3460 public void SetCatchBlock ()
3462 flags |= Flags.CatchBlock;
3465 public void SetFinallyBlock ()
3467 flags |= Flags.FinallyBlock;
3470 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3472 tryBlock.statements = statements;
3473 statements = new List<Statement> (1);
3474 statements.Add (tf);
3479 // ParametersBlock was introduced to support anonymous methods
3480 // and lambda expressions
3482 public class ParametersBlock : ExplicitBlock
3484 public class ParameterInfo : INamedBlockVariable
3486 readonly ParametersBlock block;
3488 public VariableInfo VariableInfo;
3491 public ParameterInfo (ParametersBlock block, int index)
3499 public ParametersBlock Block {
3505 Block INamedBlockVariable.Block {
3511 public bool IsDeclared {
3517 public bool IsParameter {
3523 public bool IsLocked {
3532 public Location Location {
3534 return Parameter.Location;
3538 public Parameter Parameter {
3540 return block.Parameters [index];
3544 public TypeSpec ParameterType {
3546 return Parameter.Type;
3552 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3554 return new ParameterReference (this, loc);
3559 // Block is converted into an expression
3561 sealed class BlockScopeExpression : Expression
3564 readonly ParametersBlock block;
3566 public BlockScopeExpression (Expression child, ParametersBlock block)
3572 public override bool ContainsEmitWithAwait ()
3574 return child.ContainsEmitWithAwait ();
3577 public override Expression CreateExpressionTree (ResolveContext ec)
3579 throw new NotSupportedException ();
3582 protected override Expression DoResolve (ResolveContext ec)
3587 child = child.Resolve (ec);
3591 eclass = child.eclass;
3596 public override void Emit (EmitContext ec)
3598 block.EmitScopeInitializers (ec);
3603 protected ParametersCompiled parameters;
3604 protected ParameterInfo[] parameter_info;
3605 protected bool resolved;
3606 protected ToplevelBlock top_block;
3607 protected StateMachine state_machine;
3608 protected Dictionary<string, object> labels;
3610 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3611 : base (parent, 0, start, start)
3613 if (parameters == null)
3614 throw new ArgumentNullException ("parameters");
3616 this.parameters = parameters;
3617 ParametersBlock = this;
3619 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3621 this.top_block = parent.ParametersBlock.top_block;
3622 ProcessParameters ();
3625 protected ParametersBlock (ParametersCompiled parameters, Location start)
3626 : base (null, 0, start, start)
3628 if (parameters == null)
3629 throw new ArgumentNullException ("parameters");
3631 this.parameters = parameters;
3632 ParametersBlock = this;
3636 // It's supposed to be used by method body implementation of anonymous methods
3638 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3639 : base (null, 0, source.StartLocation, source.EndLocation)
3641 this.parameters = parameters;
3642 this.statements = source.statements;
3643 this.scope_initializers = source.scope_initializers;
3645 this.resolved = true;
3646 this.reachable = source.reachable;
3647 this.am_storey = source.am_storey;
3648 this.state_machine = source.state_machine;
3649 this.flags = source.flags & Flags.ReachableEnd;
3651 ParametersBlock = this;
3654 // Overwrite original for comparison purposes when linking cross references
3655 // between anonymous methods
3657 Original = source.Original;
3662 public bool IsAsync {
3664 return (flags & Flags.HasAsyncModifier) != 0;
3667 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3672 // Block has been converted to expression tree
3674 public bool IsExpressionTree {
3676 return (flags & Flags.IsExpressionTree) != 0;
3681 // The parameters for the block.
3683 public ParametersCompiled Parameters {
3689 public StateMachine StateMachine {
3691 return state_machine;
3695 public ToplevelBlock TopBlock {
3704 public bool Resolved {
3706 return (flags & Flags.Resolved) != 0;
3710 public int TemporaryLocalsCount { get; set; }
3715 // Checks whether all `out' parameters have been assigned.
3717 public void CheckControlExit (FlowAnalysisContext fc)
3719 CheckControlExit (fc, fc.DefiniteAssignment);
3722 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3724 if (parameter_info == null)
3727 foreach (var p in parameter_info) {
3728 if (p.VariableInfo == null)
3731 if (p.VariableInfo.IsAssigned (dat))
3734 fc.Report.Error (177, p.Location,
3735 "The out parameter `{0}' must be assigned to before control leaves the current method",
3740 protected override void CloneTo (CloneContext clonectx, Statement t)
3742 base.CloneTo (clonectx, t);
3744 var target = (ParametersBlock) t;
3747 // Clone label statements as well as they contain block reference
3751 if (pb.labels != null) {
3752 target.labels = new Dictionary<string, object> ();
3754 foreach (var entry in pb.labels) {
3755 var list = entry.Value as List<LabeledStatement>;
3758 var list_clone = new List<LabeledStatement> ();
3759 foreach (var lentry in list) {
3760 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3763 target.labels.Add (entry.Key, list_clone);
3765 var labeled = (LabeledStatement) entry.Value;
3766 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3773 if (pb.Parent == null)
3776 pb = pb.Parent.ParametersBlock;
3780 public override Expression CreateExpressionTree (ResolveContext ec)
3782 if (statements.Count == 1) {
3783 Expression expr = statements[0].CreateExpressionTree (ec);
3784 if (scope_initializers != null)
3785 expr = new BlockScopeExpression (expr, this);
3790 return base.CreateExpressionTree (ec);
3793 public override void Emit (EmitContext ec)
3795 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3796 DefineStoreyContainer (ec, state_machine);
3797 state_machine.EmitStoreyInstantiation (ec, this);
3803 public void EmitEmbedded (EmitContext ec)
3805 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3806 DefineStoreyContainer (ec, state_machine);
3807 state_machine.EmitStoreyInstantiation (ec, this);
3813 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3815 var res = base.DoFlowAnalysis (fc);
3817 if (HasReachableClosingBrace)
3818 CheckControlExit (fc);
3823 public LabeledStatement GetLabel (string name, Block block)
3826 // Cloned parameters blocks can have their own cloned version of top-level labels
3828 if (labels == null) {
3830 return Parent.ParametersBlock.GetLabel (name, block);
3836 if (!labels.TryGetValue (name, out value)) {
3840 var label = value as LabeledStatement;
3842 if (label != null) {
3843 if (IsLabelVisible (label, b))
3847 List<LabeledStatement> list = (List<LabeledStatement>) value;
3848 for (int i = 0; i < list.Count; ++i) {
3850 if (IsLabelVisible (label, b))
3858 static bool IsLabelVisible (LabeledStatement label, Block b)
3861 if (label.Block == b)
3864 } while (b != null);
3869 public ParameterInfo GetParameterInfo (Parameter p)
3871 for (int i = 0; i < parameters.Count; ++i) {
3872 if (parameters[i] == p)
3873 return parameter_info[i];
3876 throw new ArgumentException ("Invalid parameter");
3879 public ParameterReference GetParameterReference (int index, Location loc)
3881 return new ParameterReference (parameter_info[index], loc);
3884 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3886 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3888 CloneContext clonectx = new CloneContext ();
3889 return Clone (clonectx);
3892 protected void ProcessParameters ()
3894 if (parameters.Count == 0)
3897 parameter_info = new ParameterInfo[parameters.Count];
3898 for (int i = 0; i < parameter_info.Length; ++i) {
3899 var p = parameters.FixedParameters[i];
3903 // TODO: Should use Parameter only and more block there
3904 parameter_info[i] = new ParameterInfo (this, i);
3906 AddLocalName (p.Name, parameter_info[i]);
3910 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3912 var src = stmt.Block;
3915 // Cannot remap label block if the label was not yet cloned which
3916 // can happen in case of anonymous method inside anoynymous method
3917 // with a label. But in this case we don't care because goto cannot
3918 // jump of out anonymous method
3920 if (src.ParametersBlock != this)
3923 var src_stmts = src.Statements;
3924 for (int i = 0; i < src_stmts.Count; ++i) {
3925 if (src_stmts[i] == stmt)
3926 return (LabeledStatement) dst.Statements[i];
3929 throw new InternalErrorException ("Should never be reached");
3932 public override bool Resolve (BlockContext bc)
3934 // TODO: if ((flags & Flags.Resolved) != 0)
3941 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3942 flags |= Flags.IsExpressionTree;
3945 PrepareAssignmentAnalysis (bc);
3947 if (!base.Resolve (bc))
3950 } catch (Exception e) {
3951 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3954 if (bc.CurrentBlock != null) {
3955 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3957 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3962 // If an asynchronous body of F is either an expression classified as nothing, or a
3963 // statement block where no return statements have expressions, the inferred return type is Task
3966 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3967 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3968 am.ReturnTypeInference = null;
3969 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3977 void PrepareAssignmentAnalysis (BlockContext bc)
3979 for (int i = 0; i < parameters.Count; ++i) {
3980 var par = parameters.FixedParameters[i];
3982 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3985 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3989 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3991 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3992 var stateMachine = new IteratorStorey (iterator);
3994 state_machine = stateMachine;
3995 iterator.SetStateMachine (stateMachine);
3997 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3998 tlb.Original = this;
3999 tlb.state_machine = stateMachine;
4000 tlb.AddStatement (new Return (iterator, iterator.Location));
4004 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4006 for (int i = 0; i < parameters.Count; i++) {
4007 Parameter p = parameters[i];
4008 Parameter.Modifier mod = p.ModFlags;
4009 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4010 host.Compiler.Report.Error (1988, p.Location,
4011 "Async methods cannot have ref or out parameters");
4015 if (p is ArglistParameter) {
4016 host.Compiler.Report.Error (4006, p.Location,
4017 "__arglist is not allowed in parameter list of async methods");
4021 if (parameters.Types[i].IsPointer) {
4022 host.Compiler.Report.Error (4005, p.Location,
4023 "Async methods cannot have unsafe parameters");
4029 host.Compiler.Report.Warning (1998, 1, loc,
4030 "Async block lacks `await' operator and will run synchronously");
4033 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4034 var initializer = new AsyncInitializer (this, host, block_type);
4035 initializer.Type = block_type;
4036 initializer.DelegateType = delegateType;
4038 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4040 state_machine = stateMachine;
4041 initializer.SetStateMachine (stateMachine);
4043 const Flags flags = Flags.CompilerGenerated;
4045 var b = this is ToplevelBlock ?
4046 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4047 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4050 b.state_machine = stateMachine;
4051 b.AddStatement (new AsyncInitializerStatement (initializer));
4059 public class ToplevelBlock : ParametersBlock
4061 LocalVariable this_variable;
4062 CompilerContext compiler;
4063 Dictionary<string, object> names;
4065 List<ExplicitBlock> this_references;
4067 public ToplevelBlock (CompilerContext ctx, Location loc)
4068 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4072 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4073 : base (parameters, start)
4075 this.compiler = ctx;
4079 ProcessParameters ();
4083 // Recreates a top level block from parameters block. Used for
4084 // compiler generated methods where the original block comes from
4085 // explicit child block. This works for already resolved blocks
4086 // only to ensure we resolve them in the correct flow order
4088 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4089 : base (source, parameters)
4091 this.compiler = source.TopBlock.compiler;
4095 public bool IsIterator {
4097 return (flags & Flags.Iterator) != 0;
4100 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4104 public Report Report {
4106 return compiler.Report;
4111 // Used by anonymous blocks to track references of `this' variable
4113 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4115 return this_references;
4120 // Returns the "this" instance variable of this block.
4121 // See AddThisVariable() for more information.
4123 public LocalVariable ThisVariable {
4125 return this_variable;
4129 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4132 names = new Dictionary<string, object> ();
4135 if (!names.TryGetValue (name, out value)) {
4136 names.Add (name, li);
4140 INamedBlockVariable existing = value as INamedBlockVariable;
4141 List<INamedBlockVariable> existing_list;
4142 if (existing != null) {
4143 existing_list = new List<INamedBlockVariable> ();
4144 existing_list.Add (existing);
4145 names[name] = existing_list;
4147 existing_list = (List<INamedBlockVariable>) value;
4151 // A collision checking between local names
4153 var variable_block = li.Block.Explicit;
4154 for (int i = 0; i < existing_list.Count; ++i) {
4155 existing = existing_list[i];
4156 Block b = existing.Block.Explicit;
4158 // Collision at same level
4159 if (variable_block == b) {
4160 li.Block.Error_AlreadyDeclared (name, li);
4164 // Collision with parent
4165 Block parent = variable_block;
4166 while ((parent = parent.Parent) != null) {
4168 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4169 i = existing_list.Count;
4174 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4175 // Collision with children
4176 while ((b = b.Parent) != null) {
4177 if (variable_block == b) {
4178 li.Block.Error_AlreadyDeclared (name, li, "child");
4179 i = existing_list.Count;
4186 existing_list.Add (li);
4189 public void AddLabel (string name, LabeledStatement label)
4192 labels = new Dictionary<string, object> ();
4195 if (!labels.TryGetValue (name, out value)) {
4196 labels.Add (name, label);
4200 LabeledStatement existing = value as LabeledStatement;
4201 List<LabeledStatement> existing_list;
4202 if (existing != null) {
4203 existing_list = new List<LabeledStatement> ();
4204 existing_list.Add (existing);
4205 labels[name] = existing_list;
4207 existing_list = (List<LabeledStatement>) value;
4211 // A collision checking between labels
4213 for (int i = 0; i < existing_list.Count; ++i) {
4214 existing = existing_list[i];
4215 Block b = existing.Block;
4217 // Collision at same level
4218 if (label.Block == b) {
4219 Report.SymbolRelatedToPreviousError (existing.loc, name);
4220 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4224 // Collision with parent
4226 while ((b = b.Parent) != null) {
4227 if (existing.Block == b) {
4228 Report.Error (158, label.loc,
4229 "The label `{0}' shadows another label by the same name in a contained scope", name);
4230 i = existing_list.Count;
4235 // Collision with with children
4237 while ((b = b.Parent) != null) {
4238 if (label.Block == b) {
4239 Report.Error (158, label.loc,
4240 "The label `{0}' shadows another label by the same name in a contained scope", name);
4241 i = existing_list.Count;
4247 existing_list.Add (label);
4250 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4252 if (this_references == null)
4253 this_references = new List<ExplicitBlock> ();
4255 if (!this_references.Contains (block))
4256 this_references.Add (block);
4259 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4261 this_references.Remove (block);
4265 // Creates an arguments set from all parameters, useful for method proxy calls
4267 public Arguments GetAllParametersArguments ()
4269 int count = parameters.Count;
4270 Arguments args = new Arguments (count);
4271 for (int i = 0; i < count; ++i) {
4272 var pi = parameter_info[i];
4273 var arg_expr = GetParameterReference (i, pi.Location);
4275 Argument.AType atype_modifier;
4276 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4277 case Parameter.Modifier.REF:
4278 atype_modifier = Argument.AType.Ref;
4280 case Parameter.Modifier.OUT:
4281 atype_modifier = Argument.AType.Out;
4288 args.Add (new Argument (arg_expr, atype_modifier));
4295 // Lookup inside a block, the returned value can represent 3 states
4297 // true+variable: A local name was found and it's valid
4298 // false+variable: A local name was found in a child block only
4299 // false+null: No local name was found
4301 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4307 if (!names.TryGetValue (name, out value))
4310 variable = value as INamedBlockVariable;
4312 if (variable != null) {
4314 if (variable.Block == b.Original)
4318 } while (b != null);
4326 } while (b != null);
4328 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4329 for (int i = 0; i < list.Count; ++i) {
4332 if (variable.Block == b.Original)
4336 } while (b != null);
4344 } while (b != null);
4354 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4356 if (block.names != null) {
4357 foreach (var n in block.names) {
4358 var variable = n.Value as INamedBlockVariable;
4359 if (variable != null) {
4360 if (variable.Block.ParametersBlock == pb)
4361 AddLocalName (n.Key, variable, false);
4365 foreach (var v in (List<INamedBlockVariable>) n.Value)
4366 if (v.Block.ParametersBlock == pb)
4367 AddLocalName (n.Key, v, false);
4373 // This is used by non-static `struct' constructors which do not have an
4374 // initializer - in this case, the constructor must initialize all of the
4375 // struct's fields. To do this, we add a "this" variable and use the flow
4376 // analysis code to ensure that it's been fully initialized before control
4377 // leaves the constructor.
4379 public void AddThisVariable (BlockContext bc)
4381 if (this_variable != null)
4382 throw new InternalErrorException (StartLocation.ToString ());
4384 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4385 this_variable.Type = bc.CurrentType;
4386 this_variable.PrepareAssignmentAnalysis (bc);
4389 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4392 // If we're a non-static struct constructor which doesn't have an
4393 // initializer, then we must initialize all of the struct's fields.
4395 if (this_variable != null)
4396 this_variable.IsThisAssigned (fc, this);
4398 base.CheckControlExit (fc, dat);
4401 public HashSet<LocalVariable> GetUndeclaredVariables ()
4406 HashSet<LocalVariable> variables = null;
4408 foreach (var entry in names) {
4409 var complex = entry.Value as List<INamedBlockVariable>;
4410 if (complex != null) {
4411 foreach (var centry in complex) {
4412 if (IsUndeclaredVariable (centry)) {
4413 if (variables == null)
4414 variables = new HashSet<LocalVariable> ();
4416 variables.Add ((LocalVariable) centry);
4419 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4420 if (variables == null)
4421 variables = new HashSet<LocalVariable> ();
4423 variables.Add ((LocalVariable)entry.Value);
4430 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4432 var lv = namedBlockVariable as LocalVariable;
4433 return lv != null && !lv.IsDeclared;
4436 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4441 foreach (var entry in names) {
4442 var complex = entry.Value as List<INamedBlockVariable>;
4443 if (complex != null) {
4444 foreach (var centry in complex) {
4445 var lv = centry as LocalVariable;
4446 if (lv != null && undeclaredVariables.Contains (lv)) {
4451 var lv = entry.Value as LocalVariable;
4452 if (lv != null && undeclaredVariables.Contains (lv))
4458 public override void Emit (EmitContext ec)
4460 if (Report.Errors > 0)
4464 if (IsCompilerGenerated) {
4465 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4473 // If `HasReturnLabel' is set, then we already emitted a
4474 // jump to the end of the method, so we must emit a `ret'
4477 // Unfortunately, System.Reflection.Emit automatically emits
4478 // a leave to the end of a finally block. This is a problem
4479 // if no code is following the try/finally block since we may
4480 // jump to a point after the end of the method.
4481 // As a workaround, we're always creating a return label in
4484 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4485 if (ec.HasReturnLabel)
4486 ec.MarkLabel (ec.ReturnLabel);
4488 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4489 ec.Mark (EndLocation);
4491 if (ec.ReturnType.Kind != MemberKind.Void)
4492 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4494 ec.Emit (OpCodes.Ret);
4497 } catch (Exception e) {
4498 throw new InternalErrorException (e, StartLocation);
4502 public bool Resolve (BlockContext bc, IMethodData md)
4507 var errors = bc.Report.Errors;
4511 if (bc.Report.Errors > errors)
4514 MarkReachable (new Reachability ());
4516 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4517 // TODO: var md = bc.CurrentMemberDefinition;
4518 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4521 if ((flags & Flags.NoFlowAnalysis) != 0)
4524 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4527 } catch (Exception e) {
4528 throw new InternalErrorException (e, StartLocation);
4535 public class SwitchLabel : Statement
4543 // if expr == null, then it is the default case.
4545 public SwitchLabel (Expression expr, Location l)
4551 public bool IsDefault {
4553 return label == null;
4557 public Expression Label {
4563 public Location Location {
4569 public Constant Converted {
4578 public bool PatternMatching { get; set; }
4580 public bool SectionStart { get; set; }
4582 public Label GetILLabel (EmitContext ec)
4584 if (il_label == null){
4585 il_label = ec.DefineLabel ();
4588 return il_label.Value;
4591 protected override void DoEmit (EmitContext ec)
4593 ec.MarkLabel (GetILLabel (ec));
4596 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4601 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4605 public override bool Resolve (BlockContext bc)
4607 if (ResolveAndReduce (bc))
4608 bc.Switch.RegisterLabel (bc, this);
4614 // Resolves the expression, reduces it to a literal if possible
4615 // and then converts it to the requested type.
4617 bool ResolveAndReduce (BlockContext bc)
4622 var switch_statement = bc.Switch;
4624 if (PatternMatching) {
4625 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4626 return label != null;
4629 var c = label.ResolveLabelConstant (bc);
4633 if (switch_statement.IsNullable && c is NullLiteral) {
4638 if (switch_statement.IsPatternMatching) {
4639 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4643 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4644 return converted != null;
4647 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4649 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4650 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4653 protected override void CloneTo (CloneContext clonectx, Statement target)
4655 var t = (SwitchLabel) target;
4657 t.label = label.Clone (clonectx);
4660 public override object Accept (StructuralVisitor visitor)
4662 return visitor.Visit (this);
4665 public string GetSignatureForError ()
4668 if (converted == null)
4671 label = converted.GetValueAsLiteral ();
4673 return string.Format ("case {0}:", label);
4677 public class Switch : LoopStatement
4679 // structure used to hold blocks of keys while calculating table switch
4680 sealed class LabelsRange : IComparable<LabelsRange>
4682 public readonly long min;
4684 public readonly List<long> label_values;
4686 public LabelsRange (long value)
4689 label_values = new List<long> ();
4690 label_values.Add (value);
4693 public LabelsRange (long min, long max, ICollection<long> values)
4697 this.label_values = new List<long> (values);
4702 return max - min + 1;
4706 public bool AddValue (long value)
4708 var gap = value - min + 1;
4709 // Ensure the range has > 50% occupancy
4710 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4714 label_values.Add (value);
4718 public int CompareTo (LabelsRange other)
4720 int nLength = label_values.Count;
4721 int nLengthOther = other.label_values.Count;
4722 if (nLengthOther == nLength)
4723 return (int) (other.min - min);
4725 return nLength - nLengthOther;
4729 sealed class DispatchStatement : Statement
4731 readonly Switch body;
4733 public DispatchStatement (Switch body)
4738 protected override void CloneTo (CloneContext clonectx, Statement target)
4740 throw new NotImplementedException ();
4743 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4748 protected override void DoEmit (EmitContext ec)
4750 body.EmitDispatch (ec);
4754 class MissingBreak : Statement
4756 readonly SwitchLabel label;
4758 public MissingBreak (SwitchLabel sl)
4764 public bool FallOut { get; set; }
4766 protected override void DoEmit (EmitContext ec)
4770 protected override void CloneTo (CloneContext clonectx, Statement target)
4774 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4777 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4778 label.GetSignatureForError ());
4780 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4781 label.GetSignatureForError ());
4787 public Expression Expr;
4790 // Mapping of all labels to their SwitchLabels
4792 Dictionary<long, SwitchLabel> labels;
4793 Dictionary<string, SwitchLabel> string_labels;
4794 List<SwitchLabel> case_labels;
4796 List<Tuple<GotoCase, Constant>> goto_cases;
4797 List<DefiniteAssignmentBitSet> end_reachable_das;
4800 /// The governing switch type
4802 public TypeSpec SwitchType;
4804 Expression new_expr;
4806 SwitchLabel case_null;
4807 SwitchLabel case_default;
4809 Label defaultLabel, nullLabel;
4810 VariableReference value;
4811 ExpressionStatement string_dictionary;
4812 FieldExpr switch_cache_field;
4813 ExplicitBlock block;
4817 // Nullable Types support
4819 Nullable.Unwrap unwrap;
4821 public Switch (Expression e, ExplicitBlock block, Location l)
4829 public SwitchLabel ActiveLabel { get; set; }
4831 public ExplicitBlock Block {
4837 public SwitchLabel DefaultLabel {
4839 return case_default;
4843 public bool IsNullable {
4845 return unwrap != null;
4849 public bool IsPatternMatching {
4851 return new_expr == null && SwitchType != null;
4855 public List<SwitchLabel> RegisteredLabels {
4861 public VariableReference ExpressionValue {
4868 // Determines the governing type for a switch. The returned
4869 // expression might be the expression from the switch, or an
4870 // expression that includes any potential conversions to
4872 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4874 switch (expr.Type.BuiltinType) {
4875 case BuiltinTypeSpec.Type.Byte:
4876 case BuiltinTypeSpec.Type.SByte:
4877 case BuiltinTypeSpec.Type.UShort:
4878 case BuiltinTypeSpec.Type.Short:
4879 case BuiltinTypeSpec.Type.UInt:
4880 case BuiltinTypeSpec.Type.Int:
4881 case BuiltinTypeSpec.Type.ULong:
4882 case BuiltinTypeSpec.Type.Long:
4883 case BuiltinTypeSpec.Type.Char:
4884 case BuiltinTypeSpec.Type.String:
4885 case BuiltinTypeSpec.Type.Bool:
4889 if (expr.Type.IsEnum)
4893 // Try to find a *user* defined implicit conversion.
4895 // If there is no implicit conversion, or if there are multiple
4896 // conversions, we have to report an error
4898 Expression converted = null;
4899 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4901 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4904 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4905 Convert.UserConversionRestriction.ProbingOnly;
4908 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4910 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4915 // Ignore over-worked ImplicitUserConversions that do
4916 // an implicit conversion in addition to the user conversion.
4918 var uc = e as UserCast;
4922 if (converted != null){
4923 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4932 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4934 var types = module.Compiler.BuiltinTypes;
4936 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4937 TypeSpec[] stypes = new[] {
4950 if (nullable != null) {
4952 Array.Resize (ref stypes, stypes.Length + 9);
4954 for (int i = 0; i < 9; ++i) {
4955 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4962 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4964 case_labels.Add (sl);
4967 if (case_default != null) {
4968 sl.Error_AlreadyOccurs (rc, case_default);
4976 if (sl.Converted == null)
4980 if (string_labels != null) {
4981 string string_value = sl.Converted.GetValue () as string;
4982 if (string_value == null)
4985 string_labels.Add (string_value, sl);
4987 if (sl.Converted.IsNull) {
4990 labels.Add (sl.Converted.GetValueAsLong (), sl);
4993 } catch (ArgumentException) {
4994 if (string_labels != null)
4995 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4997 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5002 // This method emits code for a lookup-based switch statement (non-string)
5003 // Basically it groups the cases into blocks that are at least half full,
5004 // and then spits out individual lookup opcodes for each block.
5005 // It emits the longest blocks first, and short blocks are just
5006 // handled with direct compares.
5008 void EmitTableSwitch (EmitContext ec, Expression val)
5010 if (labels != null && labels.Count > 0) {
5011 List<LabelsRange> ranges;
5012 if (string_labels != null) {
5013 // We have done all hard work for string already
5014 // setup single range only
5015 ranges = new List<LabelsRange> (1);
5016 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5018 var element_keys = new long[labels.Count];
5019 labels.Keys.CopyTo (element_keys, 0);
5020 Array.Sort (element_keys);
5023 // Build possible ranges of switch labes to reduce number
5026 ranges = new List<LabelsRange> (element_keys.Length);
5027 var range = new LabelsRange (element_keys[0]);
5029 for (int i = 1; i < element_keys.Length; ++i) {
5030 var l = element_keys[i];
5031 if (range.AddValue (l))
5034 range = new LabelsRange (l);
5038 // sort the blocks so we can tackle the largest ones first
5042 Label lbl_default = defaultLabel;
5043 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5045 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5046 LabelsRange kb = ranges[range_index];
5047 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5049 // Optimize small ranges using simple equality check
5050 if (kb.Range <= 2) {
5051 foreach (var key in kb.label_values) {
5052 SwitchLabel sl = labels[key];
5053 if (sl == case_default || sl == case_null)
5056 if (sl.Converted.IsZeroInteger) {
5057 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5060 sl.Converted.Emit (ec);
5061 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5065 // TODO: if all the keys in the block are the same and there are
5066 // no gaps/defaults then just use a range-check.
5067 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5068 // TODO: optimize constant/I4 cases
5070 // check block range (could be > 2^31)
5072 ec.EmitLong (kb.min);
5073 ec.Emit (OpCodes.Blt, lbl_default);
5076 ec.EmitLong (kb.max);
5077 ec.Emit (OpCodes.Bgt, lbl_default);
5082 ec.EmitLong (kb.min);
5083 ec.Emit (OpCodes.Sub);
5086 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5090 int first = (int) kb.min;
5093 ec.Emit (OpCodes.Sub);
5094 } else if (first < 0) {
5095 ec.EmitInt (-first);
5096 ec.Emit (OpCodes.Add);
5100 // first, build the list of labels for the switch
5102 long cJumps = kb.Range;
5103 Label[] switch_labels = new Label[cJumps];
5104 for (int iJump = 0; iJump < cJumps; iJump++) {
5105 var key = kb.label_values[iKey];
5106 if (key == kb.min + iJump) {
5107 switch_labels[iJump] = labels[key].GetILLabel (ec);
5110 switch_labels[iJump] = lbl_default;
5114 // emit the switch opcode
5115 ec.Emit (OpCodes.Switch, switch_labels);
5118 // mark the default for this block
5119 if (range_index != 0)
5120 ec.MarkLabel (lbl_default);
5123 // the last default just goes to the end
5124 if (ranges.Count > 0)
5125 ec.Emit (OpCodes.Br, lbl_default);
5129 public SwitchLabel FindLabel (Constant value)
5131 SwitchLabel sl = null;
5133 if (string_labels != null) {
5134 string s = value.GetValue () as string;
5136 if (case_null != null)
5138 else if (case_default != null)
5141 string_labels.TryGetValue (s, out sl);
5144 if (value is NullLiteral) {
5147 labels.TryGetValue (value.GetValueAsLong (), out sl);
5151 if (sl == null || sl.SectionStart)
5155 // Always return section start, it simplifies handling of switch labels
5157 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5158 var cs = case_labels [idx];
5159 if (cs.SectionStart)
5164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5166 Expr.FlowAnalysis (fc);
5168 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5169 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5170 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5172 block.FlowAnalysis (fc);
5174 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5176 if (end_reachable_das != null) {
5177 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5178 InitialDefinitiveAssignment |= sections_das;
5179 end_reachable_das = null;
5182 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5184 return case_default != null && !end_reachable;
5187 public override bool Resolve (BlockContext ec)
5189 Expr = Expr.Resolve (ec);
5194 // LAMESPEC: User conversion from non-nullable governing type has a priority
5196 new_expr = SwitchGoverningType (ec, Expr, false);
5198 if (new_expr == null) {
5199 if (Expr.Type.IsNullableType) {
5200 unwrap = Nullable.Unwrap.Create (Expr, false);
5205 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5206 // involving nullable Expr and nullable governing type is
5208 new_expr = SwitchGoverningType (ec, unwrap, true);
5212 Expression switch_expr;
5213 if (new_expr == null) {
5214 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5215 if (Expr.Type != InternalType.ErrorType) {
5216 ec.Report.Error (151, loc,
5217 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5218 Expr.Type.GetSignatureForError ());
5225 SwitchType = Expr.Type;
5227 switch_expr = new_expr;
5228 SwitchType = new_expr.Type;
5229 if (SwitchType.IsNullableType) {
5230 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5231 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5234 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5235 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5239 if (block.Statements.Count == 0)
5242 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5243 string_labels = new Dictionary<string, SwitchLabel> ();
5245 labels = new Dictionary<long, SwitchLabel> ();
5249 var constant = switch_expr as Constant;
5252 // Don't need extra variable for constant switch or switch with
5253 // only default case
5255 if (constant == null) {
5257 // Store switch expression for comparison purposes
5259 value = switch_expr as VariableReference;
5260 if (value == null && !HasOnlyDefaultSection ()) {
5261 var current_block = ec.CurrentBlock;
5262 ec.CurrentBlock = Block;
5263 // Create temporary variable inside switch scope
5264 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5266 ec.CurrentBlock = current_block;
5270 case_labels = new List<SwitchLabel> ();
5272 Switch old_switch = ec.Switch;
5274 var parent_los = ec.EnclosingLoopOrSwitch;
5275 ec.EnclosingLoopOrSwitch = this;
5277 var ok = Statement.Resolve (ec);
5279 ec.EnclosingLoopOrSwitch = parent_los;
5280 ec.Switch = old_switch;
5283 // Check if all goto cases are valid. Needs to be done after switch
5284 // is resolved because goto can jump forward in the scope.
5286 if (goto_cases != null) {
5287 foreach (var gc in goto_cases) {
5288 if (gc.Item1 == null) {
5289 if (DefaultLabel == null) {
5290 Goto.Error_UnknownLabel (ec, "default", loc);
5296 var sl = FindLabel (gc.Item2);
5298 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5300 gc.Item1.Label = sl;
5308 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5309 ResolveStringSwitchMap (ec);
5313 // Anonymous storey initialization has to happen before
5314 // any generated switch dispatch
5316 block.InsertStatement (0, new DispatchStatement (this));
5321 bool HasOnlyDefaultSection ()
5323 for (int i = 0; i < block.Statements.Count; ++i) {
5324 var s = block.Statements[i] as SwitchLabel;
5326 if (s == null || s.IsDefault)
5335 public override Reachability MarkReachable (Reachability rc)
5337 if (rc.IsUnreachable)
5340 base.MarkReachable (rc);
5342 block.MarkReachableScope (rc);
5344 if (block.Statements.Count == 0)
5347 SwitchLabel constant_label = null;
5348 var constant = new_expr as Constant;
5350 if (constant != null) {
5351 constant_label = FindLabel (constant) ?? case_default;
5352 if (constant_label == null) {
5353 block.Statements.RemoveAt (0);
5358 var section_rc = new Reachability ();
5359 SwitchLabel prev_label = null;
5361 for (int i = 0; i < block.Statements.Count; ++i) {
5362 var s = block.Statements[i];
5363 var sl = s as SwitchLabel;
5365 if (sl != null && sl.SectionStart) {
5367 // Section is marked already via goto case
5369 if (!sl.IsUnreachable) {
5370 section_rc = new Reachability ();
5374 if (constant_label != null && constant_label != sl)
5375 section_rc = Reachability.CreateUnreachable ();
5376 else if (section_rc.IsUnreachable) {
5377 section_rc = new Reachability ();
5379 if (prev_label != null) {
5380 sl.SectionStart = false;
5381 s = new MissingBreak (prev_label);
5382 s.MarkReachable (rc);
5383 block.Statements.Insert (i - 1, s);
5391 section_rc = s.MarkReachable (section_rc);
5394 if (!section_rc.IsUnreachable && prev_label != null) {
5395 prev_label.SectionStart = false;
5396 var s = new MissingBreak (prev_label) {
5400 s.MarkReachable (rc);
5401 block.Statements.Add (s);
5405 // Reachability can affect parent only when all possible paths are handled but
5406 // we still need to run reachability check on switch body to check for fall-through
5408 if (case_default == null && constant_label == null)
5412 // We have at least one local exit from the switch
5417 return Reachability.CreateUnreachable ();
5420 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5422 if (goto_cases == null)
5423 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5425 goto_cases.Add (Tuple.Create (gotoCase, value));
5429 // Converts string switch into string hashtable
5431 void ResolveStringSwitchMap (ResolveContext ec)
5433 FullNamedExpression string_dictionary_type;
5434 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5435 string_dictionary_type = new TypeExpression (
5436 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5437 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5439 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5440 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5442 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5446 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5447 Field field = new Field (ctype, string_dictionary_type,
5448 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5449 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5450 if (!field.Define ())
5452 ctype.AddField (field);
5454 var init = new List<Expression> ();
5456 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5457 string value = null;
5459 foreach (SwitchLabel sl in case_labels) {
5461 if (sl.SectionStart)
5462 labels.Add (++counter, sl);
5464 if (sl == case_default || sl == case_null)
5467 value = (string) sl.Converted.GetValue ();
5468 var init_args = new List<Expression> (2);
5469 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5471 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5472 init_args.Add (sl.Converted);
5474 init.Add (new CollectionElementInitializer (init_args, loc));
5477 Arguments args = new Arguments (1);
5478 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5479 Expression initializer = new NewInitialize (string_dictionary_type, args,
5480 new CollectionOrObjectInitializers (init, loc), loc);
5482 switch_cache_field = new FieldExpr (field, loc);
5483 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5486 void DoEmitStringSwitch (EmitContext ec)
5488 Label l_initialized = ec.DefineLabel ();
5491 // Skip initialization when value is null
5493 value.EmitBranchable (ec, nullLabel, false);
5496 // Check if string dictionary is initialized and initialize
5498 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5499 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5500 string_dictionary.EmitStatement (ec);
5502 ec.MarkLabel (l_initialized);
5504 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5506 ResolveContext rc = new ResolveContext (ec.MemberContext);
5508 if (switch_cache_field.Type.IsGeneric) {
5509 Arguments get_value_args = new Arguments (2);
5510 get_value_args.Add (new Argument (value));
5511 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5512 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5513 if (get_item == null)
5517 // A value was not found, go to default case
5519 get_item.EmitBranchable (ec, defaultLabel, false);
5521 Arguments get_value_args = new Arguments (1);
5522 get_value_args.Add (new Argument (value));
5524 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5525 if (get_item == null)
5528 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5529 get_item_object.EmitAssign (ec, get_item, true, false);
5530 ec.Emit (OpCodes.Brfalse, defaultLabel);
5532 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5533 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5535 get_item_int.EmitStatement (ec);
5536 get_item_object.Release (ec);
5539 EmitTableSwitch (ec, string_switch_variable);
5540 string_switch_variable.Release (ec);
5544 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5546 void EmitShortSwitch (EmitContext ec)
5548 MethodSpec equal_method = null;
5549 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5550 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5553 if (equal_method != null) {
5554 value.EmitBranchable (ec, nullLabel, false);
5557 for (int i = 0; i < case_labels.Count; ++i) {
5558 var label = case_labels [i];
5559 if (label == case_default || label == case_null)
5562 var constant = label.Converted;
5564 if (constant == null) {
5565 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5569 if (equal_method != null) {
5573 var call = new CallEmitter ();
5574 call.EmitPredefined (ec, equal_method, new Arguments (0));
5575 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5579 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5580 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5586 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5589 ec.Emit (OpCodes.Br, defaultLabel);
5592 void EmitDispatch (EmitContext ec)
5594 if (IsPatternMatching) {
5595 EmitShortSwitch (ec);
5599 if (value == null) {
5601 // Constant switch, we've already done the work if there is only 1 label
5605 foreach (var sl in case_labels) {
5606 if (sl.IsUnreachable)
5609 if (reachable++ > 0) {
5610 var constant = (Constant) new_expr;
5611 var constant_label = FindLabel (constant) ?? case_default;
5613 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5621 if (string_dictionary != null) {
5622 DoEmitStringSwitch (ec);
5623 } else if (case_labels.Count < 4 || string_labels != null) {
5624 EmitShortSwitch (ec);
5626 EmitTableSwitch (ec, value);
5630 protected override void DoEmit (EmitContext ec)
5633 // Setup the codegen context
5635 Label old_end = ec.LoopEnd;
5636 Switch old_switch = ec.Switch;
5638 ec.LoopEnd = ec.DefineLabel ();
5641 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5642 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5644 if (value != null) {
5647 var switch_expr = new_expr ?? Expr;
5649 unwrap.EmitCheck (ec);
5650 ec.Emit (OpCodes.Brfalse, nullLabel);
5651 value.EmitAssign (ec, switch_expr, false, false);
5652 } else if (switch_expr != value) {
5653 value.EmitAssign (ec, switch_expr, false, false);
5658 // Next statement is compiler generated we don't need extra
5659 // nop when we can use the statement for sequence point
5661 ec.Mark (block.StartLocation);
5662 block.IsCompilerGenerated = true;
5664 new_expr.EmitSideEffect (ec);
5669 // Restore context state.
5670 ec.MarkLabel (ec.LoopEnd);
5673 // Restore the previous context
5675 ec.LoopEnd = old_end;
5676 ec.Switch = old_switch;
5679 protected override void CloneTo (CloneContext clonectx, Statement t)
5681 Switch target = (Switch) t;
5683 target.Expr = Expr.Clone (clonectx);
5684 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5687 public override object Accept (StructuralVisitor visitor)
5689 return visitor.Visit (this);
5692 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5694 if (case_default == null && !(new_expr is Constant))
5697 if (end_reachable_das == null)
5698 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5700 end_reachable_das.Add (fc.DefiniteAssignment);
5703 public override void SetEndReachable ()
5705 end_reachable = true;
5709 // A place where execution can restart in a state machine
5710 public abstract class ResumableStatement : Statement
5713 protected Label resume_point;
5715 public Label PrepareForEmit (EmitContext ec)
5719 resume_point = ec.DefineLabel ();
5721 return resume_point;
5724 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5729 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5734 public abstract class TryFinallyBlock : ExceptionStatement
5736 protected Statement stmt;
5737 Label dispose_try_block;
5738 bool prepared_for_dispose, emitted_dispose;
5739 Method finally_host;
5741 protected TryFinallyBlock (Statement stmt, Location loc)
5749 public Statement Statement {
5757 protected abstract void EmitTryBody (EmitContext ec);
5758 public abstract void EmitFinallyBody (EmitContext ec);
5760 public override Label PrepareForDispose (EmitContext ec, Label end)
5762 if (!prepared_for_dispose) {
5763 prepared_for_dispose = true;
5764 dispose_try_block = ec.DefineLabel ();
5766 return dispose_try_block;
5769 protected sealed override void DoEmit (EmitContext ec)
5771 EmitTryBodyPrepare (ec);
5774 bool beginFinally = EmitBeginFinallyBlock (ec);
5776 Label start_finally = ec.DefineLabel ();
5777 if (resume_points != null && beginFinally) {
5778 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5780 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5781 ec.Emit (OpCodes.Brfalse_S, start_finally);
5782 ec.Emit (OpCodes.Endfinally);
5785 ec.MarkLabel (start_finally);
5787 if (finally_host != null) {
5788 finally_host.Define ();
5789 finally_host.PrepareEmit ();
5790 finally_host.Emit ();
5792 // Now it's safe to add, to close it properly and emit sequence points
5793 finally_host.Parent.AddMember (finally_host);
5795 var ce = new CallEmitter ();
5796 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5797 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5799 EmitFinallyBody (ec);
5803 ec.EndExceptionBlock ();
5806 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5808 if (emitted_dispose)
5811 emitted_dispose = true;
5813 Label end_of_try = ec.DefineLabel ();
5815 // Ensure that the only way we can get into this code is through a dispatcher
5816 if (have_dispatcher)
5817 ec.Emit (OpCodes.Br, end);
5819 ec.BeginExceptionBlock ();
5821 ec.MarkLabel (dispose_try_block);
5823 Label[] labels = null;
5824 for (int i = 0; i < resume_points.Count; ++i) {
5825 ResumableStatement s = resume_points[i];
5826 Label ret = s.PrepareForDispose (ec, end_of_try);
5827 if (ret.Equals (end_of_try) && labels == null)
5829 if (labels == null) {
5830 labels = new Label[resume_points.Count];
5831 for (int j = 0; j < i; ++j)
5832 labels[j] = end_of_try;
5837 if (labels != null) {
5839 for (j = 1; j < labels.Length; ++j)
5840 if (!labels[0].Equals (labels[j]))
5842 bool emit_dispatcher = j < labels.Length;
5844 if (emit_dispatcher) {
5845 ec.Emit (OpCodes.Ldloc, pc);
5846 ec.EmitInt (first_resume_pc);
5847 ec.Emit (OpCodes.Sub);
5848 ec.Emit (OpCodes.Switch, labels);
5851 foreach (ResumableStatement s in resume_points)
5852 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5855 ec.MarkLabel (end_of_try);
5857 ec.BeginFinallyBlock ();
5859 if (finally_host != null) {
5860 var ce = new CallEmitter ();
5861 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5862 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5864 EmitFinallyBody (ec);
5867 ec.EndExceptionBlock ();
5870 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5872 var res = stmt.FlowAnalysis (fc);
5877 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5879 ec.BeginFinallyBlock ();
5883 public override Reachability MarkReachable (Reachability rc)
5885 base.MarkReachable (rc);
5886 return Statement.MarkReachable (rc);
5889 public override bool Resolve (BlockContext bc)
5893 parent = bc.CurrentTryBlock;
5894 bc.CurrentTryBlock = this;
5896 using (bc.Set (ResolveContext.Options.TryScope)) {
5897 ok = stmt.Resolve (bc);
5900 bc.CurrentTryBlock = parent;
5903 // Finally block inside iterator is called from MoveNext and
5904 // Dispose methods that means we need to lift the block into
5905 // newly created host method to emit the body only once. The
5906 // original block then simply calls the newly generated method.
5908 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5909 var b = stmt as Block;
5910 if (b != null && b.Explicit.HasYield) {
5911 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5915 return base.Resolve (bc) && ok;
5920 // Base class for blocks using exception handling
5922 public abstract class ExceptionStatement : ResumableStatement
5924 protected List<ResumableStatement> resume_points;
5925 protected int first_resume_pc;
5926 protected ExceptionStatement parent;
5928 protected ExceptionStatement (Location loc)
5933 protected virtual void EmitBeginException (EmitContext ec)
5935 ec.BeginExceptionBlock ();
5938 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5940 StateMachineInitializer state_machine = null;
5941 if (resume_points != null) {
5942 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5944 ec.EmitInt ((int) IteratorStorey.State.Running);
5945 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5948 EmitBeginException (ec);
5950 if (resume_points != null) {
5951 ec.MarkLabel (resume_point);
5953 // For normal control flow, we want to fall-through the Switch
5954 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5955 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5956 ec.EmitInt (first_resume_pc);
5957 ec.Emit (OpCodes.Sub);
5959 Label[] labels = new Label[resume_points.Count];
5960 for (int i = 0; i < resume_points.Count; ++i)
5961 labels[i] = resume_points[i].PrepareForEmit (ec);
5962 ec.Emit (OpCodes.Switch, labels);
5966 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5968 if (parent != null) {
5969 // TODO: MOVE to virtual TryCatch
5970 var tc = this as TryCatch;
5971 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5973 pc = parent.AddResumePoint (s, pc, stateMachine);
5975 pc = stateMachine.AddResumePoint (this);
5978 if (resume_points == null) {
5979 resume_points = new List<ResumableStatement> ();
5980 first_resume_pc = pc;
5983 if (pc != first_resume_pc + resume_points.Count)
5984 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5986 resume_points.Add (stmt);
5991 public class Lock : TryFinallyBlock
5994 TemporaryVariableReference expr_copy;
5995 TemporaryVariableReference lock_taken;
5997 public Lock (Expression expr, Statement stmt, Location loc)
6003 public Expression Expr {
6009 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6011 expr.FlowAnalysis (fc);
6012 return base.DoFlowAnalysis (fc);
6015 public override bool Resolve (BlockContext ec)
6017 expr = expr.Resolve (ec);
6021 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6022 ec.Report.Error (185, loc,
6023 "`{0}' is not a reference type as required by the lock statement",
6024 expr.Type.GetSignatureForError ());
6027 if (expr.Type.IsGenericParameter) {
6028 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6031 VariableReference lv = expr as VariableReference;
6034 locked = lv.IsLockedByStatement;
6035 lv.IsLockedByStatement = true;
6042 // Have to keep original lock value around to unlock same location
6043 // in the case of original value has changed or is null
6045 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6046 expr_copy.Resolve (ec);
6049 // Ensure Monitor methods are available
6051 if (ResolvePredefinedMethods (ec) > 1) {
6052 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6053 lock_taken.Resolve (ec);
6056 using (ec.Set (ResolveContext.Options.LockScope)) {
6061 lv.IsLockedByStatement = locked;
6067 protected override void EmitTryBodyPrepare (EmitContext ec)
6069 expr_copy.EmitAssign (ec, expr);
6071 if (lock_taken != null) {
6073 // Initialize ref variable
6075 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6078 // Monitor.Enter (expr_copy)
6080 expr_copy.Emit (ec);
6081 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6084 base.EmitTryBodyPrepare (ec);
6087 protected override void EmitTryBody (EmitContext ec)
6090 // Monitor.Enter (expr_copy, ref lock_taken)
6092 if (lock_taken != null) {
6093 expr_copy.Emit (ec);
6094 lock_taken.LocalInfo.CreateBuilder (ec);
6095 lock_taken.AddressOf (ec, AddressOp.Load);
6096 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6099 Statement.Emit (ec);
6102 public override void EmitFinallyBody (EmitContext ec)
6105 // if (lock_taken) Monitor.Exit (expr_copy)
6107 Label skip = ec.DefineLabel ();
6109 if (lock_taken != null) {
6110 lock_taken.Emit (ec);
6111 ec.Emit (OpCodes.Brfalse_S, skip);
6114 expr_copy.Emit (ec);
6115 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6117 ec.Emit (OpCodes.Call, m);
6119 ec.MarkLabel (skip);
6122 int ResolvePredefinedMethods (ResolveContext rc)
6124 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6125 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6129 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6133 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6137 protected override void CloneTo (CloneContext clonectx, Statement t)
6139 Lock target = (Lock) t;
6141 target.expr = expr.Clone (clonectx);
6142 target.stmt = Statement.Clone (clonectx);
6145 public override object Accept (StructuralVisitor visitor)
6147 return visitor.Visit (this);
6152 public class Unchecked : Statement {
6155 public Unchecked (Block b, Location loc)
6162 public override bool Resolve (BlockContext ec)
6164 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6165 return Block.Resolve (ec);
6168 protected override void DoEmit (EmitContext ec)
6170 using (ec.With (EmitContext.Options.CheckedScope, false))
6174 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6176 return Block.FlowAnalysis (fc);
6179 public override Reachability MarkReachable (Reachability rc)
6181 base.MarkReachable (rc);
6182 return Block.MarkReachable (rc);
6185 protected override void CloneTo (CloneContext clonectx, Statement t)
6187 Unchecked target = (Unchecked) t;
6189 target.Block = clonectx.LookupBlock (Block);
6192 public override object Accept (StructuralVisitor visitor)
6194 return visitor.Visit (this);
6198 public class Checked : Statement {
6201 public Checked (Block b, Location loc)
6204 b.Unchecked = false;
6208 public override bool Resolve (BlockContext ec)
6210 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6211 return Block.Resolve (ec);
6214 protected override void DoEmit (EmitContext ec)
6216 using (ec.With (EmitContext.Options.CheckedScope, true))
6220 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6222 return Block.FlowAnalysis (fc);
6225 public override Reachability MarkReachable (Reachability rc)
6227 base.MarkReachable (rc);
6228 return Block.MarkReachable (rc);
6231 protected override void CloneTo (CloneContext clonectx, Statement t)
6233 Checked target = (Checked) t;
6235 target.Block = clonectx.LookupBlock (Block);
6238 public override object Accept (StructuralVisitor visitor)
6240 return visitor.Visit (this);
6244 public class Unsafe : Statement {
6247 public Unsafe (Block b, Location loc)
6250 Block.Unsafe = true;
6254 public override bool Resolve (BlockContext ec)
6256 if (ec.CurrentIterator != null)
6257 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6259 using (ec.Set (ResolveContext.Options.UnsafeScope))
6260 return Block.Resolve (ec);
6263 protected override void DoEmit (EmitContext ec)
6268 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6270 return Block.FlowAnalysis (fc);
6273 public override Reachability MarkReachable (Reachability rc)
6275 base.MarkReachable (rc);
6276 return Block.MarkReachable (rc);
6279 protected override void CloneTo (CloneContext clonectx, Statement t)
6281 Unsafe target = (Unsafe) t;
6283 target.Block = clonectx.LookupBlock (Block);
6286 public override object Accept (StructuralVisitor visitor)
6288 return visitor.Visit (this);
6295 public class Fixed : Statement
6297 abstract class Emitter : ShimExpression
6299 protected LocalVariable vi;
6301 protected Emitter (Expression expr, LocalVariable li)
6307 public abstract void EmitExit (EmitContext ec);
6309 public override void FlowAnalysis (FlowAnalysisContext fc)
6311 expr.FlowAnalysis (fc);
6315 sealed class ExpressionEmitter : Emitter {
6316 public ExpressionEmitter (Expression converted, LocalVariable li)
6317 : base (converted, li)
6321 protected override Expression DoResolve (ResolveContext rc)
6323 throw new NotImplementedException ();
6326 public override void Emit (EmitContext ec) {
6328 // Store pointer in pinned location
6334 public override void EmitExit (EmitContext ec)
6337 ec.Emit (OpCodes.Conv_U);
6342 class StringEmitter : Emitter
6344 LocalVariable pinned_string;
6346 public StringEmitter (Expression expr, LocalVariable li)
6351 protected override Expression DoResolve (ResolveContext rc)
6353 pinned_string = new LocalVariable (vi.Block, "$pinned",
6354 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6356 pinned_string.Type = rc.BuiltinTypes.String;
6359 eclass = ExprClass.Variable;
6360 type = rc.BuiltinTypes.Int;
6364 public override void Emit (EmitContext ec)
6366 pinned_string.CreateBuilder (ec);
6369 pinned_string.EmitAssign (ec);
6371 // TODO: Should use Binary::Add
6372 pinned_string.Emit (ec);
6373 ec.Emit (OpCodes.Conv_I);
6375 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6379 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6380 //pe.InstanceExpression = pinned_string;
6381 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6383 ec.Emit (OpCodes.Add);
6387 public override void EmitExit (EmitContext ec)
6390 pinned_string.EmitAssign (ec);
6394 public class VariableDeclaration : BlockVariable
6396 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6401 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6403 if (!Variable.Type.IsPointer && li == Variable) {
6404 bc.Report.Error (209, TypeExpression.Location,
6405 "The type of locals declared in a fixed statement must be a pointer type");
6409 var res = initializer.Resolve (bc);
6416 var ac = res.Type as ArrayContainer;
6418 TypeSpec array_type = ac.Element;
6421 // Provided that array_type is unmanaged,
6423 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6426 Expression res_init;
6427 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6430 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6431 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6432 res = expr_variable.CreateReferenceExpression (bc, loc);
6436 // and T* is implicitly convertible to the
6437 // pointer type given in the fixed statement.
6439 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6441 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6442 if (converted == null)
6446 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6448 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6449 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6450 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6451 new NullLiteral (loc),
6454 converted = converted.Resolve (bc);
6456 return new ExpressionEmitter (converted, li);
6462 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6463 return new StringEmitter (res, li).Resolve (bc);
6466 // Case 3: fixed buffer
6467 if (res is FixedBufferPtr) {
6468 return new ExpressionEmitter (res, li);
6471 bool already_fixed = true;
6474 // Case 4: & object.
6476 Unary u = res as Unary;
6478 if (u.Oper == Unary.Operator.AddressOf) {
6479 IVariableReference vr = u.Expr as IVariableReference;
6480 if (vr == null || !vr.IsFixed) {
6481 already_fixed = false;
6484 } else if (initializer is Cast) {
6485 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6489 if (already_fixed) {
6490 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6493 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6494 return new ExpressionEmitter (res, li);
6499 VariableDeclaration decl;
6500 Statement statement;
6503 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6512 public Statement Statement {
6518 public BlockVariable Variables {
6526 public override bool Resolve (BlockContext bc)
6528 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6529 if (!decl.Resolve (bc))
6533 return statement.Resolve (bc);
6536 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6538 decl.FlowAnalysis (fc);
6539 return statement.FlowAnalysis (fc);
6542 protected override void DoEmit (EmitContext ec)
6544 decl.Variable.CreateBuilder (ec);
6545 decl.Initializer.Emit (ec);
6546 if (decl.Declarators != null) {
6547 foreach (var d in decl.Declarators) {
6548 d.Variable.CreateBuilder (ec);
6549 d.Initializer.Emit (ec);
6553 statement.Emit (ec);
6559 // Clear the pinned variable
6561 ((Emitter) decl.Initializer).EmitExit (ec);
6562 if (decl.Declarators != null) {
6563 foreach (var d in decl.Declarators) {
6564 ((Emitter)d.Initializer).EmitExit (ec);
6569 public override Reachability MarkReachable (Reachability rc)
6571 base.MarkReachable (rc);
6573 decl.MarkReachable (rc);
6575 rc = statement.MarkReachable (rc);
6577 // TODO: What if there is local exit?
6578 has_ret = rc.IsUnreachable;
6582 protected override void CloneTo (CloneContext clonectx, Statement t)
6584 Fixed target = (Fixed) t;
6586 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6587 target.statement = statement.Clone (clonectx);
6590 public override object Accept (StructuralVisitor visitor)
6592 return visitor.Visit (this);
6596 public class Catch : Statement
6598 class CatchVariableStore : Statement
6600 readonly Catch ctch;
6602 public CatchVariableStore (Catch ctch)
6607 protected override void CloneTo (CloneContext clonectx, Statement target)
6611 protected override void DoEmit (EmitContext ec)
6613 // Emits catch variable debug information inside correct block
6614 ctch.EmitCatchVariableStore (ec);
6617 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6623 class FilterStatement : Statement
6625 readonly Catch ctch;
6627 public FilterStatement (Catch ctch)
6632 protected override void CloneTo (CloneContext clonectx, Statement target)
6636 protected override void DoEmit (EmitContext ec)
6638 if (ctch.li != null) {
6639 if (ctch.hoisted_temp != null)
6640 ctch.hoisted_temp.Emit (ec);
6644 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6645 ec.Emit (OpCodes.Box, ctch.type);
6648 var expr_start = ec.DefineLabel ();
6649 var end = ec.DefineLabel ();
6651 ec.Emit (OpCodes.Brtrue_S, expr_start);
6653 ec.Emit (OpCodes.Br, end);
6654 ec.MarkLabel (expr_start);
6656 ctch.Filter.Emit (ec);
6659 ec.Emit (OpCodes.Endfilter);
6660 ec.BeginFilterHandler ();
6661 ec.Emit (OpCodes.Pop);
6664 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6666 ctch.Filter.FlowAnalysis (fc);
6670 public override bool Resolve (BlockContext bc)
6672 ctch.Filter = ctch.Filter.Resolve (bc);
6674 if (ctch.Filter != null) {
6675 if (ctch.Filter.ContainsEmitWithAwait ()) {
6676 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6679 var c = ctch.Filter as Constant;
6680 if (c != null && !c.IsDefaultValue) {
6681 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6689 ExplicitBlock block;
6691 FullNamedExpression type_expr;
6692 CompilerAssign assign;
6694 LocalTemporary hoisted_temp;
6696 public Catch (ExplicitBlock block, Location loc)
6704 public ExplicitBlock Block {
6710 public TypeSpec CatchType {
6716 public Expression Filter {
6720 public bool IsGeneral {
6722 return type_expr == null;
6726 public FullNamedExpression TypeExpression {
6735 public LocalVariable Variable {
6746 protected override void DoEmit (EmitContext ec)
6748 if (Filter != null) {
6749 ec.BeginExceptionFilterBlock ();
6750 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6752 if (Block.HasAwait) {
6753 Block.EmitScopeInitialization (ec);
6762 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6764 ec.BeginCatchBlock (CatchType);
6767 ec.Emit (OpCodes.Pop);
6769 if (Block.HasAwait) {
6771 EmitCatchVariableStore (ec);
6777 void EmitCatchVariableStore (EmitContext ec)
6779 li.CreateBuilder (ec);
6782 // For hoisted catch variable we have to use a temporary local variable
6783 // for captured variable initialization during storey setup because variable
6784 // needs to be on the stack after storey instance for stfld operation
6786 if (li.HoistedVariant != null) {
6787 hoisted_temp = new LocalTemporary (li.Type);
6788 hoisted_temp.Store (ec);
6790 // switch to assignment from temporary variable and not from top of the stack
6791 assign.UpdateSource (hoisted_temp);
6795 public override bool Resolve (BlockContext bc)
6797 using (bc.Set (ResolveContext.Options.CatchScope)) {
6798 if (type_expr == null) {
6799 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6800 if (!block.HasAwait || Filter != null)
6801 block.AddScopeStatement (new CatchVariableStore (this));
6803 Expression source = new EmptyExpression (li.Type);
6804 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6805 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6808 type = type_expr.ResolveAsType (bc);
6813 CreateExceptionVariable (type);
6815 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6816 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6817 } else if (li != null) {
6819 li.PrepareAssignmentAnalysis (bc);
6821 // source variable is at the top of the stack
6822 Expression source = new EmptyExpression (li.Type);
6823 if (li.Type.IsGenericParameter)
6824 source = new UnboxCast (source, li.Type);
6826 if (!block.HasAwait || Filter != null)
6827 block.AddScopeStatement (new CatchVariableStore (this));
6830 // Uses Location.Null to hide from symbol file
6832 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6833 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6837 if (Filter != null) {
6838 Block.AddScopeStatement (new FilterStatement (this));
6841 Block.SetCatchBlock ();
6842 return Block.Resolve (bc);
6846 bool CreateExceptionVariable (TypeSpec type)
6848 if (!Block.HasAwait)
6851 // TODO: Scan the block for rethrow expression
6852 //if (!Block.HasRethrow)
6855 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6859 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6861 if (li != null && !li.IsCompilerGenerated) {
6862 fc.SetVariableAssigned (li.VariableInfo, true);
6865 return block.FlowAnalysis (fc);
6868 public override Reachability MarkReachable (Reachability rc)
6870 base.MarkReachable (rc);
6872 var c = Filter as Constant;
6873 if (c != null && c.IsDefaultValue)
6874 return Reachability.CreateUnreachable ();
6876 return block.MarkReachable (rc);
6879 protected override void CloneTo (CloneContext clonectx, Statement t)
6881 Catch target = (Catch) t;
6883 if (type_expr != null)
6884 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6887 target.Filter = Filter.Clone (clonectx);
6889 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6893 public class TryFinally : TryFinallyBlock
6896 List<DefiniteAssignmentBitSet> try_exit_dat;
6897 List<Label> redirected_jumps;
6898 Label? start_fin_label;
6900 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6906 public ExplicitBlock FinallyBlock {
6912 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6914 if (try_exit_dat == null)
6915 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6917 try_exit_dat.Add (vector);
6920 public override bool Resolve (BlockContext bc)
6922 bool ok = base.Resolve (bc);
6924 fini.SetFinallyBlock ();
6925 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6926 ok &= fini.Resolve (bc);
6932 protected override void EmitBeginException (EmitContext ec)
6934 if (fini.HasAwait && stmt is TryCatch)
6935 ec.BeginExceptionBlock ();
6937 base.EmitBeginException (ec);
6940 protected override void EmitTryBody (EmitContext ec)
6942 if (fini.HasAwait) {
6943 if (ec.TryFinallyUnwind == null)
6944 ec.TryFinallyUnwind = new List<TryFinally> ();
6946 ec.TryFinallyUnwind.Add (this);
6949 if (stmt is TryCatch)
6950 ec.EndExceptionBlock ();
6952 ec.TryFinallyUnwind.Remove (this);
6954 if (start_fin_label != null)
6955 ec.MarkLabel (start_fin_label.Value);
6963 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6968 return base.EmitBeginFinallyBlock (ec);
6971 public override void EmitFinallyBody (EmitContext ec)
6973 if (!fini.HasAwait) {
6979 // Emits catch block like
6981 // catch (object temp) {
6982 // this.exception_field = temp;
6985 var type = ec.BuiltinTypes.Object;
6986 ec.BeginCatchBlock (type);
6988 var temp = ec.GetTemporaryLocal (type);
6989 ec.Emit (OpCodes.Stloc, temp);
6991 var exception_field = ec.GetTemporaryField (type);
6993 ec.Emit (OpCodes.Ldloc, temp);
6994 exception_field.EmitAssignFromStack (ec);
6996 ec.EndExceptionBlock ();
6998 ec.FreeTemporaryLocal (temp, type);
7003 // Emits exception rethrow
7005 // if (this.exception_field != null)
7006 // throw this.exception_field;
7008 exception_field.Emit (ec);
7009 var skip_throw = ec.DefineLabel ();
7010 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7011 exception_field.Emit (ec);
7012 ec.Emit (OpCodes.Throw);
7013 ec.MarkLabel (skip_throw);
7015 exception_field.IsAvailableForReuse = true;
7017 EmitUnwindFinallyTable (ec);
7020 bool IsParentBlock (Block block)
7022 for (Block b = fini; b != null; b = b.Parent) {
7030 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7033 if (labelBlock != null) {
7034 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7035 var fin = ec.TryFinallyUnwind [idx - 1];
7036 if (!fin.IsParentBlock (labelBlock))
7043 bool set_return_state = true;
7045 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7046 var fin = ec.TryFinallyUnwind [idx];
7047 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7050 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7051 set_return_state = false;
7053 if (fin.start_fin_label == null) {
7054 fin.start_fin_label = ec.DefineLabel ();
7057 label = fin.start_fin_label.Value;
7063 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7065 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7068 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7070 if (redirected_jumps == null) {
7071 redirected_jumps = new List<Label> ();
7073 // Add fallthrough label
7074 redirected_jumps.Add (ec.DefineLabel ());
7077 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7080 int index = redirected_jumps.IndexOf (label);
7082 redirected_jumps.Add (label);
7083 index = redirected_jumps.Count - 1;
7087 // Indicates we have captured exit jump
7089 if (setReturnState) {
7090 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7091 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7096 // Emits state table of jumps outside of try block and reload of return
7097 // value when try block returns value
7099 void EmitUnwindFinallyTable (EmitContext ec)
7101 if (redirected_jumps == null)
7104 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7105 initializer.HoistedReturnState.EmitLoad (ec);
7106 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7108 // Mark fallthrough label
7109 ec.MarkLabel (redirected_jumps [0]);
7112 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7114 var da = fc.BranchDefiniteAssignment ();
7116 var tf = fc.TryFinally;
7117 fc.TryFinally = this;
7119 var res_stmt = Statement.FlowAnalysis (fc);
7123 var try_da = fc.DefiniteAssignment;
7124 fc.DefiniteAssignment = da;
7126 var res_fin = fini.FlowAnalysis (fc);
7128 if (try_exit_dat != null) {
7130 // try block has global exit but we need to run definite assignment check
7131 // for parameter block out parameter after finally block because it's always
7132 // executed before exit
7134 foreach (var try_da_part in try_exit_dat)
7135 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7137 try_exit_dat = null;
7140 fc.DefiniteAssignment |= try_da;
7141 return res_stmt | res_fin;
7144 public override Reachability MarkReachable (Reachability rc)
7147 // Mark finally block first for any exit statement in try block
7148 // to know whether the code which follows finally is reachable
7150 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7153 protected override void CloneTo (CloneContext clonectx, Statement t)
7155 TryFinally target = (TryFinally) t;
7157 target.stmt = stmt.Clone (clonectx);
7159 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7162 public override object Accept (StructuralVisitor visitor)
7164 return visitor.Visit (this);
7168 public class TryCatch : ExceptionStatement
7171 List<Catch> clauses;
7172 readonly bool inside_try_finally;
7173 List<Catch> catch_sm;
7175 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7179 this.clauses = catch_clauses;
7180 this.inside_try_finally = inside_try_finally;
7183 public List<Catch> Clauses {
7189 public bool IsTryCatchFinally {
7191 return inside_try_finally;
7195 public override bool Resolve (BlockContext bc)
7199 using (bc.Set (ResolveContext.Options.TryScope)) {
7200 parent = bc.CurrentTryBlock;
7202 if (IsTryCatchFinally) {
7203 ok = Block.Resolve (bc);
7205 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7206 bc.CurrentTryBlock = this;
7207 ok = Block.Resolve (bc);
7208 bc.CurrentTryBlock = parent;
7213 for (int i = 0; i < clauses.Count; ++i) {
7216 ok &= c.Resolve (bc);
7218 if (c.Block.HasAwait) {
7219 if (catch_sm == null)
7220 catch_sm = new List<Catch> ();
7225 if (c.Filter != null)
7228 TypeSpec resolved_type = c.CatchType;
7229 if (resolved_type == null)
7232 for (int ii = 0; ii < clauses.Count; ++ii) {
7236 if (clauses[ii].Filter != null)
7239 if (clauses[ii].IsGeneral) {
7240 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7243 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7246 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7249 bc.Report.Warning (1058, 1, c.loc,
7250 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7258 var ct = clauses[ii].CatchType;
7262 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7263 bc.Report.Error (160, c.loc,
7264 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7265 ct.GetSignatureForError ());
7271 return base.Resolve (bc) && ok;
7274 protected sealed override void DoEmit (EmitContext ec)
7276 if (!inside_try_finally)
7277 EmitTryBodyPrepare (ec);
7281 LocalBuilder state_variable = null;
7282 foreach (Catch c in clauses) {
7285 if (catch_sm != null) {
7286 if (state_variable == null) {
7288 // Cannot reuse temp variable because non-catch path assumes the value is 0
7289 // which may not be true for reused local variable
7291 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7294 var index = catch_sm.IndexOf (c);
7298 ec.EmitInt (index + 1);
7299 ec.Emit (OpCodes.Stloc, state_variable);
7303 if (!inside_try_finally)
7304 ec.EndExceptionBlock ();
7306 if (state_variable != null) {
7307 ec.Emit (OpCodes.Ldloc, state_variable);
7309 var labels = new Label [catch_sm.Count + 1];
7310 for (int i = 0; i < labels.Length; ++i) {
7311 labels [i] = ec.DefineLabel ();
7314 var end = ec.DefineLabel ();
7315 ec.Emit (OpCodes.Switch, labels);
7317 // 0 value is default label
7318 ec.MarkLabel (labels [0]);
7319 ec.Emit (OpCodes.Br, end);
7321 var atv = ec.AsyncThrowVariable;
7323 for (int i = 0; i < catch_sm.Count; ++i) {
7324 if (c != null && c.Block.HasReachableClosingBrace)
7325 ec.Emit (OpCodes.Br, end);
7327 ec.MarkLabel (labels [i + 1]);
7329 ec.AsyncThrowVariable = c.Variable;
7332 ec.AsyncThrowVariable = atv;
7338 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7340 var start_fc = fc.BranchDefiniteAssignment ();
7341 var res = Block.FlowAnalysis (fc);
7343 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7345 foreach (var c in clauses) {
7346 fc.BranchDefiniteAssignment (start_fc);
7347 if (!c.FlowAnalysis (fc)) {
7349 try_fc = fc.DefiniteAssignment;
7351 try_fc &= fc.DefiniteAssignment;
7357 fc.DefiniteAssignment = try_fc ?? start_fc;
7362 public override Reachability MarkReachable (Reachability rc)
7364 if (rc.IsUnreachable)
7367 base.MarkReachable (rc);
7369 var tc_rc = Block.MarkReachable (rc);
7371 foreach (var c in clauses)
7372 tc_rc &= c.MarkReachable (rc);
7377 protected override void CloneTo (CloneContext clonectx, Statement t)
7379 TryCatch target = (TryCatch) t;
7381 target.Block = clonectx.LookupBlock (Block);
7382 if (clauses != null){
7383 target.clauses = new List<Catch> ();
7384 foreach (Catch c in clauses)
7385 target.clauses.Add ((Catch) c.Clone (clonectx));
7389 public override object Accept (StructuralVisitor visitor)
7391 return visitor.Visit (this);
7395 public class Using : TryFinallyBlock
7397 public class VariableDeclaration : BlockVariable
7399 Statement dispose_call;
7401 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7406 public VariableDeclaration (LocalVariable li, Location loc)
7413 public VariableDeclaration (Expression expr)
7416 loc = expr.Location;
7422 public bool IsNested { get; private set; }
7426 public void EmitDispose (EmitContext ec)
7428 dispose_call.Emit (ec);
7431 public override bool Resolve (BlockContext bc)
7436 return base.Resolve (bc, false);
7439 public Expression ResolveExpression (BlockContext bc)
7441 var e = Initializer.Resolve (bc);
7445 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7446 Initializer = ResolveInitializer (bc, Variable, e);
7450 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7452 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7453 initializer = initializer.Resolve (bc);
7454 if (initializer == null)
7457 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7458 Arguments args = new Arguments (1);
7459 args.Add (new Argument (initializer));
7460 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7461 if (initializer == null)
7464 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7465 dispose_call = CreateDisposeCall (bc, var);
7466 dispose_call.Resolve (bc);
7468 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7471 if (li == Variable) {
7472 CheckIDiposableConversion (bc, li, initializer);
7473 dispose_call = CreateDisposeCall (bc, li);
7474 dispose_call.Resolve (bc);
7477 return base.ResolveInitializer (bc, li, initializer);
7480 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7484 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7485 if (type.IsNullableType) {
7486 // it's handled in CreateDisposeCall
7490 if (type != InternalType.ErrorType) {
7491 bc.Report.SymbolRelatedToPreviousError (type);
7492 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7493 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7494 type.GetSignatureForError ());
7501 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7503 var target = bc.BuiltinTypes.IDisposable;
7504 var tp = type as TypeParameterSpec;
7506 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7508 return type.ImplementsInterface (target, false);
7511 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7513 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7515 var loc = lv.Location;
7517 var idt = bc.BuiltinTypes.IDisposable;
7518 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7520 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7521 dispose_mg.InstanceExpression = type.IsNullableType ?
7522 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7526 // Hide it from symbol file via null location
7528 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7530 // Add conditional call when disposing possible null variable
7531 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7532 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7537 public void ResolveDeclaratorInitializer (BlockContext bc)
7539 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7542 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7544 for (int i = declarators.Count - 1; i >= 0; --i) {
7545 var d = declarators [i];
7546 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7547 vd.Initializer = d.Initializer;
7549 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7550 vd.dispose_call.Resolve (bc);
7552 stmt = new Using (vd, stmt, d.Variable.Location);
7559 public override object Accept (StructuralVisitor visitor)
7561 return visitor.Visit (this);
7565 VariableDeclaration decl;
7567 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7573 public Using (Expression expr, Statement stmt, Location loc)
7576 this.decl = new VariableDeclaration (expr);
7581 public Expression Expr {
7583 return decl.Variable == null ? decl.Initializer : null;
7587 public BlockVariable Variables {
7595 public override void Emit (EmitContext ec)
7598 // Don't emit sequence point it will be set on variable declaration
7603 protected override void EmitTryBodyPrepare (EmitContext ec)
7606 base.EmitTryBodyPrepare (ec);
7609 protected override void EmitTryBody (EmitContext ec)
7614 public override void EmitFinallyBody (EmitContext ec)
7616 decl.EmitDispose (ec);
7619 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7621 decl.FlowAnalysis (fc);
7622 return stmt.FlowAnalysis (fc);
7625 public override Reachability MarkReachable (Reachability rc)
7627 decl.MarkReachable (rc);
7628 return base.MarkReachable (rc);
7631 public override bool Resolve (BlockContext ec)
7633 VariableReference vr;
7634 bool vr_locked = false;
7636 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7637 if (decl.Variable == null) {
7638 vr = decl.ResolveExpression (ec) as VariableReference;
7640 vr_locked = vr.IsLockedByStatement;
7641 vr.IsLockedByStatement = true;
7644 if (decl.IsNested) {
7645 decl.ResolveDeclaratorInitializer (ec);
7647 if (!decl.Resolve (ec))
7650 if (decl.Declarators != null) {
7651 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7659 var ok = base.Resolve (ec);
7662 vr.IsLockedByStatement = vr_locked;
7667 protected override void CloneTo (CloneContext clonectx, Statement t)
7669 Using target = (Using) t;
7671 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7672 target.stmt = stmt.Clone (clonectx);
7675 public override object Accept (StructuralVisitor visitor)
7677 return visitor.Visit (this);
7682 /// Implementation of the foreach C# statement
7684 public class Foreach : LoopStatement
7686 abstract class IteratorStatement : Statement
7688 protected readonly Foreach for_each;
7690 protected IteratorStatement (Foreach @foreach)
7692 this.for_each = @foreach;
7693 this.loc = @foreach.expr.Location;
7696 protected override void CloneTo (CloneContext clonectx, Statement target)
7698 throw new NotImplementedException ();
7701 public override void Emit (EmitContext ec)
7703 if (ec.EmitAccurateDebugInfo) {
7704 ec.Emit (OpCodes.Nop);
7710 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7712 throw new NotImplementedException ();
7716 sealed class ArrayForeach : IteratorStatement
7718 TemporaryVariableReference[] lengths;
7719 Expression [] length_exprs;
7720 StatementExpression[] counter;
7721 TemporaryVariableReference[] variables;
7723 TemporaryVariableReference copy;
7725 public ArrayForeach (Foreach @foreach, int rank)
7728 counter = new StatementExpression[rank];
7729 variables = new TemporaryVariableReference[rank];
7730 length_exprs = new Expression [rank];
7733 // Only use temporary length variables when dealing with
7734 // multi-dimensional arrays
7737 lengths = new TemporaryVariableReference [rank];
7740 public override bool Resolve (BlockContext ec)
7742 Block variables_block = for_each.variable.Block;
7743 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7746 int rank = length_exprs.Length;
7747 Arguments list = new Arguments (rank);
7748 for (int i = 0; i < rank; i++) {
7749 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7751 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7752 counter[i].Resolve (ec);
7755 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7757 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7758 lengths[i].Resolve (ec);
7760 Arguments args = new Arguments (1);
7761 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7762 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7765 list.Add (new Argument (v));
7768 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7773 if (for_each.type is VarExpr) {
7774 // Infer implicitly typed local variable from foreach array type
7775 var_type = access.Type;
7777 var_type = for_each.type.ResolveAsType (ec);
7779 if (var_type == null)
7782 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7787 for_each.variable.Type = var_type;
7789 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7790 if (variable_ref == null)
7793 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7795 return for_each.body.Resolve (ec);
7798 protected override void DoEmit (EmitContext ec)
7800 copy.EmitAssign (ec, for_each.expr);
7802 int rank = length_exprs.Length;
7803 Label[] test = new Label [rank];
7804 Label[] loop = new Label [rank];
7806 for (int i = 0; i < rank; i++) {
7807 test [i] = ec.DefineLabel ();
7808 loop [i] = ec.DefineLabel ();
7810 if (lengths != null)
7811 lengths [i].EmitAssign (ec, length_exprs [i]);
7814 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7815 for (int i = 0; i < rank; i++) {
7816 variables [i].EmitAssign (ec, zero);
7818 ec.Emit (OpCodes.Br, test [i]);
7819 ec.MarkLabel (loop [i]);
7822 for_each.body.Emit (ec);
7824 ec.MarkLabel (ec.LoopBegin);
7825 ec.Mark (for_each.expr.Location);
7827 for (int i = rank - 1; i >= 0; i--){
7828 counter [i].Emit (ec);
7830 ec.MarkLabel (test [i]);
7831 variables [i].Emit (ec);
7833 if (lengths != null)
7834 lengths [i].Emit (ec);
7836 length_exprs [i].Emit (ec);
7838 ec.Emit (OpCodes.Blt, loop [i]);
7841 ec.MarkLabel (ec.LoopEnd);
7845 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7847 class RuntimeDispose : Using.VariableDeclaration
7849 public RuntimeDispose (LocalVariable lv, Location loc)
7855 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7857 // Defered to runtime check
7860 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7862 var idt = bc.BuiltinTypes.IDisposable;
7865 // Fabricates code like
7867 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7870 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7872 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7873 dispose_variable.CreateReferenceExpression (bc, loc),
7874 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7875 loc), new NullLiteral (loc));
7877 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7879 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7880 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7882 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7883 return new If (idisaposable_test, dispose, loc);
7887 LocalVariable variable;
7889 Statement statement;
7890 ExpressionStatement init;
7891 TemporaryVariableReference enumerator_variable;
7892 bool ambiguous_getenumerator_name;
7894 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7897 this.variable = var;
7901 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7903 rc.Report.SymbolRelatedToPreviousError (enumerator);
7904 rc.Report.Error (202, loc,
7905 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7906 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7909 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7912 // Option 1: Try to match by name GetEnumerator first
7914 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7915 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7917 var mg = mexpr as MethodGroupExpr;
7919 mg.InstanceExpression = expr;
7920 Arguments args = new Arguments (0);
7921 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7923 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7924 if (ambiguous_getenumerator_name)
7927 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7933 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7936 PredefinedMember<MethodSpec> iface_candidate = null;
7937 var ptypes = rc.Module.PredefinedTypes;
7938 var gen_ienumerable = ptypes.IEnumerableGeneric;
7939 if (!gen_ienumerable.Define ())
7940 gen_ienumerable = null;
7942 var ifaces = t.Interfaces;
7943 if (ifaces != null) {
7944 foreach (var iface in ifaces) {
7945 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7946 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7947 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7948 rc.Report.Error (1640, loc,
7949 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7950 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7955 // TODO: Cache this somehow
7956 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7957 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7962 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7963 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7968 if (iface_candidate == null) {
7969 if (expr.Type != InternalType.ErrorType) {
7970 rc.Report.Error (1579, loc,
7971 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7972 expr.Type.GetSignatureForError (), "GetEnumerator");
7978 var method = iface_candidate.Resolve (loc);
7982 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7983 mg.InstanceExpression = expr;
7987 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7989 var ms = MemberCache.FindMember (enumerator.ReturnType,
7990 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7991 BindingRestriction.InstanceOnly) as MethodSpec;
7993 if (ms == null || !ms.IsPublic) {
7994 Error_WrongEnumerator (rc, enumerator);
7998 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8001 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8003 var ps = MemberCache.FindMember (enumerator.ReturnType,
8004 MemberFilter.Property ("Current", null),
8005 BindingRestriction.InstanceOnly) as PropertySpec;
8007 if (ps == null || !ps.IsPublic) {
8008 Error_WrongEnumerator (rc, enumerator);
8015 public override bool Resolve (BlockContext ec)
8017 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8020 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8021 } else if (expr.Type.IsNullableType) {
8022 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8025 var get_enumerator_mg = ResolveGetEnumerator (ec);
8026 if (get_enumerator_mg == null) {
8030 var get_enumerator = get_enumerator_mg.BestCandidate;
8031 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8032 enumerator_variable.Resolve (ec);
8034 // Prepare bool MoveNext ()
8035 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8036 if (move_next_mg == null) {
8040 move_next_mg.InstanceExpression = enumerator_variable;
8042 // Prepare ~T~ Current { get; }
8043 var current_prop = ResolveCurrent (ec, get_enumerator);
8044 if (current_prop == null) {
8048 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8049 if (current_pe == null)
8052 VarExpr ve = for_each.type as VarExpr;
8056 // Source type is dynamic, set element type to dynamic too
8057 variable.Type = ec.BuiltinTypes.Dynamic;
8059 // Infer implicitly typed local variable from foreach enumerable type
8060 variable.Type = current_pe.Type;
8064 // Explicit cast of dynamic collection elements has to be done at runtime
8065 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8068 variable.Type = for_each.type.ResolveAsType (ec);
8070 if (variable.Type == null)
8073 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8074 if (current_pe == null)
8078 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8079 if (variable_ref == null)
8082 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8084 var init = new Invocation.Predefined (get_enumerator_mg, null);
8086 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8087 for_each.body, Location.Null);
8089 var enum_type = enumerator_variable.Type;
8092 // Add Dispose method call when enumerator can be IDisposable
8094 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8095 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8097 // Runtime Dispose check
8099 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8100 vd.Initializer = init;
8101 statement = new Using (vd, statement, Location.Null);
8104 // No Dispose call needed
8106 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8107 this.init.Resolve (ec);
8111 // Static Dispose check
8113 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8114 vd.Initializer = init;
8115 statement = new Using (vd, statement, Location.Null);
8118 return statement.Resolve (ec);
8121 protected override void DoEmit (EmitContext ec)
8123 enumerator_variable.LocalInfo.CreateBuilder (ec);
8126 init.EmitStatement (ec);
8128 statement.Emit (ec);
8131 #region IErrorHandler Members
8133 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8135 ec.Report.SymbolRelatedToPreviousError (best);
8136 ec.Report.Warning (278, 2, expr.Location,
8137 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8138 expr.Type.GetSignatureForError (), "enumerable",
8139 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8141 ambiguous_getenumerator_name = true;
8145 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8150 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8155 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8164 LocalVariable variable;
8168 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8172 this.variable = var;
8178 public Expression Expr {
8179 get { return expr; }
8182 public Expression TypeExpression {
8183 get { return type; }
8186 public LocalVariable Variable {
8187 get { return variable; }
8190 public override Reachability MarkReachable (Reachability rc)
8192 base.MarkReachable (rc);
8194 body.MarkReachable (rc);
8199 public override bool Resolve (BlockContext ec)
8201 expr = expr.Resolve (ec);
8206 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8210 body.AddStatement (Statement);
8212 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8213 Statement = new ArrayForeach (this, 1);
8214 } else if (expr.Type is ArrayContainer) {
8215 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8217 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8218 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8219 expr.ExprClassName);
8223 Statement = new CollectionForeach (this, variable, expr);
8226 return base.Resolve (ec);
8229 protected override void DoEmit (EmitContext ec)
8231 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8232 ec.LoopBegin = ec.DefineLabel ();
8233 ec.LoopEnd = ec.DefineLabel ();
8235 if (!(Statement is Block))
8236 ec.BeginCompilerScope ();
8238 variable.CreateBuilder (ec);
8240 Statement.Emit (ec);
8242 if (!(Statement is Block))
8245 ec.LoopBegin = old_begin;
8246 ec.LoopEnd = old_end;
8249 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8251 expr.FlowAnalysis (fc);
8253 var da = fc.BranchDefiniteAssignment ();
8254 body.FlowAnalysis (fc);
8255 fc.DefiniteAssignment = da;
8259 protected override void CloneTo (CloneContext clonectx, Statement t)
8261 Foreach target = (Foreach) t;
8263 target.type = type.Clone (clonectx);
8264 target.expr = expr.Clone (clonectx);
8265 target.body = (Block) body.Clone (clonectx);
8266 target.Statement = Statement.Clone (clonectx);
8269 public override object Accept (StructuralVisitor visitor)
8271 return visitor.Visit (this);
8275 class SentinelStatement: Statement
8277 protected override void CloneTo (CloneContext clonectx, Statement target)
8281 protected override void DoEmit (EmitContext ec)
8283 var l = ec.DefineLabel ();
8285 ec.Emit (OpCodes.Br_S, l);
8288 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8290 throw new NotImplementedException ();