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 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1297 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1298 async_return.EmitAssign (ec, expr, false, false);
1303 if (ec.TryFinallyUnwind != null)
1304 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1307 ec.Emit (OpCodes.Leave, exit_label);
1314 if (unwind_protect || ec.EmitAccurateDebugInfo)
1315 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1318 if (unwind_protect) {
1319 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1320 } else if (ec.EmitAccurateDebugInfo) {
1321 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1323 ec.Emit (OpCodes.Ret);
1327 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1330 expr.FlowAnalysis (fc);
1332 base.DoFlowAnalysis (fc);
1336 void Error_ReturnFromIterator (ResolveContext rc)
1338 rc.Report.Error (1622, loc,
1339 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1342 public override Reachability MarkReachable (Reachability rc)
1344 base.MarkReachable (rc);
1345 return Reachability.CreateUnreachable ();
1348 protected override void CloneTo (CloneContext clonectx, Statement t)
1350 Return target = (Return) t;
1351 // It's null for simple return;
1353 target.expr = expr.Clone (clonectx);
1356 public override object Accept (StructuralVisitor visitor)
1358 return visitor.Visit (this);
1362 public class Goto : ExitStatement
1365 LabeledStatement label;
1366 TryFinally try_finally;
1368 public Goto (string label, Location l)
1374 public string Target {
1375 get { return target; }
1378 protected override bool IsLocalExit {
1384 protected override bool DoResolve (BlockContext bc)
1386 label = bc.CurrentBlock.LookupLabel (target);
1387 if (label == null) {
1388 Error_UnknownLabel (bc, target, loc);
1392 try_finally = bc.CurrentTryBlock as TryFinally;
1394 CheckExitBoundaries (bc, label.Block);
1399 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1401 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1405 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1407 // Goto to unreachable label
1411 if (fc.AddReachedLabel (label))
1414 label.Block.ScanGotoJump (label, fc);
1418 public override Reachability MarkReachable (Reachability rc)
1420 if (rc.IsUnreachable)
1423 base.MarkReachable (rc);
1425 if (try_finally != null) {
1426 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1427 label.AddGotoReference (rc);
1432 label.AddGotoReference (rc);
1435 return Reachability.CreateUnreachable ();
1438 protected override void CloneTo (CloneContext clonectx, Statement target)
1443 protected override void DoEmit (EmitContext ec)
1445 // This should only happen for goto from try block to unrechable label
1449 Label l = label.LabelTarget (ec);
1451 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1452 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1453 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1456 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1459 bool IsLeavingFinally (Block labelBlock)
1461 var b = try_finally.Statement as Block;
1463 if (b == labelBlock)
1472 public override object Accept (StructuralVisitor visitor)
1474 return visitor.Visit (this);
1478 public class LabeledStatement : Statement {
1485 public LabeledStatement (string name, Block block, Location l)
1492 public Label LabelTarget (EmitContext ec)
1497 label = ec.DefineLabel ();
1502 public Block Block {
1508 public string Name {
1509 get { return name; }
1512 protected override void CloneTo (CloneContext clonectx, Statement target)
1514 var t = (LabeledStatement) target;
1516 t.block = clonectx.RemapBlockCopy (block);
1519 public override bool Resolve (BlockContext bc)
1524 protected override void DoEmit (EmitContext ec)
1527 ec.MarkLabel (label);
1530 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1533 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1539 public override Reachability MarkReachable (Reachability rc)
1541 base.MarkReachable (rc);
1544 rc = new Reachability ();
1549 public void AddGotoReference (Reachability rc)
1557 block.ScanGotoJump (this);
1560 public override object Accept (StructuralVisitor visitor)
1562 return visitor.Visit (this);
1568 /// `goto default' statement
1570 public class GotoDefault : SwitchGoto
1572 public GotoDefault (Location l)
1577 public override bool Resolve (BlockContext bc)
1579 if (bc.Switch == null) {
1580 Error_GotoCaseRequiresSwitchBlock (bc);
1584 bc.Switch.RegisterGotoCase (null, null);
1590 protected override void DoEmit (EmitContext ec)
1592 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1595 public override Reachability MarkReachable (Reachability rc)
1597 if (!rc.IsUnreachable) {
1598 var label = switch_statement.DefaultLabel;
1599 if (label.IsUnreachable) {
1600 label.MarkReachable (rc);
1601 switch_statement.Block.ScanGotoJump (label);
1605 return base.MarkReachable (rc);
1608 public override object Accept (StructuralVisitor visitor)
1610 return visitor.Visit (this);
1615 /// `goto case' statement
1617 public class GotoCase : SwitchGoto
1621 public GotoCase (Expression e, Location l)
1627 public Expression Expr {
1633 public SwitchLabel Label { get; set; }
1635 public override bool Resolve (BlockContext ec)
1637 if (ec.Switch == null) {
1638 Error_GotoCaseRequiresSwitchBlock (ec);
1642 Constant c = expr.ResolveLabelConstant (ec);
1648 if (ec.Switch.IsNullable && c is NullLiteral) {
1651 TypeSpec type = ec.Switch.SwitchType;
1652 res = c.Reduce (ec, type);
1654 c.Error_ValueCannotBeConverted (ec, type, true);
1658 if (!Convert.ImplicitStandardConversionExists (c, type))
1659 ec.Report.Warning (469, 2, loc,
1660 "The `goto case' value is not implicitly convertible to type `{0}'",
1661 type.GetSignatureForError ());
1665 ec.Switch.RegisterGotoCase (this, res);
1672 protected override void DoEmit (EmitContext ec)
1674 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1677 protected override void CloneTo (CloneContext clonectx, Statement t)
1679 GotoCase target = (GotoCase) t;
1681 target.expr = expr.Clone (clonectx);
1684 public override Reachability MarkReachable (Reachability rc)
1686 if (!rc.IsUnreachable) {
1687 var label = switch_statement.FindLabel ((Constant) expr);
1688 if (label.IsUnreachable) {
1689 label.MarkReachable (rc);
1690 switch_statement.Block.ScanGotoJump (label);
1694 return base.MarkReachable (rc);
1697 public override object Accept (StructuralVisitor visitor)
1699 return visitor.Visit (this);
1703 public abstract class SwitchGoto : Statement
1705 protected bool unwind_protect;
1706 protected Switch switch_statement;
1708 protected SwitchGoto (Location loc)
1713 protected override void CloneTo (CloneContext clonectx, Statement target)
1718 public override bool Resolve (BlockContext bc)
1720 CheckExitBoundaries (bc, bc.Switch.Block);
1722 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1723 switch_statement = bc.Switch;
1728 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1733 public override Reachability MarkReachable (Reachability rc)
1735 base.MarkReachable (rc);
1736 return Reachability.CreateUnreachable ();
1739 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1741 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1745 public class Throw : Statement {
1748 public Throw (Expression expr, Location l)
1754 public Expression Expr {
1760 public override bool Resolve (BlockContext ec)
1763 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1764 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1765 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1766 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1767 if (b.IsFinallyBlock) {
1768 ec.Report.Error (724, loc,
1769 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1778 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1783 var et = ec.BuiltinTypes.Exception;
1784 if (Convert.ImplicitConversionExists (ec, expr, et))
1785 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1787 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1792 protected override void DoEmit (EmitContext ec)
1795 var atv = ec.AsyncThrowVariable;
1797 if (atv.HoistedVariant != null) {
1798 atv.HoistedVariant.Emit (ec);
1803 ec.Emit (OpCodes.Throw);
1805 ec.Emit (OpCodes.Rethrow);
1810 ec.Emit (OpCodes.Throw);
1814 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1817 expr.FlowAnalysis (fc);
1822 public override Reachability MarkReachable (Reachability rc)
1824 base.MarkReachable (rc);
1825 return Reachability.CreateUnreachable ();
1828 protected override void CloneTo (CloneContext clonectx, Statement t)
1830 Throw target = (Throw) t;
1833 target.expr = expr.Clone (clonectx);
1836 public override object Accept (StructuralVisitor visitor)
1838 return visitor.Visit (this);
1842 public class Break : LocalExitStatement
1844 public Break (Location l)
1849 public override object Accept (StructuralVisitor visitor)
1851 return visitor.Visit (this);
1854 protected override void DoEmit (EmitContext ec)
1858 if (ec.TryFinallyUnwind != null) {
1859 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1860 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1863 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1866 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1868 enclosing_loop.AddEndDefiniteAssignment (fc);
1872 protected override bool DoResolve (BlockContext bc)
1874 enclosing_loop = bc.EnclosingLoopOrSwitch;
1875 return base.DoResolve (bc);
1878 public override Reachability MarkReachable (Reachability rc)
1880 base.MarkReachable (rc);
1882 if (!rc.IsUnreachable)
1883 enclosing_loop.SetEndReachable ();
1885 return Reachability.CreateUnreachable ();
1889 public class Continue : LocalExitStatement
1891 public Continue (Location l)
1896 public override object Accept (StructuralVisitor visitor)
1898 return visitor.Visit (this);
1902 protected override void DoEmit (EmitContext ec)
1904 var l = ec.LoopBegin;
1906 if (ec.TryFinallyUnwind != null) {
1907 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1908 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1911 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1914 protected override bool DoResolve (BlockContext bc)
1916 enclosing_loop = bc.EnclosingLoop;
1917 return base.DoResolve (bc);
1920 public override Reachability MarkReachable (Reachability rc)
1922 base.MarkReachable (rc);
1924 if (!rc.IsUnreachable)
1925 enclosing_loop.SetIteratorReachable ();
1927 return Reachability.CreateUnreachable ();
1931 public abstract class LocalExitStatement : ExitStatement
1933 protected LoopStatement enclosing_loop;
1935 protected LocalExitStatement (Location loc)
1940 protected override bool IsLocalExit {
1946 protected override void CloneTo (CloneContext clonectx, Statement t)
1951 protected override bool DoResolve (BlockContext bc)
1953 if (enclosing_loop == null) {
1954 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1958 var block = enclosing_loop.Statement as Block;
1960 // Don't need to do extra checks for simple statements loops
1961 if (block != null) {
1962 CheckExitBoundaries (bc, block);
1969 public interface ILocalVariable
1971 void Emit (EmitContext ec);
1972 void EmitAssign (EmitContext ec);
1973 void EmitAddressOf (EmitContext ec);
1976 public interface INamedBlockVariable
1978 Block Block { get; }
1979 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1980 bool IsDeclared { get; }
1981 bool IsParameter { get; }
1982 Location Location { get; }
1985 public class BlockVariableDeclarator
1988 Expression initializer;
1990 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1992 if (li.Type != null)
1993 throw new ArgumentException ("Expected null variable type");
1996 this.initializer = initializer;
2001 public LocalVariable Variable {
2007 public Expression Initializer {
2012 initializer = value;
2018 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2020 var t = (BlockVariableDeclarator) MemberwiseClone ();
2021 if (initializer != null)
2022 t.initializer = initializer.Clone (cloneCtx);
2028 public class BlockVariable : Statement
2030 Expression initializer;
2031 protected FullNamedExpression type_expr;
2032 protected LocalVariable li;
2033 protected List<BlockVariableDeclarator> declarators;
2036 public BlockVariable (FullNamedExpression type, LocalVariable li)
2038 this.type_expr = type;
2040 this.loc = type_expr.Location;
2043 protected BlockVariable (LocalVariable li)
2050 public List<BlockVariableDeclarator> Declarators {
2056 public Expression Initializer {
2061 initializer = value;
2065 public FullNamedExpression TypeExpression {
2071 public LocalVariable Variable {
2079 public void AddDeclarator (BlockVariableDeclarator decl)
2081 if (declarators == null)
2082 declarators = new List<BlockVariableDeclarator> ();
2084 declarators.Add (decl);
2087 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2089 if (bc.Report.Errors != 0)
2092 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2094 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2095 new MemberName (li.Name, li.Location), null);
2097 container.AddField (f);
2100 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2104 public override bool Resolve (BlockContext bc)
2106 return Resolve (bc, true);
2109 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2111 if (type == null && !li.IsCompilerGenerated) {
2112 var vexpr = type_expr as VarExpr;
2115 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2116 // same name exists or as a keyword when no type was found
2118 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2119 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2120 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2123 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2127 if (li.IsConstant) {
2128 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2132 if (Initializer == null) {
2133 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2137 if (declarators != null) {
2138 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2142 Initializer = Initializer.Resolve (bc);
2143 if (Initializer != null) {
2144 ((VarExpr) type_expr).InferType (bc, Initializer);
2145 type = type_expr.Type;
2147 // Set error type to indicate the var was placed correctly but could
2150 // var a = missing ();
2152 type = InternalType.ErrorType;
2157 type = type_expr.ResolveAsType (bc);
2161 if (li.IsConstant && !type.IsConstantCompatible) {
2162 Const.Error_InvalidConstantType (type, loc, bc.Report);
2167 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2172 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2174 CreateEvaluatorVariable (bc, li);
2175 } else if (type != InternalType.ErrorType) {
2176 li.PrepareAssignmentAnalysis (bc);
2179 if (initializer != null) {
2180 initializer = ResolveInitializer (bc, li, initializer);
2181 // li.Variable.DefinitelyAssigned
2184 if (declarators != null) {
2185 foreach (var d in declarators) {
2186 d.Variable.Type = li.Type;
2188 CreateEvaluatorVariable (bc, d.Variable);
2189 } else if (type != InternalType.ErrorType) {
2190 d.Variable.PrepareAssignmentAnalysis (bc);
2193 if (d.Initializer != null && resolveDeclaratorInitializers) {
2194 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2195 // d.Variable.DefinitelyAssigned
2203 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2205 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2206 return a.ResolveStatement (bc);
2209 protected override void DoEmit (EmitContext ec)
2211 li.CreateBuilder (ec);
2213 if (Initializer != null && !IsUnreachable)
2214 ((ExpressionStatement) Initializer).EmitStatement (ec);
2216 if (declarators != null) {
2217 foreach (var d in declarators) {
2218 d.Variable.CreateBuilder (ec);
2219 if (d.Initializer != null && !IsUnreachable) {
2220 ec.Mark (d.Variable.Location);
2221 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2227 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2229 if (Initializer != null)
2230 Initializer.FlowAnalysis (fc);
2232 if (declarators != null) {
2233 foreach (var d in declarators) {
2234 if (d.Initializer != null)
2235 d.Initializer.FlowAnalysis (fc);
2242 public override Reachability MarkReachable (Reachability rc)
2244 var init = initializer as ExpressionStatement;
2246 init.MarkReachable (rc);
2248 return base.MarkReachable (rc);
2251 protected override void CloneTo (CloneContext clonectx, Statement target)
2253 BlockVariable t = (BlockVariable) target;
2255 if (type_expr != null)
2256 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2258 if (initializer != null)
2259 t.initializer = initializer.Clone (clonectx);
2261 if (declarators != null) {
2262 t.declarators = null;
2263 foreach (var d in declarators)
2264 t.AddDeclarator (d.Clone (clonectx));
2268 public override object Accept (StructuralVisitor visitor)
2270 return visitor.Visit (this);
2274 public class BlockConstant : BlockVariable
2276 public BlockConstant (FullNamedExpression type, LocalVariable li)
2281 public override void Emit (EmitContext ec)
2283 // Nothing to emit, not even sequence point
2286 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2288 initializer = initializer.Resolve (bc);
2289 if (initializer == null)
2292 var c = initializer as Constant;
2294 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2298 c = c.ConvertImplicitly (li.Type);
2300 if (TypeSpec.IsReferenceType (li.Type))
2301 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2303 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2308 li.ConstantValue = c;
2312 public override object Accept (StructuralVisitor visitor)
2314 return visitor.Visit (this);
2319 // The information about a user-perceived local variable
2321 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2328 AddressTaken = 1 << 2,
2329 CompilerGenerated = 1 << 3,
2331 ForeachVariable = 1 << 5,
2332 FixedVariable = 1 << 6,
2333 UsingVariable = 1 << 7,
2335 SymbolFileHidden = 1 << 9,
2337 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2341 readonly string name;
2342 readonly Location loc;
2343 readonly Block block;
2345 Constant const_value;
2347 public VariableInfo VariableInfo;
2348 HoistedVariable hoisted_variant;
2350 LocalBuilder builder;
2352 public LocalVariable (Block block, string name, Location loc)
2359 public LocalVariable (Block block, string name, Flags flags, Location loc)
2360 : this (block, name, loc)
2366 // Used by variable declarators
2368 public LocalVariable (LocalVariable li, string name, Location loc)
2369 : this (li.block, name, li.flags, loc)
2375 public bool AddressTaken {
2377 return (flags & Flags.AddressTaken) != 0;
2381 public Block Block {
2387 public Constant ConstantValue {
2392 const_value = value;
2397 // Hoisted local variable variant
2399 public HoistedVariable HoistedVariant {
2401 return hoisted_variant;
2404 hoisted_variant = value;
2408 public bool IsDeclared {
2410 return type != null;
2414 public bool IsCompilerGenerated {
2416 return (flags & Flags.CompilerGenerated) != 0;
2420 public bool IsConstant {
2422 return (flags & Flags.Constant) != 0;
2426 public bool IsLocked {
2428 return (flags & Flags.IsLocked) != 0;
2431 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2435 public bool IsThis {
2437 return (flags & Flags.IsThis) != 0;
2441 public bool IsFixed {
2443 return (flags & Flags.FixedVariable) != 0;
2446 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2450 bool INamedBlockVariable.IsParameter {
2456 public bool IsReadonly {
2458 return (flags & Flags.ReadonlyMask) != 0;
2462 public Location Location {
2468 public string Name {
2474 public TypeSpec Type {
2485 public void CreateBuilder (EmitContext ec)
2487 if ((flags & Flags.Used) == 0) {
2488 if (VariableInfo == null) {
2489 // Missing flow analysis or wrong variable flags
2490 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2493 if (VariableInfo.IsEverAssigned)
2494 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2496 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2499 if (HoistedVariant != null)
2502 if (builder != null) {
2503 if ((flags & Flags.CompilerGenerated) != 0)
2506 // To avoid Used warning duplicates
2507 throw new InternalErrorException ("Already created variable `{0}'", name);
2511 // All fixed variabled are pinned, a slot has to be alocated
2513 builder = ec.DeclareLocal (Type, IsFixed);
2514 if ((flags & Flags.SymbolFileHidden) == 0)
2515 ec.DefineLocalVariable (name, builder);
2518 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2520 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2521 if (!writeToSymbolFile)
2522 li.flags |= Flags.SymbolFileHidden;
2528 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2530 if (IsConstant && const_value != null)
2531 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2533 return new LocalVariableReference (this, loc);
2536 public void Emit (EmitContext ec)
2538 // TODO: Need something better for temporary variables
2539 if ((flags & Flags.CompilerGenerated) != 0)
2542 ec.Emit (OpCodes.Ldloc, builder);
2545 public void EmitAssign (EmitContext ec)
2547 // TODO: Need something better for temporary variables
2548 if ((flags & Flags.CompilerGenerated) != 0)
2551 ec.Emit (OpCodes.Stloc, builder);
2554 public void EmitAddressOf (EmitContext ec)
2556 // TODO: Need something better for temporary variables
2557 if ((flags & Flags.CompilerGenerated) != 0)
2560 ec.Emit (OpCodes.Ldloca, builder);
2563 public static string GetCompilerGeneratedName (Block block)
2565 // HACK: Debugger depends on the name semantics
2566 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2569 public string GetReadOnlyContext ()
2571 switch (flags & Flags.ReadonlyMask) {
2572 case Flags.FixedVariable:
2573 return "fixed variable";
2574 case Flags.ForeachVariable:
2575 return "foreach iteration variable";
2576 case Flags.UsingVariable:
2577 return "using variable";
2580 throw new InternalErrorException ("Variable is not readonly");
2583 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2585 if (VariableInfo == null)
2586 throw new Exception ();
2588 if (IsAssigned (fc))
2591 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2594 public bool IsAssigned (FlowAnalysisContext fc)
2596 return fc.IsDefinitelyAssigned (VariableInfo);
2599 public void PrepareAssignmentAnalysis (BlockContext bc)
2602 // No need to run assignment analysis for these guys
2604 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2607 VariableInfo = VariableInfo.Create (bc, this);
2611 // Mark the variables as referenced in the user code
2613 public void SetIsUsed ()
2615 flags |= Flags.Used;
2618 public void SetHasAddressTaken ()
2620 flags |= (Flags.AddressTaken | Flags.Used);
2623 public override string ToString ()
2625 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2630 /// Block represents a C# block.
2634 /// This class is used in a number of places: either to represent
2635 /// explicit blocks that the programmer places or implicit blocks.
2637 /// Implicit blocks are used as labels or to introduce variable
2640 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2641 /// they contain extra information that is not necessary on normal blocks.
2643 public class Block : Statement {
2650 HasCapturedVariable = 64,
2651 HasCapturedThis = 1 << 7,
2652 IsExpressionTree = 1 << 8,
2653 CompilerGenerated = 1 << 9,
2654 HasAsyncModifier = 1 << 10,
2656 YieldBlock = 1 << 12,
2657 AwaitBlock = 1 << 13,
2658 FinallyBlock = 1 << 14,
2659 CatchBlock = 1 << 15,
2661 NoFlowAnalysis = 1 << 21,
2662 InitializationEmitted = 1 << 22
2665 public Block Parent;
2666 public Location StartLocation;
2667 public Location EndLocation;
2669 public ExplicitBlock Explicit;
2670 public ParametersBlock ParametersBlock;
2672 protected Flags flags;
2675 // The statements in this block
2677 protected List<Statement> statements;
2679 protected List<Statement> scope_initializers;
2681 int? resolving_init_idx;
2687 public int ID = id++;
2689 static int clone_id_counter;
2693 // int assignable_slots;
2695 public Block (Block parent, Location start, Location end)
2696 : this (parent, 0, start, end)
2700 public Block (Block parent, Flags flags, Location start, Location end)
2702 if (parent != null) {
2703 // the appropriate constructors will fixup these fields
2704 ParametersBlock = parent.ParametersBlock;
2705 Explicit = parent.Explicit;
2708 this.Parent = parent;
2710 this.StartLocation = start;
2711 this.EndLocation = end;
2713 statements = new List<Statement> (4);
2715 this.original = this;
2720 public Block Original {
2729 public bool IsCompilerGenerated {
2730 get { return (flags & Flags.CompilerGenerated) != 0; }
2731 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2735 public bool IsCatchBlock {
2737 return (flags & Flags.CatchBlock) != 0;
2741 public bool IsFinallyBlock {
2743 return (flags & Flags.FinallyBlock) != 0;
2747 public bool Unchecked {
2748 get { return (flags & Flags.Unchecked) != 0; }
2749 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2752 public bool Unsafe {
2753 get { return (flags & Flags.Unsafe) != 0; }
2754 set { flags |= Flags.Unsafe; }
2757 public List<Statement> Statements {
2758 get { return statements; }
2763 public void SetEndLocation (Location loc)
2768 public void AddLabel (LabeledStatement target)
2770 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2773 public void AddLocalName (LocalVariable li)
2775 AddLocalName (li.Name, li);
2778 public void AddLocalName (string name, INamedBlockVariable li)
2780 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2783 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2785 if (reason == null) {
2786 Error_AlreadyDeclared (name, variable);
2790 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2791 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2792 "to `{0}', which is already used in a `{1}' scope to denote something else",
2796 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2798 var pi = variable as ParametersBlock.ParameterInfo;
2800 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2802 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2803 "A local variable named `{0}' is already defined in this scope", name);
2807 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2809 ParametersBlock.TopBlock.Report.Error (412, loc,
2810 "The type parameter name `{0}' is the same as local variable or parameter name",
2815 // It should be used by expressions which require to
2816 // register a statement during resolve process.
2818 public void AddScopeStatement (Statement s)
2820 if (scope_initializers == null)
2821 scope_initializers = new List<Statement> ();
2824 // Simple recursive helper, when resolve scope initializer another
2825 // new scope initializer can be added, this ensures it's initialized
2826 // before existing one. For now this can happen with expression trees
2827 // in base ctor initializer only
2829 if (resolving_init_idx.HasValue) {
2830 scope_initializers.Insert (resolving_init_idx.Value, s);
2831 ++resolving_init_idx;
2833 scope_initializers.Add (s);
2837 public void InsertStatement (int index, Statement s)
2839 statements.Insert (index, s);
2842 public void AddStatement (Statement s)
2847 public LabeledStatement LookupLabel (string name)
2849 return ParametersBlock.GetLabel (name, this);
2852 public override Reachability MarkReachable (Reachability rc)
2854 if (rc.IsUnreachable)
2857 MarkReachableScope (rc);
2859 foreach (var s in statements) {
2860 rc = s.MarkReachable (rc);
2861 if (rc.IsUnreachable) {
2862 if ((flags & Flags.ReachableEnd) != 0)
2863 return new Reachability ();
2869 flags |= Flags.ReachableEnd;
2874 public void MarkReachableScope (Reachability rc)
2876 base.MarkReachable (rc);
2878 if (scope_initializers != null) {
2879 foreach (var si in scope_initializers)
2880 si.MarkReachable (rc);
2884 public override bool Resolve (BlockContext bc)
2886 if ((flags & Flags.Resolved) != 0)
2889 Block prev_block = bc.CurrentBlock;
2890 bc.CurrentBlock = this;
2893 // Compiler generated scope statements
2895 if (scope_initializers != null) {
2896 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2897 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2900 resolving_init_idx = null;
2904 int statement_count = statements.Count;
2905 for (int ix = 0; ix < statement_count; ix++){
2906 Statement s = statements [ix];
2908 if (!s.Resolve (bc)) {
2910 statements [ix] = new EmptyStatement (s.loc);
2915 bc.CurrentBlock = prev_block;
2917 flags |= Flags.Resolved;
2921 protected override void DoEmit (EmitContext ec)
2923 for (int ix = 0; ix < statements.Count; ix++){
2924 statements [ix].Emit (ec);
2928 public override void Emit (EmitContext ec)
2930 if (scope_initializers != null)
2931 EmitScopeInitializers (ec);
2936 protected void EmitScopeInitializers (EmitContext ec)
2938 foreach (Statement s in scope_initializers)
2942 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2944 if (scope_initializers != null) {
2945 foreach (var si in scope_initializers)
2946 si.FlowAnalysis (fc);
2949 return DoFlowAnalysis (fc, 0);
2952 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2954 bool end_unreachable = !reachable;
2955 bool goto_flow_analysis = startIndex != 0;
2956 for (; startIndex < statements.Count; ++startIndex) {
2957 var s = statements[startIndex];
2959 end_unreachable = s.FlowAnalysis (fc);
2960 if (s.IsUnreachable) {
2961 statements [startIndex] = RewriteUnreachableStatement (s);
2966 // Statement end reachability is needed mostly due to goto support. Consider
2975 // X label is reachable only via goto not as another statement after if. We need
2976 // this for flow-analysis only to carry variable info correctly.
2978 if (end_unreachable) {
2979 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2981 var f = s as TryFinally;
2982 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
2984 // Special case for try-finally with unreachable code after
2985 // finally block. Try block has to include leave opcode but there is
2986 // no label to leave to after unreachable finally block closing
2987 // brace. This sentinel ensures there is always IL instruction to
2988 // leave to even if we know it'll never be reached.
2990 statements.Insert (startIndex + 1, new SentinelStatement ());
2992 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2993 s = statements [startIndex];
2994 if (s is SwitchLabel) {
2995 if (!after_goto_case)
2996 s.FlowAnalysis (fc);
3001 if (s.IsUnreachable) {
3002 s.FlowAnalysis (fc);
3003 statements [startIndex] = RewriteUnreachableStatement (s);
3009 // Idea is to stop after goto case because goto case will always have at least same
3010 // variable assigned as switch case label. This saves a lot for complex goto case tests
3012 if (after_goto_case)
3018 var lb = s as LabeledStatement;
3019 if (lb != null && fc.AddReachedLabel (lb))
3024 // The condition should be true unless there is forward jumping goto
3026 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3029 return !Explicit.HasReachableClosingBrace;
3032 static Statement RewriteUnreachableStatement (Statement s)
3034 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3035 // analysis. Even csc report unreachable warning for it but it's actually used hence
3036 // we try to emulate this behaviour
3044 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3047 return new EmptyStatement (s.loc);
3050 public void ScanGotoJump (Statement label)
3053 for (i = 0; i < statements.Count; ++i) {
3054 if (statements[i] == label)
3058 var rc = new Reachability ();
3059 for (++i; i < statements.Count; ++i) {
3060 var s = statements[i];
3061 rc = s.MarkReachable (rc);
3062 if (rc.IsUnreachable)
3066 flags |= Flags.ReachableEnd;
3069 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3072 for (i = 0; i < statements.Count; ++i) {
3073 if (statements[i] == label)
3077 DoFlowAnalysis (fc, ++i);
3081 public override string ToString ()
3083 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3087 protected override void CloneTo (CloneContext clonectx, Statement t)
3089 Block target = (Block) t;
3091 target.clone_id = ++clone_id_counter;
3094 clonectx.AddBlockMap (this, target);
3095 if (original != this)
3096 clonectx.AddBlockMap (original, target);
3098 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3099 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3102 target.Parent = clonectx.RemapBlockCopy (Parent);
3104 target.statements = new List<Statement> (statements.Count);
3105 foreach (Statement s in statements)
3106 target.statements.Add (s.Clone (clonectx));
3109 public override object Accept (StructuralVisitor visitor)
3111 return visitor.Visit (this);
3115 public class ExplicitBlock : Block
3117 protected AnonymousMethodStorey am_storey;
3118 int debug_scope_index;
3120 public ExplicitBlock (Block parent, Location start, Location end)
3121 : this (parent, (Flags) 0, start, end)
3125 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3126 : base (parent, flags, start, end)
3128 this.Explicit = this;
3133 public AnonymousMethodStorey AnonymousMethodStorey {
3139 public bool HasAwait {
3141 return (flags & Flags.AwaitBlock) != 0;
3145 public bool HasCapturedThis {
3147 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3150 return (flags & Flags.HasCapturedThis) != 0;
3155 // Used to indicate that the block has reference to parent
3156 // block and cannot be made static when defining anonymous method
3158 public bool HasCapturedVariable {
3160 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3163 return (flags & Flags.HasCapturedVariable) != 0;
3167 public bool HasReachableClosingBrace {
3169 return (flags & Flags.ReachableEnd) != 0;
3172 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3176 public bool HasYield {
3178 return (flags & Flags.YieldBlock) != 0;
3185 // Creates anonymous method storey in current block
3187 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3190 // Return same story for iterator and async blocks unless we are
3191 // in nested anonymous method
3193 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3194 return ec.CurrentAnonymousMethod.Storey;
3196 if (am_storey == null) {
3197 MemberBase mc = ec.MemberContext as MemberBase;
3200 // Creates anonymous method storey for this block
3202 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3208 public void EmitScopeInitialization (EmitContext ec)
3210 if ((flags & Flags.InitializationEmitted) != 0)
3213 if (am_storey != null) {
3214 DefineStoreyContainer (ec, am_storey);
3215 am_storey.EmitStoreyInstantiation (ec, this);
3218 if (scope_initializers != null)
3219 EmitScopeInitializers (ec);
3221 flags |= Flags.InitializationEmitted;
3224 public override void Emit (EmitContext ec)
3226 if (Parent != null) {
3227 // TODO: It's needed only when scope has variable (normal or lifted)
3228 ec.BeginScope (GetDebugSymbolScopeIndex ());
3231 EmitScopeInitialization (ec);
3233 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3234 ec.Emit (OpCodes.Nop);
3242 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3243 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3244 ec.Emit (OpCodes.Nop);
3248 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3250 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3251 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3252 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3256 // Creates anonymous method storey
3258 storey.CreateContainer ();
3259 storey.DefineContainer ();
3261 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3264 // Only first storey in path will hold this reference. All children blocks will
3265 // reference it indirectly using $ref field
3267 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3268 if (b.Parent != null) {
3269 var s = b.Parent.Explicit.AnonymousMethodStorey;
3271 storey.HoistedThis = s.HoistedThis;
3276 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3277 if (storey.HoistedThis == null)
3278 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3280 if (storey.HoistedThis != null)
3286 // We are the first storey on path and 'this' has to be hoisted
3288 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3289 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3291 // ThisReferencesFromChildrenBlock holds all reference even if they
3292 // are not on this path. It saves some memory otherwise it'd have to
3293 // be in every explicit block. We run this check to see if the reference
3294 // is valid for this storey
3296 Block block_on_path = ref_block;
3297 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3299 if (block_on_path == null)
3302 if (storey.HoistedThis == null) {
3303 storey.AddCapturedThisField (ec, null);
3306 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3308 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3310 if (b_storey != null) {
3312 // Don't add storey cross reference for `this' when the storey ends up not
3313 // beeing attached to any parent
3315 if (b.ParametersBlock.StateMachine == null) {
3316 AnonymousMethodStorey s = null;
3317 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3318 s = ab.Explicit.AnonymousMethodStorey;
3323 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3325 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3326 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3333 // Stop propagation inside same top block
3335 if (b.ParametersBlock == ParametersBlock.Original) {
3336 b_storey.AddParentStoreyReference (ec, storey);
3337 // b_storey.HoistedThis = storey.HoistedThis;
3341 b = pb = b.ParametersBlock;
3343 pb = b as ParametersBlock;
3346 if (pb != null && pb.StateMachine != null) {
3347 if (pb.StateMachine == storey)
3351 // If we are state machine with no parent. We can hook into parent without additional
3352 // reference and capture this directly
3354 ExplicitBlock parent_storey_block = pb;
3355 while (parent_storey_block.Parent != null) {
3356 parent_storey_block = parent_storey_block.Parent.Explicit;
3357 if (parent_storey_block.AnonymousMethodStorey != null) {
3362 if (parent_storey_block.AnonymousMethodStorey == null) {
3363 if (pb.StateMachine.HoistedThis == null) {
3364 pb.StateMachine.AddCapturedThisField (ec, null);
3365 b.HasCapturedThis = true;
3371 var parent_this_block = pb;
3372 while (parent_this_block.Parent != null) {
3373 parent_this_block = parent_this_block.Parent.ParametersBlock;
3374 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3380 // Add reference to closest storey which holds captured this
3382 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3386 // Add parent storey reference only when this is not captured directly
3388 if (b_storey != null) {
3389 b_storey.AddParentStoreyReference (ec, storey);
3390 b_storey.HoistedThis = storey.HoistedThis;
3397 var ref_blocks = storey.ReferencesFromChildrenBlock;
3398 if (ref_blocks != null) {
3399 foreach (ExplicitBlock ref_block in ref_blocks) {
3400 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3401 if (b.AnonymousMethodStorey != null) {
3402 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3405 // Stop propagation inside same top block
3407 if (b.ParametersBlock == ParametersBlock.Original)
3410 b = b.ParametersBlock;
3413 var pb = b as ParametersBlock;
3414 if (pb != null && pb.StateMachine != null) {
3415 if (pb.StateMachine == storey)
3418 pb.StateMachine.AddParentStoreyReference (ec, storey);
3421 b.HasCapturedVariable = true;
3427 storey.PrepareEmit ();
3428 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3431 public int GetDebugSymbolScopeIndex ()
3433 if (debug_scope_index == 0)
3434 debug_scope_index = ++ParametersBlock.debug_scope_index;
3436 return debug_scope_index;
3439 public void RegisterAsyncAwait ()
3442 while ((block.flags & Flags.AwaitBlock) == 0) {
3443 block.flags |= Flags.AwaitBlock;
3445 if (block is ParametersBlock)
3448 block = block.Parent.Explicit;
3452 public void RegisterIteratorYield ()
3454 ParametersBlock.TopBlock.IsIterator = true;
3457 while ((block.flags & Flags.YieldBlock) == 0) {
3458 block.flags |= Flags.YieldBlock;
3460 if (block.Parent == null)
3463 block = block.Parent.Explicit;
3467 public void SetCatchBlock ()
3469 flags |= Flags.CatchBlock;
3472 public void SetFinallyBlock ()
3474 flags |= Flags.FinallyBlock;
3477 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3479 tryBlock.statements = statements;
3480 statements = new List<Statement> (1);
3481 statements.Add (tf);
3486 // ParametersBlock was introduced to support anonymous methods
3487 // and lambda expressions
3489 public class ParametersBlock : ExplicitBlock
3491 public class ParameterInfo : INamedBlockVariable
3493 readonly ParametersBlock block;
3495 public VariableInfo VariableInfo;
3498 public ParameterInfo (ParametersBlock block, int index)
3506 public ParametersBlock Block {
3512 Block INamedBlockVariable.Block {
3518 public bool IsDeclared {
3524 public bool IsParameter {
3530 public bool IsLocked {
3539 public Location Location {
3541 return Parameter.Location;
3545 public Parameter Parameter {
3547 return block.Parameters [index];
3551 public TypeSpec ParameterType {
3553 return Parameter.Type;
3559 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3561 return new ParameterReference (this, loc);
3566 // Block is converted into an expression
3568 sealed class BlockScopeExpression : Expression
3571 readonly ParametersBlock block;
3573 public BlockScopeExpression (Expression child, ParametersBlock block)
3579 public override bool ContainsEmitWithAwait ()
3581 return child.ContainsEmitWithAwait ();
3584 public override Expression CreateExpressionTree (ResolveContext ec)
3586 throw new NotSupportedException ();
3589 protected override Expression DoResolve (ResolveContext ec)
3594 child = child.Resolve (ec);
3598 eclass = child.eclass;
3603 public override void Emit (EmitContext ec)
3605 block.EmitScopeInitializers (ec);
3610 protected ParametersCompiled parameters;
3611 protected ParameterInfo[] parameter_info;
3612 protected bool resolved;
3613 protected ToplevelBlock top_block;
3614 protected StateMachine state_machine;
3615 protected Dictionary<string, object> labels;
3617 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3618 : base (parent, 0, start, start)
3620 if (parameters == null)
3621 throw new ArgumentNullException ("parameters");
3623 this.parameters = parameters;
3624 ParametersBlock = this;
3626 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3628 this.top_block = parent.ParametersBlock.top_block;
3629 ProcessParameters ();
3632 protected ParametersBlock (ParametersCompiled parameters, Location start)
3633 : base (null, 0, start, start)
3635 if (parameters == null)
3636 throw new ArgumentNullException ("parameters");
3638 this.parameters = parameters;
3639 ParametersBlock = this;
3643 // It's supposed to be used by method body implementation of anonymous methods
3645 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3646 : base (null, 0, source.StartLocation, source.EndLocation)
3648 this.parameters = parameters;
3649 this.statements = source.statements;
3650 this.scope_initializers = source.scope_initializers;
3652 this.resolved = true;
3653 this.reachable = source.reachable;
3654 this.am_storey = source.am_storey;
3655 this.state_machine = source.state_machine;
3656 this.flags = source.flags & Flags.ReachableEnd;
3658 ParametersBlock = this;
3661 // Overwrite original for comparison purposes when linking cross references
3662 // between anonymous methods
3664 Original = source.Original;
3669 public bool IsAsync {
3671 return (flags & Flags.HasAsyncModifier) != 0;
3674 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3679 // Block has been converted to expression tree
3681 public bool IsExpressionTree {
3683 return (flags & Flags.IsExpressionTree) != 0;
3688 // The parameters for the block.
3690 public ParametersCompiled Parameters {
3696 public StateMachine StateMachine {
3698 return state_machine;
3702 public ToplevelBlock TopBlock {
3711 public bool Resolved {
3713 return (flags & Flags.Resolved) != 0;
3717 public int TemporaryLocalsCount { get; set; }
3722 // Checks whether all `out' parameters have been assigned.
3724 public void CheckControlExit (FlowAnalysisContext fc)
3726 CheckControlExit (fc, fc.DefiniteAssignment);
3729 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3731 if (parameter_info == null)
3734 foreach (var p in parameter_info) {
3735 if (p.VariableInfo == null)
3738 if (p.VariableInfo.IsAssigned (dat))
3741 fc.Report.Error (177, p.Location,
3742 "The out parameter `{0}' must be assigned to before control leaves the current method",
3747 protected override void CloneTo (CloneContext clonectx, Statement t)
3749 base.CloneTo (clonectx, t);
3751 var target = (ParametersBlock) t;
3754 // Clone label statements as well as they contain block reference
3758 if (pb.labels != null) {
3759 target.labels = new Dictionary<string, object> ();
3761 foreach (var entry in pb.labels) {
3762 var list = entry.Value as List<LabeledStatement>;
3765 var list_clone = new List<LabeledStatement> ();
3766 foreach (var lentry in list) {
3767 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3770 target.labels.Add (entry.Key, list_clone);
3772 var labeled = (LabeledStatement) entry.Value;
3773 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3780 if (pb.Parent == null)
3783 pb = pb.Parent.ParametersBlock;
3787 public override Expression CreateExpressionTree (ResolveContext ec)
3789 if (statements.Count == 1) {
3790 Expression expr = statements[0].CreateExpressionTree (ec);
3791 if (scope_initializers != null)
3792 expr = new BlockScopeExpression (expr, this);
3797 return base.CreateExpressionTree (ec);
3800 public override void Emit (EmitContext ec)
3802 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3803 DefineStoreyContainer (ec, state_machine);
3804 state_machine.EmitStoreyInstantiation (ec, this);
3810 public void EmitEmbedded (EmitContext ec)
3812 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3813 DefineStoreyContainer (ec, state_machine);
3814 state_machine.EmitStoreyInstantiation (ec, this);
3820 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3822 var res = base.DoFlowAnalysis (fc);
3824 if (HasReachableClosingBrace)
3825 CheckControlExit (fc);
3830 public LabeledStatement GetLabel (string name, Block block)
3833 // Cloned parameters blocks can have their own cloned version of top-level labels
3835 if (labels == null) {
3837 return Parent.ParametersBlock.GetLabel (name, block);
3843 if (!labels.TryGetValue (name, out value)) {
3847 var label = value as LabeledStatement;
3849 if (label != null) {
3850 if (IsLabelVisible (label, b))
3854 List<LabeledStatement> list = (List<LabeledStatement>) value;
3855 for (int i = 0; i < list.Count; ++i) {
3857 if (IsLabelVisible (label, b))
3865 static bool IsLabelVisible (LabeledStatement label, Block b)
3868 if (label.Block == b)
3871 } while (b != null);
3876 public ParameterInfo GetParameterInfo (Parameter p)
3878 for (int i = 0; i < parameters.Count; ++i) {
3879 if (parameters[i] == p)
3880 return parameter_info[i];
3883 throw new ArgumentException ("Invalid parameter");
3886 public ParameterReference GetParameterReference (int index, Location loc)
3888 return new ParameterReference (parameter_info[index], loc);
3891 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3893 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3895 CloneContext clonectx = new CloneContext ();
3896 return Clone (clonectx);
3899 protected void ProcessParameters ()
3901 if (parameters.Count == 0)
3904 parameter_info = new ParameterInfo[parameters.Count];
3905 for (int i = 0; i < parameter_info.Length; ++i) {
3906 var p = parameters.FixedParameters[i];
3910 // TODO: Should use Parameter only and more block there
3911 parameter_info[i] = new ParameterInfo (this, i);
3913 AddLocalName (p.Name, parameter_info[i]);
3917 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3919 var src = stmt.Block;
3922 // Cannot remap label block if the label was not yet cloned which
3923 // can happen in case of anonymous method inside anoynymous method
3924 // with a label. But in this case we don't care because goto cannot
3925 // jump of out anonymous method
3927 if (src.ParametersBlock != this)
3930 var src_stmts = src.Statements;
3931 for (int i = 0; i < src_stmts.Count; ++i) {
3932 if (src_stmts[i] == stmt)
3933 return (LabeledStatement) dst.Statements[i];
3936 throw new InternalErrorException ("Should never be reached");
3939 public override bool Resolve (BlockContext bc)
3941 // TODO: if ((flags & Flags.Resolved) != 0)
3948 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3949 flags |= Flags.IsExpressionTree;
3952 PrepareAssignmentAnalysis (bc);
3954 if (!base.Resolve (bc))
3957 } catch (Exception e) {
3958 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3961 if (bc.CurrentBlock != null) {
3962 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3964 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3969 // If an asynchronous body of F is either an expression classified as nothing, or a
3970 // statement block where no return statements have expressions, the inferred return type is Task
3973 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3974 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3975 am.ReturnTypeInference = null;
3976 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3984 void PrepareAssignmentAnalysis (BlockContext bc)
3986 for (int i = 0; i < parameters.Count; ++i) {
3987 var par = parameters.FixedParameters[i];
3989 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3992 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3996 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3998 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3999 var stateMachine = new IteratorStorey (iterator);
4001 state_machine = stateMachine;
4002 iterator.SetStateMachine (stateMachine);
4004 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4005 tlb.Original = this;
4006 tlb.state_machine = stateMachine;
4007 tlb.AddStatement (new Return (iterator, iterator.Location));
4011 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4013 for (int i = 0; i < parameters.Count; i++) {
4014 Parameter p = parameters[i];
4015 Parameter.Modifier mod = p.ModFlags;
4016 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4017 host.Compiler.Report.Error (1988, p.Location,
4018 "Async methods cannot have ref or out parameters");
4022 if (p is ArglistParameter) {
4023 host.Compiler.Report.Error (4006, p.Location,
4024 "__arglist is not allowed in parameter list of async methods");
4028 if (parameters.Types[i].IsPointer) {
4029 host.Compiler.Report.Error (4005, p.Location,
4030 "Async methods cannot have unsafe parameters");
4036 host.Compiler.Report.Warning (1998, 1, loc,
4037 "Async block lacks `await' operator and will run synchronously");
4040 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4041 var initializer = new AsyncInitializer (this, host, block_type);
4042 initializer.Type = block_type;
4043 initializer.DelegateType = delegateType;
4045 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4047 state_machine = stateMachine;
4048 initializer.SetStateMachine (stateMachine);
4050 const Flags flags = Flags.CompilerGenerated;
4052 var b = this is ToplevelBlock ?
4053 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4054 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4057 b.state_machine = stateMachine;
4058 b.AddStatement (new AsyncInitializerStatement (initializer));
4066 public class ToplevelBlock : ParametersBlock
4068 LocalVariable this_variable;
4069 CompilerContext compiler;
4070 Dictionary<string, object> names;
4072 List<ExplicitBlock> this_references;
4074 public ToplevelBlock (CompilerContext ctx, Location loc)
4075 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4079 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4080 : base (parameters, start)
4082 this.compiler = ctx;
4086 ProcessParameters ();
4090 // Recreates a top level block from parameters block. Used for
4091 // compiler generated methods where the original block comes from
4092 // explicit child block. This works for already resolved blocks
4093 // only to ensure we resolve them in the correct flow order
4095 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4096 : base (source, parameters)
4098 this.compiler = source.TopBlock.compiler;
4102 public bool IsIterator {
4104 return (flags & Flags.Iterator) != 0;
4107 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4111 public Report Report {
4113 return compiler.Report;
4118 // Used by anonymous blocks to track references of `this' variable
4120 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4122 return this_references;
4127 // Returns the "this" instance variable of this block.
4128 // See AddThisVariable() for more information.
4130 public LocalVariable ThisVariable {
4132 return this_variable;
4136 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4139 names = new Dictionary<string, object> ();
4142 if (!names.TryGetValue (name, out value)) {
4143 names.Add (name, li);
4147 INamedBlockVariable existing = value as INamedBlockVariable;
4148 List<INamedBlockVariable> existing_list;
4149 if (existing != null) {
4150 existing_list = new List<INamedBlockVariable> ();
4151 existing_list.Add (existing);
4152 names[name] = existing_list;
4154 existing_list = (List<INamedBlockVariable>) value;
4158 // A collision checking between local names
4160 var variable_block = li.Block.Explicit;
4161 for (int i = 0; i < existing_list.Count; ++i) {
4162 existing = existing_list[i];
4163 Block b = existing.Block.Explicit;
4165 // Collision at same level
4166 if (variable_block == b) {
4167 li.Block.Error_AlreadyDeclared (name, li);
4171 // Collision with parent
4172 Block parent = variable_block;
4173 while ((parent = parent.Parent) != null) {
4175 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4176 i = existing_list.Count;
4181 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4182 // Collision with children
4183 while ((b = b.Parent) != null) {
4184 if (variable_block == b) {
4185 li.Block.Error_AlreadyDeclared (name, li, "child");
4186 i = existing_list.Count;
4193 existing_list.Add (li);
4196 public void AddLabel (string name, LabeledStatement label)
4199 labels = new Dictionary<string, object> ();
4202 if (!labels.TryGetValue (name, out value)) {
4203 labels.Add (name, label);
4207 LabeledStatement existing = value as LabeledStatement;
4208 List<LabeledStatement> existing_list;
4209 if (existing != null) {
4210 existing_list = new List<LabeledStatement> ();
4211 existing_list.Add (existing);
4212 labels[name] = existing_list;
4214 existing_list = (List<LabeledStatement>) value;
4218 // A collision checking between labels
4220 for (int i = 0; i < existing_list.Count; ++i) {
4221 existing = existing_list[i];
4222 Block b = existing.Block;
4224 // Collision at same level
4225 if (label.Block == b) {
4226 Report.SymbolRelatedToPreviousError (existing.loc, name);
4227 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4231 // Collision with parent
4233 while ((b = b.Parent) != null) {
4234 if (existing.Block == b) {
4235 Report.Error (158, label.loc,
4236 "The label `{0}' shadows another label by the same name in a contained scope", name);
4237 i = existing_list.Count;
4242 // Collision with with children
4244 while ((b = b.Parent) != null) {
4245 if (label.Block == b) {
4246 Report.Error (158, label.loc,
4247 "The label `{0}' shadows another label by the same name in a contained scope", name);
4248 i = existing_list.Count;
4254 existing_list.Add (label);
4257 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4259 if (this_references == null)
4260 this_references = new List<ExplicitBlock> ();
4262 if (!this_references.Contains (block))
4263 this_references.Add (block);
4266 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4268 this_references.Remove (block);
4272 // Creates an arguments set from all parameters, useful for method proxy calls
4274 public Arguments GetAllParametersArguments ()
4276 int count = parameters.Count;
4277 Arguments args = new Arguments (count);
4278 for (int i = 0; i < count; ++i) {
4279 var pi = parameter_info[i];
4280 var arg_expr = GetParameterReference (i, pi.Location);
4282 Argument.AType atype_modifier;
4283 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4284 case Parameter.Modifier.REF:
4285 atype_modifier = Argument.AType.Ref;
4287 case Parameter.Modifier.OUT:
4288 atype_modifier = Argument.AType.Out;
4295 args.Add (new Argument (arg_expr, atype_modifier));
4302 // Lookup inside a block, the returned value can represent 3 states
4304 // true+variable: A local name was found and it's valid
4305 // false+variable: A local name was found in a child block only
4306 // false+null: No local name was found
4308 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4314 if (!names.TryGetValue (name, out value))
4317 variable = value as INamedBlockVariable;
4319 if (variable != null) {
4321 if (variable.Block == b.Original)
4325 } while (b != null);
4333 } while (b != null);
4335 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4336 for (int i = 0; i < list.Count; ++i) {
4339 if (variable.Block == b.Original)
4343 } while (b != null);
4351 } while (b != null);
4361 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4363 if (block.names != null) {
4364 foreach (var n in block.names) {
4365 var variable = n.Value as INamedBlockVariable;
4366 if (variable != null) {
4367 if (variable.Block.ParametersBlock == pb)
4368 AddLocalName (n.Key, variable, false);
4372 foreach (var v in (List<INamedBlockVariable>) n.Value)
4373 if (v.Block.ParametersBlock == pb)
4374 AddLocalName (n.Key, v, false);
4380 // This is used by non-static `struct' constructors which do not have an
4381 // initializer - in this case, the constructor must initialize all of the
4382 // struct's fields. To do this, we add a "this" variable and use the flow
4383 // analysis code to ensure that it's been fully initialized before control
4384 // leaves the constructor.
4386 public void AddThisVariable (BlockContext bc)
4388 if (this_variable != null)
4389 throw new InternalErrorException (StartLocation.ToString ());
4391 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4392 this_variable.Type = bc.CurrentType;
4393 this_variable.PrepareAssignmentAnalysis (bc);
4396 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4399 // If we're a non-static struct constructor which doesn't have an
4400 // initializer, then we must initialize all of the struct's fields.
4402 if (this_variable != null)
4403 this_variable.IsThisAssigned (fc, this);
4405 base.CheckControlExit (fc, dat);
4408 public HashSet<LocalVariable> GetUndeclaredVariables ()
4413 HashSet<LocalVariable> variables = null;
4415 foreach (var entry in names) {
4416 var complex = entry.Value as List<INamedBlockVariable>;
4417 if (complex != null) {
4418 foreach (var centry in complex) {
4419 if (IsUndeclaredVariable (centry)) {
4420 if (variables == null)
4421 variables = new HashSet<LocalVariable> ();
4423 variables.Add ((LocalVariable) centry);
4426 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4427 if (variables == null)
4428 variables = new HashSet<LocalVariable> ();
4430 variables.Add ((LocalVariable)entry.Value);
4437 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4439 var lv = namedBlockVariable as LocalVariable;
4440 return lv != null && !lv.IsDeclared;
4443 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4448 foreach (var entry in names) {
4449 var complex = entry.Value as List<INamedBlockVariable>;
4450 if (complex != null) {
4451 foreach (var centry in complex) {
4452 var lv = centry as LocalVariable;
4453 if (lv != null && undeclaredVariables.Contains (lv)) {
4458 var lv = entry.Value as LocalVariable;
4459 if (lv != null && undeclaredVariables.Contains (lv))
4465 public override void Emit (EmitContext ec)
4467 if (Report.Errors > 0)
4471 if (IsCompilerGenerated) {
4472 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4480 // If `HasReturnLabel' is set, then we already emitted a
4481 // jump to the end of the method, so we must emit a `ret'
4484 // Unfortunately, System.Reflection.Emit automatically emits
4485 // a leave to the end of a finally block. This is a problem
4486 // if no code is following the try/finally block since we may
4487 // jump to a point after the end of the method.
4488 // As a workaround, we're always creating a return label in
4491 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4492 if (ec.HasReturnLabel)
4493 ec.MarkLabel (ec.ReturnLabel);
4495 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4496 ec.Mark (EndLocation);
4498 if (ec.ReturnType.Kind != MemberKind.Void)
4499 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4501 ec.Emit (OpCodes.Ret);
4504 } catch (Exception e) {
4505 throw new InternalErrorException (e, StartLocation);
4509 public bool Resolve (BlockContext bc, IMethodData md)
4514 var errors = bc.Report.Errors;
4518 if (bc.Report.Errors > errors)
4521 MarkReachable (new Reachability ());
4523 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4524 // TODO: var md = bc.CurrentMemberDefinition;
4525 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4528 if ((flags & Flags.NoFlowAnalysis) != 0)
4531 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4534 } catch (Exception e) {
4535 throw new InternalErrorException (e, StartLocation);
4542 public class SwitchLabel : Statement
4550 // if expr == null, then it is the default case.
4552 public SwitchLabel (Expression expr, Location l)
4558 public bool IsDefault {
4560 return label == null;
4564 public Expression Label {
4570 public Location Location {
4576 public Constant Converted {
4585 public bool PatternMatching { get; set; }
4587 public bool SectionStart { get; set; }
4589 public Label GetILLabel (EmitContext ec)
4591 if (il_label == null){
4592 il_label = ec.DefineLabel ();
4595 return il_label.Value;
4598 protected override void DoEmit (EmitContext ec)
4600 ec.MarkLabel (GetILLabel (ec));
4603 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4608 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4612 public override bool Resolve (BlockContext bc)
4614 if (ResolveAndReduce (bc))
4615 bc.Switch.RegisterLabel (bc, this);
4621 // Resolves the expression, reduces it to a literal if possible
4622 // and then converts it to the requested type.
4624 bool ResolveAndReduce (BlockContext bc)
4629 var switch_statement = bc.Switch;
4631 if (PatternMatching) {
4632 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4633 return label != null;
4636 var c = label.ResolveLabelConstant (bc);
4640 if (switch_statement.IsNullable && c is NullLiteral) {
4645 if (switch_statement.IsPatternMatching) {
4646 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4650 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4651 return converted != null;
4654 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4656 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4657 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4660 protected override void CloneTo (CloneContext clonectx, Statement target)
4662 var t = (SwitchLabel) target;
4664 t.label = label.Clone (clonectx);
4667 public override object Accept (StructuralVisitor visitor)
4669 return visitor.Visit (this);
4672 public string GetSignatureForError ()
4675 if (converted == null)
4678 label = converted.GetValueAsLiteral ();
4680 return string.Format ("case {0}:", label);
4684 public class Switch : LoopStatement
4686 // structure used to hold blocks of keys while calculating table switch
4687 sealed class LabelsRange : IComparable<LabelsRange>
4689 public readonly long min;
4691 public readonly List<long> label_values;
4693 public LabelsRange (long value)
4696 label_values = new List<long> ();
4697 label_values.Add (value);
4700 public LabelsRange (long min, long max, ICollection<long> values)
4704 this.label_values = new List<long> (values);
4709 return max - min + 1;
4713 public bool AddValue (long value)
4715 var gap = value - min + 1;
4716 // Ensure the range has > 50% occupancy
4717 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4721 label_values.Add (value);
4725 public int CompareTo (LabelsRange other)
4727 int nLength = label_values.Count;
4728 int nLengthOther = other.label_values.Count;
4729 if (nLengthOther == nLength)
4730 return (int) (other.min - min);
4732 return nLength - nLengthOther;
4736 sealed class DispatchStatement : Statement
4738 readonly Switch body;
4740 public DispatchStatement (Switch body)
4745 protected override void CloneTo (CloneContext clonectx, Statement target)
4747 throw new NotImplementedException ();
4750 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4755 protected override void DoEmit (EmitContext ec)
4757 body.EmitDispatch (ec);
4761 class MissingBreak : Statement
4763 readonly SwitchLabel label;
4765 public MissingBreak (SwitchLabel sl)
4771 public bool FallOut { get; set; }
4773 protected override void DoEmit (EmitContext ec)
4777 protected override void CloneTo (CloneContext clonectx, Statement target)
4781 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4784 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4785 label.GetSignatureForError ());
4787 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4788 label.GetSignatureForError ());
4794 public Expression Expr;
4797 // Mapping of all labels to their SwitchLabels
4799 Dictionary<long, SwitchLabel> labels;
4800 Dictionary<string, SwitchLabel> string_labels;
4801 List<SwitchLabel> case_labels;
4803 List<Tuple<GotoCase, Constant>> goto_cases;
4804 List<DefiniteAssignmentBitSet> end_reachable_das;
4807 /// The governing switch type
4809 public TypeSpec SwitchType;
4811 Expression new_expr;
4813 SwitchLabel case_null;
4814 SwitchLabel case_default;
4816 Label defaultLabel, nullLabel;
4817 VariableReference value;
4818 ExpressionStatement string_dictionary;
4819 FieldExpr switch_cache_field;
4820 ExplicitBlock block;
4824 // Nullable Types support
4826 Nullable.Unwrap unwrap;
4828 public Switch (Expression e, ExplicitBlock block, Location l)
4836 public SwitchLabel ActiveLabel { get; set; }
4838 public ExplicitBlock Block {
4844 public SwitchLabel DefaultLabel {
4846 return case_default;
4850 public bool IsNullable {
4852 return unwrap != null;
4856 public bool IsPatternMatching {
4858 return new_expr == null && SwitchType != null;
4862 public List<SwitchLabel> RegisteredLabels {
4868 public VariableReference ExpressionValue {
4875 // Determines the governing type for a switch. The returned
4876 // expression might be the expression from the switch, or an
4877 // expression that includes any potential conversions to
4879 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4881 switch (expr.Type.BuiltinType) {
4882 case BuiltinTypeSpec.Type.Byte:
4883 case BuiltinTypeSpec.Type.SByte:
4884 case BuiltinTypeSpec.Type.UShort:
4885 case BuiltinTypeSpec.Type.Short:
4886 case BuiltinTypeSpec.Type.UInt:
4887 case BuiltinTypeSpec.Type.Int:
4888 case BuiltinTypeSpec.Type.ULong:
4889 case BuiltinTypeSpec.Type.Long:
4890 case BuiltinTypeSpec.Type.Char:
4891 case BuiltinTypeSpec.Type.String:
4892 case BuiltinTypeSpec.Type.Bool:
4896 if (expr.Type.IsEnum)
4900 // Try to find a *user* defined implicit conversion.
4902 // If there is no implicit conversion, or if there are multiple
4903 // conversions, we have to report an error
4905 Expression converted = null;
4906 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4908 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4911 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4912 Convert.UserConversionRestriction.ProbingOnly;
4915 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4917 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4922 // Ignore over-worked ImplicitUserConversions that do
4923 // an implicit conversion in addition to the user conversion.
4925 var uc = e as UserCast;
4929 if (converted != null){
4930 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4939 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4941 var types = module.Compiler.BuiltinTypes;
4943 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4944 TypeSpec[] stypes = new[] {
4957 if (nullable != null) {
4959 Array.Resize (ref stypes, stypes.Length + 9);
4961 for (int i = 0; i < 9; ++i) {
4962 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4969 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4971 case_labels.Add (sl);
4974 if (case_default != null) {
4975 sl.Error_AlreadyOccurs (rc, case_default);
4983 if (sl.Converted == null)
4987 if (string_labels != null) {
4988 string string_value = sl.Converted.GetValue () as string;
4989 if (string_value == null)
4992 string_labels.Add (string_value, sl);
4994 if (sl.Converted.IsNull) {
4997 labels.Add (sl.Converted.GetValueAsLong (), sl);
5000 } catch (ArgumentException) {
5001 if (string_labels != null)
5002 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5004 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5009 // This method emits code for a lookup-based switch statement (non-string)
5010 // Basically it groups the cases into blocks that are at least half full,
5011 // and then spits out individual lookup opcodes for each block.
5012 // It emits the longest blocks first, and short blocks are just
5013 // handled with direct compares.
5015 void EmitTableSwitch (EmitContext ec, Expression val)
5017 if (labels != null && labels.Count > 0) {
5018 List<LabelsRange> ranges;
5019 if (string_labels != null) {
5020 // We have done all hard work for string already
5021 // setup single range only
5022 ranges = new List<LabelsRange> (1);
5023 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5025 var element_keys = new long[labels.Count];
5026 labels.Keys.CopyTo (element_keys, 0);
5027 Array.Sort (element_keys);
5030 // Build possible ranges of switch labes to reduce number
5033 ranges = new List<LabelsRange> (element_keys.Length);
5034 var range = new LabelsRange (element_keys[0]);
5036 for (int i = 1; i < element_keys.Length; ++i) {
5037 var l = element_keys[i];
5038 if (range.AddValue (l))
5041 range = new LabelsRange (l);
5045 // sort the blocks so we can tackle the largest ones first
5049 Label lbl_default = defaultLabel;
5050 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5052 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5053 LabelsRange kb = ranges[range_index];
5054 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5056 // Optimize small ranges using simple equality check
5057 if (kb.Range <= 2) {
5058 foreach (var key in kb.label_values) {
5059 SwitchLabel sl = labels[key];
5060 if (sl == case_default || sl == case_null)
5063 if (sl.Converted.IsZeroInteger) {
5064 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5067 sl.Converted.Emit (ec);
5068 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5072 // TODO: if all the keys in the block are the same and there are
5073 // no gaps/defaults then just use a range-check.
5074 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5075 // TODO: optimize constant/I4 cases
5077 // check block range (could be > 2^31)
5079 ec.EmitLong (kb.min);
5080 ec.Emit (OpCodes.Blt, lbl_default);
5083 ec.EmitLong (kb.max);
5084 ec.Emit (OpCodes.Bgt, lbl_default);
5089 ec.EmitLong (kb.min);
5090 ec.Emit (OpCodes.Sub);
5093 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5097 int first = (int) kb.min;
5100 ec.Emit (OpCodes.Sub);
5101 } else if (first < 0) {
5102 ec.EmitInt (-first);
5103 ec.Emit (OpCodes.Add);
5107 // first, build the list of labels for the switch
5109 long cJumps = kb.Range;
5110 Label[] switch_labels = new Label[cJumps];
5111 for (int iJump = 0; iJump < cJumps; iJump++) {
5112 var key = kb.label_values[iKey];
5113 if (key == kb.min + iJump) {
5114 switch_labels[iJump] = labels[key].GetILLabel (ec);
5117 switch_labels[iJump] = lbl_default;
5121 // emit the switch opcode
5122 ec.Emit (OpCodes.Switch, switch_labels);
5125 // mark the default for this block
5126 if (range_index != 0)
5127 ec.MarkLabel (lbl_default);
5130 // the last default just goes to the end
5131 if (ranges.Count > 0)
5132 ec.Emit (OpCodes.Br, lbl_default);
5136 public SwitchLabel FindLabel (Constant value)
5138 SwitchLabel sl = null;
5140 if (string_labels != null) {
5141 string s = value.GetValue () as string;
5143 if (case_null != null)
5145 else if (case_default != null)
5148 string_labels.TryGetValue (s, out sl);
5151 if (value is NullLiteral) {
5154 labels.TryGetValue (value.GetValueAsLong (), out sl);
5158 if (sl == null || sl.SectionStart)
5162 // Always return section start, it simplifies handling of switch labels
5164 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5165 var cs = case_labels [idx];
5166 if (cs.SectionStart)
5171 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5173 Expr.FlowAnalysis (fc);
5175 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5176 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5177 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5179 block.FlowAnalysis (fc);
5181 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5183 if (end_reachable_das != null) {
5184 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5185 InitialDefinitiveAssignment |= sections_das;
5186 end_reachable_das = null;
5189 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5191 return case_default != null && !end_reachable;
5194 public override bool Resolve (BlockContext ec)
5196 Expr = Expr.Resolve (ec);
5201 // LAMESPEC: User conversion from non-nullable governing type has a priority
5203 new_expr = SwitchGoverningType (ec, Expr, false);
5205 if (new_expr == null) {
5206 if (Expr.Type.IsNullableType) {
5207 unwrap = Nullable.Unwrap.Create (Expr, false);
5212 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5213 // involving nullable Expr and nullable governing type is
5215 new_expr = SwitchGoverningType (ec, unwrap, true);
5219 Expression switch_expr;
5220 if (new_expr == null) {
5221 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5222 if (Expr.Type != InternalType.ErrorType) {
5223 ec.Report.Error (151, loc,
5224 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5225 Expr.Type.GetSignatureForError ());
5232 SwitchType = Expr.Type;
5234 switch_expr = new_expr;
5235 SwitchType = new_expr.Type;
5236 if (SwitchType.IsNullableType) {
5237 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5238 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5241 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5242 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5246 if (block.Statements.Count == 0)
5249 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5250 string_labels = new Dictionary<string, SwitchLabel> ();
5252 labels = new Dictionary<long, SwitchLabel> ();
5256 var constant = switch_expr as Constant;
5259 // Don't need extra variable for constant switch or switch with
5260 // only default case
5262 if (constant == null) {
5264 // Store switch expression for comparison purposes
5266 value = switch_expr as VariableReference;
5267 if (value == null && !HasOnlyDefaultSection ()) {
5268 var current_block = ec.CurrentBlock;
5269 ec.CurrentBlock = Block;
5270 // Create temporary variable inside switch scope
5271 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5273 ec.CurrentBlock = current_block;
5277 case_labels = new List<SwitchLabel> ();
5279 Switch old_switch = ec.Switch;
5281 var parent_los = ec.EnclosingLoopOrSwitch;
5282 ec.EnclosingLoopOrSwitch = this;
5284 var ok = Statement.Resolve (ec);
5286 ec.EnclosingLoopOrSwitch = parent_los;
5287 ec.Switch = old_switch;
5290 // Check if all goto cases are valid. Needs to be done after switch
5291 // is resolved because goto can jump forward in the scope.
5293 if (goto_cases != null) {
5294 foreach (var gc in goto_cases) {
5295 if (gc.Item1 == null) {
5296 if (DefaultLabel == null) {
5297 Goto.Error_UnknownLabel (ec, "default", loc);
5303 var sl = FindLabel (gc.Item2);
5305 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5307 gc.Item1.Label = sl;
5315 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5316 ResolveStringSwitchMap (ec);
5320 // Anonymous storey initialization has to happen before
5321 // any generated switch dispatch
5323 block.InsertStatement (0, new DispatchStatement (this));
5328 bool HasOnlyDefaultSection ()
5330 for (int i = 0; i < block.Statements.Count; ++i) {
5331 var s = block.Statements[i] as SwitchLabel;
5333 if (s == null || s.IsDefault)
5342 public override Reachability MarkReachable (Reachability rc)
5344 if (rc.IsUnreachable)
5347 base.MarkReachable (rc);
5349 block.MarkReachableScope (rc);
5351 if (block.Statements.Count == 0)
5354 SwitchLabel constant_label = null;
5355 var constant = new_expr as Constant;
5357 if (constant != null) {
5358 constant_label = FindLabel (constant) ?? case_default;
5359 if (constant_label == null) {
5360 block.Statements.RemoveAt (0);
5365 var section_rc = new Reachability ();
5366 SwitchLabel prev_label = null;
5368 for (int i = 0; i < block.Statements.Count; ++i) {
5369 var s = block.Statements[i];
5370 var sl = s as SwitchLabel;
5372 if (sl != null && sl.SectionStart) {
5374 // Section is marked already via goto case
5376 if (!sl.IsUnreachable) {
5377 section_rc = new Reachability ();
5381 if (section_rc.IsUnreachable) {
5383 // Common case. Previous label section end is unreachable as
5384 // it ends with break, return, etc. For next section revert
5385 // to reachable again unless we have constant switch block
5387 section_rc = constant_label != null && constant_label != sl ?
5388 Reachability.CreateUnreachable () :
5389 new Reachability ();
5390 } else if (prev_label != null) {
5392 // Error case as control cannot fall through from one case label
5394 sl.SectionStart = false;
5395 s = new MissingBreak (prev_label);
5396 s.MarkReachable (rc);
5397 block.Statements.Insert (i - 1, s);
5399 } else if (constant_label != null && constant_label != sl) {
5401 // Special case for the first unreachable label in constant
5404 section_rc = Reachability.CreateUnreachable ();
5410 section_rc = s.MarkReachable (section_rc);
5413 if (!section_rc.IsUnreachable && prev_label != null) {
5414 prev_label.SectionStart = false;
5415 var s = new MissingBreak (prev_label) {
5419 s.MarkReachable (rc);
5420 block.Statements.Add (s);
5424 // Reachability can affect parent only when all possible paths are handled but
5425 // we still need to run reachability check on switch body to check for fall-through
5427 if (case_default == null && constant_label == null)
5431 // We have at least one local exit from the switch
5436 return Reachability.CreateUnreachable ();
5439 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5441 if (goto_cases == null)
5442 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5444 goto_cases.Add (Tuple.Create (gotoCase, value));
5448 // Converts string switch into string hashtable
5450 void ResolveStringSwitchMap (ResolveContext ec)
5452 FullNamedExpression string_dictionary_type;
5453 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5454 string_dictionary_type = new TypeExpression (
5455 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5456 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5458 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5459 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5461 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5465 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5466 Field field = new Field (ctype, string_dictionary_type,
5467 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5468 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5469 if (!field.Define ())
5471 ctype.AddField (field);
5473 var init = new List<Expression> ();
5475 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5476 string value = null;
5478 foreach (SwitchLabel sl in case_labels) {
5480 if (sl.SectionStart)
5481 labels.Add (++counter, sl);
5483 if (sl == case_default || sl == case_null)
5486 value = (string) sl.Converted.GetValue ();
5487 var init_args = new List<Expression> (2);
5488 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5490 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5491 init_args.Add (sl.Converted);
5493 init.Add (new CollectionElementInitializer (init_args, loc));
5496 Arguments args = new Arguments (1);
5497 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5498 Expression initializer = new NewInitialize (string_dictionary_type, args,
5499 new CollectionOrObjectInitializers (init, loc), loc);
5501 switch_cache_field = new FieldExpr (field, loc);
5502 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5505 void DoEmitStringSwitch (EmitContext ec)
5507 Label l_initialized = ec.DefineLabel ();
5510 // Skip initialization when value is null
5512 value.EmitBranchable (ec, nullLabel, false);
5515 // Check if string dictionary is initialized and initialize
5517 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5518 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5519 string_dictionary.EmitStatement (ec);
5521 ec.MarkLabel (l_initialized);
5523 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5525 ResolveContext rc = new ResolveContext (ec.MemberContext);
5527 if (switch_cache_field.Type.IsGeneric) {
5528 Arguments get_value_args = new Arguments (2);
5529 get_value_args.Add (new Argument (value));
5530 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5531 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5532 if (get_item == null)
5536 // A value was not found, go to default case
5538 get_item.EmitBranchable (ec, defaultLabel, false);
5540 Arguments get_value_args = new Arguments (1);
5541 get_value_args.Add (new Argument (value));
5543 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5544 if (get_item == null)
5547 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5548 get_item_object.EmitAssign (ec, get_item, true, false);
5549 ec.Emit (OpCodes.Brfalse, defaultLabel);
5551 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5552 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5554 get_item_int.EmitStatement (ec);
5555 get_item_object.Release (ec);
5558 EmitTableSwitch (ec, string_switch_variable);
5559 string_switch_variable.Release (ec);
5563 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5565 void EmitShortSwitch (EmitContext ec)
5567 MethodSpec equal_method = null;
5568 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5569 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5572 if (equal_method != null) {
5573 value.EmitBranchable (ec, nullLabel, false);
5576 for (int i = 0; i < case_labels.Count; ++i) {
5577 var label = case_labels [i];
5578 if (label == case_default || label == case_null)
5581 var constant = label.Converted;
5583 if (constant == null) {
5584 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5588 if (equal_method != null) {
5592 var call = new CallEmitter ();
5593 call.EmitPredefined (ec, equal_method, new Arguments (0));
5594 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5598 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5599 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5605 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5608 ec.Emit (OpCodes.Br, defaultLabel);
5611 void EmitDispatch (EmitContext ec)
5613 if (IsPatternMatching) {
5614 EmitShortSwitch (ec);
5618 if (value == null) {
5620 // Constant switch, we've already done the work if there is only 1 label
5624 foreach (var sl in case_labels) {
5625 if (sl.IsUnreachable)
5628 if (reachable++ > 0) {
5629 var constant = (Constant) new_expr;
5630 var constant_label = FindLabel (constant) ?? case_default;
5632 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5640 if (string_dictionary != null) {
5641 DoEmitStringSwitch (ec);
5642 } else if (case_labels.Count < 4 || string_labels != null) {
5643 EmitShortSwitch (ec);
5645 EmitTableSwitch (ec, value);
5649 protected override void DoEmit (EmitContext ec)
5652 // Setup the codegen context
5654 Label old_end = ec.LoopEnd;
5655 Switch old_switch = ec.Switch;
5657 ec.LoopEnd = ec.DefineLabel ();
5660 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5661 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5663 if (value != null) {
5666 var switch_expr = new_expr ?? Expr;
5668 unwrap.EmitCheck (ec);
5669 ec.Emit (OpCodes.Brfalse, nullLabel);
5670 value.EmitAssign (ec, switch_expr, false, false);
5671 } else if (switch_expr != value) {
5672 value.EmitAssign (ec, switch_expr, false, false);
5677 // Next statement is compiler generated we don't need extra
5678 // nop when we can use the statement for sequence point
5680 ec.Mark (block.StartLocation);
5681 block.IsCompilerGenerated = true;
5683 new_expr.EmitSideEffect (ec);
5688 // Restore context state.
5689 ec.MarkLabel (ec.LoopEnd);
5692 // Restore the previous context
5694 ec.LoopEnd = old_end;
5695 ec.Switch = old_switch;
5698 protected override void CloneTo (CloneContext clonectx, Statement t)
5700 Switch target = (Switch) t;
5702 target.Expr = Expr.Clone (clonectx);
5703 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5706 public override object Accept (StructuralVisitor visitor)
5708 return visitor.Visit (this);
5711 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5713 if (case_default == null && !(new_expr is Constant))
5716 if (end_reachable_das == null)
5717 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5719 end_reachable_das.Add (fc.DefiniteAssignment);
5722 public override void SetEndReachable ()
5724 end_reachable = true;
5728 // A place where execution can restart in a state machine
5729 public abstract class ResumableStatement : Statement
5732 protected Label resume_point;
5734 public Label PrepareForEmit (EmitContext ec)
5738 resume_point = ec.DefineLabel ();
5740 return resume_point;
5743 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5748 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5753 public abstract class TryFinallyBlock : ExceptionStatement
5755 protected Statement stmt;
5756 Label dispose_try_block;
5757 bool prepared_for_dispose, emitted_dispose;
5758 Method finally_host;
5760 protected TryFinallyBlock (Statement stmt, Location loc)
5768 public Statement Statement {
5776 protected abstract void EmitTryBody (EmitContext ec);
5777 public abstract void EmitFinallyBody (EmitContext ec);
5779 public override Label PrepareForDispose (EmitContext ec, Label end)
5781 if (!prepared_for_dispose) {
5782 prepared_for_dispose = true;
5783 dispose_try_block = ec.DefineLabel ();
5785 return dispose_try_block;
5788 protected sealed override void DoEmit (EmitContext ec)
5790 EmitTryBodyPrepare (ec);
5793 bool beginFinally = EmitBeginFinallyBlock (ec);
5795 Label start_finally = ec.DefineLabel ();
5796 if (resume_points != null && beginFinally) {
5797 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5799 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5800 ec.Emit (OpCodes.Brfalse_S, start_finally);
5801 ec.Emit (OpCodes.Endfinally);
5804 ec.MarkLabel (start_finally);
5806 if (finally_host != null) {
5807 finally_host.Define ();
5808 finally_host.PrepareEmit ();
5809 finally_host.Emit ();
5811 // Now it's safe to add, to close it properly and emit sequence points
5812 finally_host.Parent.AddMember (finally_host);
5814 var ce = new CallEmitter ();
5815 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5816 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5818 EmitFinallyBody (ec);
5822 ec.EndExceptionBlock ();
5825 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5827 if (emitted_dispose)
5830 emitted_dispose = true;
5832 Label end_of_try = ec.DefineLabel ();
5834 // Ensure that the only way we can get into this code is through a dispatcher
5835 if (have_dispatcher)
5836 ec.Emit (OpCodes.Br, end);
5838 ec.BeginExceptionBlock ();
5840 ec.MarkLabel (dispose_try_block);
5842 Label[] labels = null;
5843 for (int i = 0; i < resume_points.Count; ++i) {
5844 ResumableStatement s = resume_points[i];
5845 Label ret = s.PrepareForDispose (ec, end_of_try);
5846 if (ret.Equals (end_of_try) && labels == null)
5848 if (labels == null) {
5849 labels = new Label[resume_points.Count];
5850 for (int j = 0; j < i; ++j)
5851 labels[j] = end_of_try;
5856 if (labels != null) {
5858 for (j = 1; j < labels.Length; ++j)
5859 if (!labels[0].Equals (labels[j]))
5861 bool emit_dispatcher = j < labels.Length;
5863 if (emit_dispatcher) {
5864 ec.Emit (OpCodes.Ldloc, pc);
5865 ec.EmitInt (first_resume_pc);
5866 ec.Emit (OpCodes.Sub);
5867 ec.Emit (OpCodes.Switch, labels);
5870 foreach (ResumableStatement s in resume_points)
5871 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5874 ec.MarkLabel (end_of_try);
5876 ec.BeginFinallyBlock ();
5878 if (finally_host != null) {
5879 var ce = new CallEmitter ();
5880 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5881 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5883 EmitFinallyBody (ec);
5886 ec.EndExceptionBlock ();
5889 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5891 var res = stmt.FlowAnalysis (fc);
5892 parent_try_block = null;
5896 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5898 ec.BeginFinallyBlock ();
5902 public override Reachability MarkReachable (Reachability rc)
5904 base.MarkReachable (rc);
5905 return Statement.MarkReachable (rc);
5908 public override bool Resolve (BlockContext bc)
5912 parent_try_block = bc.CurrentTryBlock;
5913 bc.CurrentTryBlock = this;
5915 if (stmt is TryCatch) {
5916 ok = stmt.Resolve (bc);
5918 using (bc.Set (ResolveContext.Options.TryScope)) {
5919 ok = stmt.Resolve (bc);
5923 bc.CurrentTryBlock = parent_try_block;
5926 // Finally block inside iterator is called from MoveNext and
5927 // Dispose methods that means we need to lift the block into
5928 // newly created host method to emit the body only once. The
5929 // original block then simply calls the newly generated method.
5931 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5932 var b = stmt as Block;
5933 if (b != null && b.Explicit.HasYield) {
5934 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5938 return base.Resolve (bc) && ok;
5943 // Base class for blocks using exception handling
5945 public abstract class ExceptionStatement : ResumableStatement
5947 protected List<ResumableStatement> resume_points;
5948 protected int first_resume_pc;
5949 protected ExceptionStatement parent_try_block;
5950 protected int first_catch_resume_pc = -1;
5952 protected ExceptionStatement (Location loc)
5957 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5959 StateMachineInitializer state_machine = null;
5960 if (resume_points != null) {
5961 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5963 ec.EmitInt ((int) IteratorStorey.State.Running);
5964 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5968 // The resume points in catch section when this is try-catch-finally
5970 if (IsRewrittenTryCatchFinally ()) {
5971 ec.BeginExceptionBlock ();
5973 if (first_catch_resume_pc >= 0) {
5975 ec.MarkLabel (resume_point);
5977 // For normal control flow, we want to fall-through the Switch
5978 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5979 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5980 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
5981 ec.Emit (OpCodes.Sub);
5983 var labels = new Label [resume_points.Count - first_catch_resume_pc];
5984 for (int i = 0; i < labels.Length; ++i)
5985 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
5986 ec.Emit (OpCodes.Switch, labels);
5990 ec.BeginExceptionBlock ();
5993 // The resume points for try section
5995 if (resume_points != null && first_catch_resume_pc != 0) {
5996 if (first_catch_resume_pc < 0)
5997 ec.MarkLabel (resume_point);
5999 // For normal control flow, we want to fall-through the Switch
6000 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6001 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6002 ec.EmitInt (first_resume_pc);
6003 ec.Emit (OpCodes.Sub);
6005 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6006 for (int i = 0; i < labels.Length; ++i)
6007 labels[i] = resume_points[i].PrepareForEmit (ec);
6008 ec.Emit (OpCodes.Switch, labels);
6012 bool IsRewrittenTryCatchFinally ()
6014 var tf = this as TryFinally;
6018 var tc = tf.Statement as TryCatch;
6022 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6025 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6027 if (parent_try_block != null) {
6028 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6030 pc = stateMachine.AddResumePoint (this);
6033 if (resume_points == null) {
6034 resume_points = new List<ResumableStatement> ();
6035 first_resume_pc = pc;
6038 if (pc != first_resume_pc + resume_points.Count)
6039 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6041 var tf = this as TryFinally;
6042 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6043 first_catch_resume_pc = resume_points.Count;
6046 resume_points.Add (stmt);
6051 public class Lock : TryFinallyBlock
6054 TemporaryVariableReference expr_copy;
6055 TemporaryVariableReference lock_taken;
6057 public Lock (Expression expr, Statement stmt, Location loc)
6063 public Expression Expr {
6069 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6071 expr.FlowAnalysis (fc);
6072 return base.DoFlowAnalysis (fc);
6075 public override bool Resolve (BlockContext ec)
6077 expr = expr.Resolve (ec);
6081 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6082 ec.Report.Error (185, loc,
6083 "`{0}' is not a reference type as required by the lock statement",
6084 expr.Type.GetSignatureForError ());
6087 if (expr.Type.IsGenericParameter) {
6088 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6091 VariableReference lv = expr as VariableReference;
6094 locked = lv.IsLockedByStatement;
6095 lv.IsLockedByStatement = true;
6102 // Have to keep original lock value around to unlock same location
6103 // in the case of original value has changed or is null
6105 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6106 expr_copy.Resolve (ec);
6109 // Ensure Monitor methods are available
6111 if (ResolvePredefinedMethods (ec) > 1) {
6112 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6113 lock_taken.Resolve (ec);
6116 using (ec.Set (ResolveContext.Options.LockScope)) {
6121 lv.IsLockedByStatement = locked;
6127 protected override void EmitTryBodyPrepare (EmitContext ec)
6129 expr_copy.EmitAssign (ec, expr);
6131 if (lock_taken != null) {
6133 // Initialize ref variable
6135 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6138 // Monitor.Enter (expr_copy)
6140 expr_copy.Emit (ec);
6141 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6144 base.EmitTryBodyPrepare (ec);
6147 protected override void EmitTryBody (EmitContext ec)
6150 // Monitor.Enter (expr_copy, ref lock_taken)
6152 if (lock_taken != null) {
6153 expr_copy.Emit (ec);
6154 lock_taken.LocalInfo.CreateBuilder (ec);
6155 lock_taken.AddressOf (ec, AddressOp.Load);
6156 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6159 Statement.Emit (ec);
6162 public override void EmitFinallyBody (EmitContext ec)
6165 // if (lock_taken) Monitor.Exit (expr_copy)
6167 Label skip = ec.DefineLabel ();
6169 if (lock_taken != null) {
6170 lock_taken.Emit (ec);
6171 ec.Emit (OpCodes.Brfalse_S, skip);
6174 expr_copy.Emit (ec);
6175 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6177 ec.Emit (OpCodes.Call, m);
6179 ec.MarkLabel (skip);
6182 int ResolvePredefinedMethods (ResolveContext rc)
6184 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6185 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6189 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6193 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6197 protected override void CloneTo (CloneContext clonectx, Statement t)
6199 Lock target = (Lock) t;
6201 target.expr = expr.Clone (clonectx);
6202 target.stmt = Statement.Clone (clonectx);
6205 public override object Accept (StructuralVisitor visitor)
6207 return visitor.Visit (this);
6212 public class Unchecked : Statement {
6215 public Unchecked (Block b, Location loc)
6222 public override bool Resolve (BlockContext ec)
6224 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6225 return Block.Resolve (ec);
6228 protected override void DoEmit (EmitContext ec)
6230 using (ec.With (EmitContext.Options.CheckedScope, false))
6234 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6236 return Block.FlowAnalysis (fc);
6239 public override Reachability MarkReachable (Reachability rc)
6241 base.MarkReachable (rc);
6242 return Block.MarkReachable (rc);
6245 protected override void CloneTo (CloneContext clonectx, Statement t)
6247 Unchecked target = (Unchecked) t;
6249 target.Block = clonectx.LookupBlock (Block);
6252 public override object Accept (StructuralVisitor visitor)
6254 return visitor.Visit (this);
6258 public class Checked : Statement {
6261 public Checked (Block b, Location loc)
6264 b.Unchecked = false;
6268 public override bool Resolve (BlockContext ec)
6270 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6271 return Block.Resolve (ec);
6274 protected override void DoEmit (EmitContext ec)
6276 using (ec.With (EmitContext.Options.CheckedScope, true))
6280 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6282 return Block.FlowAnalysis (fc);
6285 public override Reachability MarkReachable (Reachability rc)
6287 base.MarkReachable (rc);
6288 return Block.MarkReachable (rc);
6291 protected override void CloneTo (CloneContext clonectx, Statement t)
6293 Checked target = (Checked) t;
6295 target.Block = clonectx.LookupBlock (Block);
6298 public override object Accept (StructuralVisitor visitor)
6300 return visitor.Visit (this);
6304 public class Unsafe : Statement {
6307 public Unsafe (Block b, Location loc)
6310 Block.Unsafe = true;
6314 public override bool Resolve (BlockContext ec)
6316 if (ec.CurrentIterator != null)
6317 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6319 using (ec.Set (ResolveContext.Options.UnsafeScope))
6320 return Block.Resolve (ec);
6323 protected override void DoEmit (EmitContext ec)
6328 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6330 return Block.FlowAnalysis (fc);
6333 public override Reachability MarkReachable (Reachability rc)
6335 base.MarkReachable (rc);
6336 return Block.MarkReachable (rc);
6339 protected override void CloneTo (CloneContext clonectx, Statement t)
6341 Unsafe target = (Unsafe) t;
6343 target.Block = clonectx.LookupBlock (Block);
6346 public override object Accept (StructuralVisitor visitor)
6348 return visitor.Visit (this);
6355 public class Fixed : Statement
6357 abstract class Emitter : ShimExpression
6359 protected LocalVariable vi;
6361 protected Emitter (Expression expr, LocalVariable li)
6367 public abstract void EmitExit (EmitContext ec);
6369 public override void FlowAnalysis (FlowAnalysisContext fc)
6371 expr.FlowAnalysis (fc);
6375 sealed class ExpressionEmitter : Emitter {
6376 public ExpressionEmitter (Expression converted, LocalVariable li)
6377 : base (converted, li)
6381 protected override Expression DoResolve (ResolveContext rc)
6383 throw new NotImplementedException ();
6386 public override void Emit (EmitContext ec) {
6388 // Store pointer in pinned location
6394 public override void EmitExit (EmitContext ec)
6397 ec.Emit (OpCodes.Conv_U);
6402 class StringEmitter : Emitter
6404 LocalVariable pinned_string;
6406 public StringEmitter (Expression expr, LocalVariable li)
6411 protected override Expression DoResolve (ResolveContext rc)
6413 pinned_string = new LocalVariable (vi.Block, "$pinned",
6414 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6416 pinned_string.Type = rc.BuiltinTypes.String;
6419 eclass = ExprClass.Variable;
6420 type = rc.BuiltinTypes.Int;
6424 public override void Emit (EmitContext ec)
6426 pinned_string.CreateBuilder (ec);
6429 pinned_string.EmitAssign (ec);
6431 // TODO: Should use Binary::Add
6432 pinned_string.Emit (ec);
6433 ec.Emit (OpCodes.Conv_I);
6435 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6439 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6440 //pe.InstanceExpression = pinned_string;
6441 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6443 ec.Emit (OpCodes.Add);
6447 public override void EmitExit (EmitContext ec)
6450 pinned_string.EmitAssign (ec);
6454 public class VariableDeclaration : BlockVariable
6456 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6461 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6463 if (!Variable.Type.IsPointer && li == Variable) {
6464 bc.Report.Error (209, TypeExpression.Location,
6465 "The type of locals declared in a fixed statement must be a pointer type");
6469 var res = initializer.Resolve (bc);
6476 var ac = res.Type as ArrayContainer;
6478 TypeSpec array_type = ac.Element;
6481 // Provided that array_type is unmanaged,
6483 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6486 Expression res_init;
6487 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6490 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6491 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6492 res = expr_variable.CreateReferenceExpression (bc, loc);
6496 // and T* is implicitly convertible to the
6497 // pointer type given in the fixed statement.
6499 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6501 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6502 if (converted == null)
6506 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6508 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6509 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6510 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6511 new NullLiteral (loc),
6514 converted = converted.Resolve (bc);
6516 return new ExpressionEmitter (converted, li);
6522 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6523 return new StringEmitter (res, li).Resolve (bc);
6526 // Case 3: fixed buffer
6527 if (res is FixedBufferPtr) {
6528 return new ExpressionEmitter (res, li);
6531 bool already_fixed = true;
6534 // Case 4: & object.
6536 Unary u = res as Unary;
6538 if (u.Oper == Unary.Operator.AddressOf) {
6539 IVariableReference vr = u.Expr as IVariableReference;
6540 if (vr == null || !vr.IsFixed) {
6541 already_fixed = false;
6544 } else if (initializer is Cast) {
6545 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6549 if (already_fixed) {
6550 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6553 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6554 return new ExpressionEmitter (res, li);
6559 VariableDeclaration decl;
6560 Statement statement;
6563 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6572 public Statement Statement {
6578 public BlockVariable Variables {
6586 public override bool Resolve (BlockContext bc)
6588 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6589 if (!decl.Resolve (bc))
6593 return statement.Resolve (bc);
6596 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6598 decl.FlowAnalysis (fc);
6599 return statement.FlowAnalysis (fc);
6602 protected override void DoEmit (EmitContext ec)
6604 decl.Variable.CreateBuilder (ec);
6605 decl.Initializer.Emit (ec);
6606 if (decl.Declarators != null) {
6607 foreach (var d in decl.Declarators) {
6608 d.Variable.CreateBuilder (ec);
6609 d.Initializer.Emit (ec);
6613 statement.Emit (ec);
6619 // Clear the pinned variable
6621 ((Emitter) decl.Initializer).EmitExit (ec);
6622 if (decl.Declarators != null) {
6623 foreach (var d in decl.Declarators) {
6624 ((Emitter)d.Initializer).EmitExit (ec);
6629 public override Reachability MarkReachable (Reachability rc)
6631 base.MarkReachable (rc);
6633 decl.MarkReachable (rc);
6635 rc = statement.MarkReachable (rc);
6637 // TODO: What if there is local exit?
6638 has_ret = rc.IsUnreachable;
6642 protected override void CloneTo (CloneContext clonectx, Statement t)
6644 Fixed target = (Fixed) t;
6646 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6647 target.statement = statement.Clone (clonectx);
6650 public override object Accept (StructuralVisitor visitor)
6652 return visitor.Visit (this);
6656 public class Catch : Statement
6658 class CatchVariableStore : Statement
6660 readonly Catch ctch;
6662 public CatchVariableStore (Catch ctch)
6667 protected override void CloneTo (CloneContext clonectx, Statement target)
6671 protected override void DoEmit (EmitContext ec)
6673 // Emits catch variable debug information inside correct block
6674 ctch.EmitCatchVariableStore (ec);
6677 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6683 class FilterStatement : Statement
6685 readonly Catch ctch;
6687 public FilterStatement (Catch ctch)
6692 protected override void CloneTo (CloneContext clonectx, Statement target)
6696 protected override void DoEmit (EmitContext ec)
6698 if (ctch.li != null) {
6699 if (ctch.hoisted_temp != null)
6700 ctch.hoisted_temp.Emit (ec);
6704 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6705 ec.Emit (OpCodes.Box, ctch.type);
6708 var expr_start = ec.DefineLabel ();
6709 var end = ec.DefineLabel ();
6711 ec.Emit (OpCodes.Brtrue_S, expr_start);
6713 ec.Emit (OpCodes.Br, end);
6714 ec.MarkLabel (expr_start);
6716 ctch.Filter.Emit (ec);
6719 ec.Emit (OpCodes.Endfilter);
6720 ec.BeginFilterHandler ();
6721 ec.Emit (OpCodes.Pop);
6724 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6726 ctch.Filter.FlowAnalysis (fc);
6730 public override bool Resolve (BlockContext bc)
6732 ctch.Filter = ctch.Filter.Resolve (bc);
6734 if (ctch.Filter != null) {
6735 if (ctch.Filter.ContainsEmitWithAwait ()) {
6736 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6739 var c = ctch.Filter as Constant;
6740 if (c != null && !c.IsDefaultValue) {
6741 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6749 ExplicitBlock block;
6751 FullNamedExpression type_expr;
6752 CompilerAssign assign;
6754 LocalTemporary hoisted_temp;
6756 public Catch (ExplicitBlock block, Location loc)
6764 public ExplicitBlock Block {
6770 public TypeSpec CatchType {
6776 public Expression Filter {
6780 public bool IsGeneral {
6782 return type_expr == null;
6786 public FullNamedExpression TypeExpression {
6795 public LocalVariable Variable {
6806 protected override void DoEmit (EmitContext ec)
6808 if (Filter != null) {
6809 ec.BeginExceptionFilterBlock ();
6810 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6812 if (Block.HasAwait) {
6813 Block.EmitScopeInitialization (ec);
6822 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6824 ec.BeginCatchBlock (CatchType);
6827 ec.Emit (OpCodes.Pop);
6829 if (Block.HasAwait) {
6831 EmitCatchVariableStore (ec);
6837 void EmitCatchVariableStore (EmitContext ec)
6839 li.CreateBuilder (ec);
6842 // For hoisted catch variable we have to use a temporary local variable
6843 // for captured variable initialization during storey setup because variable
6844 // needs to be on the stack after storey instance for stfld operation
6846 if (li.HoistedVariant != null) {
6847 hoisted_temp = new LocalTemporary (li.Type);
6848 hoisted_temp.Store (ec);
6850 // switch to assignment from temporary variable and not from top of the stack
6851 assign.UpdateSource (hoisted_temp);
6855 public override bool Resolve (BlockContext bc)
6857 using (bc.Set (ResolveContext.Options.CatchScope)) {
6858 if (type_expr == null) {
6859 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6860 if (!block.HasAwait || Filter != null)
6861 block.AddScopeStatement (new CatchVariableStore (this));
6863 Expression source = new EmptyExpression (li.Type);
6864 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6865 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6868 type = type_expr.ResolveAsType (bc);
6873 CreateExceptionVariable (type);
6875 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6876 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6877 } else if (li != null) {
6879 li.PrepareAssignmentAnalysis (bc);
6881 // source variable is at the top of the stack
6882 Expression source = new EmptyExpression (li.Type);
6883 if (li.Type.IsGenericParameter)
6884 source = new UnboxCast (source, li.Type);
6886 if (!block.HasAwait || Filter != null)
6887 block.AddScopeStatement (new CatchVariableStore (this));
6890 // Uses Location.Null to hide from symbol file
6892 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6893 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6897 if (Filter != null) {
6898 Block.AddScopeStatement (new FilterStatement (this));
6901 Block.SetCatchBlock ();
6902 return Block.Resolve (bc);
6906 bool CreateExceptionVariable (TypeSpec type)
6908 if (!Block.HasAwait)
6911 // TODO: Scan the block for rethrow expression
6912 //if (!Block.HasRethrow)
6915 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6919 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6921 if (li != null && !li.IsCompilerGenerated) {
6922 fc.SetVariableAssigned (li.VariableInfo, true);
6925 return block.FlowAnalysis (fc);
6928 public override Reachability MarkReachable (Reachability rc)
6930 base.MarkReachable (rc);
6932 var c = Filter as Constant;
6933 if (c != null && c.IsDefaultValue)
6934 return Reachability.CreateUnreachable ();
6936 return block.MarkReachable (rc);
6939 protected override void CloneTo (CloneContext clonectx, Statement t)
6941 Catch target = (Catch) t;
6943 if (type_expr != null)
6944 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6947 target.Filter = Filter.Clone (clonectx);
6949 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6953 public class TryFinally : TryFinallyBlock
6956 List<DefiniteAssignmentBitSet> try_exit_dat;
6957 List<Label> redirected_jumps;
6958 Label? start_fin_label;
6960 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6966 public ExplicitBlock FinallyBlock {
6972 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6974 if (try_exit_dat == null)
6975 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6977 try_exit_dat.Add (vector);
6980 public override bool Resolve (BlockContext bc)
6982 bool ok = base.Resolve (bc);
6984 fini.SetFinallyBlock ();
6985 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6986 ok &= fini.Resolve (bc);
6992 protected override void EmitTryBody (EmitContext ec)
6994 if (fini.HasAwait) {
6995 if (ec.TryFinallyUnwind == null)
6996 ec.TryFinallyUnwind = new List<TryFinally> ();
6998 ec.TryFinallyUnwind.Add (this);
7001 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7002 ec.EndExceptionBlock ();
7004 ec.TryFinallyUnwind.Remove (this);
7006 if (start_fin_label != null)
7007 ec.MarkLabel (start_fin_label.Value);
7015 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7020 return base.EmitBeginFinallyBlock (ec);
7023 public override void EmitFinallyBody (EmitContext ec)
7025 if (!fini.HasAwait) {
7031 // Emits catch block like
7033 // catch (object temp) {
7034 // this.exception_field = temp;
7037 var type = ec.BuiltinTypes.Object;
7038 ec.BeginCatchBlock (type);
7040 var temp = ec.GetTemporaryLocal (type);
7041 ec.Emit (OpCodes.Stloc, temp);
7043 var exception_field = ec.GetTemporaryField (type);
7044 exception_field.AutomaticallyReuse = false;
7046 ec.Emit (OpCodes.Ldloc, temp);
7047 exception_field.EmitAssignFromStack (ec);
7049 ec.EndExceptionBlock ();
7051 ec.FreeTemporaryLocal (temp, type);
7056 // Emits exception rethrow
7058 // if (this.exception_field != null)
7059 // throw this.exception_field;
7061 exception_field.Emit (ec);
7062 var skip_throw = ec.DefineLabel ();
7063 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7064 exception_field.Emit (ec);
7065 ec.Emit (OpCodes.Throw);
7066 ec.MarkLabel (skip_throw);
7068 exception_field.PrepareCleanup (ec);
7070 EmitUnwindFinallyTable (ec);
7073 bool IsParentBlock (Block block)
7075 for (Block b = fini; b != null; b = b.Parent) {
7083 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7086 if (labelBlock != null) {
7087 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7088 var fin = ec.TryFinallyUnwind [idx - 1];
7089 if (!fin.IsParentBlock (labelBlock))
7096 bool set_return_state = true;
7098 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7099 var fin = ec.TryFinallyUnwind [idx];
7100 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7103 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7104 set_return_state = false;
7106 if (fin.start_fin_label == null) {
7107 fin.start_fin_label = ec.DefineLabel ();
7110 label = fin.start_fin_label.Value;
7116 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7118 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7121 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7123 if (redirected_jumps == null) {
7124 redirected_jumps = new List<Label> ();
7126 // Add fallthrough label
7127 redirected_jumps.Add (ec.DefineLabel ());
7130 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7133 int index = redirected_jumps.IndexOf (label);
7135 redirected_jumps.Add (label);
7136 index = redirected_jumps.Count - 1;
7140 // Indicates we have captured exit jump
7142 if (setReturnState) {
7143 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7144 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7149 // Emits state table of jumps outside of try block and reload of return
7150 // value when try block returns value
7152 void EmitUnwindFinallyTable (EmitContext ec)
7154 if (redirected_jumps == null)
7157 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7158 initializer.HoistedReturnState.EmitLoad (ec);
7159 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7161 // Mark fallthrough label
7162 ec.MarkLabel (redirected_jumps [0]);
7165 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7167 var da = fc.BranchDefiniteAssignment ();
7169 var tf = fc.TryFinally;
7170 fc.TryFinally = this;
7172 var res_stmt = Statement.FlowAnalysis (fc);
7176 var try_da = fc.DefiniteAssignment;
7177 fc.DefiniteAssignment = da;
7179 var res_fin = fini.FlowAnalysis (fc);
7181 if (try_exit_dat != null) {
7183 // try block has global exit but we need to run definite assignment check
7184 // for parameter block out parameter after finally block because it's always
7185 // executed before exit
7187 foreach (var try_da_part in try_exit_dat)
7188 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7190 try_exit_dat = null;
7193 fc.DefiniteAssignment |= try_da;
7194 return res_stmt | res_fin;
7197 public override Reachability MarkReachable (Reachability rc)
7200 // Mark finally block first for any exit statement in try block
7201 // to know whether the code which follows finally is reachable
7203 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7206 protected override void CloneTo (CloneContext clonectx, Statement t)
7208 TryFinally target = (TryFinally) t;
7210 target.stmt = stmt.Clone (clonectx);
7212 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7215 public override object Accept (StructuralVisitor visitor)
7217 return visitor.Visit (this);
7221 public class TryCatch : ExceptionStatement
7224 List<Catch> clauses;
7225 readonly bool inside_try_finally;
7226 List<Catch> catch_sm;
7228 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7232 this.clauses = catch_clauses;
7233 this.inside_try_finally = inside_try_finally;
7236 public List<Catch> Clauses {
7242 public bool HasClauseWithAwait {
7244 return catch_sm != null;
7248 public bool IsTryCatchFinally {
7250 return inside_try_finally;
7254 public override bool Resolve (BlockContext bc)
7258 using (bc.Set (ResolveContext.Options.TryScope)) {
7260 parent_try_block = bc.CurrentTryBlock;
7262 if (IsTryCatchFinally) {
7263 ok = Block.Resolve (bc);
7265 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7266 bc.CurrentTryBlock = this;
7267 ok = Block.Resolve (bc);
7268 bc.CurrentTryBlock = parent_try_block;
7273 var prev_catch = bc.CurrentTryCatch;
7274 bc.CurrentTryCatch = this;
7276 for (int i = 0; i < clauses.Count; ++i) {
7279 ok &= c.Resolve (bc);
7281 if (c.Block.HasAwait) {
7282 if (catch_sm == null)
7283 catch_sm = new List<Catch> ();
7288 if (c.Filter != null)
7291 TypeSpec resolved_type = c.CatchType;
7292 if (resolved_type == null)
7295 for (int ii = 0; ii < clauses.Count; ++ii) {
7299 if (clauses[ii].Filter != null)
7302 if (clauses[ii].IsGeneral) {
7303 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7306 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7309 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7312 bc.Report.Warning (1058, 1, c.loc,
7313 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7321 var ct = clauses[ii].CatchType;
7325 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7326 bc.Report.Error (160, c.loc,
7327 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7328 ct.GetSignatureForError ());
7334 bc.CurrentTryCatch = prev_catch;
7336 return base.Resolve (bc) && ok;
7339 protected sealed override void DoEmit (EmitContext ec)
7341 if (!inside_try_finally)
7342 EmitTryBodyPrepare (ec);
7346 LocalBuilder state_variable = null;
7347 foreach (Catch c in clauses) {
7350 if (catch_sm != null) {
7351 if (state_variable == null) {
7353 // Cannot reuse temp variable because non-catch path assumes the value is 0
7354 // which may not be true for reused local variable
7356 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7359 var index = catch_sm.IndexOf (c);
7363 ec.EmitInt (index + 1);
7364 ec.Emit (OpCodes.Stloc, state_variable);
7368 if (state_variable == null) {
7369 if (!inside_try_finally)
7370 ec.EndExceptionBlock ();
7372 ec.EndExceptionBlock ();
7374 ec.Emit (OpCodes.Ldloc, state_variable);
7376 var labels = new Label [catch_sm.Count + 1];
7377 for (int i = 0; i < labels.Length; ++i) {
7378 labels [i] = ec.DefineLabel ();
7381 var end = ec.DefineLabel ();
7382 ec.Emit (OpCodes.Switch, labels);
7384 // 0 value is default label
7385 ec.MarkLabel (labels [0]);
7386 ec.Emit (OpCodes.Br, end);
7388 var atv = ec.AsyncThrowVariable;
7390 for (int i = 0; i < catch_sm.Count; ++i) {
7391 if (c != null && c.Block.HasReachableClosingBrace)
7392 ec.Emit (OpCodes.Br, end);
7394 ec.MarkLabel (labels [i + 1]);
7396 ec.AsyncThrowVariable = c.Variable;
7399 ec.AsyncThrowVariable = atv;
7405 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7407 var start_fc = fc.BranchDefiniteAssignment ();
7408 var res = Block.FlowAnalysis (fc);
7410 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7412 foreach (var c in clauses) {
7413 fc.BranchDefiniteAssignment (start_fc);
7414 if (!c.FlowAnalysis (fc)) {
7416 try_fc = fc.DefiniteAssignment;
7418 try_fc &= fc.DefiniteAssignment;
7424 fc.DefiniteAssignment = try_fc ?? start_fc;
7425 parent_try_block = null;
7429 public override Reachability MarkReachable (Reachability rc)
7431 if (rc.IsUnreachable)
7434 base.MarkReachable (rc);
7436 var tc_rc = Block.MarkReachable (rc);
7438 foreach (var c in clauses)
7439 tc_rc &= c.MarkReachable (rc);
7444 protected override void CloneTo (CloneContext clonectx, Statement t)
7446 TryCatch target = (TryCatch) t;
7448 target.Block = clonectx.LookupBlock (Block);
7449 if (clauses != null){
7450 target.clauses = new List<Catch> ();
7451 foreach (Catch c in clauses)
7452 target.clauses.Add ((Catch) c.Clone (clonectx));
7456 public override object Accept (StructuralVisitor visitor)
7458 return visitor.Visit (this);
7462 public class Using : TryFinallyBlock
7464 public class VariableDeclaration : BlockVariable
7466 Statement dispose_call;
7468 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7473 public VariableDeclaration (LocalVariable li, Location loc)
7480 public VariableDeclaration (Expression expr)
7483 loc = expr.Location;
7489 public bool IsNested { get; private set; }
7493 public void EmitDispose (EmitContext ec)
7495 dispose_call.Emit (ec);
7498 public override bool Resolve (BlockContext bc)
7503 return base.Resolve (bc, false);
7506 public Expression ResolveExpression (BlockContext bc)
7508 var e = Initializer.Resolve (bc);
7512 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7513 Initializer = ResolveInitializer (bc, Variable, e);
7517 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7519 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7520 initializer = initializer.Resolve (bc);
7521 if (initializer == null)
7524 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7525 Arguments args = new Arguments (1);
7526 args.Add (new Argument (initializer));
7527 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7528 if (initializer == null)
7531 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7532 dispose_call = CreateDisposeCall (bc, var);
7533 dispose_call.Resolve (bc);
7535 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7538 if (li == Variable) {
7539 CheckIDiposableConversion (bc, li, initializer);
7540 dispose_call = CreateDisposeCall (bc, li);
7541 dispose_call.Resolve (bc);
7544 return base.ResolveInitializer (bc, li, initializer);
7547 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7551 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7552 if (type.IsNullableType) {
7553 // it's handled in CreateDisposeCall
7557 if (type != InternalType.ErrorType) {
7558 bc.Report.SymbolRelatedToPreviousError (type);
7559 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7560 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7561 type.GetSignatureForError ());
7568 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7570 var target = bc.BuiltinTypes.IDisposable;
7571 var tp = type as TypeParameterSpec;
7573 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7575 return type.ImplementsInterface (target, false);
7578 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7580 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7582 var loc = lv.Location;
7584 var idt = bc.BuiltinTypes.IDisposable;
7585 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7587 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7588 dispose_mg.InstanceExpression = type.IsNullableType ?
7589 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7593 // Hide it from symbol file via null location
7595 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7597 // Add conditional call when disposing possible null variable
7598 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7599 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7604 public void ResolveDeclaratorInitializer (BlockContext bc)
7606 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7609 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7611 for (int i = declarators.Count - 1; i >= 0; --i) {
7612 var d = declarators [i];
7613 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7614 vd.Initializer = d.Initializer;
7616 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7617 vd.dispose_call.Resolve (bc);
7619 stmt = new Using (vd, stmt, d.Variable.Location);
7626 public override object Accept (StructuralVisitor visitor)
7628 return visitor.Visit (this);
7632 VariableDeclaration decl;
7634 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7640 public Using (Expression expr, Statement stmt, Location loc)
7643 this.decl = new VariableDeclaration (expr);
7648 public Expression Expr {
7650 return decl.Variable == null ? decl.Initializer : null;
7654 public BlockVariable Variables {
7662 public override void Emit (EmitContext ec)
7665 // Don't emit sequence point it will be set on variable declaration
7670 protected override void EmitTryBodyPrepare (EmitContext ec)
7673 base.EmitTryBodyPrepare (ec);
7676 protected override void EmitTryBody (EmitContext ec)
7681 public override void EmitFinallyBody (EmitContext ec)
7683 decl.EmitDispose (ec);
7686 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7688 decl.FlowAnalysis (fc);
7689 return stmt.FlowAnalysis (fc);
7692 public override Reachability MarkReachable (Reachability rc)
7694 decl.MarkReachable (rc);
7695 return base.MarkReachable (rc);
7698 public override bool Resolve (BlockContext ec)
7700 VariableReference vr;
7701 bool vr_locked = false;
7703 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7704 if (decl.Variable == null) {
7705 vr = decl.ResolveExpression (ec) as VariableReference;
7707 vr_locked = vr.IsLockedByStatement;
7708 vr.IsLockedByStatement = true;
7711 if (decl.IsNested) {
7712 decl.ResolveDeclaratorInitializer (ec);
7714 if (!decl.Resolve (ec))
7717 if (decl.Declarators != null) {
7718 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7726 var ok = base.Resolve (ec);
7729 vr.IsLockedByStatement = vr_locked;
7734 protected override void CloneTo (CloneContext clonectx, Statement t)
7736 Using target = (Using) t;
7738 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7739 target.stmt = stmt.Clone (clonectx);
7742 public override object Accept (StructuralVisitor visitor)
7744 return visitor.Visit (this);
7749 /// Implementation of the foreach C# statement
7751 public class Foreach : LoopStatement
7753 abstract class IteratorStatement : Statement
7755 protected readonly Foreach for_each;
7757 protected IteratorStatement (Foreach @foreach)
7759 this.for_each = @foreach;
7760 this.loc = @foreach.expr.Location;
7763 protected override void CloneTo (CloneContext clonectx, Statement target)
7765 throw new NotImplementedException ();
7768 public override void Emit (EmitContext ec)
7770 if (ec.EmitAccurateDebugInfo) {
7771 ec.Emit (OpCodes.Nop);
7777 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7779 throw new NotImplementedException ();
7783 sealed class ArrayForeach : IteratorStatement
7785 TemporaryVariableReference[] lengths;
7786 Expression [] length_exprs;
7787 StatementExpression[] counter;
7788 TemporaryVariableReference[] variables;
7790 TemporaryVariableReference copy;
7792 public ArrayForeach (Foreach @foreach, int rank)
7795 counter = new StatementExpression[rank];
7796 variables = new TemporaryVariableReference[rank];
7797 length_exprs = new Expression [rank];
7800 // Only use temporary length variables when dealing with
7801 // multi-dimensional arrays
7804 lengths = new TemporaryVariableReference [rank];
7807 public override bool Resolve (BlockContext ec)
7809 Block variables_block = for_each.variable.Block;
7810 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7813 int rank = length_exprs.Length;
7814 Arguments list = new Arguments (rank);
7815 for (int i = 0; i < rank; i++) {
7816 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7818 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7819 counter[i].Resolve (ec);
7822 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7824 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7825 lengths[i].Resolve (ec);
7827 Arguments args = new Arguments (1);
7828 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7829 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7832 list.Add (new Argument (v));
7835 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7840 if (for_each.type is VarExpr) {
7841 // Infer implicitly typed local variable from foreach array type
7842 var_type = access.Type;
7844 var_type = for_each.type.ResolveAsType (ec);
7846 if (var_type == null)
7849 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7854 for_each.variable.Type = var_type;
7856 var prev_block = ec.CurrentBlock;
7857 ec.CurrentBlock = variables_block;
7858 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7859 ec.CurrentBlock = prev_block;
7861 if (variable_ref == null)
7864 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7866 return for_each.body.Resolve (ec);
7869 protected override void DoEmit (EmitContext ec)
7871 copy.EmitAssign (ec, for_each.expr);
7873 int rank = length_exprs.Length;
7874 Label[] test = new Label [rank];
7875 Label[] loop = new Label [rank];
7877 for (int i = 0; i < rank; i++) {
7878 test [i] = ec.DefineLabel ();
7879 loop [i] = ec.DefineLabel ();
7881 if (lengths != null)
7882 lengths [i].EmitAssign (ec, length_exprs [i]);
7885 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7886 for (int i = 0; i < rank; i++) {
7887 variables [i].EmitAssign (ec, zero);
7889 ec.Emit (OpCodes.Br, test [i]);
7890 ec.MarkLabel (loop [i]);
7893 for_each.body.Emit (ec);
7895 ec.MarkLabel (ec.LoopBegin);
7896 ec.Mark (for_each.expr.Location);
7898 for (int i = rank - 1; i >= 0; i--){
7899 counter [i].Emit (ec);
7901 ec.MarkLabel (test [i]);
7902 variables [i].Emit (ec);
7904 if (lengths != null)
7905 lengths [i].Emit (ec);
7907 length_exprs [i].Emit (ec);
7909 ec.Emit (OpCodes.Blt, loop [i]);
7912 ec.MarkLabel (ec.LoopEnd);
7916 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7918 class RuntimeDispose : Using.VariableDeclaration
7920 public RuntimeDispose (LocalVariable lv, Location loc)
7926 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7928 // Defered to runtime check
7931 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7933 var idt = bc.BuiltinTypes.IDisposable;
7936 // Fabricates code like
7938 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7941 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7943 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7944 dispose_variable.CreateReferenceExpression (bc, loc),
7945 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7946 loc), new NullLiteral (loc));
7948 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7950 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7951 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7953 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7954 return new If (idisaposable_test, dispose, loc);
7958 LocalVariable variable;
7960 Statement statement;
7961 ExpressionStatement init;
7962 TemporaryVariableReference enumerator_variable;
7963 bool ambiguous_getenumerator_name;
7965 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7968 this.variable = var;
7972 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7974 rc.Report.SymbolRelatedToPreviousError (enumerator);
7975 rc.Report.Error (202, loc,
7976 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7977 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7980 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7983 // Option 1: Try to match by name GetEnumerator first
7985 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7986 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7988 var mg = mexpr as MethodGroupExpr;
7990 mg.InstanceExpression = expr;
7991 Arguments args = new Arguments (0);
7992 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7994 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7995 if (ambiguous_getenumerator_name)
7998 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8004 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8007 PredefinedMember<MethodSpec> iface_candidate = null;
8008 var ptypes = rc.Module.PredefinedTypes;
8009 var gen_ienumerable = ptypes.IEnumerableGeneric;
8010 if (!gen_ienumerable.Define ())
8011 gen_ienumerable = null;
8013 var ifaces = t.Interfaces;
8014 if (ifaces != null) {
8015 foreach (var iface in ifaces) {
8016 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8017 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8018 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8019 rc.Report.Error (1640, loc,
8020 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8021 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8026 // TODO: Cache this somehow
8027 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8028 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8033 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8034 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8039 if (iface_candidate == null) {
8040 if (expr.Type != InternalType.ErrorType) {
8041 rc.Report.Error (1579, loc,
8042 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8043 expr.Type.GetSignatureForError (), "GetEnumerator");
8049 var method = iface_candidate.Resolve (loc);
8053 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8054 mg.InstanceExpression = expr;
8058 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8060 var ms = MemberCache.FindMember (enumerator.ReturnType,
8061 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8062 BindingRestriction.InstanceOnly) as MethodSpec;
8064 if (ms == null || !ms.IsPublic) {
8065 Error_WrongEnumerator (rc, enumerator);
8069 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8072 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8074 var ps = MemberCache.FindMember (enumerator.ReturnType,
8075 MemberFilter.Property ("Current", null),
8076 BindingRestriction.InstanceOnly) as PropertySpec;
8078 if (ps == null || !ps.IsPublic) {
8079 Error_WrongEnumerator (rc, enumerator);
8086 public override bool Resolve (BlockContext ec)
8088 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8091 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8092 } else if (expr.Type.IsNullableType) {
8093 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8096 var get_enumerator_mg = ResolveGetEnumerator (ec);
8097 if (get_enumerator_mg == null) {
8101 var get_enumerator = get_enumerator_mg.BestCandidate;
8102 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8103 enumerator_variable.Resolve (ec);
8105 // Prepare bool MoveNext ()
8106 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8107 if (move_next_mg == null) {
8111 move_next_mg.InstanceExpression = enumerator_variable;
8113 // Prepare ~T~ Current { get; }
8114 var current_prop = ResolveCurrent (ec, get_enumerator);
8115 if (current_prop == null) {
8119 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8120 if (current_pe == null)
8123 VarExpr ve = for_each.type as VarExpr;
8127 // Source type is dynamic, set element type to dynamic too
8128 variable.Type = ec.BuiltinTypes.Dynamic;
8130 // Infer implicitly typed local variable from foreach enumerable type
8131 variable.Type = current_pe.Type;
8135 // Explicit cast of dynamic collection elements has to be done at runtime
8136 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8139 variable.Type = for_each.type.ResolveAsType (ec);
8141 if (variable.Type == null)
8144 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8145 if (current_pe == null)
8149 var prev_block = ec.CurrentBlock;
8150 ec.CurrentBlock = for_each.variable.Block;
8151 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8152 ec.CurrentBlock = prev_block;
8153 if (variable_ref == null)
8156 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8158 var init = new Invocation.Predefined (get_enumerator_mg, null);
8160 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8161 for_each.body, Location.Null);
8163 var enum_type = enumerator_variable.Type;
8166 // Add Dispose method call when enumerator can be IDisposable
8168 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8169 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8171 // Runtime Dispose check
8173 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8174 vd.Initializer = init;
8175 statement = new Using (vd, statement, Location.Null);
8178 // No Dispose call needed
8180 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8181 this.init.Resolve (ec);
8185 // Static Dispose check
8187 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8188 vd.Initializer = init;
8189 statement = new Using (vd, statement, Location.Null);
8192 return statement.Resolve (ec);
8195 protected override void DoEmit (EmitContext ec)
8197 enumerator_variable.LocalInfo.CreateBuilder (ec);
8200 init.EmitStatement (ec);
8202 statement.Emit (ec);
8205 #region IErrorHandler Members
8207 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8209 ec.Report.SymbolRelatedToPreviousError (best);
8210 ec.Report.Warning (278, 2, expr.Location,
8211 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8212 expr.Type.GetSignatureForError (), "enumerable",
8213 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8215 ambiguous_getenumerator_name = true;
8219 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8224 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8229 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8238 LocalVariable variable;
8242 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8246 this.variable = var;
8252 public Expression Expr {
8253 get { return expr; }
8256 public Expression TypeExpression {
8257 get { return type; }
8260 public LocalVariable Variable {
8261 get { return variable; }
8264 public override Reachability MarkReachable (Reachability rc)
8266 base.MarkReachable (rc);
8268 body.MarkReachable (rc);
8273 public override bool Resolve (BlockContext ec)
8275 expr = expr.Resolve (ec);
8280 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8284 body.AddStatement (Statement);
8286 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8287 Statement = new ArrayForeach (this, 1);
8288 } else if (expr.Type is ArrayContainer) {
8289 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8291 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8292 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8293 expr.ExprClassName);
8297 Statement = new CollectionForeach (this, variable, expr);
8300 return base.Resolve (ec);
8303 protected override void DoEmit (EmitContext ec)
8305 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8306 ec.LoopBegin = ec.DefineLabel ();
8307 ec.LoopEnd = ec.DefineLabel ();
8309 if (!(Statement is Block))
8310 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8312 variable.CreateBuilder (ec);
8314 Statement.Emit (ec);
8316 if (!(Statement is Block))
8319 ec.LoopBegin = old_begin;
8320 ec.LoopEnd = old_end;
8323 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8325 expr.FlowAnalysis (fc);
8327 var da = fc.BranchDefiniteAssignment ();
8328 body.FlowAnalysis (fc);
8329 fc.DefiniteAssignment = da;
8333 protected override void CloneTo (CloneContext clonectx, Statement t)
8335 Foreach target = (Foreach) t;
8337 target.type = type.Clone (clonectx);
8338 target.expr = expr.Clone (clonectx);
8339 target.body = (Block) body.Clone (clonectx);
8340 target.Statement = Statement.Clone (clonectx);
8343 public override object Accept (StructuralVisitor visitor)
8345 return visitor.Visit (this);
8349 class SentinelStatement: Statement
8351 protected override void CloneTo (CloneContext clonectx, Statement target)
8355 protected override void DoEmit (EmitContext ec)
8357 var l = ec.DefineLabel ();
8359 ec.Emit (OpCodes.Br_S, l);
8362 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8364 throw new NotImplementedException ();