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;
3122 int debug_scope_index;
3124 public ExplicitBlock (Block parent, Location start, Location end)
3125 : this (parent, (Flags) 0, start, end)
3129 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3130 : base (parent, flags, start, end)
3132 this.Explicit = this;
3137 public AnonymousMethodStorey AnonymousMethodStorey {
3143 public bool HasAwait {
3145 return (flags & Flags.AwaitBlock) != 0;
3149 public bool HasCapturedThis {
3151 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3154 return (flags & Flags.HasCapturedThis) != 0;
3159 // Used to indicate that the block has reference to parent
3160 // block and cannot be made static when defining anonymous method
3162 public bool HasCapturedVariable {
3164 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3167 return (flags & Flags.HasCapturedVariable) != 0;
3171 public bool HasReachableClosingBrace {
3173 return (flags & Flags.ReachableEnd) != 0;
3176 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3180 public bool HasYield {
3182 return (flags & Flags.YieldBlock) != 0;
3189 // Creates anonymous method storey in current block
3191 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3194 // Return same story for iterator and async blocks unless we are
3195 // in nested anonymous method
3197 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3198 return ec.CurrentAnonymousMethod.Storey;
3200 if (am_storey == null) {
3201 MemberBase mc = ec.MemberContext as MemberBase;
3204 // Creates anonymous method storey for this block
3206 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3212 public void EmitScopeInitialization (EmitContext ec)
3214 if ((flags & Flags.InitializationEmitted) != 0)
3217 if (am_storey != null) {
3218 DefineStoreyContainer (ec, am_storey);
3219 am_storey.EmitStoreyInstantiation (ec, this);
3222 if (scope_initializers != null)
3223 EmitScopeInitializers (ec);
3225 flags |= Flags.InitializationEmitted;
3228 public override void Emit (EmitContext ec)
3230 if (Parent != null) {
3231 // TODO: It's needed only when scope has variable (normal or lifted)
3232 ec.BeginScope (GetDebugSymbolScopeIndex ());
3235 EmitScopeInitialization (ec);
3237 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3238 ec.Emit (OpCodes.Nop);
3246 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3247 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3248 ec.Emit (OpCodes.Nop);
3252 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3254 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3255 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3256 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3260 // Creates anonymous method storey
3262 storey.CreateContainer ();
3263 storey.DefineContainer ();
3265 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3268 // Only first storey in path will hold this reference. All children blocks will
3269 // reference it indirectly using $ref field
3271 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3272 if (b.Parent != null) {
3273 var s = b.Parent.Explicit.AnonymousMethodStorey;
3275 storey.HoistedThis = s.HoistedThis;
3280 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3281 if (storey.HoistedThis == null)
3282 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3284 if (storey.HoistedThis != null)
3290 // We are the first storey on path and 'this' has to be hoisted
3292 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3293 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3295 // ThisReferencesFromChildrenBlock holds all reference even if they
3296 // are not on this path. It saves some memory otherwise it'd have to
3297 // be in every explicit block. We run this check to see if the reference
3298 // is valid for this storey
3300 Block block_on_path = ref_block;
3301 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3303 if (block_on_path == null)
3306 if (storey.HoistedThis == null) {
3307 storey.AddCapturedThisField (ec, null);
3310 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3312 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3314 if (b_storey != null) {
3316 // Don't add storey cross reference for `this' when the storey ends up not
3317 // beeing attached to any parent
3319 if (b.ParametersBlock.StateMachine == null) {
3320 AnonymousMethodStorey s = null;
3321 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3322 s = ab.Explicit.AnonymousMethodStorey;
3327 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3329 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3330 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3337 // Stop propagation inside same top block
3339 if (b.ParametersBlock == ParametersBlock.Original) {
3340 b_storey.AddParentStoreyReference (ec, storey);
3341 // b_storey.HoistedThis = storey.HoistedThis;
3345 b = pb = b.ParametersBlock;
3347 pb = b as ParametersBlock;
3350 if (pb != null && pb.StateMachine != null) {
3351 if (pb.StateMachine == storey)
3355 // If we are state machine with no parent. We can hook into parent without additional
3356 // reference and capture this directly
3358 ExplicitBlock parent_storey_block = pb;
3359 while (parent_storey_block.Parent != null) {
3360 parent_storey_block = parent_storey_block.Parent.Explicit;
3361 if (parent_storey_block.AnonymousMethodStorey != null) {
3366 if (parent_storey_block.AnonymousMethodStorey == null) {
3367 if (pb.StateMachine.HoistedThis == null) {
3368 pb.StateMachine.AddCapturedThisField (ec, null);
3369 b.HasCapturedThis = true;
3375 var parent_this_block = pb;
3376 while (parent_this_block.Parent != null) {
3377 parent_this_block = parent_this_block.Parent.ParametersBlock;
3378 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3384 // Add reference to closest storey which holds captured this
3386 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3390 // Add parent storey reference only when this is not captured directly
3392 if (b_storey != null) {
3393 b_storey.AddParentStoreyReference (ec, storey);
3394 b_storey.HoistedThis = storey.HoistedThis;
3401 var ref_blocks = storey.ReferencesFromChildrenBlock;
3402 if (ref_blocks != null) {
3403 foreach (ExplicitBlock ref_block in ref_blocks) {
3404 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3405 if (b.AnonymousMethodStorey != null) {
3406 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3409 // Stop propagation inside same top block
3411 if (b.ParametersBlock == ParametersBlock.Original)
3414 b = b.ParametersBlock;
3417 var pb = b as ParametersBlock;
3418 if (pb != null && pb.StateMachine != null) {
3419 if (pb.StateMachine == storey)
3422 pb.StateMachine.AddParentStoreyReference (ec, storey);
3425 b.HasCapturedVariable = true;
3431 storey.PrepareEmit ();
3432 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3435 public int GetDebugSymbolScopeIndex ()
3437 if (debug_scope_index == 0)
3438 debug_scope_index = ++ParametersBlock.debug_scope_index;
3440 return debug_scope_index;
3443 public void RegisterAsyncAwait ()
3446 while ((block.flags & Flags.AwaitBlock) == 0) {
3447 block.flags |= Flags.AwaitBlock;
3449 if (block is ParametersBlock)
3452 block = block.Parent.Explicit;
3456 public void RegisterIteratorYield ()
3458 ParametersBlock.TopBlock.IsIterator = true;
3461 while ((block.flags & Flags.YieldBlock) == 0) {
3462 block.flags |= Flags.YieldBlock;
3464 if (block.Parent == null)
3467 block = block.Parent.Explicit;
3471 public void SetCatchBlock ()
3473 flags |= Flags.CatchBlock;
3476 public void SetFinallyBlock ()
3478 flags |= Flags.FinallyBlock;
3481 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3483 tryBlock.statements = statements;
3484 statements = new List<Statement> (1);
3485 statements.Add (tf);
3490 // ParametersBlock was introduced to support anonymous methods
3491 // and lambda expressions
3493 public class ParametersBlock : ExplicitBlock
3495 public class ParameterInfo : INamedBlockVariable
3497 readonly ParametersBlock block;
3499 public VariableInfo VariableInfo;
3502 public ParameterInfo (ParametersBlock block, int index)
3510 public ParametersBlock Block {
3516 Block INamedBlockVariable.Block {
3522 public bool IsDeclared {
3528 public bool IsParameter {
3534 public bool IsLocked {
3543 public Location Location {
3545 return Parameter.Location;
3549 public Parameter Parameter {
3551 return block.Parameters [index];
3555 public TypeSpec ParameterType {
3557 return Parameter.Type;
3563 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3565 return new ParameterReference (this, loc);
3570 // Block is converted into an expression
3572 sealed class BlockScopeExpression : Expression
3575 readonly ParametersBlock block;
3577 public BlockScopeExpression (Expression child, ParametersBlock block)
3583 public override bool ContainsEmitWithAwait ()
3585 return child.ContainsEmitWithAwait ();
3588 public override Expression CreateExpressionTree (ResolveContext ec)
3590 throw new NotSupportedException ();
3593 protected override Expression DoResolve (ResolveContext ec)
3598 child = child.Resolve (ec);
3602 eclass = child.eclass;
3607 public override void Emit (EmitContext ec)
3609 block.EmitScopeInitializers (ec);
3614 protected ParametersCompiled parameters;
3615 protected ParameterInfo[] parameter_info;
3616 protected bool resolved;
3617 protected ToplevelBlock top_block;
3618 protected StateMachine state_machine;
3619 protected Dictionary<string, object> labels;
3621 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3622 : base (parent, 0, start, start)
3624 if (parameters == null)
3625 throw new ArgumentNullException ("parameters");
3627 this.parameters = parameters;
3628 ParametersBlock = this;
3630 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3632 this.top_block = parent.ParametersBlock.top_block;
3633 ProcessParameters ();
3636 protected ParametersBlock (ParametersCompiled parameters, Location start)
3637 : base (null, 0, start, start)
3639 if (parameters == null)
3640 throw new ArgumentNullException ("parameters");
3642 this.parameters = parameters;
3643 ParametersBlock = this;
3647 // It's supposed to be used by method body implementation of anonymous methods
3649 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3650 : base (null, 0, source.StartLocation, source.EndLocation)
3652 this.parameters = parameters;
3653 this.statements = source.statements;
3654 this.scope_initializers = source.scope_initializers;
3656 this.resolved = true;
3657 this.reachable = source.reachable;
3658 this.am_storey = source.am_storey;
3659 this.state_machine = source.state_machine;
3660 this.flags = source.flags & Flags.ReachableEnd;
3662 ParametersBlock = this;
3665 // Overwrite original for comparison purposes when linking cross references
3666 // between anonymous methods
3668 Original = source.Original;
3673 public bool IsAsync {
3675 return (flags & Flags.HasAsyncModifier) != 0;
3678 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3683 // Block has been converted to expression tree
3685 public bool IsExpressionTree {
3687 return (flags & Flags.IsExpressionTree) != 0;
3692 // The parameters for the block.
3694 public ParametersCompiled Parameters {
3700 public StateMachine StateMachine {
3702 return state_machine;
3706 public ToplevelBlock TopBlock {
3715 public bool Resolved {
3717 return (flags & Flags.Resolved) != 0;
3721 public int TemporaryLocalsCount { get; set; }
3726 // Checks whether all `out' parameters have been assigned.
3728 public void CheckControlExit (FlowAnalysisContext fc)
3730 CheckControlExit (fc, fc.DefiniteAssignment);
3733 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3735 if (parameter_info == null)
3738 foreach (var p in parameter_info) {
3739 if (p.VariableInfo == null)
3742 if (p.VariableInfo.IsAssigned (dat))
3745 fc.Report.Error (177, p.Location,
3746 "The out parameter `{0}' must be assigned to before control leaves the current method",
3751 protected override void CloneTo (CloneContext clonectx, Statement t)
3753 base.CloneTo (clonectx, t);
3755 var target = (ParametersBlock) t;
3758 // Clone label statements as well as they contain block reference
3762 if (pb.labels != null) {
3763 target.labels = new Dictionary<string, object> ();
3765 foreach (var entry in pb.labels) {
3766 var list = entry.Value as List<LabeledStatement>;
3769 var list_clone = new List<LabeledStatement> ();
3770 foreach (var lentry in list) {
3771 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3774 target.labels.Add (entry.Key, list_clone);
3776 var labeled = (LabeledStatement) entry.Value;
3777 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3784 if (pb.Parent == null)
3787 pb = pb.Parent.ParametersBlock;
3791 public override Expression CreateExpressionTree (ResolveContext ec)
3793 if (statements.Count == 1) {
3794 Expression expr = statements[0].CreateExpressionTree (ec);
3795 if (scope_initializers != null)
3796 expr = new BlockScopeExpression (expr, this);
3801 return base.CreateExpressionTree (ec);
3804 public override void Emit (EmitContext ec)
3806 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3807 DefineStoreyContainer (ec, state_machine);
3808 state_machine.EmitStoreyInstantiation (ec, this);
3814 public void EmitEmbedded (EmitContext ec)
3816 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3817 DefineStoreyContainer (ec, state_machine);
3818 state_machine.EmitStoreyInstantiation (ec, this);
3824 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3826 var res = base.DoFlowAnalysis (fc);
3828 if (HasReachableClosingBrace)
3829 CheckControlExit (fc);
3834 public LabeledStatement GetLabel (string name, Block block)
3837 // Cloned parameters blocks can have their own cloned version of top-level labels
3839 if (labels == null) {
3841 return Parent.ParametersBlock.GetLabel (name, block);
3847 if (!labels.TryGetValue (name, out value)) {
3851 var label = value as LabeledStatement;
3853 if (label != null) {
3854 if (IsLabelVisible (label, b))
3858 List<LabeledStatement> list = (List<LabeledStatement>) value;
3859 for (int i = 0; i < list.Count; ++i) {
3861 if (IsLabelVisible (label, b))
3869 static bool IsLabelVisible (LabeledStatement label, Block b)
3872 if (label.Block == b)
3875 } while (b != null);
3880 public ParameterInfo GetParameterInfo (Parameter p)
3882 for (int i = 0; i < parameters.Count; ++i) {
3883 if (parameters[i] == p)
3884 return parameter_info[i];
3887 throw new ArgumentException ("Invalid parameter");
3890 public ParameterReference GetParameterReference (int index, Location loc)
3892 return new ParameterReference (parameter_info[index], loc);
3895 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3897 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3899 CloneContext clonectx = new CloneContext ();
3900 return Clone (clonectx);
3903 protected void ProcessParameters ()
3905 if (parameters.Count == 0)
3908 parameter_info = new ParameterInfo[parameters.Count];
3909 for (int i = 0; i < parameter_info.Length; ++i) {
3910 var p = parameters.FixedParameters[i];
3914 // TODO: Should use Parameter only and more block there
3915 parameter_info[i] = new ParameterInfo (this, i);
3917 AddLocalName (p.Name, parameter_info[i]);
3921 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3923 var src = stmt.Block;
3926 // Cannot remap label block if the label was not yet cloned which
3927 // can happen in case of anonymous method inside anoynymous method
3928 // with a label. But in this case we don't care because goto cannot
3929 // jump of out anonymous method
3931 if (src.ParametersBlock != this)
3934 var src_stmts = src.Statements;
3935 for (int i = 0; i < src_stmts.Count; ++i) {
3936 if (src_stmts[i] == stmt)
3937 return (LabeledStatement) dst.Statements[i];
3940 throw new InternalErrorException ("Should never be reached");
3943 public override bool Resolve (BlockContext bc)
3945 // TODO: if ((flags & Flags.Resolved) != 0)
3952 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3953 flags |= Flags.IsExpressionTree;
3956 PrepareAssignmentAnalysis (bc);
3958 if (!base.Resolve (bc))
3961 } catch (Exception e) {
3962 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3965 if (bc.CurrentBlock != null) {
3966 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3968 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3973 // If an asynchronous body of F is either an expression classified as nothing, or a
3974 // statement block where no return statements have expressions, the inferred return type is Task
3977 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3978 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3979 am.ReturnTypeInference = null;
3980 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3988 void PrepareAssignmentAnalysis (BlockContext bc)
3990 for (int i = 0; i < parameters.Count; ++i) {
3991 var par = parameters.FixedParameters[i];
3993 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3996 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4000 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4002 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4003 var stateMachine = new IteratorStorey (iterator);
4005 state_machine = stateMachine;
4006 iterator.SetStateMachine (stateMachine);
4008 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4009 tlb.Original = this;
4010 tlb.state_machine = stateMachine;
4011 tlb.AddStatement (new Return (iterator, iterator.Location));
4015 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4017 for (int i = 0; i < parameters.Count; i++) {
4018 Parameter p = parameters[i];
4019 Parameter.Modifier mod = p.ModFlags;
4020 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4021 host.Compiler.Report.Error (1988, p.Location,
4022 "Async methods cannot have ref or out parameters");
4026 if (p is ArglistParameter) {
4027 host.Compiler.Report.Error (4006, p.Location,
4028 "__arglist is not allowed in parameter list of async methods");
4032 if (parameters.Types[i].IsPointer) {
4033 host.Compiler.Report.Error (4005, p.Location,
4034 "Async methods cannot have unsafe parameters");
4040 host.Compiler.Report.Warning (1998, 1, loc,
4041 "Async block lacks `await' operator and will run synchronously");
4044 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4045 var initializer = new AsyncInitializer (this, host, block_type);
4046 initializer.Type = block_type;
4047 initializer.DelegateType = delegateType;
4049 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4051 state_machine = stateMachine;
4052 initializer.SetStateMachine (stateMachine);
4054 const Flags flags = Flags.CompilerGenerated;
4056 var b = this is ToplevelBlock ?
4057 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4058 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4061 b.state_machine = stateMachine;
4062 b.AddStatement (new AsyncInitializerStatement (initializer));
4070 public class ToplevelBlock : ParametersBlock
4072 LocalVariable this_variable;
4073 CompilerContext compiler;
4074 Dictionary<string, object> names;
4076 List<ExplicitBlock> this_references;
4078 public ToplevelBlock (CompilerContext ctx, Location loc)
4079 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4083 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4084 : base (parameters, start)
4086 this.compiler = ctx;
4090 ProcessParameters ();
4094 // Recreates a top level block from parameters block. Used for
4095 // compiler generated methods where the original block comes from
4096 // explicit child block. This works for already resolved blocks
4097 // only to ensure we resolve them in the correct flow order
4099 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4100 : base (source, parameters)
4102 this.compiler = source.TopBlock.compiler;
4106 public bool IsIterator {
4108 return (flags & Flags.Iterator) != 0;
4111 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4115 public Report Report {
4117 return compiler.Report;
4122 // Used by anonymous blocks to track references of `this' variable
4124 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4126 return this_references;
4131 // Returns the "this" instance variable of this block.
4132 // See AddThisVariable() for more information.
4134 public LocalVariable ThisVariable {
4136 return this_variable;
4140 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4143 names = new Dictionary<string, object> ();
4146 if (!names.TryGetValue (name, out value)) {
4147 names.Add (name, li);
4151 INamedBlockVariable existing = value as INamedBlockVariable;
4152 List<INamedBlockVariable> existing_list;
4153 if (existing != null) {
4154 existing_list = new List<INamedBlockVariable> ();
4155 existing_list.Add (existing);
4156 names[name] = existing_list;
4158 existing_list = (List<INamedBlockVariable>) value;
4162 // A collision checking between local names
4164 var variable_block = li.Block.Explicit;
4165 for (int i = 0; i < existing_list.Count; ++i) {
4166 existing = existing_list[i];
4167 Block b = existing.Block.Explicit;
4169 // Collision at same level
4170 if (variable_block == b) {
4171 li.Block.Error_AlreadyDeclared (name, li);
4175 // Collision with parent
4176 Block parent = variable_block;
4177 while ((parent = parent.Parent) != null) {
4179 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4180 i = existing_list.Count;
4185 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4186 // Collision with children
4187 while ((b = b.Parent) != null) {
4188 if (variable_block == b) {
4189 li.Block.Error_AlreadyDeclared (name, li, "child");
4190 i = existing_list.Count;
4197 existing_list.Add (li);
4200 public void AddLabel (string name, LabeledStatement label)
4203 labels = new Dictionary<string, object> ();
4206 if (!labels.TryGetValue (name, out value)) {
4207 labels.Add (name, label);
4211 LabeledStatement existing = value as LabeledStatement;
4212 List<LabeledStatement> existing_list;
4213 if (existing != null) {
4214 existing_list = new List<LabeledStatement> ();
4215 existing_list.Add (existing);
4216 labels[name] = existing_list;
4218 existing_list = (List<LabeledStatement>) value;
4222 // A collision checking between labels
4224 for (int i = 0; i < existing_list.Count; ++i) {
4225 existing = existing_list[i];
4226 Block b = existing.Block;
4228 // Collision at same level
4229 if (label.Block == b) {
4230 Report.SymbolRelatedToPreviousError (existing.loc, name);
4231 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4235 // Collision with parent
4237 while ((b = b.Parent) != null) {
4238 if (existing.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;
4246 // Collision with with children
4248 while ((b = b.Parent) != null) {
4249 if (label.Block == b) {
4250 Report.Error (158, label.loc,
4251 "The label `{0}' shadows another label by the same name in a contained scope", name);
4252 i = existing_list.Count;
4258 existing_list.Add (label);
4261 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4263 if (this_references == null)
4264 this_references = new List<ExplicitBlock> ();
4266 if (!this_references.Contains (block))
4267 this_references.Add (block);
4270 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4272 this_references.Remove (block);
4276 // Creates an arguments set from all parameters, useful for method proxy calls
4278 public Arguments GetAllParametersArguments ()
4280 int count = parameters.Count;
4281 Arguments args = new Arguments (count);
4282 for (int i = 0; i < count; ++i) {
4283 var pi = parameter_info[i];
4284 var arg_expr = GetParameterReference (i, pi.Location);
4286 Argument.AType atype_modifier;
4287 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4288 case Parameter.Modifier.REF:
4289 atype_modifier = Argument.AType.Ref;
4291 case Parameter.Modifier.OUT:
4292 atype_modifier = Argument.AType.Out;
4299 args.Add (new Argument (arg_expr, atype_modifier));
4306 // Lookup inside a block, the returned value can represent 3 states
4308 // true+variable: A local name was found and it's valid
4309 // false+variable: A local name was found in a child block only
4310 // false+null: No local name was found
4312 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4318 if (!names.TryGetValue (name, out value))
4321 variable = value as INamedBlockVariable;
4323 if (variable != null) {
4325 if (variable.Block == b.Original)
4329 } while (b != null);
4337 } while (b != null);
4339 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4340 for (int i = 0; i < list.Count; ++i) {
4343 if (variable.Block == b.Original)
4347 } while (b != null);
4355 } while (b != null);
4365 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4367 if (block.names != null) {
4368 foreach (var n in block.names) {
4369 var variable = n.Value as INamedBlockVariable;
4370 if (variable != null) {
4371 if (variable.Block.ParametersBlock == pb)
4372 AddLocalName (n.Key, variable, false);
4376 foreach (var v in (List<INamedBlockVariable>) n.Value)
4377 if (v.Block.ParametersBlock == pb)
4378 AddLocalName (n.Key, v, false);
4384 // This is used by non-static `struct' constructors which do not have an
4385 // initializer - in this case, the constructor must initialize all of the
4386 // struct's fields. To do this, we add a "this" variable and use the flow
4387 // analysis code to ensure that it's been fully initialized before control
4388 // leaves the constructor.
4390 public void AddThisVariable (BlockContext bc)
4392 if (this_variable != null)
4393 throw new InternalErrorException (StartLocation.ToString ());
4395 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4396 this_variable.Type = bc.CurrentType;
4397 this_variable.PrepareAssignmentAnalysis (bc);
4400 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4403 // If we're a non-static struct constructor which doesn't have an
4404 // initializer, then we must initialize all of the struct's fields.
4406 if (this_variable != null)
4407 this_variable.IsThisAssigned (fc, this);
4409 base.CheckControlExit (fc, dat);
4412 public HashSet<LocalVariable> GetUndeclaredVariables ()
4417 HashSet<LocalVariable> variables = null;
4419 foreach (var entry in names) {
4420 var complex = entry.Value as List<INamedBlockVariable>;
4421 if (complex != null) {
4422 foreach (var centry in complex) {
4423 if (IsUndeclaredVariable (centry)) {
4424 if (variables == null)
4425 variables = new HashSet<LocalVariable> ();
4427 variables.Add ((LocalVariable) centry);
4430 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4431 if (variables == null)
4432 variables = new HashSet<LocalVariable> ();
4434 variables.Add ((LocalVariable)entry.Value);
4441 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4443 var lv = namedBlockVariable as LocalVariable;
4444 return lv != null && !lv.IsDeclared;
4447 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4452 foreach (var entry in names) {
4453 var complex = entry.Value as List<INamedBlockVariable>;
4454 if (complex != null) {
4455 foreach (var centry in complex) {
4456 var lv = centry as LocalVariable;
4457 if (lv != null && undeclaredVariables.Contains (lv)) {
4462 var lv = entry.Value as LocalVariable;
4463 if (lv != null && undeclaredVariables.Contains (lv))
4469 public override void Emit (EmitContext ec)
4471 if (Report.Errors > 0)
4475 if (IsCompilerGenerated) {
4476 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4484 // If `HasReturnLabel' is set, then we already emitted a
4485 // jump to the end of the method, so we must emit a `ret'
4488 // Unfortunately, System.Reflection.Emit automatically emits
4489 // a leave to the end of a finally block. This is a problem
4490 // if no code is following the try/finally block since we may
4491 // jump to a point after the end of the method.
4492 // As a workaround, we're always creating a return label in
4495 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4496 if (ec.HasReturnLabel)
4497 ec.MarkLabel (ec.ReturnLabel);
4499 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4500 ec.Mark (EndLocation);
4502 if (ec.ReturnType.Kind != MemberKind.Void)
4503 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4505 ec.Emit (OpCodes.Ret);
4508 } catch (Exception e) {
4509 throw new InternalErrorException (e, StartLocation);
4513 public bool Resolve (BlockContext bc, IMethodData md)
4518 var errors = bc.Report.Errors;
4522 if (bc.Report.Errors > errors)
4525 MarkReachable (new Reachability ());
4527 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4528 // TODO: var md = bc.CurrentMemberDefinition;
4529 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4532 if ((flags & Flags.NoFlowAnalysis) != 0)
4535 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4538 } catch (Exception e) {
4539 throw new InternalErrorException (e, StartLocation);
4546 public class SwitchLabel : Statement
4554 // if expr == null, then it is the default case.
4556 public SwitchLabel (Expression expr, Location l)
4562 public bool IsDefault {
4564 return label == null;
4568 public Expression Label {
4574 public Location Location {
4580 public Constant Converted {
4589 public bool PatternMatching { get; set; }
4591 public bool SectionStart { get; set; }
4593 public Label GetILLabel (EmitContext ec)
4595 if (il_label == null){
4596 il_label = ec.DefineLabel ();
4599 return il_label.Value;
4602 protected override void DoEmit (EmitContext ec)
4604 ec.MarkLabel (GetILLabel (ec));
4607 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4612 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4616 public override bool Resolve (BlockContext bc)
4618 if (ResolveAndReduce (bc))
4619 bc.Switch.RegisterLabel (bc, this);
4625 // Resolves the expression, reduces it to a literal if possible
4626 // and then converts it to the requested type.
4628 bool ResolveAndReduce (BlockContext bc)
4633 var switch_statement = bc.Switch;
4635 if (PatternMatching) {
4636 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4637 return label != null;
4640 var c = label.ResolveLabelConstant (bc);
4644 if (switch_statement.IsNullable && c is NullLiteral) {
4649 if (switch_statement.IsPatternMatching) {
4650 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4654 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4655 return converted != null;
4658 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4660 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4661 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4664 protected override void CloneTo (CloneContext clonectx, Statement target)
4666 var t = (SwitchLabel) target;
4668 t.label = label.Clone (clonectx);
4671 public override object Accept (StructuralVisitor visitor)
4673 return visitor.Visit (this);
4676 public string GetSignatureForError ()
4679 if (converted == null)
4682 label = converted.GetValueAsLiteral ();
4684 return string.Format ("case {0}:", label);
4688 public class Switch : LoopStatement
4690 // structure used to hold blocks of keys while calculating table switch
4691 sealed class LabelsRange : IComparable<LabelsRange>
4693 public readonly long min;
4695 public readonly List<long> label_values;
4697 public LabelsRange (long value)
4700 label_values = new List<long> ();
4701 label_values.Add (value);
4704 public LabelsRange (long min, long max, ICollection<long> values)
4708 this.label_values = new List<long> (values);
4713 return max - min + 1;
4717 public bool AddValue (long value)
4719 var gap = value - min + 1;
4720 // Ensure the range has > 50% occupancy
4721 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4725 label_values.Add (value);
4729 public int CompareTo (LabelsRange other)
4731 int nLength = label_values.Count;
4732 int nLengthOther = other.label_values.Count;
4733 if (nLengthOther == nLength)
4734 return (int) (other.min - min);
4736 return nLength - nLengthOther;
4740 sealed class DispatchStatement : Statement
4742 readonly Switch body;
4744 public DispatchStatement (Switch body)
4749 protected override void CloneTo (CloneContext clonectx, Statement target)
4751 throw new NotImplementedException ();
4754 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4759 protected override void DoEmit (EmitContext ec)
4761 body.EmitDispatch (ec);
4765 class MissingBreak : Statement
4767 readonly SwitchLabel label;
4769 public MissingBreak (SwitchLabel sl)
4775 public bool FallOut { get; set; }
4777 protected override void DoEmit (EmitContext ec)
4781 protected override void CloneTo (CloneContext clonectx, Statement target)
4785 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4788 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4789 label.GetSignatureForError ());
4791 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4792 label.GetSignatureForError ());
4798 public Expression Expr;
4801 // Mapping of all labels to their SwitchLabels
4803 Dictionary<long, SwitchLabel> labels;
4804 Dictionary<string, SwitchLabel> string_labels;
4805 List<SwitchLabel> case_labels;
4807 List<Tuple<GotoCase, Constant>> goto_cases;
4808 List<DefiniteAssignmentBitSet> end_reachable_das;
4811 /// The governing switch type
4813 public TypeSpec SwitchType;
4815 Expression new_expr;
4817 SwitchLabel case_null;
4818 SwitchLabel case_default;
4820 Label defaultLabel, nullLabel;
4821 VariableReference value;
4822 ExpressionStatement string_dictionary;
4823 FieldExpr switch_cache_field;
4824 ExplicitBlock block;
4828 // Nullable Types support
4830 Nullable.Unwrap unwrap;
4832 public Switch (Expression e, ExplicitBlock block, Location l)
4840 public SwitchLabel ActiveLabel { get; set; }
4842 public ExplicitBlock Block {
4848 public SwitchLabel DefaultLabel {
4850 return case_default;
4854 public bool IsNullable {
4856 return unwrap != null;
4860 public bool IsPatternMatching {
4862 return new_expr == null && SwitchType != null;
4866 public List<SwitchLabel> RegisteredLabels {
4872 public VariableReference ExpressionValue {
4879 // Determines the governing type for a switch. The returned
4880 // expression might be the expression from the switch, or an
4881 // expression that includes any potential conversions to
4883 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4885 switch (expr.Type.BuiltinType) {
4886 case BuiltinTypeSpec.Type.Byte:
4887 case BuiltinTypeSpec.Type.SByte:
4888 case BuiltinTypeSpec.Type.UShort:
4889 case BuiltinTypeSpec.Type.Short:
4890 case BuiltinTypeSpec.Type.UInt:
4891 case BuiltinTypeSpec.Type.Int:
4892 case BuiltinTypeSpec.Type.ULong:
4893 case BuiltinTypeSpec.Type.Long:
4894 case BuiltinTypeSpec.Type.Char:
4895 case BuiltinTypeSpec.Type.String:
4896 case BuiltinTypeSpec.Type.Bool:
4900 if (expr.Type.IsEnum)
4904 // Try to find a *user* defined implicit conversion.
4906 // If there is no implicit conversion, or if there are multiple
4907 // conversions, we have to report an error
4909 Expression converted = null;
4910 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4912 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4915 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4916 Convert.UserConversionRestriction.ProbingOnly;
4919 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4921 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4926 // Ignore over-worked ImplicitUserConversions that do
4927 // an implicit conversion in addition to the user conversion.
4929 var uc = e as UserCast;
4933 if (converted != null){
4934 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4943 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4945 var types = module.Compiler.BuiltinTypes;
4947 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4948 TypeSpec[] stypes = new[] {
4961 if (nullable != null) {
4963 Array.Resize (ref stypes, stypes.Length + 9);
4965 for (int i = 0; i < 9; ++i) {
4966 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4973 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4975 case_labels.Add (sl);
4978 if (case_default != null) {
4979 sl.Error_AlreadyOccurs (rc, case_default);
4987 if (sl.Converted == null)
4991 if (string_labels != null) {
4992 string string_value = sl.Converted.GetValue () as string;
4993 if (string_value == null)
4996 string_labels.Add (string_value, sl);
4998 if (sl.Converted.IsNull) {
5001 labels.Add (sl.Converted.GetValueAsLong (), sl);
5004 } catch (ArgumentException) {
5005 if (string_labels != null)
5006 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5008 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5013 // This method emits code for a lookup-based switch statement (non-string)
5014 // Basically it groups the cases into blocks that are at least half full,
5015 // and then spits out individual lookup opcodes for each block.
5016 // It emits the longest blocks first, and short blocks are just
5017 // handled with direct compares.
5019 void EmitTableSwitch (EmitContext ec, Expression val)
5021 if (labels != null && labels.Count > 0) {
5022 List<LabelsRange> ranges;
5023 if (string_labels != null) {
5024 // We have done all hard work for string already
5025 // setup single range only
5026 ranges = new List<LabelsRange> (1);
5027 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5029 var element_keys = new long[labels.Count];
5030 labels.Keys.CopyTo (element_keys, 0);
5031 Array.Sort (element_keys);
5034 // Build possible ranges of switch labes to reduce number
5037 ranges = new List<LabelsRange> (element_keys.Length);
5038 var range = new LabelsRange (element_keys[0]);
5040 for (int i = 1; i < element_keys.Length; ++i) {
5041 var l = element_keys[i];
5042 if (range.AddValue (l))
5045 range = new LabelsRange (l);
5049 // sort the blocks so we can tackle the largest ones first
5053 Label lbl_default = defaultLabel;
5054 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5056 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5057 LabelsRange kb = ranges[range_index];
5058 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5060 // Optimize small ranges using simple equality check
5061 if (kb.Range <= 2) {
5062 foreach (var key in kb.label_values) {
5063 SwitchLabel sl = labels[key];
5064 if (sl == case_default || sl == case_null)
5067 if (sl.Converted.IsZeroInteger) {
5068 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5071 sl.Converted.Emit (ec);
5072 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5076 // TODO: if all the keys in the block are the same and there are
5077 // no gaps/defaults then just use a range-check.
5078 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5079 // TODO: optimize constant/I4 cases
5081 // check block range (could be > 2^31)
5083 ec.EmitLong (kb.min);
5084 ec.Emit (OpCodes.Blt, lbl_default);
5087 ec.EmitLong (kb.max);
5088 ec.Emit (OpCodes.Bgt, lbl_default);
5093 ec.EmitLong (kb.min);
5094 ec.Emit (OpCodes.Sub);
5097 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5101 int first = (int) kb.min;
5104 ec.Emit (OpCodes.Sub);
5105 } else if (first < 0) {
5106 ec.EmitInt (-first);
5107 ec.Emit (OpCodes.Add);
5111 // first, build the list of labels for the switch
5113 long cJumps = kb.Range;
5114 Label[] switch_labels = new Label[cJumps];
5115 for (int iJump = 0; iJump < cJumps; iJump++) {
5116 var key = kb.label_values[iKey];
5117 if (key == kb.min + iJump) {
5118 switch_labels[iJump] = labels[key].GetILLabel (ec);
5121 switch_labels[iJump] = lbl_default;
5125 // emit the switch opcode
5126 ec.Emit (OpCodes.Switch, switch_labels);
5129 // mark the default for this block
5130 if (range_index != 0)
5131 ec.MarkLabel (lbl_default);
5134 // the last default just goes to the end
5135 if (ranges.Count > 0)
5136 ec.Emit (OpCodes.Br, lbl_default);
5140 public SwitchLabel FindLabel (Constant value)
5142 SwitchLabel sl = null;
5144 if (string_labels != null) {
5145 string s = value.GetValue () as string;
5147 if (case_null != null)
5149 else if (case_default != null)
5152 string_labels.TryGetValue (s, out sl);
5155 if (value is NullLiteral) {
5158 labels.TryGetValue (value.GetValueAsLong (), out sl);
5162 if (sl == null || sl.SectionStart)
5166 // Always return section start, it simplifies handling of switch labels
5168 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5169 var cs = case_labels [idx];
5170 if (cs.SectionStart)
5175 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5177 Expr.FlowAnalysis (fc);
5179 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5180 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5181 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5183 block.FlowAnalysis (fc);
5185 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5187 if (end_reachable_das != null) {
5188 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5189 InitialDefinitiveAssignment |= sections_das;
5190 end_reachable_das = null;
5193 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5195 return case_default != null && !end_reachable;
5198 public override bool Resolve (BlockContext ec)
5200 Expr = Expr.Resolve (ec);
5205 // LAMESPEC: User conversion from non-nullable governing type has a priority
5207 new_expr = SwitchGoverningType (ec, Expr, false);
5209 if (new_expr == null) {
5210 if (Expr.Type.IsNullableType) {
5211 unwrap = Nullable.Unwrap.Create (Expr, false);
5216 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5217 // involving nullable Expr and nullable governing type is
5219 new_expr = SwitchGoverningType (ec, unwrap, true);
5223 Expression switch_expr;
5224 if (new_expr == null) {
5225 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5226 if (Expr.Type != InternalType.ErrorType) {
5227 ec.Report.Error (151, loc,
5228 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5229 Expr.Type.GetSignatureForError ());
5236 SwitchType = Expr.Type;
5238 switch_expr = new_expr;
5239 SwitchType = new_expr.Type;
5240 if (SwitchType.IsNullableType) {
5241 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5242 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5245 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5246 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5250 if (block.Statements.Count == 0)
5253 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5254 string_labels = new Dictionary<string, SwitchLabel> ();
5256 labels = new Dictionary<long, SwitchLabel> ();
5260 var constant = switch_expr as Constant;
5263 // Don't need extra variable for constant switch or switch with
5264 // only default case
5266 if (constant == null) {
5268 // Store switch expression for comparison purposes
5270 value = switch_expr as VariableReference;
5271 if (value == null && !HasOnlyDefaultSection ()) {
5272 var current_block = ec.CurrentBlock;
5273 ec.CurrentBlock = Block;
5274 // Create temporary variable inside switch scope
5275 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5277 ec.CurrentBlock = current_block;
5281 case_labels = new List<SwitchLabel> ();
5283 Switch old_switch = ec.Switch;
5285 var parent_los = ec.EnclosingLoopOrSwitch;
5286 ec.EnclosingLoopOrSwitch = this;
5288 var ok = Statement.Resolve (ec);
5290 ec.EnclosingLoopOrSwitch = parent_los;
5291 ec.Switch = old_switch;
5294 // Check if all goto cases are valid. Needs to be done after switch
5295 // is resolved because goto can jump forward in the scope.
5297 if (goto_cases != null) {
5298 foreach (var gc in goto_cases) {
5299 if (gc.Item1 == null) {
5300 if (DefaultLabel == null) {
5301 Goto.Error_UnknownLabel (ec, "default", loc);
5307 var sl = FindLabel (gc.Item2);
5309 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5311 gc.Item1.Label = sl;
5319 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5320 ResolveStringSwitchMap (ec);
5324 // Anonymous storey initialization has to happen before
5325 // any generated switch dispatch
5327 block.InsertStatement (0, new DispatchStatement (this));
5332 bool HasOnlyDefaultSection ()
5334 for (int i = 0; i < block.Statements.Count; ++i) {
5335 var s = block.Statements[i] as SwitchLabel;
5337 if (s == null || s.IsDefault)
5346 public override Reachability MarkReachable (Reachability rc)
5348 if (rc.IsUnreachable)
5351 base.MarkReachable (rc);
5353 block.MarkReachableScope (rc);
5355 if (block.Statements.Count == 0)
5358 SwitchLabel constant_label = null;
5359 var constant = new_expr as Constant;
5361 if (constant != null) {
5362 constant_label = FindLabel (constant) ?? case_default;
5363 if (constant_label == null) {
5364 block.Statements.RemoveAt (0);
5369 var section_rc = new Reachability ();
5370 SwitchLabel prev_label = null;
5372 for (int i = 0; i < block.Statements.Count; ++i) {
5373 var s = block.Statements[i];
5374 var sl = s as SwitchLabel;
5376 if (sl != null && sl.SectionStart) {
5378 // Section is marked already via goto case
5380 if (!sl.IsUnreachable) {
5381 section_rc = new Reachability ();
5385 if (constant_label != null && constant_label != sl)
5386 section_rc = Reachability.CreateUnreachable ();
5387 else if (section_rc.IsUnreachable) {
5388 section_rc = new Reachability ();
5390 if (prev_label != null) {
5391 sl.SectionStart = false;
5392 s = new MissingBreak (prev_label);
5393 s.MarkReachable (rc);
5394 block.Statements.Insert (i - 1, s);
5402 section_rc = s.MarkReachable (section_rc);
5405 if (!section_rc.IsUnreachable && prev_label != null) {
5406 prev_label.SectionStart = false;
5407 var s = new MissingBreak (prev_label) {
5411 s.MarkReachable (rc);
5412 block.Statements.Add (s);
5416 // Reachability can affect parent only when all possible paths are handled but
5417 // we still need to run reachability check on switch body to check for fall-through
5419 if (case_default == null && constant_label == null)
5423 // We have at least one local exit from the switch
5428 return Reachability.CreateUnreachable ();
5431 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5433 if (goto_cases == null)
5434 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5436 goto_cases.Add (Tuple.Create (gotoCase, value));
5440 // Converts string switch into string hashtable
5442 void ResolveStringSwitchMap (ResolveContext ec)
5444 FullNamedExpression string_dictionary_type;
5445 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5446 string_dictionary_type = new TypeExpression (
5447 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5448 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5450 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5451 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5453 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5457 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5458 Field field = new Field (ctype, string_dictionary_type,
5459 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5460 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5461 if (!field.Define ())
5463 ctype.AddField (field);
5465 var init = new List<Expression> ();
5467 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5468 string value = null;
5470 foreach (SwitchLabel sl in case_labels) {
5472 if (sl.SectionStart)
5473 labels.Add (++counter, sl);
5475 if (sl == case_default || sl == case_null)
5478 value = (string) sl.Converted.GetValue ();
5479 var init_args = new List<Expression> (2);
5480 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5482 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5483 init_args.Add (sl.Converted);
5485 init.Add (new CollectionElementInitializer (init_args, loc));
5488 Arguments args = new Arguments (1);
5489 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5490 Expression initializer = new NewInitialize (string_dictionary_type, args,
5491 new CollectionOrObjectInitializers (init, loc), loc);
5493 switch_cache_field = new FieldExpr (field, loc);
5494 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5497 void DoEmitStringSwitch (EmitContext ec)
5499 Label l_initialized = ec.DefineLabel ();
5502 // Skip initialization when value is null
5504 value.EmitBranchable (ec, nullLabel, false);
5507 // Check if string dictionary is initialized and initialize
5509 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5510 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5511 string_dictionary.EmitStatement (ec);
5513 ec.MarkLabel (l_initialized);
5515 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5517 ResolveContext rc = new ResolveContext (ec.MemberContext);
5519 if (switch_cache_field.Type.IsGeneric) {
5520 Arguments get_value_args = new Arguments (2);
5521 get_value_args.Add (new Argument (value));
5522 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5523 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5524 if (get_item == null)
5528 // A value was not found, go to default case
5530 get_item.EmitBranchable (ec, defaultLabel, false);
5532 Arguments get_value_args = new Arguments (1);
5533 get_value_args.Add (new Argument (value));
5535 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5536 if (get_item == null)
5539 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5540 get_item_object.EmitAssign (ec, get_item, true, false);
5541 ec.Emit (OpCodes.Brfalse, defaultLabel);
5543 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5544 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5546 get_item_int.EmitStatement (ec);
5547 get_item_object.Release (ec);
5550 EmitTableSwitch (ec, string_switch_variable);
5551 string_switch_variable.Release (ec);
5555 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5557 void EmitShortSwitch (EmitContext ec)
5559 MethodSpec equal_method = null;
5560 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5561 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5564 if (equal_method != null) {
5565 value.EmitBranchable (ec, nullLabel, false);
5568 for (int i = 0; i < case_labels.Count; ++i) {
5569 var label = case_labels [i];
5570 if (label == case_default || label == case_null)
5573 var constant = label.Converted;
5575 if (constant == null) {
5576 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5580 if (equal_method != null) {
5584 var call = new CallEmitter ();
5585 call.EmitPredefined (ec, equal_method, new Arguments (0));
5586 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5590 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5591 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5597 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5600 ec.Emit (OpCodes.Br, defaultLabel);
5603 void EmitDispatch (EmitContext ec)
5605 if (IsPatternMatching) {
5606 EmitShortSwitch (ec);
5610 if (value == null) {
5612 // Constant switch, we've already done the work if there is only 1 label
5616 foreach (var sl in case_labels) {
5617 if (sl.IsUnreachable)
5620 if (reachable++ > 0) {
5621 var constant = (Constant) new_expr;
5622 var constant_label = FindLabel (constant) ?? case_default;
5624 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5632 if (string_dictionary != null) {
5633 DoEmitStringSwitch (ec);
5634 } else if (case_labels.Count < 4 || string_labels != null) {
5635 EmitShortSwitch (ec);
5637 EmitTableSwitch (ec, value);
5641 protected override void DoEmit (EmitContext ec)
5644 // Setup the codegen context
5646 Label old_end = ec.LoopEnd;
5647 Switch old_switch = ec.Switch;
5649 ec.LoopEnd = ec.DefineLabel ();
5652 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5653 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5655 if (value != null) {
5658 var switch_expr = new_expr ?? Expr;
5660 unwrap.EmitCheck (ec);
5661 ec.Emit (OpCodes.Brfalse, nullLabel);
5662 value.EmitAssign (ec, switch_expr, false, false);
5663 } else if (switch_expr != value) {
5664 value.EmitAssign (ec, switch_expr, false, false);
5669 // Next statement is compiler generated we don't need extra
5670 // nop when we can use the statement for sequence point
5672 ec.Mark (block.StartLocation);
5673 block.IsCompilerGenerated = true;
5675 new_expr.EmitSideEffect (ec);
5680 // Restore context state.
5681 ec.MarkLabel (ec.LoopEnd);
5684 // Restore the previous context
5686 ec.LoopEnd = old_end;
5687 ec.Switch = old_switch;
5690 protected override void CloneTo (CloneContext clonectx, Statement t)
5692 Switch target = (Switch) t;
5694 target.Expr = Expr.Clone (clonectx);
5695 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5698 public override object Accept (StructuralVisitor visitor)
5700 return visitor.Visit (this);
5703 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5705 if (case_default == null && !(new_expr is Constant))
5708 if (end_reachable_das == null)
5709 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5711 end_reachable_das.Add (fc.DefiniteAssignment);
5714 public override void SetEndReachable ()
5716 end_reachable = true;
5720 // A place where execution can restart in a state machine
5721 public abstract class ResumableStatement : Statement
5724 protected Label resume_point;
5726 public Label PrepareForEmit (EmitContext ec)
5730 resume_point = ec.DefineLabel ();
5732 return resume_point;
5735 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5740 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5745 public abstract class TryFinallyBlock : ExceptionStatement
5747 protected Statement stmt;
5748 Label dispose_try_block;
5749 bool prepared_for_dispose, emitted_dispose;
5750 Method finally_host;
5752 protected TryFinallyBlock (Statement stmt, Location loc)
5760 public Statement Statement {
5768 protected abstract void EmitTryBody (EmitContext ec);
5769 public abstract void EmitFinallyBody (EmitContext ec);
5771 public override Label PrepareForDispose (EmitContext ec, Label end)
5773 if (!prepared_for_dispose) {
5774 prepared_for_dispose = true;
5775 dispose_try_block = ec.DefineLabel ();
5777 return dispose_try_block;
5780 protected sealed override void DoEmit (EmitContext ec)
5782 EmitTryBodyPrepare (ec);
5785 bool beginFinally = EmitBeginFinallyBlock (ec);
5787 Label start_finally = ec.DefineLabel ();
5788 if (resume_points != null && beginFinally) {
5789 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5791 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5792 ec.Emit (OpCodes.Brfalse_S, start_finally);
5793 ec.Emit (OpCodes.Endfinally);
5796 ec.MarkLabel (start_finally);
5798 if (finally_host != null) {
5799 finally_host.Define ();
5800 finally_host.PrepareEmit ();
5801 finally_host.Emit ();
5803 // Now it's safe to add, to close it properly and emit sequence points
5804 finally_host.Parent.AddMember (finally_host);
5806 var ce = new CallEmitter ();
5807 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5808 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5810 EmitFinallyBody (ec);
5814 ec.EndExceptionBlock ();
5817 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5819 if (emitted_dispose)
5822 emitted_dispose = true;
5824 Label end_of_try = ec.DefineLabel ();
5826 // Ensure that the only way we can get into this code is through a dispatcher
5827 if (have_dispatcher)
5828 ec.Emit (OpCodes.Br, end);
5830 ec.BeginExceptionBlock ();
5832 ec.MarkLabel (dispose_try_block);
5834 Label[] labels = null;
5835 for (int i = 0; i < resume_points.Count; ++i) {
5836 ResumableStatement s = resume_points[i];
5837 Label ret = s.PrepareForDispose (ec, end_of_try);
5838 if (ret.Equals (end_of_try) && labels == null)
5840 if (labels == null) {
5841 labels = new Label[resume_points.Count];
5842 for (int j = 0; j < i; ++j)
5843 labels[j] = end_of_try;
5848 if (labels != null) {
5850 for (j = 1; j < labels.Length; ++j)
5851 if (!labels[0].Equals (labels[j]))
5853 bool emit_dispatcher = j < labels.Length;
5855 if (emit_dispatcher) {
5856 ec.Emit (OpCodes.Ldloc, pc);
5857 ec.EmitInt (first_resume_pc);
5858 ec.Emit (OpCodes.Sub);
5859 ec.Emit (OpCodes.Switch, labels);
5862 foreach (ResumableStatement s in resume_points)
5863 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5866 ec.MarkLabel (end_of_try);
5868 ec.BeginFinallyBlock ();
5870 if (finally_host != null) {
5871 var ce = new CallEmitter ();
5872 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5873 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5875 EmitFinallyBody (ec);
5878 ec.EndExceptionBlock ();
5881 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5883 var res = stmt.FlowAnalysis (fc);
5888 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5890 ec.BeginFinallyBlock ();
5894 public override Reachability MarkReachable (Reachability rc)
5896 base.MarkReachable (rc);
5897 return Statement.MarkReachable (rc);
5900 public override bool Resolve (BlockContext bc)
5904 parent = bc.CurrentTryBlock;
5905 bc.CurrentTryBlock = this;
5907 using (bc.Set (ResolveContext.Options.TryScope)) {
5908 ok = stmt.Resolve (bc);
5911 bc.CurrentTryBlock = parent;
5914 // Finally block inside iterator is called from MoveNext and
5915 // Dispose methods that means we need to lift the block into
5916 // newly created host method to emit the body only once. The
5917 // original block then simply calls the newly generated method.
5919 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5920 var b = stmt as Block;
5921 if (b != null && b.Explicit.HasYield) {
5922 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5926 return base.Resolve (bc) && ok;
5931 // Base class for blocks using exception handling
5933 public abstract class ExceptionStatement : ResumableStatement
5935 protected List<ResumableStatement> resume_points;
5936 protected int first_resume_pc;
5937 protected ExceptionStatement parent;
5939 protected ExceptionStatement (Location loc)
5944 protected virtual void EmitBeginException (EmitContext ec)
5946 ec.BeginExceptionBlock ();
5949 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5951 StateMachineInitializer state_machine = null;
5952 if (resume_points != null) {
5953 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5955 ec.EmitInt ((int) IteratorStorey.State.Running);
5956 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5959 EmitBeginException (ec);
5961 if (resume_points != null) {
5962 ec.MarkLabel (resume_point);
5964 // For normal control flow, we want to fall-through the Switch
5965 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5966 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5967 ec.EmitInt (first_resume_pc);
5968 ec.Emit (OpCodes.Sub);
5970 Label[] labels = new Label[resume_points.Count];
5971 for (int i = 0; i < resume_points.Count; ++i)
5972 labels[i] = resume_points[i].PrepareForEmit (ec);
5973 ec.Emit (OpCodes.Switch, labels);
5977 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5979 if (parent != null) {
5980 // TODO: MOVE to virtual TryCatch
5981 var tc = this as TryCatch;
5982 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5984 pc = parent.AddResumePoint (s, pc, stateMachine);
5986 pc = stateMachine.AddResumePoint (this);
5989 if (resume_points == null) {
5990 resume_points = new List<ResumableStatement> ();
5991 first_resume_pc = pc;
5994 if (pc != first_resume_pc + resume_points.Count)
5995 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5997 resume_points.Add (stmt);
6002 public class Lock : TryFinallyBlock
6005 TemporaryVariableReference expr_copy;
6006 TemporaryVariableReference lock_taken;
6008 public Lock (Expression expr, Statement stmt, Location loc)
6014 public Expression Expr {
6020 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6022 expr.FlowAnalysis (fc);
6023 return base.DoFlowAnalysis (fc);
6026 public override bool Resolve (BlockContext ec)
6028 expr = expr.Resolve (ec);
6032 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6033 ec.Report.Error (185, loc,
6034 "`{0}' is not a reference type as required by the lock statement",
6035 expr.Type.GetSignatureForError ());
6038 if (expr.Type.IsGenericParameter) {
6039 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6042 VariableReference lv = expr as VariableReference;
6045 locked = lv.IsLockedByStatement;
6046 lv.IsLockedByStatement = true;
6053 // Have to keep original lock value around to unlock same location
6054 // in the case of original value has changed or is null
6056 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6057 expr_copy.Resolve (ec);
6060 // Ensure Monitor methods are available
6062 if (ResolvePredefinedMethods (ec) > 1) {
6063 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6064 lock_taken.Resolve (ec);
6067 using (ec.Set (ResolveContext.Options.LockScope)) {
6072 lv.IsLockedByStatement = locked;
6078 protected override void EmitTryBodyPrepare (EmitContext ec)
6080 expr_copy.EmitAssign (ec, expr);
6082 if (lock_taken != null) {
6084 // Initialize ref variable
6086 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6089 // Monitor.Enter (expr_copy)
6091 expr_copy.Emit (ec);
6092 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6095 base.EmitTryBodyPrepare (ec);
6098 protected override void EmitTryBody (EmitContext ec)
6101 // Monitor.Enter (expr_copy, ref lock_taken)
6103 if (lock_taken != null) {
6104 expr_copy.Emit (ec);
6105 lock_taken.LocalInfo.CreateBuilder (ec);
6106 lock_taken.AddressOf (ec, AddressOp.Load);
6107 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6110 Statement.Emit (ec);
6113 public override void EmitFinallyBody (EmitContext ec)
6116 // if (lock_taken) Monitor.Exit (expr_copy)
6118 Label skip = ec.DefineLabel ();
6120 if (lock_taken != null) {
6121 lock_taken.Emit (ec);
6122 ec.Emit (OpCodes.Brfalse_S, skip);
6125 expr_copy.Emit (ec);
6126 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6128 ec.Emit (OpCodes.Call, m);
6130 ec.MarkLabel (skip);
6133 int ResolvePredefinedMethods (ResolveContext rc)
6135 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6136 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6140 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6144 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6148 protected override void CloneTo (CloneContext clonectx, Statement t)
6150 Lock target = (Lock) t;
6152 target.expr = expr.Clone (clonectx);
6153 target.stmt = Statement.Clone (clonectx);
6156 public override object Accept (StructuralVisitor visitor)
6158 return visitor.Visit (this);
6163 public class Unchecked : Statement {
6166 public Unchecked (Block b, Location loc)
6173 public override bool Resolve (BlockContext ec)
6175 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6176 return Block.Resolve (ec);
6179 protected override void DoEmit (EmitContext ec)
6181 using (ec.With (EmitContext.Options.CheckedScope, false))
6185 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6187 return Block.FlowAnalysis (fc);
6190 public override Reachability MarkReachable (Reachability rc)
6192 base.MarkReachable (rc);
6193 return Block.MarkReachable (rc);
6196 protected override void CloneTo (CloneContext clonectx, Statement t)
6198 Unchecked target = (Unchecked) t;
6200 target.Block = clonectx.LookupBlock (Block);
6203 public override object Accept (StructuralVisitor visitor)
6205 return visitor.Visit (this);
6209 public class Checked : Statement {
6212 public Checked (Block b, Location loc)
6215 b.Unchecked = false;
6219 public override bool Resolve (BlockContext ec)
6221 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6222 return Block.Resolve (ec);
6225 protected override void DoEmit (EmitContext ec)
6227 using (ec.With (EmitContext.Options.CheckedScope, true))
6231 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6233 return Block.FlowAnalysis (fc);
6236 public override Reachability MarkReachable (Reachability rc)
6238 base.MarkReachable (rc);
6239 return Block.MarkReachable (rc);
6242 protected override void CloneTo (CloneContext clonectx, Statement t)
6244 Checked target = (Checked) t;
6246 target.Block = clonectx.LookupBlock (Block);
6249 public override object Accept (StructuralVisitor visitor)
6251 return visitor.Visit (this);
6255 public class Unsafe : Statement {
6258 public Unsafe (Block b, Location loc)
6261 Block.Unsafe = true;
6265 public override bool Resolve (BlockContext ec)
6267 if (ec.CurrentIterator != null)
6268 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6270 using (ec.Set (ResolveContext.Options.UnsafeScope))
6271 return Block.Resolve (ec);
6274 protected override void DoEmit (EmitContext ec)
6279 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6281 return Block.FlowAnalysis (fc);
6284 public override Reachability MarkReachable (Reachability rc)
6286 base.MarkReachable (rc);
6287 return Block.MarkReachable (rc);
6290 protected override void CloneTo (CloneContext clonectx, Statement t)
6292 Unsafe target = (Unsafe) t;
6294 target.Block = clonectx.LookupBlock (Block);
6297 public override object Accept (StructuralVisitor visitor)
6299 return visitor.Visit (this);
6306 public class Fixed : Statement
6308 abstract class Emitter : ShimExpression
6310 protected LocalVariable vi;
6312 protected Emitter (Expression expr, LocalVariable li)
6318 public abstract void EmitExit (EmitContext ec);
6320 public override void FlowAnalysis (FlowAnalysisContext fc)
6322 expr.FlowAnalysis (fc);
6326 sealed class ExpressionEmitter : Emitter {
6327 public ExpressionEmitter (Expression converted, LocalVariable li)
6328 : base (converted, li)
6332 protected override Expression DoResolve (ResolveContext rc)
6334 throw new NotImplementedException ();
6337 public override void Emit (EmitContext ec) {
6339 // Store pointer in pinned location
6345 public override void EmitExit (EmitContext ec)
6348 ec.Emit (OpCodes.Conv_U);
6353 class StringEmitter : Emitter
6355 LocalVariable pinned_string;
6357 public StringEmitter (Expression expr, LocalVariable li)
6362 protected override Expression DoResolve (ResolveContext rc)
6364 pinned_string = new LocalVariable (vi.Block, "$pinned",
6365 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6367 pinned_string.Type = rc.BuiltinTypes.String;
6370 eclass = ExprClass.Variable;
6371 type = rc.BuiltinTypes.Int;
6375 public override void Emit (EmitContext ec)
6377 pinned_string.CreateBuilder (ec);
6380 pinned_string.EmitAssign (ec);
6382 // TODO: Should use Binary::Add
6383 pinned_string.Emit (ec);
6384 ec.Emit (OpCodes.Conv_I);
6386 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6390 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6391 //pe.InstanceExpression = pinned_string;
6392 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6394 ec.Emit (OpCodes.Add);
6398 public override void EmitExit (EmitContext ec)
6401 pinned_string.EmitAssign (ec);
6405 public class VariableDeclaration : BlockVariable
6407 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6412 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6414 if (!Variable.Type.IsPointer && li == Variable) {
6415 bc.Report.Error (209, TypeExpression.Location,
6416 "The type of locals declared in a fixed statement must be a pointer type");
6420 var res = initializer.Resolve (bc);
6427 var ac = res.Type as ArrayContainer;
6429 TypeSpec array_type = ac.Element;
6432 // Provided that array_type is unmanaged,
6434 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6437 Expression res_init;
6438 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6441 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6442 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6443 res = expr_variable.CreateReferenceExpression (bc, loc);
6447 // and T* is implicitly convertible to the
6448 // pointer type given in the fixed statement.
6450 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6452 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6453 if (converted == null)
6457 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6459 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6460 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6461 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6462 new NullLiteral (loc),
6465 converted = converted.Resolve (bc);
6467 return new ExpressionEmitter (converted, li);
6473 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6474 return new StringEmitter (res, li).Resolve (bc);
6477 // Case 3: fixed buffer
6478 if (res is FixedBufferPtr) {
6479 return new ExpressionEmitter (res, li);
6482 bool already_fixed = true;
6485 // Case 4: & object.
6487 Unary u = res as Unary;
6489 if (u.Oper == Unary.Operator.AddressOf) {
6490 IVariableReference vr = u.Expr as IVariableReference;
6491 if (vr == null || !vr.IsFixed) {
6492 already_fixed = false;
6495 } else if (initializer is Cast) {
6496 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6500 if (already_fixed) {
6501 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6504 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6505 return new ExpressionEmitter (res, li);
6510 VariableDeclaration decl;
6511 Statement statement;
6514 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6523 public Statement Statement {
6529 public BlockVariable Variables {
6537 public override bool Resolve (BlockContext bc)
6539 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6540 if (!decl.Resolve (bc))
6544 return statement.Resolve (bc);
6547 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6549 decl.FlowAnalysis (fc);
6550 return statement.FlowAnalysis (fc);
6553 protected override void DoEmit (EmitContext ec)
6555 decl.Variable.CreateBuilder (ec);
6556 decl.Initializer.Emit (ec);
6557 if (decl.Declarators != null) {
6558 foreach (var d in decl.Declarators) {
6559 d.Variable.CreateBuilder (ec);
6560 d.Initializer.Emit (ec);
6564 statement.Emit (ec);
6570 // Clear the pinned variable
6572 ((Emitter) decl.Initializer).EmitExit (ec);
6573 if (decl.Declarators != null) {
6574 foreach (var d in decl.Declarators) {
6575 ((Emitter)d.Initializer).EmitExit (ec);
6580 public override Reachability MarkReachable (Reachability rc)
6582 base.MarkReachable (rc);
6584 decl.MarkReachable (rc);
6586 rc = statement.MarkReachable (rc);
6588 // TODO: What if there is local exit?
6589 has_ret = rc.IsUnreachable;
6593 protected override void CloneTo (CloneContext clonectx, Statement t)
6595 Fixed target = (Fixed) t;
6597 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6598 target.statement = statement.Clone (clonectx);
6601 public override object Accept (StructuralVisitor visitor)
6603 return visitor.Visit (this);
6607 public class Catch : Statement
6609 class CatchVariableStore : Statement
6611 readonly Catch ctch;
6613 public CatchVariableStore (Catch ctch)
6618 protected override void CloneTo (CloneContext clonectx, Statement target)
6622 protected override void DoEmit (EmitContext ec)
6624 // Emits catch variable debug information inside correct block
6625 ctch.EmitCatchVariableStore (ec);
6628 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6634 class FilterStatement : Statement
6636 readonly Catch ctch;
6638 public FilterStatement (Catch ctch)
6643 protected override void CloneTo (CloneContext clonectx, Statement target)
6647 protected override void DoEmit (EmitContext ec)
6649 if (ctch.li != null) {
6650 if (ctch.hoisted_temp != null)
6651 ctch.hoisted_temp.Emit (ec);
6655 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6656 ec.Emit (OpCodes.Box, ctch.type);
6659 var expr_start = ec.DefineLabel ();
6660 var end = ec.DefineLabel ();
6662 ec.Emit (OpCodes.Brtrue_S, expr_start);
6664 ec.Emit (OpCodes.Br, end);
6665 ec.MarkLabel (expr_start);
6667 ctch.Filter.Emit (ec);
6670 ec.Emit (OpCodes.Endfilter);
6671 ec.BeginFilterHandler ();
6672 ec.Emit (OpCodes.Pop);
6675 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6677 ctch.Filter.FlowAnalysis (fc);
6681 public override bool Resolve (BlockContext bc)
6683 ctch.Filter = ctch.Filter.Resolve (bc);
6685 if (ctch.Filter != null) {
6686 if (ctch.Filter.ContainsEmitWithAwait ()) {
6687 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6690 var c = ctch.Filter as Constant;
6691 if (c != null && !c.IsDefaultValue) {
6692 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6700 ExplicitBlock block;
6702 FullNamedExpression type_expr;
6703 CompilerAssign assign;
6705 LocalTemporary hoisted_temp;
6707 public Catch (ExplicitBlock block, Location loc)
6715 public ExplicitBlock Block {
6721 public TypeSpec CatchType {
6727 public Expression Filter {
6731 public bool IsGeneral {
6733 return type_expr == null;
6737 public FullNamedExpression TypeExpression {
6746 public LocalVariable Variable {
6757 protected override void DoEmit (EmitContext ec)
6759 if (Filter != null) {
6760 ec.BeginExceptionFilterBlock ();
6761 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6763 if (Block.HasAwait) {
6764 Block.EmitScopeInitialization (ec);
6773 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6775 ec.BeginCatchBlock (CatchType);
6778 ec.Emit (OpCodes.Pop);
6780 if (Block.HasAwait) {
6782 EmitCatchVariableStore (ec);
6788 void EmitCatchVariableStore (EmitContext ec)
6790 li.CreateBuilder (ec);
6793 // For hoisted catch variable we have to use a temporary local variable
6794 // for captured variable initialization during storey setup because variable
6795 // needs to be on the stack after storey instance for stfld operation
6797 if (li.HoistedVariant != null) {
6798 hoisted_temp = new LocalTemporary (li.Type);
6799 hoisted_temp.Store (ec);
6801 // switch to assignment from temporary variable and not from top of the stack
6802 assign.UpdateSource (hoisted_temp);
6806 public override bool Resolve (BlockContext bc)
6808 using (bc.Set (ResolveContext.Options.CatchScope)) {
6809 if (type_expr == null) {
6810 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6811 if (!block.HasAwait || Filter != null)
6812 block.AddScopeStatement (new CatchVariableStore (this));
6814 Expression source = new EmptyExpression (li.Type);
6815 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6816 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6819 type = type_expr.ResolveAsType (bc);
6824 CreateExceptionVariable (type);
6826 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6827 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6828 } else if (li != null) {
6830 li.PrepareAssignmentAnalysis (bc);
6832 // source variable is at the top of the stack
6833 Expression source = new EmptyExpression (li.Type);
6834 if (li.Type.IsGenericParameter)
6835 source = new UnboxCast (source, li.Type);
6837 if (!block.HasAwait || Filter != null)
6838 block.AddScopeStatement (new CatchVariableStore (this));
6841 // Uses Location.Null to hide from symbol file
6843 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6844 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6848 if (Filter != null) {
6849 Block.AddScopeStatement (new FilterStatement (this));
6852 Block.SetCatchBlock ();
6853 return Block.Resolve (bc);
6857 bool CreateExceptionVariable (TypeSpec type)
6859 if (!Block.HasAwait)
6862 // TODO: Scan the block for rethrow expression
6863 //if (!Block.HasRethrow)
6866 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6870 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6872 if (li != null && !li.IsCompilerGenerated) {
6873 fc.SetVariableAssigned (li.VariableInfo, true);
6876 return block.FlowAnalysis (fc);
6879 public override Reachability MarkReachable (Reachability rc)
6881 base.MarkReachable (rc);
6883 var c = Filter as Constant;
6884 if (c != null && c.IsDefaultValue)
6885 return Reachability.CreateUnreachable ();
6887 return block.MarkReachable (rc);
6890 protected override void CloneTo (CloneContext clonectx, Statement t)
6892 Catch target = (Catch) t;
6894 if (type_expr != null)
6895 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6898 target.Filter = Filter.Clone (clonectx);
6900 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6904 public class TryFinally : TryFinallyBlock
6907 List<DefiniteAssignmentBitSet> try_exit_dat;
6908 List<Label> redirected_jumps;
6909 Label? start_fin_label;
6911 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6917 public ExplicitBlock FinallyBlock {
6923 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6925 if (try_exit_dat == null)
6926 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6928 try_exit_dat.Add (vector);
6931 public override bool Resolve (BlockContext bc)
6933 bool ok = base.Resolve (bc);
6935 fini.SetFinallyBlock ();
6936 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6937 ok &= fini.Resolve (bc);
6943 protected override void EmitBeginException (EmitContext ec)
6945 if (fini.HasAwait && stmt is TryCatch)
6946 ec.BeginExceptionBlock ();
6948 base.EmitBeginException (ec);
6951 protected override void EmitTryBody (EmitContext ec)
6953 if (fini.HasAwait) {
6954 if (ec.TryFinallyUnwind == null)
6955 ec.TryFinallyUnwind = new List<TryFinally> ();
6957 ec.TryFinallyUnwind.Add (this);
6960 if (stmt is TryCatch)
6961 ec.EndExceptionBlock ();
6963 ec.TryFinallyUnwind.Remove (this);
6965 if (start_fin_label != null)
6966 ec.MarkLabel (start_fin_label.Value);
6974 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6979 return base.EmitBeginFinallyBlock (ec);
6982 public override void EmitFinallyBody (EmitContext ec)
6984 if (!fini.HasAwait) {
6990 // Emits catch block like
6992 // catch (object temp) {
6993 // this.exception_field = temp;
6996 var type = ec.BuiltinTypes.Object;
6997 ec.BeginCatchBlock (type);
6999 var temp = ec.GetTemporaryLocal (type);
7000 ec.Emit (OpCodes.Stloc, temp);
7002 var exception_field = ec.GetTemporaryField (type);
7004 ec.Emit (OpCodes.Ldloc, temp);
7005 exception_field.EmitAssignFromStack (ec);
7007 ec.EndExceptionBlock ();
7009 ec.FreeTemporaryLocal (temp, type);
7014 // Emits exception rethrow
7016 // if (this.exception_field != null)
7017 // throw this.exception_field;
7019 exception_field.Emit (ec);
7020 var skip_throw = ec.DefineLabel ();
7021 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7022 exception_field.Emit (ec);
7023 ec.Emit (OpCodes.Throw);
7024 ec.MarkLabel (skip_throw);
7026 exception_field.IsAvailableForReuse = true;
7028 EmitUnwindFinallyTable (ec);
7031 bool IsParentBlock (Block block)
7033 for (Block b = fini; b != null; b = b.Parent) {
7041 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7044 if (labelBlock != null) {
7045 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7046 var fin = ec.TryFinallyUnwind [idx - 1];
7047 if (!fin.IsParentBlock (labelBlock))
7054 bool set_return_state = true;
7056 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7057 var fin = ec.TryFinallyUnwind [idx];
7058 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7061 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7062 set_return_state = false;
7064 if (fin.start_fin_label == null) {
7065 fin.start_fin_label = ec.DefineLabel ();
7068 label = fin.start_fin_label.Value;
7074 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7076 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7079 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7081 if (redirected_jumps == null) {
7082 redirected_jumps = new List<Label> ();
7084 // Add fallthrough label
7085 redirected_jumps.Add (ec.DefineLabel ());
7088 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7091 int index = redirected_jumps.IndexOf (label);
7093 redirected_jumps.Add (label);
7094 index = redirected_jumps.Count - 1;
7098 // Indicates we have captured exit jump
7100 if (setReturnState) {
7101 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7102 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7107 // Emits state table of jumps outside of try block and reload of return
7108 // value when try block returns value
7110 void EmitUnwindFinallyTable (EmitContext ec)
7112 if (redirected_jumps == null)
7115 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7116 initializer.HoistedReturnState.EmitLoad (ec);
7117 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7119 // Mark fallthrough label
7120 ec.MarkLabel (redirected_jumps [0]);
7123 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7125 var da = fc.BranchDefiniteAssignment ();
7127 var tf = fc.TryFinally;
7128 fc.TryFinally = this;
7130 var res_stmt = Statement.FlowAnalysis (fc);
7134 var try_da = fc.DefiniteAssignment;
7135 fc.DefiniteAssignment = da;
7137 var res_fin = fini.FlowAnalysis (fc);
7139 if (try_exit_dat != null) {
7141 // try block has global exit but we need to run definite assignment check
7142 // for parameter block out parameter after finally block because it's always
7143 // executed before exit
7145 foreach (var try_da_part in try_exit_dat)
7146 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7148 try_exit_dat = null;
7151 fc.DefiniteAssignment |= try_da;
7152 return res_stmt | res_fin;
7155 public override Reachability MarkReachable (Reachability rc)
7158 // Mark finally block first for any exit statement in try block
7159 // to know whether the code which follows finally is reachable
7161 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7164 protected override void CloneTo (CloneContext clonectx, Statement t)
7166 TryFinally target = (TryFinally) t;
7168 target.stmt = stmt.Clone (clonectx);
7170 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7173 public override object Accept (StructuralVisitor visitor)
7175 return visitor.Visit (this);
7179 public class TryCatch : ExceptionStatement
7182 List<Catch> clauses;
7183 readonly bool inside_try_finally;
7184 List<Catch> catch_sm;
7186 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7190 this.clauses = catch_clauses;
7191 this.inside_try_finally = inside_try_finally;
7194 public List<Catch> Clauses {
7200 public bool IsTryCatchFinally {
7202 return inside_try_finally;
7206 public override bool Resolve (BlockContext bc)
7210 using (bc.Set (ResolveContext.Options.TryScope)) {
7211 parent = bc.CurrentTryBlock;
7213 if (IsTryCatchFinally) {
7214 ok = Block.Resolve (bc);
7216 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7217 bc.CurrentTryBlock = this;
7218 ok = Block.Resolve (bc);
7219 bc.CurrentTryBlock = parent;
7224 for (int i = 0; i < clauses.Count; ++i) {
7227 ok &= c.Resolve (bc);
7229 if (c.Block.HasAwait) {
7230 if (catch_sm == null)
7231 catch_sm = new List<Catch> ();
7236 if (c.Filter != null)
7239 TypeSpec resolved_type = c.CatchType;
7240 if (resolved_type == null)
7243 for (int ii = 0; ii < clauses.Count; ++ii) {
7247 if (clauses[ii].Filter != null)
7250 if (clauses[ii].IsGeneral) {
7251 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7254 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7257 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7260 bc.Report.Warning (1058, 1, c.loc,
7261 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7269 var ct = clauses[ii].CatchType;
7273 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7274 bc.Report.Error (160, c.loc,
7275 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7276 ct.GetSignatureForError ());
7282 return base.Resolve (bc) && ok;
7285 protected sealed override void DoEmit (EmitContext ec)
7287 if (!inside_try_finally)
7288 EmitTryBodyPrepare (ec);
7292 LocalBuilder state_variable = null;
7293 foreach (Catch c in clauses) {
7296 if (catch_sm != null) {
7297 if (state_variable == null) {
7299 // Cannot reuse temp variable because non-catch path assumes the value is 0
7300 // which may not be true for reused local variable
7302 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7305 var index = catch_sm.IndexOf (c);
7309 ec.EmitInt (index + 1);
7310 ec.Emit (OpCodes.Stloc, state_variable);
7314 if (!inside_try_finally)
7315 ec.EndExceptionBlock ();
7317 if (state_variable != null) {
7318 ec.Emit (OpCodes.Ldloc, state_variable);
7320 var labels = new Label [catch_sm.Count + 1];
7321 for (int i = 0; i < labels.Length; ++i) {
7322 labels [i] = ec.DefineLabel ();
7325 var end = ec.DefineLabel ();
7326 ec.Emit (OpCodes.Switch, labels);
7328 // 0 value is default label
7329 ec.MarkLabel (labels [0]);
7330 ec.Emit (OpCodes.Br, end);
7332 var atv = ec.AsyncThrowVariable;
7334 for (int i = 0; i < catch_sm.Count; ++i) {
7335 if (c != null && c.Block.HasReachableClosingBrace)
7336 ec.Emit (OpCodes.Br, end);
7338 ec.MarkLabel (labels [i + 1]);
7340 ec.AsyncThrowVariable = c.Variable;
7343 ec.AsyncThrowVariable = atv;
7349 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7351 var start_fc = fc.BranchDefiniteAssignment ();
7352 var res = Block.FlowAnalysis (fc);
7354 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7356 foreach (var c in clauses) {
7357 fc.BranchDefiniteAssignment (start_fc);
7358 if (!c.FlowAnalysis (fc)) {
7360 try_fc = fc.DefiniteAssignment;
7362 try_fc &= fc.DefiniteAssignment;
7368 fc.DefiniteAssignment = try_fc ?? start_fc;
7373 public override Reachability MarkReachable (Reachability rc)
7375 if (rc.IsUnreachable)
7378 base.MarkReachable (rc);
7380 var tc_rc = Block.MarkReachable (rc);
7382 foreach (var c in clauses)
7383 tc_rc &= c.MarkReachable (rc);
7388 protected override void CloneTo (CloneContext clonectx, Statement t)
7390 TryCatch target = (TryCatch) t;
7392 target.Block = clonectx.LookupBlock (Block);
7393 if (clauses != null){
7394 target.clauses = new List<Catch> ();
7395 foreach (Catch c in clauses)
7396 target.clauses.Add ((Catch) c.Clone (clonectx));
7400 public override object Accept (StructuralVisitor visitor)
7402 return visitor.Visit (this);
7406 public class Using : TryFinallyBlock
7408 public class VariableDeclaration : BlockVariable
7410 Statement dispose_call;
7412 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7417 public VariableDeclaration (LocalVariable li, Location loc)
7424 public VariableDeclaration (Expression expr)
7427 loc = expr.Location;
7433 public bool IsNested { get; private set; }
7437 public void EmitDispose (EmitContext ec)
7439 dispose_call.Emit (ec);
7442 public override bool Resolve (BlockContext bc)
7447 return base.Resolve (bc, false);
7450 public Expression ResolveExpression (BlockContext bc)
7452 var e = Initializer.Resolve (bc);
7456 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7457 Initializer = ResolveInitializer (bc, Variable, e);
7461 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7463 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7464 initializer = initializer.Resolve (bc);
7465 if (initializer == null)
7468 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7469 Arguments args = new Arguments (1);
7470 args.Add (new Argument (initializer));
7471 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7472 if (initializer == null)
7475 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7476 dispose_call = CreateDisposeCall (bc, var);
7477 dispose_call.Resolve (bc);
7479 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7482 if (li == Variable) {
7483 CheckIDiposableConversion (bc, li, initializer);
7484 dispose_call = CreateDisposeCall (bc, li);
7485 dispose_call.Resolve (bc);
7488 return base.ResolveInitializer (bc, li, initializer);
7491 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7495 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7496 if (type.IsNullableType) {
7497 // it's handled in CreateDisposeCall
7501 if (type != InternalType.ErrorType) {
7502 bc.Report.SymbolRelatedToPreviousError (type);
7503 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7504 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7505 type.GetSignatureForError ());
7512 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7514 var target = bc.BuiltinTypes.IDisposable;
7515 var tp = type as TypeParameterSpec;
7517 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7519 return type.ImplementsInterface (target, false);
7522 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7524 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7526 var loc = lv.Location;
7528 var idt = bc.BuiltinTypes.IDisposable;
7529 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7531 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7532 dispose_mg.InstanceExpression = type.IsNullableType ?
7533 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7537 // Hide it from symbol file via null location
7539 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7541 // Add conditional call when disposing possible null variable
7542 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7543 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7548 public void ResolveDeclaratorInitializer (BlockContext bc)
7550 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7553 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7555 for (int i = declarators.Count - 1; i >= 0; --i) {
7556 var d = declarators [i];
7557 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7558 vd.Initializer = d.Initializer;
7560 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7561 vd.dispose_call.Resolve (bc);
7563 stmt = new Using (vd, stmt, d.Variable.Location);
7570 public override object Accept (StructuralVisitor visitor)
7572 return visitor.Visit (this);
7576 VariableDeclaration decl;
7578 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7584 public Using (Expression expr, Statement stmt, Location loc)
7587 this.decl = new VariableDeclaration (expr);
7592 public Expression Expr {
7594 return decl.Variable == null ? decl.Initializer : null;
7598 public BlockVariable Variables {
7606 public override void Emit (EmitContext ec)
7609 // Don't emit sequence point it will be set on variable declaration
7614 protected override void EmitTryBodyPrepare (EmitContext ec)
7617 base.EmitTryBodyPrepare (ec);
7620 protected override void EmitTryBody (EmitContext ec)
7625 public override void EmitFinallyBody (EmitContext ec)
7627 decl.EmitDispose (ec);
7630 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7632 decl.FlowAnalysis (fc);
7633 return stmt.FlowAnalysis (fc);
7636 public override Reachability MarkReachable (Reachability rc)
7638 decl.MarkReachable (rc);
7639 return base.MarkReachable (rc);
7642 public override bool Resolve (BlockContext ec)
7644 VariableReference vr;
7645 bool vr_locked = false;
7647 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7648 if (decl.Variable == null) {
7649 vr = decl.ResolveExpression (ec) as VariableReference;
7651 vr_locked = vr.IsLockedByStatement;
7652 vr.IsLockedByStatement = true;
7655 if (decl.IsNested) {
7656 decl.ResolveDeclaratorInitializer (ec);
7658 if (!decl.Resolve (ec))
7661 if (decl.Declarators != null) {
7662 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7670 var ok = base.Resolve (ec);
7673 vr.IsLockedByStatement = vr_locked;
7678 protected override void CloneTo (CloneContext clonectx, Statement t)
7680 Using target = (Using) t;
7682 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7683 target.stmt = stmt.Clone (clonectx);
7686 public override object Accept (StructuralVisitor visitor)
7688 return visitor.Visit (this);
7693 /// Implementation of the foreach C# statement
7695 public class Foreach : LoopStatement
7697 abstract class IteratorStatement : Statement
7699 protected readonly Foreach for_each;
7701 protected IteratorStatement (Foreach @foreach)
7703 this.for_each = @foreach;
7704 this.loc = @foreach.expr.Location;
7707 protected override void CloneTo (CloneContext clonectx, Statement target)
7709 throw new NotImplementedException ();
7712 public override void Emit (EmitContext ec)
7714 if (ec.EmitAccurateDebugInfo) {
7715 ec.Emit (OpCodes.Nop);
7721 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7723 throw new NotImplementedException ();
7727 sealed class ArrayForeach : IteratorStatement
7729 TemporaryVariableReference[] lengths;
7730 Expression [] length_exprs;
7731 StatementExpression[] counter;
7732 TemporaryVariableReference[] variables;
7734 TemporaryVariableReference copy;
7736 public ArrayForeach (Foreach @foreach, int rank)
7739 counter = new StatementExpression[rank];
7740 variables = new TemporaryVariableReference[rank];
7741 length_exprs = new Expression [rank];
7744 // Only use temporary length variables when dealing with
7745 // multi-dimensional arrays
7748 lengths = new TemporaryVariableReference [rank];
7751 public override bool Resolve (BlockContext ec)
7753 Block variables_block = for_each.variable.Block;
7754 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7757 int rank = length_exprs.Length;
7758 Arguments list = new Arguments (rank);
7759 for (int i = 0; i < rank; i++) {
7760 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7762 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7763 counter[i].Resolve (ec);
7766 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7768 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7769 lengths[i].Resolve (ec);
7771 Arguments args = new Arguments (1);
7772 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7773 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7776 list.Add (new Argument (v));
7779 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7784 if (for_each.type is VarExpr) {
7785 // Infer implicitly typed local variable from foreach array type
7786 var_type = access.Type;
7788 var_type = for_each.type.ResolveAsType (ec);
7790 if (var_type == null)
7793 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7798 for_each.variable.Type = var_type;
7800 var prev_block = ec.CurrentBlock;
7801 ec.CurrentBlock = variables_block;
7802 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7803 ec.CurrentBlock = prev_block;
7805 if (variable_ref == null)
7808 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7810 return for_each.body.Resolve (ec);
7813 protected override void DoEmit (EmitContext ec)
7815 copy.EmitAssign (ec, for_each.expr);
7817 int rank = length_exprs.Length;
7818 Label[] test = new Label [rank];
7819 Label[] loop = new Label [rank];
7821 for (int i = 0; i < rank; i++) {
7822 test [i] = ec.DefineLabel ();
7823 loop [i] = ec.DefineLabel ();
7825 if (lengths != null)
7826 lengths [i].EmitAssign (ec, length_exprs [i]);
7829 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7830 for (int i = 0; i < rank; i++) {
7831 variables [i].EmitAssign (ec, zero);
7833 ec.Emit (OpCodes.Br, test [i]);
7834 ec.MarkLabel (loop [i]);
7837 for_each.body.Emit (ec);
7839 ec.MarkLabel (ec.LoopBegin);
7840 ec.Mark (for_each.expr.Location);
7842 for (int i = rank - 1; i >= 0; i--){
7843 counter [i].Emit (ec);
7845 ec.MarkLabel (test [i]);
7846 variables [i].Emit (ec);
7848 if (lengths != null)
7849 lengths [i].Emit (ec);
7851 length_exprs [i].Emit (ec);
7853 ec.Emit (OpCodes.Blt, loop [i]);
7856 ec.MarkLabel (ec.LoopEnd);
7860 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7862 class RuntimeDispose : Using.VariableDeclaration
7864 public RuntimeDispose (LocalVariable lv, Location loc)
7870 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7872 // Defered to runtime check
7875 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7877 var idt = bc.BuiltinTypes.IDisposable;
7880 // Fabricates code like
7882 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7885 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7887 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7888 dispose_variable.CreateReferenceExpression (bc, loc),
7889 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7890 loc), new NullLiteral (loc));
7892 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7894 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7895 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7897 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7898 return new If (idisaposable_test, dispose, loc);
7902 LocalVariable variable;
7904 Statement statement;
7905 ExpressionStatement init;
7906 TemporaryVariableReference enumerator_variable;
7907 bool ambiguous_getenumerator_name;
7909 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7912 this.variable = var;
7916 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7918 rc.Report.SymbolRelatedToPreviousError (enumerator);
7919 rc.Report.Error (202, loc,
7920 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7921 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7924 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7927 // Option 1: Try to match by name GetEnumerator first
7929 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7930 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7932 var mg = mexpr as MethodGroupExpr;
7934 mg.InstanceExpression = expr;
7935 Arguments args = new Arguments (0);
7936 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7938 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7939 if (ambiguous_getenumerator_name)
7942 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7948 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7951 PredefinedMember<MethodSpec> iface_candidate = null;
7952 var ptypes = rc.Module.PredefinedTypes;
7953 var gen_ienumerable = ptypes.IEnumerableGeneric;
7954 if (!gen_ienumerable.Define ())
7955 gen_ienumerable = null;
7957 var ifaces = t.Interfaces;
7958 if (ifaces != null) {
7959 foreach (var iface in ifaces) {
7960 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7961 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7962 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7963 rc.Report.Error (1640, loc,
7964 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7965 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7970 // TODO: Cache this somehow
7971 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7972 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7977 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7978 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7983 if (iface_candidate == null) {
7984 if (expr.Type != InternalType.ErrorType) {
7985 rc.Report.Error (1579, loc,
7986 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7987 expr.Type.GetSignatureForError (), "GetEnumerator");
7993 var method = iface_candidate.Resolve (loc);
7997 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7998 mg.InstanceExpression = expr;
8002 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8004 var ms = MemberCache.FindMember (enumerator.ReturnType,
8005 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8006 BindingRestriction.InstanceOnly) as MethodSpec;
8008 if (ms == null || !ms.IsPublic) {
8009 Error_WrongEnumerator (rc, enumerator);
8013 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8016 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8018 var ps = MemberCache.FindMember (enumerator.ReturnType,
8019 MemberFilter.Property ("Current", null),
8020 BindingRestriction.InstanceOnly) as PropertySpec;
8022 if (ps == null || !ps.IsPublic) {
8023 Error_WrongEnumerator (rc, enumerator);
8030 public override bool Resolve (BlockContext ec)
8032 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8035 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8036 } else if (expr.Type.IsNullableType) {
8037 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8040 var get_enumerator_mg = ResolveGetEnumerator (ec);
8041 if (get_enumerator_mg == null) {
8045 var get_enumerator = get_enumerator_mg.BestCandidate;
8046 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8047 enumerator_variable.Resolve (ec);
8049 // Prepare bool MoveNext ()
8050 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8051 if (move_next_mg == null) {
8055 move_next_mg.InstanceExpression = enumerator_variable;
8057 // Prepare ~T~ Current { get; }
8058 var current_prop = ResolveCurrent (ec, get_enumerator);
8059 if (current_prop == null) {
8063 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8064 if (current_pe == null)
8067 VarExpr ve = for_each.type as VarExpr;
8071 // Source type is dynamic, set element type to dynamic too
8072 variable.Type = ec.BuiltinTypes.Dynamic;
8074 // Infer implicitly typed local variable from foreach enumerable type
8075 variable.Type = current_pe.Type;
8079 // Explicit cast of dynamic collection elements has to be done at runtime
8080 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8083 variable.Type = for_each.type.ResolveAsType (ec);
8085 if (variable.Type == null)
8088 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8089 if (current_pe == null)
8093 var prev_block = ec.CurrentBlock;
8094 ec.CurrentBlock = for_each.variable.Block;
8095 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8096 ec.CurrentBlock = prev_block;
8097 if (variable_ref == null)
8100 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8102 var init = new Invocation.Predefined (get_enumerator_mg, null);
8104 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8105 for_each.body, Location.Null);
8107 var enum_type = enumerator_variable.Type;
8110 // Add Dispose method call when enumerator can be IDisposable
8112 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8113 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8115 // Runtime Dispose check
8117 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8118 vd.Initializer = init;
8119 statement = new Using (vd, statement, Location.Null);
8122 // No Dispose call needed
8124 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8125 this.init.Resolve (ec);
8129 // Static Dispose check
8131 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8132 vd.Initializer = init;
8133 statement = new Using (vd, statement, Location.Null);
8136 return statement.Resolve (ec);
8139 protected override void DoEmit (EmitContext ec)
8141 enumerator_variable.LocalInfo.CreateBuilder (ec);
8144 init.EmitStatement (ec);
8146 statement.Emit (ec);
8149 #region IErrorHandler Members
8151 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8153 ec.Report.SymbolRelatedToPreviousError (best);
8154 ec.Report.Warning (278, 2, expr.Location,
8155 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8156 expr.Type.GetSignatureForError (), "enumerable",
8157 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8159 ambiguous_getenumerator_name = true;
8163 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8168 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8173 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8182 LocalVariable variable;
8186 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8190 this.variable = var;
8196 public Expression Expr {
8197 get { return expr; }
8200 public Expression TypeExpression {
8201 get { return type; }
8204 public LocalVariable Variable {
8205 get { return variable; }
8208 public override Reachability MarkReachable (Reachability rc)
8210 base.MarkReachable (rc);
8212 body.MarkReachable (rc);
8217 public override bool Resolve (BlockContext ec)
8219 expr = expr.Resolve (ec);
8224 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8228 body.AddStatement (Statement);
8230 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8231 Statement = new ArrayForeach (this, 1);
8232 } else if (expr.Type is ArrayContainer) {
8233 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8235 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8236 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8237 expr.ExprClassName);
8241 Statement = new CollectionForeach (this, variable, expr);
8244 return base.Resolve (ec);
8247 protected override void DoEmit (EmitContext ec)
8249 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8250 ec.LoopBegin = ec.DefineLabel ();
8251 ec.LoopEnd = ec.DefineLabel ();
8253 if (!(Statement is Block))
8254 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8256 variable.CreateBuilder (ec);
8258 Statement.Emit (ec);
8260 if (!(Statement is Block))
8263 ec.LoopBegin = old_begin;
8264 ec.LoopEnd = old_end;
8267 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8269 expr.FlowAnalysis (fc);
8271 var da = fc.BranchDefiniteAssignment ();
8272 body.FlowAnalysis (fc);
8273 fc.DefiniteAssignment = da;
8277 protected override void CloneTo (CloneContext clonectx, Statement t)
8279 Foreach target = (Foreach) t;
8281 target.type = type.Clone (clonectx);
8282 target.expr = expr.Clone (clonectx);
8283 target.body = (Block) body.Clone (clonectx);
8284 target.Statement = Statement.Clone (clonectx);
8287 public override object Accept (StructuralVisitor visitor)
8289 return visitor.Visit (this);
8293 class SentinelStatement: Statement
8295 protected override void CloneTo (CloneContext clonectx, Statement target)
8299 protected override void DoEmit (EmitContext ec)
8301 var l = ec.DefineLabel ();
8303 ec.Emit (OpCodes.Br_S, l);
8306 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8308 throw new NotImplementedException ();