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,
2660 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2662 NoFlowAnalysis = 1 << 21,
2663 InitializationEmitted = 1 << 22
2666 public Block Parent;
2667 public Location StartLocation;
2668 public Location EndLocation;
2670 public ExplicitBlock Explicit;
2671 public ParametersBlock ParametersBlock;
2673 protected Flags flags;
2676 // The statements in this block
2678 protected List<Statement> statements;
2680 protected List<Statement> scope_initializers;
2682 int? resolving_init_idx;
2688 public int ID = id++;
2690 static int clone_id_counter;
2694 // int assignable_slots;
2696 public Block (Block parent, Location start, Location end)
2697 : this (parent, 0, start, end)
2701 public Block (Block parent, Flags flags, Location start, Location end)
2703 if (parent != null) {
2704 // the appropriate constructors will fixup these fields
2705 ParametersBlock = parent.ParametersBlock;
2706 Explicit = parent.Explicit;
2709 this.Parent = parent;
2711 this.StartLocation = start;
2712 this.EndLocation = end;
2714 statements = new List<Statement> (4);
2716 this.original = this;
2721 public Block Original {
2730 public bool IsCompilerGenerated {
2731 get { return (flags & Flags.CompilerGenerated) != 0; }
2732 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2736 public bool IsCatchBlock {
2738 return (flags & Flags.CatchBlock) != 0;
2742 public bool IsFinallyBlock {
2744 return (flags & Flags.FinallyBlock) != 0;
2748 public bool Unchecked {
2749 get { return (flags & Flags.Unchecked) != 0; }
2750 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2753 public bool Unsafe {
2754 get { return (flags & Flags.Unsafe) != 0; }
2755 set { flags |= Flags.Unsafe; }
2758 public List<Statement> Statements {
2759 get { return statements; }
2764 public void SetEndLocation (Location loc)
2769 public void AddLabel (LabeledStatement target)
2771 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2774 public void AddLocalName (LocalVariable li)
2776 AddLocalName (li.Name, li);
2779 public void AddLocalName (string name, INamedBlockVariable li)
2781 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2784 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2786 if (reason == null) {
2787 Error_AlreadyDeclared (name, variable);
2791 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2792 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2793 "to `{0}', which is already used in a `{1}' scope to denote something else",
2797 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2799 var pi = variable as ParametersBlock.ParameterInfo;
2801 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2803 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2804 "A local variable named `{0}' is already defined in this scope", name);
2808 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2810 ParametersBlock.TopBlock.Report.Error (412, loc,
2811 "The type parameter name `{0}' is the same as local variable or parameter name",
2816 // It should be used by expressions which require to
2817 // register a statement during resolve process.
2819 public void AddScopeStatement (Statement s)
2821 if (scope_initializers == null)
2822 scope_initializers = new List<Statement> ();
2825 // Simple recursive helper, when resolve scope initializer another
2826 // new scope initializer can be added, this ensures it's initialized
2827 // before existing one. For now this can happen with expression trees
2828 // in base ctor initializer only
2830 if (resolving_init_idx.HasValue) {
2831 scope_initializers.Insert (resolving_init_idx.Value, s);
2832 ++resolving_init_idx;
2834 scope_initializers.Add (s);
2838 public void InsertStatement (int index, Statement s)
2840 statements.Insert (index, s);
2843 public void AddStatement (Statement s)
2848 public LabeledStatement LookupLabel (string name)
2850 return ParametersBlock.GetLabel (name, this);
2853 public override Reachability MarkReachable (Reachability rc)
2855 if (rc.IsUnreachable)
2858 MarkReachableScope (rc);
2860 foreach (var s in statements) {
2861 rc = s.MarkReachable (rc);
2862 if (rc.IsUnreachable) {
2863 if ((flags & Flags.ReachableEnd) != 0)
2864 return new Reachability ();
2870 flags |= Flags.ReachableEnd;
2875 public void MarkReachableScope (Reachability rc)
2877 base.MarkReachable (rc);
2879 if (scope_initializers != null) {
2880 foreach (var si in scope_initializers)
2881 si.MarkReachable (rc);
2885 public override bool Resolve (BlockContext bc)
2887 if ((flags & Flags.Resolved) != 0)
2890 Block prev_block = bc.CurrentBlock;
2891 bc.CurrentBlock = this;
2894 // Compiler generated scope statements
2896 if (scope_initializers != null) {
2897 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2898 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2901 resolving_init_idx = null;
2905 int statement_count = statements.Count;
2906 for (int ix = 0; ix < statement_count; ix++){
2907 Statement s = statements [ix];
2909 if (!s.Resolve (bc)) {
2911 statements [ix] = new EmptyStatement (s.loc);
2916 bc.CurrentBlock = prev_block;
2918 flags |= Flags.Resolved;
2922 protected override void DoEmit (EmitContext ec)
2924 for (int ix = 0; ix < statements.Count; ix++){
2925 statements [ix].Emit (ec);
2929 public override void Emit (EmitContext ec)
2931 if (scope_initializers != null)
2932 EmitScopeInitializers (ec);
2937 protected void EmitScopeInitializers (EmitContext ec)
2939 foreach (Statement s in scope_initializers)
2943 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2945 if (scope_initializers != null) {
2946 foreach (var si in scope_initializers)
2947 si.FlowAnalysis (fc);
2950 return DoFlowAnalysis (fc, 0);
2953 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2955 bool end_unreachable = !reachable;
2956 bool goto_flow_analysis = startIndex != 0;
2957 for (; startIndex < statements.Count; ++startIndex) {
2958 var s = statements[startIndex];
2960 end_unreachable = s.FlowAnalysis (fc);
2961 if (s.IsUnreachable) {
2962 statements [startIndex] = RewriteUnreachableStatement (s);
2967 // Statement end reachability is needed mostly due to goto support. Consider
2976 // X label is reachable only via goto not as another statement after if. We need
2977 // this for flow-analysis only to carry variable info correctly.
2979 if (end_unreachable) {
2980 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2982 var f = s as TryFinally;
2983 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
2985 // Special case for try-finally with unreachable code after
2986 // finally block. Try block has to include leave opcode but there is
2987 // no label to leave to after unreachable finally block closing
2988 // brace. This sentinel ensures there is always IL instruction to
2989 // leave to even if we know it'll never be reached.
2991 statements.Insert (startIndex + 1, new SentinelStatement ());
2993 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2994 s = statements [startIndex];
2995 if (s is SwitchLabel) {
2996 if (!after_goto_case)
2997 s.FlowAnalysis (fc);
3002 if (s.IsUnreachable) {
3003 s.FlowAnalysis (fc);
3004 statements [startIndex] = RewriteUnreachableStatement (s);
3010 // Idea is to stop after goto case because goto case will always have at least same
3011 // variable assigned as switch case label. This saves a lot for complex goto case tests
3013 if (after_goto_case)
3019 var lb = s as LabeledStatement;
3020 if (lb != null && fc.AddReachedLabel (lb))
3025 // The condition should be true unless there is forward jumping goto
3027 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3030 return !Explicit.HasReachableClosingBrace;
3033 static Statement RewriteUnreachableStatement (Statement s)
3035 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3036 // analysis. Even csc report unreachable warning for it but it's actually used hence
3037 // we try to emulate this behaviour
3045 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3048 return new EmptyStatement (s.loc);
3051 public void ScanGotoJump (Statement label)
3054 for (i = 0; i < statements.Count; ++i) {
3055 if (statements[i] == label)
3059 var rc = new Reachability ();
3060 for (++i; i < statements.Count; ++i) {
3061 var s = statements[i];
3062 rc = s.MarkReachable (rc);
3063 if (rc.IsUnreachable)
3067 flags |= Flags.ReachableEnd;
3070 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3073 for (i = 0; i < statements.Count; ++i) {
3074 if (statements[i] == label)
3078 DoFlowAnalysis (fc, ++i);
3082 public override string ToString ()
3084 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3088 protected override void CloneTo (CloneContext clonectx, Statement t)
3090 Block target = (Block) t;
3092 target.clone_id = ++clone_id_counter;
3095 clonectx.AddBlockMap (this, target);
3096 if (original != this)
3097 clonectx.AddBlockMap (original, target);
3099 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3100 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3103 target.Parent = clonectx.RemapBlockCopy (Parent);
3105 target.statements = new List<Statement> (statements.Count);
3106 foreach (Statement s in statements)
3107 target.statements.Add (s.Clone (clonectx));
3110 public override object Accept (StructuralVisitor visitor)
3112 return visitor.Visit (this);
3116 public class ExplicitBlock : Block
3118 protected AnonymousMethodStorey am_storey;
3119 int debug_scope_index;
3121 public ExplicitBlock (Block parent, Location start, Location end)
3122 : this (parent, (Flags) 0, start, end)
3126 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3127 : base (parent, flags, start, end)
3129 this.Explicit = this;
3134 public AnonymousMethodStorey AnonymousMethodStorey {
3140 public bool HasAwait {
3142 return (flags & Flags.AwaitBlock) != 0;
3146 public bool HasCapturedThis {
3148 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3151 return (flags & Flags.HasCapturedThis) != 0;
3156 // Used to indicate that the block has reference to parent
3157 // block and cannot be made static when defining anonymous method
3159 public bool HasCapturedVariable {
3161 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3164 return (flags & Flags.HasCapturedVariable) != 0;
3168 public bool HasReachableClosingBrace {
3170 return (flags & Flags.ReachableEnd) != 0;
3173 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3177 public bool HasYield {
3179 return (flags & Flags.YieldBlock) != 0;
3186 // Creates anonymous method storey in current block
3188 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3191 // Return same story for iterator and async blocks unless we are
3192 // in nested anonymous method
3194 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3195 return ec.CurrentAnonymousMethod.Storey;
3197 if (am_storey == null) {
3198 MemberBase mc = ec.MemberContext as MemberBase;
3201 // Creates anonymous method storey for this block
3203 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3209 public void EmitScopeInitialization (EmitContext ec)
3211 if ((flags & Flags.InitializationEmitted) != 0)
3214 if (am_storey != null) {
3215 DefineStoreyContainer (ec, am_storey);
3216 am_storey.EmitStoreyInstantiation (ec, this);
3219 if (scope_initializers != null)
3220 EmitScopeInitializers (ec);
3222 flags |= Flags.InitializationEmitted;
3225 public override void Emit (EmitContext ec)
3227 if (Parent != null) {
3228 // TODO: It's needed only when scope has variable (normal or lifted)
3229 ec.BeginScope (GetDebugSymbolScopeIndex ());
3232 EmitScopeInitialization (ec);
3234 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3235 ec.Emit (OpCodes.Nop);
3243 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3244 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3245 ec.Emit (OpCodes.Nop);
3249 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3251 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3252 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3253 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3257 // Creates anonymous method storey
3259 storey.CreateContainer ();
3260 storey.DefineContainer ();
3261 storey.ExpandBaseInterfaces ();
3263 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3266 // Only first storey in path will hold this reference. All children blocks will
3267 // reference it indirectly using $ref field
3269 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3270 if (b.Parent != null) {
3271 var s = b.Parent.Explicit.AnonymousMethodStorey;
3273 storey.HoistedThis = s.HoistedThis;
3278 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3279 if (storey.HoistedThis == null)
3280 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3282 if (storey.HoistedThis != null)
3288 // We are the first storey on path and 'this' has to be hoisted
3290 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3291 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3293 // ThisReferencesFromChildrenBlock holds all reference even if they
3294 // are not on this path. It saves some memory otherwise it'd have to
3295 // be in every explicit block. We run this check to see if the reference
3296 // is valid for this storey
3298 Block block_on_path = ref_block;
3299 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3301 if (block_on_path == null)
3304 if (storey.HoistedThis == null) {
3305 storey.AddCapturedThisField (ec, null);
3308 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3310 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3312 if (b_storey != null) {
3314 // Don't add storey cross reference for `this' when the storey ends up not
3315 // beeing attached to any parent
3317 if (b.ParametersBlock.StateMachine == null) {
3318 AnonymousMethodStorey s = null;
3319 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3320 s = ab.Explicit.AnonymousMethodStorey;
3325 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3327 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3328 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3335 // Stop propagation inside same top block
3337 if (b.ParametersBlock == ParametersBlock.Original) {
3338 b_storey.AddParentStoreyReference (ec, storey);
3339 // b_storey.HoistedThis = storey.HoistedThis;
3343 b = pb = b.ParametersBlock;
3345 pb = b as ParametersBlock;
3348 if (pb != null && pb.StateMachine != null) {
3349 if (pb.StateMachine == storey)
3353 // If we are state machine with no parent. We can hook into parent without additional
3354 // reference and capture this directly
3356 ExplicitBlock parent_storey_block = pb;
3357 while (parent_storey_block.Parent != null) {
3358 parent_storey_block = parent_storey_block.Parent.Explicit;
3359 if (parent_storey_block.AnonymousMethodStorey != null) {
3364 if (parent_storey_block.AnonymousMethodStorey == null) {
3365 if (pb.StateMachine.HoistedThis == null) {
3366 pb.StateMachine.AddCapturedThisField (ec, null);
3367 b.HasCapturedThis = true;
3373 var parent_this_block = pb;
3374 while (parent_this_block.Parent != null) {
3375 parent_this_block = parent_this_block.Parent.ParametersBlock;
3376 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3382 // Add reference to closest storey which holds captured this
3384 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3388 // Add parent storey reference only when this is not captured directly
3390 if (b_storey != null) {
3391 b_storey.AddParentStoreyReference (ec, storey);
3392 b_storey.HoistedThis = storey.HoistedThis;
3399 var ref_blocks = storey.ReferencesFromChildrenBlock;
3400 if (ref_blocks != null) {
3401 foreach (ExplicitBlock ref_block in ref_blocks) {
3402 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3403 if (b.AnonymousMethodStorey != null) {
3404 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3407 // Stop propagation inside same top block
3409 if (b.ParametersBlock == ParametersBlock.Original)
3412 b = b.ParametersBlock;
3415 var pb = b as ParametersBlock;
3416 if (pb != null && pb.StateMachine != null) {
3417 if (pb.StateMachine == storey)
3420 pb.StateMachine.AddParentStoreyReference (ec, storey);
3423 b.HasCapturedVariable = true;
3429 storey.PrepareEmit ();
3430 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3433 public int GetDebugSymbolScopeIndex ()
3435 if (debug_scope_index == 0)
3436 debug_scope_index = ++ParametersBlock.debug_scope_index;
3438 return debug_scope_index;
3441 public void RegisterAsyncAwait ()
3444 while ((block.flags & Flags.AwaitBlock) == 0) {
3445 block.flags |= Flags.AwaitBlock;
3447 if (block is ParametersBlock)
3450 block = block.Parent.Explicit;
3454 public void RegisterIteratorYield ()
3456 ParametersBlock.TopBlock.IsIterator = true;
3459 while ((block.flags & Flags.YieldBlock) == 0) {
3460 block.flags |= Flags.YieldBlock;
3462 if (block.Parent == null)
3465 block = block.Parent.Explicit;
3469 public void SetCatchBlock ()
3471 flags |= Flags.CatchBlock;
3474 public void SetFinallyBlock ()
3476 flags |= Flags.FinallyBlock;
3479 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3481 tryBlock.statements = statements;
3482 statements = new List<Statement> (1);
3483 statements.Add (tf);
3488 // ParametersBlock was introduced to support anonymous methods
3489 // and lambda expressions
3491 public class ParametersBlock : ExplicitBlock
3493 public class ParameterInfo : INamedBlockVariable
3495 readonly ParametersBlock block;
3497 public VariableInfo VariableInfo;
3500 public ParameterInfo (ParametersBlock block, int index)
3508 public ParametersBlock Block {
3514 Block INamedBlockVariable.Block {
3520 public bool IsDeclared {
3526 public bool IsParameter {
3532 public bool IsLocked {
3541 public Location Location {
3543 return Parameter.Location;
3547 public Parameter Parameter {
3549 return block.Parameters [index];
3553 public TypeSpec ParameterType {
3555 return Parameter.Type;
3561 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3563 return new ParameterReference (this, loc);
3568 // Block is converted into an expression
3570 sealed class BlockScopeExpression : Expression
3573 readonly ParametersBlock block;
3575 public BlockScopeExpression (Expression child, ParametersBlock block)
3581 public override bool ContainsEmitWithAwait ()
3583 return child.ContainsEmitWithAwait ();
3586 public override Expression CreateExpressionTree (ResolveContext ec)
3588 throw new NotSupportedException ();
3591 protected override Expression DoResolve (ResolveContext ec)
3596 child = child.Resolve (ec);
3600 eclass = child.eclass;
3605 public override void Emit (EmitContext ec)
3607 block.EmitScopeInitializers (ec);
3612 protected ParametersCompiled parameters;
3613 protected ParameterInfo[] parameter_info;
3614 protected bool resolved;
3615 protected ToplevelBlock top_block;
3616 protected StateMachine state_machine;
3617 protected Dictionary<string, object> labels;
3619 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3620 : base (parent, 0, start, start)
3622 if (parameters == null)
3623 throw new ArgumentNullException ("parameters");
3625 this.parameters = parameters;
3626 ParametersBlock = this;
3628 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3630 this.top_block = parent.ParametersBlock.top_block;
3631 ProcessParameters ();
3634 protected ParametersBlock (ParametersCompiled parameters, Location start)
3635 : base (null, 0, start, start)
3637 if (parameters == null)
3638 throw new ArgumentNullException ("parameters");
3640 this.parameters = parameters;
3641 ParametersBlock = this;
3645 // It's supposed to be used by method body implementation of anonymous methods
3647 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3648 : base (null, 0, source.StartLocation, source.EndLocation)
3650 this.parameters = parameters;
3651 this.statements = source.statements;
3652 this.scope_initializers = source.scope_initializers;
3654 this.resolved = true;
3655 this.reachable = source.reachable;
3656 this.am_storey = source.am_storey;
3657 this.state_machine = source.state_machine;
3658 this.flags = source.flags & Flags.ReachableEnd;
3660 ParametersBlock = this;
3663 // Overwrite original for comparison purposes when linking cross references
3664 // between anonymous methods
3666 Original = source.Original;
3671 public bool HasReferenceToStoreyForInstanceLambdas {
3673 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3676 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3680 public bool IsAsync {
3682 return (flags & Flags.HasAsyncModifier) != 0;
3685 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3690 // Block has been converted to expression tree
3692 public bool IsExpressionTree {
3694 return (flags & Flags.IsExpressionTree) != 0;
3699 // The parameters for the block.
3701 public ParametersCompiled Parameters {
3707 public StateMachine StateMachine {
3709 return state_machine;
3713 public ToplevelBlock TopBlock {
3722 public bool Resolved {
3724 return (flags & Flags.Resolved) != 0;
3728 public int TemporaryLocalsCount { get; set; }
3733 // Checks whether all `out' parameters have been assigned.
3735 public void CheckControlExit (FlowAnalysisContext fc)
3737 CheckControlExit (fc, fc.DefiniteAssignment);
3740 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3742 if (parameter_info == null)
3745 foreach (var p in parameter_info) {
3746 if (p.VariableInfo == null)
3749 if (p.VariableInfo.IsAssigned (dat))
3752 fc.Report.Error (177, p.Location,
3753 "The out parameter `{0}' must be assigned to before control leaves the current method",
3758 protected override void CloneTo (CloneContext clonectx, Statement t)
3760 base.CloneTo (clonectx, t);
3762 var target = (ParametersBlock) t;
3765 // Clone label statements as well as they contain block reference
3769 if (pb.labels != null) {
3770 target.labels = new Dictionary<string, object> ();
3772 foreach (var entry in pb.labels) {
3773 var list = entry.Value as List<LabeledStatement>;
3776 var list_clone = new List<LabeledStatement> ();
3777 foreach (var lentry in list) {
3778 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3781 target.labels.Add (entry.Key, list_clone);
3783 var labeled = (LabeledStatement) entry.Value;
3784 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3791 if (pb.Parent == null)
3794 pb = pb.Parent.ParametersBlock;
3798 public override Expression CreateExpressionTree (ResolveContext ec)
3800 if (statements.Count == 1) {
3801 Expression expr = statements[0].CreateExpressionTree (ec);
3802 if (scope_initializers != null)
3803 expr = new BlockScopeExpression (expr, this);
3808 return base.CreateExpressionTree (ec);
3811 public override void Emit (EmitContext ec)
3813 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3814 DefineStoreyContainer (ec, state_machine);
3815 state_machine.EmitStoreyInstantiation (ec, this);
3821 public void EmitEmbedded (EmitContext ec)
3823 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3824 DefineStoreyContainer (ec, state_machine);
3825 state_machine.EmitStoreyInstantiation (ec, this);
3831 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3833 var res = base.DoFlowAnalysis (fc);
3835 if (HasReachableClosingBrace)
3836 CheckControlExit (fc);
3841 public LabeledStatement GetLabel (string name, Block block)
3844 // Cloned parameters blocks can have their own cloned version of top-level labels
3846 if (labels == null) {
3848 return Parent.ParametersBlock.GetLabel (name, block);
3854 if (!labels.TryGetValue (name, out value)) {
3858 var label = value as LabeledStatement;
3860 if (label != null) {
3861 if (IsLabelVisible (label, b))
3865 List<LabeledStatement> list = (List<LabeledStatement>) value;
3866 for (int i = 0; i < list.Count; ++i) {
3868 if (IsLabelVisible (label, b))
3876 static bool IsLabelVisible (LabeledStatement label, Block b)
3879 if (label.Block == b)
3882 } while (b != null);
3887 public ParameterInfo GetParameterInfo (Parameter p)
3889 for (int i = 0; i < parameters.Count; ++i) {
3890 if (parameters[i] == p)
3891 return parameter_info[i];
3894 throw new ArgumentException ("Invalid parameter");
3897 public ParameterReference GetParameterReference (int index, Location loc)
3899 return new ParameterReference (parameter_info[index], loc);
3902 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3904 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3906 CloneContext clonectx = new CloneContext ();
3907 return Clone (clonectx);
3910 protected void ProcessParameters ()
3912 if (parameters.Count == 0)
3915 parameter_info = new ParameterInfo[parameters.Count];
3916 for (int i = 0; i < parameter_info.Length; ++i) {
3917 var p = parameters.FixedParameters[i];
3921 // TODO: Should use Parameter only and more block there
3922 parameter_info[i] = new ParameterInfo (this, i);
3924 AddLocalName (p.Name, parameter_info[i]);
3928 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3930 var src = stmt.Block;
3933 // Cannot remap label block if the label was not yet cloned which
3934 // can happen in case of anonymous method inside anoynymous method
3935 // with a label. But in this case we don't care because goto cannot
3936 // jump of out anonymous method
3938 if (src.ParametersBlock != this)
3941 var src_stmts = src.Statements;
3942 for (int i = 0; i < src_stmts.Count; ++i) {
3943 if (src_stmts[i] == stmt)
3944 return (LabeledStatement) dst.Statements[i];
3947 throw new InternalErrorException ("Should never be reached");
3950 public override bool Resolve (BlockContext bc)
3952 // TODO: if ((flags & Flags.Resolved) != 0)
3959 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3960 flags |= Flags.IsExpressionTree;
3963 PrepareAssignmentAnalysis (bc);
3965 if (!base.Resolve (bc))
3968 } catch (Exception e) {
3969 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3972 if (bc.CurrentBlock != null) {
3973 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3975 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3980 // If an asynchronous body of F is either an expression classified as nothing, or a
3981 // statement block where no return statements have expressions, the inferred return type is Task
3984 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3985 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3986 am.ReturnTypeInference = null;
3987 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3995 void PrepareAssignmentAnalysis (BlockContext bc)
3997 for (int i = 0; i < parameters.Count; ++i) {
3998 var par = parameters.FixedParameters[i];
4000 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4003 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4007 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4009 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4010 var stateMachine = new IteratorStorey (iterator);
4012 state_machine = stateMachine;
4013 iterator.SetStateMachine (stateMachine);
4015 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4016 tlb.Original = this;
4017 tlb.state_machine = stateMachine;
4018 tlb.AddStatement (new Return (iterator, iterator.Location));
4022 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4024 for (int i = 0; i < parameters.Count; i++) {
4025 Parameter p = parameters[i];
4026 Parameter.Modifier mod = p.ModFlags;
4027 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4028 host.Compiler.Report.Error (1988, p.Location,
4029 "Async methods cannot have ref or out parameters");
4033 if (p is ArglistParameter) {
4034 host.Compiler.Report.Error (4006, p.Location,
4035 "__arglist is not allowed in parameter list of async methods");
4039 if (parameters.Types[i].IsPointer) {
4040 host.Compiler.Report.Error (4005, p.Location,
4041 "Async methods cannot have unsafe parameters");
4047 host.Compiler.Report.Warning (1998, 1, loc,
4048 "Async block lacks `await' operator and will run synchronously");
4051 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4052 var initializer = new AsyncInitializer (this, host, block_type);
4053 initializer.Type = block_type;
4054 initializer.DelegateType = delegateType;
4056 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4058 state_machine = stateMachine;
4059 initializer.SetStateMachine (stateMachine);
4061 const Flags flags = Flags.CompilerGenerated;
4063 var b = this is ToplevelBlock ?
4064 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4065 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4068 b.state_machine = stateMachine;
4069 b.AddStatement (new AsyncInitializerStatement (initializer));
4077 public class ToplevelBlock : ParametersBlock
4079 LocalVariable this_variable;
4080 CompilerContext compiler;
4081 Dictionary<string, object> names;
4083 List<ExplicitBlock> this_references;
4085 public ToplevelBlock (CompilerContext ctx, Location loc)
4086 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4090 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4091 : base (parameters, start)
4093 this.compiler = ctx;
4097 ProcessParameters ();
4101 // Recreates a top level block from parameters block. Used for
4102 // compiler generated methods where the original block comes from
4103 // explicit child block. This works for already resolved blocks
4104 // only to ensure we resolve them in the correct flow order
4106 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4107 : base (source, parameters)
4109 this.compiler = source.TopBlock.compiler;
4113 public bool IsIterator {
4115 return (flags & Flags.Iterator) != 0;
4118 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4122 public Report Report {
4124 return compiler.Report;
4129 // Used by anonymous blocks to track references of `this' variable
4131 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4133 return this_references;
4138 // Returns the "this" instance variable of this block.
4139 // See AddThisVariable() for more information.
4141 public LocalVariable ThisVariable {
4143 return this_variable;
4147 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4150 names = new Dictionary<string, object> ();
4153 if (!names.TryGetValue (name, out value)) {
4154 names.Add (name, li);
4158 INamedBlockVariable existing = value as INamedBlockVariable;
4159 List<INamedBlockVariable> existing_list;
4160 if (existing != null) {
4161 existing_list = new List<INamedBlockVariable> ();
4162 existing_list.Add (existing);
4163 names[name] = existing_list;
4165 existing_list = (List<INamedBlockVariable>) value;
4169 // A collision checking between local names
4171 var variable_block = li.Block.Explicit;
4172 for (int i = 0; i < existing_list.Count; ++i) {
4173 existing = existing_list[i];
4174 Block b = existing.Block.Explicit;
4176 // Collision at same level
4177 if (variable_block == b) {
4178 li.Block.Error_AlreadyDeclared (name, li);
4182 // Collision with parent
4183 Block parent = variable_block;
4184 while ((parent = parent.Parent) != null) {
4186 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4187 i = existing_list.Count;
4192 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4193 // Collision with children
4194 while ((b = b.Parent) != null) {
4195 if (variable_block == b) {
4196 li.Block.Error_AlreadyDeclared (name, li, "child");
4197 i = existing_list.Count;
4204 existing_list.Add (li);
4207 public void AddLabel (string name, LabeledStatement label)
4210 labels = new Dictionary<string, object> ();
4213 if (!labels.TryGetValue (name, out value)) {
4214 labels.Add (name, label);
4218 LabeledStatement existing = value as LabeledStatement;
4219 List<LabeledStatement> existing_list;
4220 if (existing != null) {
4221 existing_list = new List<LabeledStatement> ();
4222 existing_list.Add (existing);
4223 labels[name] = existing_list;
4225 existing_list = (List<LabeledStatement>) value;
4229 // A collision checking between labels
4231 for (int i = 0; i < existing_list.Count; ++i) {
4232 existing = existing_list[i];
4233 Block b = existing.Block;
4235 // Collision at same level
4236 if (label.Block == b) {
4237 Report.SymbolRelatedToPreviousError (existing.loc, name);
4238 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4242 // Collision with parent
4244 while ((b = b.Parent) != null) {
4245 if (existing.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;
4253 // Collision with with children
4255 while ((b = b.Parent) != null) {
4256 if (label.Block == b) {
4257 Report.Error (158, label.loc,
4258 "The label `{0}' shadows another label by the same name in a contained scope", name);
4259 i = existing_list.Count;
4265 existing_list.Add (label);
4268 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4270 if (this_references == null)
4271 this_references = new List<ExplicitBlock> ();
4273 if (!this_references.Contains (block))
4274 this_references.Add (block);
4277 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4279 this_references.Remove (block);
4283 // Creates an arguments set from all parameters, useful for method proxy calls
4285 public Arguments GetAllParametersArguments ()
4287 int count = parameters.Count;
4288 Arguments args = new Arguments (count);
4289 for (int i = 0; i < count; ++i) {
4290 var pi = parameter_info[i];
4291 var arg_expr = GetParameterReference (i, pi.Location);
4293 Argument.AType atype_modifier;
4294 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4295 case Parameter.Modifier.REF:
4296 atype_modifier = Argument.AType.Ref;
4298 case Parameter.Modifier.OUT:
4299 atype_modifier = Argument.AType.Out;
4306 args.Add (new Argument (arg_expr, atype_modifier));
4313 // Lookup inside a block, the returned value can represent 3 states
4315 // true+variable: A local name was found and it's valid
4316 // false+variable: A local name was found in a child block only
4317 // false+null: No local name was found
4319 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4325 if (!names.TryGetValue (name, out value))
4328 variable = value as INamedBlockVariable;
4330 if (variable != null) {
4332 if (variable.Block == b.Original)
4336 } while (b != null);
4344 } while (b != null);
4346 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4347 for (int i = 0; i < list.Count; ++i) {
4350 if (variable.Block == b.Original)
4354 } while (b != null);
4362 } while (b != null);
4372 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4374 if (block.names != null) {
4375 foreach (var n in block.names) {
4376 var variable = n.Value as INamedBlockVariable;
4377 if (variable != null) {
4378 if (variable.Block.ParametersBlock == pb)
4379 AddLocalName (n.Key, variable, false);
4383 foreach (var v in (List<INamedBlockVariable>) n.Value)
4384 if (v.Block.ParametersBlock == pb)
4385 AddLocalName (n.Key, v, false);
4391 // This is used by non-static `struct' constructors which do not have an
4392 // initializer - in this case, the constructor must initialize all of the
4393 // struct's fields. To do this, we add a "this" variable and use the flow
4394 // analysis code to ensure that it's been fully initialized before control
4395 // leaves the constructor.
4397 public void AddThisVariable (BlockContext bc)
4399 if (this_variable != null)
4400 throw new InternalErrorException (StartLocation.ToString ());
4402 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4403 this_variable.Type = bc.CurrentType;
4404 this_variable.PrepareAssignmentAnalysis (bc);
4407 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4410 // If we're a non-static struct constructor which doesn't have an
4411 // initializer, then we must initialize all of the struct's fields.
4413 if (this_variable != null)
4414 this_variable.IsThisAssigned (fc, this);
4416 base.CheckControlExit (fc, dat);
4419 public HashSet<LocalVariable> GetUndeclaredVariables ()
4424 HashSet<LocalVariable> variables = null;
4426 foreach (var entry in names) {
4427 var complex = entry.Value as List<INamedBlockVariable>;
4428 if (complex != null) {
4429 foreach (var centry in complex) {
4430 if (IsUndeclaredVariable (centry)) {
4431 if (variables == null)
4432 variables = new HashSet<LocalVariable> ();
4434 variables.Add ((LocalVariable) centry);
4437 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4438 if (variables == null)
4439 variables = new HashSet<LocalVariable> ();
4441 variables.Add ((LocalVariable)entry.Value);
4448 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4450 var lv = namedBlockVariable as LocalVariable;
4451 return lv != null && !lv.IsDeclared;
4454 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4459 foreach (var entry in names) {
4460 var complex = entry.Value as List<INamedBlockVariable>;
4461 if (complex != null) {
4462 foreach (var centry in complex) {
4463 var lv = centry as LocalVariable;
4464 if (lv != null && undeclaredVariables.Contains (lv)) {
4469 var lv = entry.Value as LocalVariable;
4470 if (lv != null && undeclaredVariables.Contains (lv))
4476 public override void Emit (EmitContext ec)
4478 if (Report.Errors > 0)
4482 if (IsCompilerGenerated) {
4483 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4491 // If `HasReturnLabel' is set, then we already emitted a
4492 // jump to the end of the method, so we must emit a `ret'
4495 // Unfortunately, System.Reflection.Emit automatically emits
4496 // a leave to the end of a finally block. This is a problem
4497 // if no code is following the try/finally block since we may
4498 // jump to a point after the end of the method.
4499 // As a workaround, we're always creating a return label in
4502 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4503 if (ec.HasReturnLabel)
4504 ec.MarkLabel (ec.ReturnLabel);
4506 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4507 ec.Mark (EndLocation);
4509 if (ec.ReturnType.Kind != MemberKind.Void)
4510 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4512 ec.Emit (OpCodes.Ret);
4515 } catch (Exception e) {
4516 throw new InternalErrorException (e, StartLocation);
4520 public bool Resolve (BlockContext bc, IMethodData md)
4525 var errors = bc.Report.Errors;
4529 if (bc.Report.Errors > errors)
4532 MarkReachable (new Reachability ());
4534 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4535 // TODO: var md = bc.CurrentMemberDefinition;
4536 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4539 if ((flags & Flags.NoFlowAnalysis) != 0)
4542 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4545 } catch (Exception e) {
4546 throw new InternalErrorException (e, StartLocation);
4553 public class SwitchLabel : Statement
4561 // if expr == null, then it is the default case.
4563 public SwitchLabel (Expression expr, Location l)
4569 public bool IsDefault {
4571 return label == null;
4575 public Expression Label {
4581 public Location Location {
4587 public Constant Converted {
4596 public bool PatternMatching { get; set; }
4598 public bool SectionStart { get; set; }
4600 public Label GetILLabel (EmitContext ec)
4602 if (il_label == null){
4603 il_label = ec.DefineLabel ();
4606 return il_label.Value;
4609 protected override void DoEmit (EmitContext ec)
4611 ec.MarkLabel (GetILLabel (ec));
4614 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4619 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4623 public override bool Resolve (BlockContext bc)
4625 if (ResolveAndReduce (bc))
4626 bc.Switch.RegisterLabel (bc, this);
4632 // Resolves the expression, reduces it to a literal if possible
4633 // and then converts it to the requested type.
4635 bool ResolveAndReduce (BlockContext bc)
4640 var switch_statement = bc.Switch;
4642 if (PatternMatching) {
4643 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4644 return label != null;
4647 var c = label.ResolveLabelConstant (bc);
4651 if (switch_statement.IsNullable && c is NullLiteral) {
4656 if (switch_statement.IsPatternMatching) {
4657 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4661 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4662 return converted != null;
4665 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4667 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4668 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4671 protected override void CloneTo (CloneContext clonectx, Statement target)
4673 var t = (SwitchLabel) target;
4675 t.label = label.Clone (clonectx);
4678 public override object Accept (StructuralVisitor visitor)
4680 return visitor.Visit (this);
4683 public string GetSignatureForError ()
4686 if (converted == null)
4689 label = converted.GetValueAsLiteral ();
4691 return string.Format ("case {0}:", label);
4695 public class Switch : LoopStatement
4697 // structure used to hold blocks of keys while calculating table switch
4698 sealed class LabelsRange : IComparable<LabelsRange>
4700 public readonly long min;
4702 public readonly List<long> label_values;
4704 public LabelsRange (long value)
4707 label_values = new List<long> ();
4708 label_values.Add (value);
4711 public LabelsRange (long min, long max, ICollection<long> values)
4715 this.label_values = new List<long> (values);
4720 return max - min + 1;
4724 public bool AddValue (long value)
4726 var gap = value - min + 1;
4727 // Ensure the range has > 50% occupancy
4728 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4732 label_values.Add (value);
4736 public int CompareTo (LabelsRange other)
4738 int nLength = label_values.Count;
4739 int nLengthOther = other.label_values.Count;
4740 if (nLengthOther == nLength)
4741 return (int) (other.min - min);
4743 return nLength - nLengthOther;
4747 sealed class DispatchStatement : Statement
4749 readonly Switch body;
4751 public DispatchStatement (Switch body)
4756 protected override void CloneTo (CloneContext clonectx, Statement target)
4758 throw new NotImplementedException ();
4761 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4766 protected override void DoEmit (EmitContext ec)
4768 body.EmitDispatch (ec);
4772 class MissingBreak : Statement
4774 readonly SwitchLabel label;
4776 public MissingBreak (SwitchLabel sl)
4782 public bool FallOut { get; set; }
4784 protected override void DoEmit (EmitContext ec)
4788 protected override void CloneTo (CloneContext clonectx, Statement target)
4792 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4795 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4796 label.GetSignatureForError ());
4798 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4799 label.GetSignatureForError ());
4805 public Expression Expr;
4808 // Mapping of all labels to their SwitchLabels
4810 Dictionary<long, SwitchLabel> labels;
4811 Dictionary<string, SwitchLabel> string_labels;
4812 List<SwitchLabel> case_labels;
4814 List<Tuple<GotoCase, Constant>> goto_cases;
4815 List<DefiniteAssignmentBitSet> end_reachable_das;
4818 /// The governing switch type
4820 public TypeSpec SwitchType;
4822 Expression new_expr;
4824 SwitchLabel case_null;
4825 SwitchLabel case_default;
4827 Label defaultLabel, nullLabel;
4828 VariableReference value;
4829 ExpressionStatement string_dictionary;
4830 FieldExpr switch_cache_field;
4831 ExplicitBlock block;
4835 // Nullable Types support
4837 Nullable.Unwrap unwrap;
4839 public Switch (Expression e, ExplicitBlock block, Location l)
4847 public SwitchLabel ActiveLabel { get; set; }
4849 public ExplicitBlock Block {
4855 public SwitchLabel DefaultLabel {
4857 return case_default;
4861 public bool IsNullable {
4863 return unwrap != null;
4867 public bool IsPatternMatching {
4869 return new_expr == null && SwitchType != null;
4873 public List<SwitchLabel> RegisteredLabels {
4879 public VariableReference ExpressionValue {
4886 // Determines the governing type for a switch. The returned
4887 // expression might be the expression from the switch, or an
4888 // expression that includes any potential conversions to
4890 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4892 switch (expr.Type.BuiltinType) {
4893 case BuiltinTypeSpec.Type.Byte:
4894 case BuiltinTypeSpec.Type.SByte:
4895 case BuiltinTypeSpec.Type.UShort:
4896 case BuiltinTypeSpec.Type.Short:
4897 case BuiltinTypeSpec.Type.UInt:
4898 case BuiltinTypeSpec.Type.Int:
4899 case BuiltinTypeSpec.Type.ULong:
4900 case BuiltinTypeSpec.Type.Long:
4901 case BuiltinTypeSpec.Type.Char:
4902 case BuiltinTypeSpec.Type.String:
4903 case BuiltinTypeSpec.Type.Bool:
4907 if (expr.Type.IsEnum)
4911 // Try to find a *user* defined implicit conversion.
4913 // If there is no implicit conversion, or if there are multiple
4914 // conversions, we have to report an error
4916 Expression converted = null;
4917 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4919 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4922 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4923 Convert.UserConversionRestriction.ProbingOnly;
4926 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4928 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4933 // Ignore over-worked ImplicitUserConversions that do
4934 // an implicit conversion in addition to the user conversion.
4936 var uc = e as UserCast;
4940 if (converted != null){
4941 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4950 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4952 var types = module.Compiler.BuiltinTypes;
4954 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4955 TypeSpec[] stypes = new[] {
4968 if (nullable != null) {
4970 Array.Resize (ref stypes, stypes.Length + 9);
4972 for (int i = 0; i < 9; ++i) {
4973 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4980 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4982 case_labels.Add (sl);
4985 if (case_default != null) {
4986 sl.Error_AlreadyOccurs (rc, case_default);
4994 if (sl.Converted == null)
4998 if (string_labels != null) {
4999 string string_value = sl.Converted.GetValue () as string;
5000 if (string_value == null)
5003 string_labels.Add (string_value, sl);
5005 if (sl.Converted.IsNull) {
5008 labels.Add (sl.Converted.GetValueAsLong (), sl);
5011 } catch (ArgumentException) {
5012 if (string_labels != null)
5013 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5015 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5020 // This method emits code for a lookup-based switch statement (non-string)
5021 // Basically it groups the cases into blocks that are at least half full,
5022 // and then spits out individual lookup opcodes for each block.
5023 // It emits the longest blocks first, and short blocks are just
5024 // handled with direct compares.
5026 void EmitTableSwitch (EmitContext ec, Expression val)
5028 if (labels != null && labels.Count > 0) {
5029 List<LabelsRange> ranges;
5030 if (string_labels != null) {
5031 // We have done all hard work for string already
5032 // setup single range only
5033 ranges = new List<LabelsRange> (1);
5034 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5036 var element_keys = new long[labels.Count];
5037 labels.Keys.CopyTo (element_keys, 0);
5038 Array.Sort (element_keys);
5041 // Build possible ranges of switch labes to reduce number
5044 ranges = new List<LabelsRange> (element_keys.Length);
5045 var range = new LabelsRange (element_keys[0]);
5047 for (int i = 1; i < element_keys.Length; ++i) {
5048 var l = element_keys[i];
5049 if (range.AddValue (l))
5052 range = new LabelsRange (l);
5056 // sort the blocks so we can tackle the largest ones first
5060 Label lbl_default = defaultLabel;
5061 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5063 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5064 LabelsRange kb = ranges[range_index];
5065 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5067 // Optimize small ranges using simple equality check
5068 if (kb.Range <= 2) {
5069 foreach (var key in kb.label_values) {
5070 SwitchLabel sl = labels[key];
5071 if (sl == case_default || sl == case_null)
5074 if (sl.Converted.IsZeroInteger) {
5075 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5078 sl.Converted.Emit (ec);
5079 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5083 // TODO: if all the keys in the block are the same and there are
5084 // no gaps/defaults then just use a range-check.
5085 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5086 // TODO: optimize constant/I4 cases
5088 // check block range (could be > 2^31)
5090 ec.EmitLong (kb.min);
5091 ec.Emit (OpCodes.Blt, lbl_default);
5094 ec.EmitLong (kb.max);
5095 ec.Emit (OpCodes.Bgt, lbl_default);
5100 ec.EmitLong (kb.min);
5101 ec.Emit (OpCodes.Sub);
5104 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5108 int first = (int) kb.min;
5111 ec.Emit (OpCodes.Sub);
5112 } else if (first < 0) {
5113 ec.EmitInt (-first);
5114 ec.Emit (OpCodes.Add);
5118 // first, build the list of labels for the switch
5120 long cJumps = kb.Range;
5121 Label[] switch_labels = new Label[cJumps];
5122 for (int iJump = 0; iJump < cJumps; iJump++) {
5123 var key = kb.label_values[iKey];
5124 if (key == kb.min + iJump) {
5125 switch_labels[iJump] = labels[key].GetILLabel (ec);
5128 switch_labels[iJump] = lbl_default;
5132 // emit the switch opcode
5133 ec.Emit (OpCodes.Switch, switch_labels);
5136 // mark the default for this block
5137 if (range_index != 0)
5138 ec.MarkLabel (lbl_default);
5141 // the last default just goes to the end
5142 if (ranges.Count > 0)
5143 ec.Emit (OpCodes.Br, lbl_default);
5147 public SwitchLabel FindLabel (Constant value)
5149 SwitchLabel sl = null;
5151 if (string_labels != null) {
5152 string s = value.GetValue () as string;
5154 if (case_null != null)
5156 else if (case_default != null)
5159 string_labels.TryGetValue (s, out sl);
5162 if (value is NullLiteral) {
5165 labels.TryGetValue (value.GetValueAsLong (), out sl);
5169 if (sl == null || sl.SectionStart)
5173 // Always return section start, it simplifies handling of switch labels
5175 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5176 var cs = case_labels [idx];
5177 if (cs.SectionStart)
5182 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5184 Expr.FlowAnalysis (fc);
5186 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5187 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5188 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5190 block.FlowAnalysis (fc);
5192 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5194 if (end_reachable_das != null) {
5195 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5196 InitialDefinitiveAssignment |= sections_das;
5197 end_reachable_das = null;
5200 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5202 return case_default != null && !end_reachable;
5205 public override bool Resolve (BlockContext ec)
5207 Expr = Expr.Resolve (ec);
5212 // LAMESPEC: User conversion from non-nullable governing type has a priority
5214 new_expr = SwitchGoverningType (ec, Expr, false);
5216 if (new_expr == null) {
5217 if (Expr.Type.IsNullableType) {
5218 unwrap = Nullable.Unwrap.Create (Expr, false);
5223 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5224 // involving nullable Expr and nullable governing type is
5226 new_expr = SwitchGoverningType (ec, unwrap, true);
5230 Expression switch_expr;
5231 if (new_expr == null) {
5232 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5233 if (Expr.Type != InternalType.ErrorType) {
5234 ec.Report.Error (151, loc,
5235 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5236 Expr.Type.GetSignatureForError ());
5243 SwitchType = Expr.Type;
5245 switch_expr = new_expr;
5246 SwitchType = new_expr.Type;
5247 if (SwitchType.IsNullableType) {
5248 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5249 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5252 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5253 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5257 if (block.Statements.Count == 0)
5260 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5261 string_labels = new Dictionary<string, SwitchLabel> ();
5263 labels = new Dictionary<long, SwitchLabel> ();
5267 var constant = switch_expr as Constant;
5270 // Don't need extra variable for constant switch or switch with
5271 // only default case
5273 if (constant == null) {
5275 // Store switch expression for comparison purposes
5277 value = switch_expr as VariableReference;
5278 if (value == null && !HasOnlyDefaultSection ()) {
5279 var current_block = ec.CurrentBlock;
5280 ec.CurrentBlock = Block;
5281 // Create temporary variable inside switch scope
5282 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5284 ec.CurrentBlock = current_block;
5288 case_labels = new List<SwitchLabel> ();
5290 Switch old_switch = ec.Switch;
5292 var parent_los = ec.EnclosingLoopOrSwitch;
5293 ec.EnclosingLoopOrSwitch = this;
5295 var ok = Statement.Resolve (ec);
5297 ec.EnclosingLoopOrSwitch = parent_los;
5298 ec.Switch = old_switch;
5301 // Check if all goto cases are valid. Needs to be done after switch
5302 // is resolved because goto can jump forward in the scope.
5304 if (goto_cases != null) {
5305 foreach (var gc in goto_cases) {
5306 if (gc.Item1 == null) {
5307 if (DefaultLabel == null) {
5308 Goto.Error_UnknownLabel (ec, "default", loc);
5314 var sl = FindLabel (gc.Item2);
5316 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5318 gc.Item1.Label = sl;
5326 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5327 ResolveStringSwitchMap (ec);
5331 // Anonymous storey initialization has to happen before
5332 // any generated switch dispatch
5334 block.InsertStatement (0, new DispatchStatement (this));
5339 bool HasOnlyDefaultSection ()
5341 for (int i = 0; i < block.Statements.Count; ++i) {
5342 var s = block.Statements[i] as SwitchLabel;
5344 if (s == null || s.IsDefault)
5353 public override Reachability MarkReachable (Reachability rc)
5355 if (rc.IsUnreachable)
5358 base.MarkReachable (rc);
5360 block.MarkReachableScope (rc);
5362 if (block.Statements.Count == 0)
5365 SwitchLabel constant_label = null;
5366 var constant = new_expr as Constant;
5368 if (constant != null) {
5369 constant_label = FindLabel (constant) ?? case_default;
5370 if (constant_label == null) {
5371 block.Statements.RemoveAt (0);
5376 var section_rc = new Reachability ();
5377 SwitchLabel prev_label = null;
5379 for (int i = 0; i < block.Statements.Count; ++i) {
5380 var s = block.Statements[i];
5381 var sl = s as SwitchLabel;
5383 if (sl != null && sl.SectionStart) {
5385 // Section is marked already via goto case
5387 if (!sl.IsUnreachable) {
5388 section_rc = new Reachability ();
5392 if (section_rc.IsUnreachable) {
5394 // Common case. Previous label section end is unreachable as
5395 // it ends with break, return, etc. For next section revert
5396 // to reachable again unless we have constant switch block
5398 section_rc = constant_label != null && constant_label != sl ?
5399 Reachability.CreateUnreachable () :
5400 new Reachability ();
5401 } else if (prev_label != null) {
5403 // Error case as control cannot fall through from one case label
5405 sl.SectionStart = false;
5406 s = new MissingBreak (prev_label);
5407 s.MarkReachable (rc);
5408 block.Statements.Insert (i - 1, s);
5410 } else if (constant_label != null && constant_label != sl) {
5412 // Special case for the first unreachable label in constant
5415 section_rc = Reachability.CreateUnreachable ();
5421 section_rc = s.MarkReachable (section_rc);
5424 if (!section_rc.IsUnreachable && prev_label != null) {
5425 prev_label.SectionStart = false;
5426 var s = new MissingBreak (prev_label) {
5430 s.MarkReachable (rc);
5431 block.Statements.Add (s);
5435 // Reachability can affect parent only when all possible paths are handled but
5436 // we still need to run reachability check on switch body to check for fall-through
5438 if (case_default == null && constant_label == null)
5442 // We have at least one local exit from the switch
5447 return Reachability.CreateUnreachable ();
5450 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5452 if (goto_cases == null)
5453 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5455 goto_cases.Add (Tuple.Create (gotoCase, value));
5459 // Converts string switch into string hashtable
5461 void ResolveStringSwitchMap (ResolveContext ec)
5463 FullNamedExpression string_dictionary_type;
5464 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5465 string_dictionary_type = new TypeExpression (
5466 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5467 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5469 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5470 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5472 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5476 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5477 Field field = new Field (ctype, string_dictionary_type,
5478 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5479 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5480 if (!field.Define ())
5482 ctype.AddField (field);
5484 var init = new List<Expression> ();
5486 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5487 string value = null;
5489 foreach (SwitchLabel sl in case_labels) {
5491 if (sl.SectionStart)
5492 labels.Add (++counter, sl);
5494 if (sl == case_default || sl == case_null)
5497 value = (string) sl.Converted.GetValue ();
5498 var init_args = new List<Expression> (2);
5499 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5501 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5502 init_args.Add (sl.Converted);
5504 init.Add (new CollectionElementInitializer (init_args, loc));
5507 Arguments args = new Arguments (1);
5508 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5509 Expression initializer = new NewInitialize (string_dictionary_type, args,
5510 new CollectionOrObjectInitializers (init, loc), loc);
5512 switch_cache_field = new FieldExpr (field, loc);
5513 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5516 void DoEmitStringSwitch (EmitContext ec)
5518 Label l_initialized = ec.DefineLabel ();
5521 // Skip initialization when value is null
5523 value.EmitBranchable (ec, nullLabel, false);
5526 // Check if string dictionary is initialized and initialize
5528 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5529 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5530 string_dictionary.EmitStatement (ec);
5532 ec.MarkLabel (l_initialized);
5534 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5536 ResolveContext rc = new ResolveContext (ec.MemberContext);
5538 if (switch_cache_field.Type.IsGeneric) {
5539 Arguments get_value_args = new Arguments (2);
5540 get_value_args.Add (new Argument (value));
5541 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5542 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5543 if (get_item == null)
5547 // A value was not found, go to default case
5549 get_item.EmitBranchable (ec, defaultLabel, false);
5551 Arguments get_value_args = new Arguments (1);
5552 get_value_args.Add (new Argument (value));
5554 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5555 if (get_item == null)
5558 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5559 get_item_object.EmitAssign (ec, get_item, true, false);
5560 ec.Emit (OpCodes.Brfalse, defaultLabel);
5562 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5563 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5565 get_item_int.EmitStatement (ec);
5566 get_item_object.Release (ec);
5569 EmitTableSwitch (ec, string_switch_variable);
5570 string_switch_variable.Release (ec);
5574 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5576 void EmitShortSwitch (EmitContext ec)
5578 MethodSpec equal_method = null;
5579 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5580 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5583 if (equal_method != null) {
5584 value.EmitBranchable (ec, nullLabel, false);
5587 for (int i = 0; i < case_labels.Count; ++i) {
5588 var label = case_labels [i];
5589 if (label == case_default || label == case_null)
5592 var constant = label.Converted;
5594 if (constant == null) {
5595 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5599 if (equal_method != null) {
5603 var call = new CallEmitter ();
5604 call.EmitPredefined (ec, equal_method, new Arguments (0));
5605 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5609 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5610 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5616 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5619 ec.Emit (OpCodes.Br, defaultLabel);
5622 void EmitDispatch (EmitContext ec)
5624 if (IsPatternMatching) {
5625 EmitShortSwitch (ec);
5629 if (value == null) {
5631 // Constant switch, we've already done the work if there is only 1 label
5635 foreach (var sl in case_labels) {
5636 if (sl.IsUnreachable)
5639 if (reachable++ > 0) {
5640 var constant = (Constant) new_expr;
5641 var constant_label = FindLabel (constant) ?? case_default;
5643 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5651 if (string_dictionary != null) {
5652 DoEmitStringSwitch (ec);
5653 } else if (case_labels.Count < 4 || string_labels != null) {
5654 EmitShortSwitch (ec);
5656 EmitTableSwitch (ec, value);
5660 protected override void DoEmit (EmitContext ec)
5663 // Setup the codegen context
5665 Label old_end = ec.LoopEnd;
5666 Switch old_switch = ec.Switch;
5668 ec.LoopEnd = ec.DefineLabel ();
5671 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5672 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5674 if (value != null) {
5677 var switch_expr = new_expr ?? Expr;
5679 unwrap.EmitCheck (ec);
5680 ec.Emit (OpCodes.Brfalse, nullLabel);
5681 value.EmitAssign (ec, switch_expr, false, false);
5682 } else if (switch_expr != value) {
5683 value.EmitAssign (ec, switch_expr, false, false);
5688 // Next statement is compiler generated we don't need extra
5689 // nop when we can use the statement for sequence point
5691 ec.Mark (block.StartLocation);
5692 block.IsCompilerGenerated = true;
5694 new_expr.EmitSideEffect (ec);
5699 // Restore context state.
5700 ec.MarkLabel (ec.LoopEnd);
5703 // Restore the previous context
5705 ec.LoopEnd = old_end;
5706 ec.Switch = old_switch;
5709 protected override void CloneTo (CloneContext clonectx, Statement t)
5711 Switch target = (Switch) t;
5713 target.Expr = Expr.Clone (clonectx);
5714 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5717 public override object Accept (StructuralVisitor visitor)
5719 return visitor.Visit (this);
5722 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5724 if (case_default == null && !(new_expr is Constant))
5727 if (end_reachable_das == null)
5728 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5730 end_reachable_das.Add (fc.DefiniteAssignment);
5733 public override void SetEndReachable ()
5735 end_reachable = true;
5739 // A place where execution can restart in a state machine
5740 public abstract class ResumableStatement : Statement
5743 protected Label resume_point;
5745 public Label PrepareForEmit (EmitContext ec)
5749 resume_point = ec.DefineLabel ();
5751 return resume_point;
5754 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5759 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5764 public abstract class TryFinallyBlock : ExceptionStatement
5766 protected Statement stmt;
5767 Label dispose_try_block;
5768 bool prepared_for_dispose, emitted_dispose;
5769 Method finally_host;
5771 protected TryFinallyBlock (Statement stmt, Location loc)
5779 public Statement Statement {
5787 protected abstract void EmitTryBody (EmitContext ec);
5788 public abstract void EmitFinallyBody (EmitContext ec);
5790 public override Label PrepareForDispose (EmitContext ec, Label end)
5792 if (!prepared_for_dispose) {
5793 prepared_for_dispose = true;
5794 dispose_try_block = ec.DefineLabel ();
5796 return dispose_try_block;
5799 protected sealed override void DoEmit (EmitContext ec)
5801 EmitTryBodyPrepare (ec);
5804 bool beginFinally = EmitBeginFinallyBlock (ec);
5806 Label start_finally = ec.DefineLabel ();
5807 if (resume_points != null && beginFinally) {
5808 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5810 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5811 ec.Emit (OpCodes.Brfalse_S, start_finally);
5812 ec.Emit (OpCodes.Endfinally);
5815 ec.MarkLabel (start_finally);
5817 if (finally_host != null) {
5818 finally_host.Define ();
5819 finally_host.PrepareEmit ();
5820 finally_host.Emit ();
5822 // Now it's safe to add, to close it properly and emit sequence points
5823 finally_host.Parent.AddMember (finally_host);
5825 var ce = new CallEmitter ();
5826 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5827 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5829 EmitFinallyBody (ec);
5833 ec.EndExceptionBlock ();
5836 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5838 if (emitted_dispose)
5841 emitted_dispose = true;
5843 Label end_of_try = ec.DefineLabel ();
5845 // Ensure that the only way we can get into this code is through a dispatcher
5846 if (have_dispatcher)
5847 ec.Emit (OpCodes.Br, end);
5849 ec.BeginExceptionBlock ();
5851 ec.MarkLabel (dispose_try_block);
5853 Label[] labels = null;
5854 for (int i = 0; i < resume_points.Count; ++i) {
5855 ResumableStatement s = resume_points[i];
5856 Label ret = s.PrepareForDispose (ec, end_of_try);
5857 if (ret.Equals (end_of_try) && labels == null)
5859 if (labels == null) {
5860 labels = new Label[resume_points.Count];
5861 for (int j = 0; j < i; ++j)
5862 labels[j] = end_of_try;
5867 if (labels != null) {
5869 for (j = 1; j < labels.Length; ++j)
5870 if (!labels[0].Equals (labels[j]))
5872 bool emit_dispatcher = j < labels.Length;
5874 if (emit_dispatcher) {
5875 ec.Emit (OpCodes.Ldloc, pc);
5876 ec.EmitInt (first_resume_pc);
5877 ec.Emit (OpCodes.Sub);
5878 ec.Emit (OpCodes.Switch, labels);
5881 foreach (ResumableStatement s in resume_points)
5882 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5885 ec.MarkLabel (end_of_try);
5887 ec.BeginFinallyBlock ();
5889 if (finally_host != null) {
5890 var ce = new CallEmitter ();
5891 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5892 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5894 EmitFinallyBody (ec);
5897 ec.EndExceptionBlock ();
5900 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5902 var res = stmt.FlowAnalysis (fc);
5903 parent_try_block = null;
5907 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5909 ec.BeginFinallyBlock ();
5913 public override Reachability MarkReachable (Reachability rc)
5915 base.MarkReachable (rc);
5916 return Statement.MarkReachable (rc);
5919 public override bool Resolve (BlockContext bc)
5923 parent_try_block = bc.CurrentTryBlock;
5924 bc.CurrentTryBlock = this;
5926 if (stmt is TryCatch) {
5927 ok = stmt.Resolve (bc);
5929 using (bc.Set (ResolveContext.Options.TryScope)) {
5930 ok = stmt.Resolve (bc);
5934 bc.CurrentTryBlock = parent_try_block;
5937 // Finally block inside iterator is called from MoveNext and
5938 // Dispose methods that means we need to lift the block into
5939 // newly created host method to emit the body only once. The
5940 // original block then simply calls the newly generated method.
5942 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5943 var b = stmt as Block;
5944 if (b != null && b.Explicit.HasYield) {
5945 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5949 return base.Resolve (bc) && ok;
5954 // Base class for blocks using exception handling
5956 public abstract class ExceptionStatement : ResumableStatement
5958 protected List<ResumableStatement> resume_points;
5959 protected int first_resume_pc;
5960 protected ExceptionStatement parent_try_block;
5961 protected int first_catch_resume_pc = -1;
5963 protected ExceptionStatement (Location loc)
5968 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5970 StateMachineInitializer state_machine = null;
5971 if (resume_points != null) {
5972 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5974 ec.EmitInt ((int) IteratorStorey.State.Running);
5975 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5979 // The resume points in catch section when this is try-catch-finally
5981 if (IsRewrittenTryCatchFinally ()) {
5982 ec.BeginExceptionBlock ();
5984 if (first_catch_resume_pc >= 0) {
5986 ec.MarkLabel (resume_point);
5988 // For normal control flow, we want to fall-through the Switch
5989 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5990 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5991 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
5992 ec.Emit (OpCodes.Sub);
5994 var labels = new Label [resume_points.Count - first_catch_resume_pc];
5995 for (int i = 0; i < labels.Length; ++i)
5996 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
5997 ec.Emit (OpCodes.Switch, labels);
6001 ec.BeginExceptionBlock ();
6004 // The resume points for try section
6006 if (resume_points != null && first_catch_resume_pc != 0) {
6007 if (first_catch_resume_pc < 0)
6008 ec.MarkLabel (resume_point);
6010 // For normal control flow, we want to fall-through the Switch
6011 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6012 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6013 ec.EmitInt (first_resume_pc);
6014 ec.Emit (OpCodes.Sub);
6016 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6017 for (int i = 0; i < labels.Length; ++i)
6018 labels[i] = resume_points[i].PrepareForEmit (ec);
6019 ec.Emit (OpCodes.Switch, labels);
6023 bool IsRewrittenTryCatchFinally ()
6025 var tf = this as TryFinally;
6029 var tc = tf.Statement as TryCatch;
6033 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6036 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6038 if (parent_try_block != null) {
6039 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6041 pc = stateMachine.AddResumePoint (this);
6044 if (resume_points == null) {
6045 resume_points = new List<ResumableStatement> ();
6046 first_resume_pc = pc;
6049 if (pc != first_resume_pc + resume_points.Count)
6050 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6052 var tf = this as TryFinally;
6053 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6054 first_catch_resume_pc = resume_points.Count;
6057 resume_points.Add (stmt);
6062 public class Lock : TryFinallyBlock
6065 TemporaryVariableReference expr_copy;
6066 TemporaryVariableReference lock_taken;
6068 public Lock (Expression expr, Statement stmt, Location loc)
6074 public Expression Expr {
6080 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6082 expr.FlowAnalysis (fc);
6083 return base.DoFlowAnalysis (fc);
6086 public override bool Resolve (BlockContext ec)
6088 expr = expr.Resolve (ec);
6092 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6093 ec.Report.Error (185, loc,
6094 "`{0}' is not a reference type as required by the lock statement",
6095 expr.Type.GetSignatureForError ());
6098 if (expr.Type.IsGenericParameter) {
6099 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6102 VariableReference lv = expr as VariableReference;
6105 locked = lv.IsLockedByStatement;
6106 lv.IsLockedByStatement = true;
6113 // Have to keep original lock value around to unlock same location
6114 // in the case of original value has changed or is null
6116 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6117 expr_copy.Resolve (ec);
6120 // Ensure Monitor methods are available
6122 if (ResolvePredefinedMethods (ec) > 1) {
6123 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6124 lock_taken.Resolve (ec);
6127 using (ec.Set (ResolveContext.Options.LockScope)) {
6132 lv.IsLockedByStatement = locked;
6138 protected override void EmitTryBodyPrepare (EmitContext ec)
6140 expr_copy.EmitAssign (ec, expr);
6142 if (lock_taken != null) {
6144 // Initialize ref variable
6146 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6149 // Monitor.Enter (expr_copy)
6151 expr_copy.Emit (ec);
6152 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6155 base.EmitTryBodyPrepare (ec);
6158 protected override void EmitTryBody (EmitContext ec)
6161 // Monitor.Enter (expr_copy, ref lock_taken)
6163 if (lock_taken != null) {
6164 expr_copy.Emit (ec);
6165 lock_taken.LocalInfo.CreateBuilder (ec);
6166 lock_taken.AddressOf (ec, AddressOp.Load);
6167 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6170 Statement.Emit (ec);
6173 public override void EmitFinallyBody (EmitContext ec)
6176 // if (lock_taken) Monitor.Exit (expr_copy)
6178 Label skip = ec.DefineLabel ();
6180 if (lock_taken != null) {
6181 lock_taken.Emit (ec);
6182 ec.Emit (OpCodes.Brfalse_S, skip);
6185 expr_copy.Emit (ec);
6186 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6188 ec.Emit (OpCodes.Call, m);
6190 ec.MarkLabel (skip);
6193 int ResolvePredefinedMethods (ResolveContext rc)
6195 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6196 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6200 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6204 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6208 protected override void CloneTo (CloneContext clonectx, Statement t)
6210 Lock target = (Lock) t;
6212 target.expr = expr.Clone (clonectx);
6213 target.stmt = Statement.Clone (clonectx);
6216 public override object Accept (StructuralVisitor visitor)
6218 return visitor.Visit (this);
6223 public class Unchecked : Statement {
6226 public Unchecked (Block b, Location loc)
6233 public override bool Resolve (BlockContext ec)
6235 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6236 return Block.Resolve (ec);
6239 protected override void DoEmit (EmitContext ec)
6241 using (ec.With (EmitContext.Options.CheckedScope, false))
6245 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6247 return Block.FlowAnalysis (fc);
6250 public override Reachability MarkReachable (Reachability rc)
6252 base.MarkReachable (rc);
6253 return Block.MarkReachable (rc);
6256 protected override void CloneTo (CloneContext clonectx, Statement t)
6258 Unchecked target = (Unchecked) t;
6260 target.Block = clonectx.LookupBlock (Block);
6263 public override object Accept (StructuralVisitor visitor)
6265 return visitor.Visit (this);
6269 public class Checked : Statement {
6272 public Checked (Block b, Location loc)
6275 b.Unchecked = false;
6279 public override bool Resolve (BlockContext ec)
6281 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6282 return Block.Resolve (ec);
6285 protected override void DoEmit (EmitContext ec)
6287 using (ec.With (EmitContext.Options.CheckedScope, true))
6291 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6293 return Block.FlowAnalysis (fc);
6296 public override Reachability MarkReachable (Reachability rc)
6298 base.MarkReachable (rc);
6299 return Block.MarkReachable (rc);
6302 protected override void CloneTo (CloneContext clonectx, Statement t)
6304 Checked target = (Checked) t;
6306 target.Block = clonectx.LookupBlock (Block);
6309 public override object Accept (StructuralVisitor visitor)
6311 return visitor.Visit (this);
6315 public class Unsafe : Statement {
6318 public Unsafe (Block b, Location loc)
6321 Block.Unsafe = true;
6325 public override bool Resolve (BlockContext ec)
6327 if (ec.CurrentIterator != null)
6328 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6330 using (ec.Set (ResolveContext.Options.UnsafeScope))
6331 return Block.Resolve (ec);
6334 protected override void DoEmit (EmitContext ec)
6339 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6341 return Block.FlowAnalysis (fc);
6344 public override Reachability MarkReachable (Reachability rc)
6346 base.MarkReachable (rc);
6347 return Block.MarkReachable (rc);
6350 protected override void CloneTo (CloneContext clonectx, Statement t)
6352 Unsafe target = (Unsafe) t;
6354 target.Block = clonectx.LookupBlock (Block);
6357 public override object Accept (StructuralVisitor visitor)
6359 return visitor.Visit (this);
6366 public class Fixed : Statement
6368 abstract class Emitter : ShimExpression
6370 protected LocalVariable vi;
6372 protected Emitter (Expression expr, LocalVariable li)
6378 public abstract void EmitExit (EmitContext ec);
6380 public override void FlowAnalysis (FlowAnalysisContext fc)
6382 expr.FlowAnalysis (fc);
6386 sealed class ExpressionEmitter : Emitter {
6387 public ExpressionEmitter (Expression converted, LocalVariable li)
6388 : base (converted, li)
6392 protected override Expression DoResolve (ResolveContext rc)
6394 throw new NotImplementedException ();
6397 public override void Emit (EmitContext ec) {
6399 // Store pointer in pinned location
6405 public override void EmitExit (EmitContext ec)
6408 ec.Emit (OpCodes.Conv_U);
6413 class StringEmitter : Emitter
6415 LocalVariable pinned_string;
6417 public StringEmitter (Expression expr, LocalVariable li)
6422 protected override Expression DoResolve (ResolveContext rc)
6424 pinned_string = new LocalVariable (vi.Block, "$pinned",
6425 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6427 pinned_string.Type = rc.BuiltinTypes.String;
6430 eclass = ExprClass.Variable;
6431 type = rc.BuiltinTypes.Int;
6435 public override void Emit (EmitContext ec)
6437 pinned_string.CreateBuilder (ec);
6440 pinned_string.EmitAssign (ec);
6442 // TODO: Should use Binary::Add
6443 pinned_string.Emit (ec);
6444 ec.Emit (OpCodes.Conv_I);
6446 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6450 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6451 //pe.InstanceExpression = pinned_string;
6452 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6454 ec.Emit (OpCodes.Add);
6458 public override void EmitExit (EmitContext ec)
6461 pinned_string.EmitAssign (ec);
6465 public class VariableDeclaration : BlockVariable
6467 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6472 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6474 if (!Variable.Type.IsPointer && li == Variable) {
6475 bc.Report.Error (209, TypeExpression.Location,
6476 "The type of locals declared in a fixed statement must be a pointer type");
6480 var res = initializer.Resolve (bc);
6487 var ac = res.Type as ArrayContainer;
6489 TypeSpec array_type = ac.Element;
6492 // Provided that array_type is unmanaged,
6494 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6497 Expression res_init;
6498 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6501 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6502 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6503 res = expr_variable.CreateReferenceExpression (bc, loc);
6507 // and T* is implicitly convertible to the
6508 // pointer type given in the fixed statement.
6510 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6512 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6513 if (converted == null)
6517 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6519 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6520 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6521 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6522 new NullLiteral (loc),
6525 converted = converted.Resolve (bc);
6527 return new ExpressionEmitter (converted, li);
6533 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6534 return new StringEmitter (res, li).Resolve (bc);
6537 // Case 3: fixed buffer
6538 if (res is FixedBufferPtr) {
6539 return new ExpressionEmitter (res, li);
6542 bool already_fixed = true;
6545 // Case 4: & object.
6547 Unary u = res as Unary;
6549 if (u.Oper == Unary.Operator.AddressOf) {
6550 IVariableReference vr = u.Expr as IVariableReference;
6551 if (vr == null || !vr.IsFixed) {
6552 already_fixed = false;
6555 } else if (initializer is Cast) {
6556 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6560 if (already_fixed) {
6561 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6564 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6565 return new ExpressionEmitter (res, li);
6570 VariableDeclaration decl;
6571 Statement statement;
6574 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6583 public Statement Statement {
6589 public BlockVariable Variables {
6597 public override bool Resolve (BlockContext bc)
6599 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6600 if (!decl.Resolve (bc))
6604 return statement.Resolve (bc);
6607 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6609 decl.FlowAnalysis (fc);
6610 return statement.FlowAnalysis (fc);
6613 protected override void DoEmit (EmitContext ec)
6615 decl.Variable.CreateBuilder (ec);
6616 decl.Initializer.Emit (ec);
6617 if (decl.Declarators != null) {
6618 foreach (var d in decl.Declarators) {
6619 d.Variable.CreateBuilder (ec);
6620 d.Initializer.Emit (ec);
6624 statement.Emit (ec);
6630 // Clear the pinned variable
6632 ((Emitter) decl.Initializer).EmitExit (ec);
6633 if (decl.Declarators != null) {
6634 foreach (var d in decl.Declarators) {
6635 ((Emitter)d.Initializer).EmitExit (ec);
6640 public override Reachability MarkReachable (Reachability rc)
6642 base.MarkReachable (rc);
6644 decl.MarkReachable (rc);
6646 rc = statement.MarkReachable (rc);
6648 // TODO: What if there is local exit?
6649 has_ret = rc.IsUnreachable;
6653 protected override void CloneTo (CloneContext clonectx, Statement t)
6655 Fixed target = (Fixed) t;
6657 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6658 target.statement = statement.Clone (clonectx);
6661 public override object Accept (StructuralVisitor visitor)
6663 return visitor.Visit (this);
6667 public class Catch : Statement
6669 class CatchVariableStore : Statement
6671 readonly Catch ctch;
6673 public CatchVariableStore (Catch ctch)
6678 protected override void CloneTo (CloneContext clonectx, Statement target)
6682 protected override void DoEmit (EmitContext ec)
6684 // Emits catch variable debug information inside correct block
6685 ctch.EmitCatchVariableStore (ec);
6688 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6694 class FilterStatement : Statement
6696 readonly Catch ctch;
6698 public FilterStatement (Catch ctch)
6703 protected override void CloneTo (CloneContext clonectx, Statement target)
6707 protected override void DoEmit (EmitContext ec)
6709 if (ctch.li != null) {
6710 if (ctch.hoisted_temp != null)
6711 ctch.hoisted_temp.Emit (ec);
6715 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6716 ec.Emit (OpCodes.Box, ctch.type);
6719 var expr_start = ec.DefineLabel ();
6720 var end = ec.DefineLabel ();
6722 ec.Emit (OpCodes.Brtrue_S, expr_start);
6724 ec.Emit (OpCodes.Br, end);
6725 ec.MarkLabel (expr_start);
6727 ctch.Filter.Emit (ec);
6730 ec.Emit (OpCodes.Endfilter);
6731 ec.BeginFilterHandler ();
6732 ec.Emit (OpCodes.Pop);
6735 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6737 ctch.Filter.FlowAnalysis (fc);
6741 public override bool Resolve (BlockContext bc)
6743 ctch.Filter = ctch.Filter.Resolve (bc);
6745 if (ctch.Filter != null) {
6746 if (ctch.Filter.ContainsEmitWithAwait ()) {
6747 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6750 var c = ctch.Filter as Constant;
6751 if (c != null && !c.IsDefaultValue) {
6752 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6760 ExplicitBlock block;
6762 FullNamedExpression type_expr;
6763 CompilerAssign assign;
6765 LocalTemporary hoisted_temp;
6767 public Catch (ExplicitBlock block, Location loc)
6775 public ExplicitBlock Block {
6781 public TypeSpec CatchType {
6787 public Expression Filter {
6791 public bool IsGeneral {
6793 return type_expr == null;
6797 public FullNamedExpression TypeExpression {
6806 public LocalVariable Variable {
6817 protected override void DoEmit (EmitContext ec)
6819 if (Filter != null) {
6820 ec.BeginExceptionFilterBlock ();
6821 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6823 if (Block.HasAwait) {
6824 Block.EmitScopeInitialization (ec);
6833 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6835 ec.BeginCatchBlock (CatchType);
6838 ec.Emit (OpCodes.Pop);
6840 if (Block.HasAwait) {
6842 EmitCatchVariableStore (ec);
6848 void EmitCatchVariableStore (EmitContext ec)
6850 li.CreateBuilder (ec);
6853 // For hoisted catch variable we have to use a temporary local variable
6854 // for captured variable initialization during storey setup because variable
6855 // needs to be on the stack after storey instance for stfld operation
6857 if (li.HoistedVariant != null) {
6858 hoisted_temp = new LocalTemporary (li.Type);
6859 hoisted_temp.Store (ec);
6861 // switch to assignment from temporary variable and not from top of the stack
6862 assign.UpdateSource (hoisted_temp);
6866 public override bool Resolve (BlockContext bc)
6868 using (bc.Set (ResolveContext.Options.CatchScope)) {
6869 if (type_expr == null) {
6870 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6871 if (!block.HasAwait || Filter != null)
6872 block.AddScopeStatement (new CatchVariableStore (this));
6874 Expression source = new EmptyExpression (li.Type);
6875 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6876 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6879 type = type_expr.ResolveAsType (bc);
6884 CreateExceptionVariable (type);
6886 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6887 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6888 } else if (li != null) {
6890 li.PrepareAssignmentAnalysis (bc);
6892 // source variable is at the top of the stack
6893 Expression source = new EmptyExpression (li.Type);
6894 if (li.Type.IsGenericParameter)
6895 source = new UnboxCast (source, li.Type);
6897 if (!block.HasAwait || Filter != null)
6898 block.AddScopeStatement (new CatchVariableStore (this));
6901 // Uses Location.Null to hide from symbol file
6903 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6904 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6908 if (Filter != null) {
6909 Block.AddScopeStatement (new FilterStatement (this));
6912 Block.SetCatchBlock ();
6913 return Block.Resolve (bc);
6917 bool CreateExceptionVariable (TypeSpec type)
6919 if (!Block.HasAwait)
6922 // TODO: Scan the block for rethrow expression
6923 //if (!Block.HasRethrow)
6926 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6930 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6932 if (li != null && !li.IsCompilerGenerated) {
6933 fc.SetVariableAssigned (li.VariableInfo, true);
6936 return block.FlowAnalysis (fc);
6939 public override Reachability MarkReachable (Reachability rc)
6941 base.MarkReachable (rc);
6943 var c = Filter as Constant;
6944 if (c != null && c.IsDefaultValue)
6945 return Reachability.CreateUnreachable ();
6947 return block.MarkReachable (rc);
6950 protected override void CloneTo (CloneContext clonectx, Statement t)
6952 Catch target = (Catch) t;
6954 if (type_expr != null)
6955 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6958 target.Filter = Filter.Clone (clonectx);
6960 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6964 public class TryFinally : TryFinallyBlock
6967 List<DefiniteAssignmentBitSet> try_exit_dat;
6968 List<Label> redirected_jumps;
6969 Label? start_fin_label;
6971 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6977 public ExplicitBlock FinallyBlock {
6983 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6985 if (try_exit_dat == null)
6986 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6988 try_exit_dat.Add (vector);
6991 public override bool Resolve (BlockContext bc)
6993 bool ok = base.Resolve (bc);
6995 fini.SetFinallyBlock ();
6996 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6997 ok &= fini.Resolve (bc);
7003 protected override void EmitTryBody (EmitContext ec)
7005 if (fini.HasAwait) {
7006 if (ec.TryFinallyUnwind == null)
7007 ec.TryFinallyUnwind = new List<TryFinally> ();
7009 ec.TryFinallyUnwind.Add (this);
7012 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7013 ec.EndExceptionBlock ();
7015 ec.TryFinallyUnwind.Remove (this);
7017 if (start_fin_label != null)
7018 ec.MarkLabel (start_fin_label.Value);
7026 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7031 return base.EmitBeginFinallyBlock (ec);
7034 public override void EmitFinallyBody (EmitContext ec)
7036 if (!fini.HasAwait) {
7042 // Emits catch block like
7044 // catch (object temp) {
7045 // this.exception_field = temp;
7048 var type = ec.BuiltinTypes.Object;
7049 ec.BeginCatchBlock (type);
7051 var temp = ec.GetTemporaryLocal (type);
7052 ec.Emit (OpCodes.Stloc, temp);
7054 var exception_field = ec.GetTemporaryField (type);
7055 exception_field.AutomaticallyReuse = false;
7057 ec.Emit (OpCodes.Ldloc, temp);
7058 exception_field.EmitAssignFromStack (ec);
7060 ec.EndExceptionBlock ();
7062 ec.FreeTemporaryLocal (temp, type);
7067 // Emits exception rethrow
7069 // if (this.exception_field != null)
7070 // throw this.exception_field;
7072 exception_field.Emit (ec);
7073 var skip_throw = ec.DefineLabel ();
7074 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7075 exception_field.Emit (ec);
7076 ec.Emit (OpCodes.Throw);
7077 ec.MarkLabel (skip_throw);
7079 exception_field.PrepareCleanup (ec);
7081 EmitUnwindFinallyTable (ec);
7084 bool IsParentBlock (Block block)
7086 for (Block b = fini; b != null; b = b.Parent) {
7094 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7097 if (labelBlock != null) {
7098 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7099 var fin = ec.TryFinallyUnwind [idx - 1];
7100 if (!fin.IsParentBlock (labelBlock))
7107 bool set_return_state = true;
7109 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7110 var fin = ec.TryFinallyUnwind [idx];
7111 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7114 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7115 set_return_state = false;
7117 if (fin.start_fin_label == null) {
7118 fin.start_fin_label = ec.DefineLabel ();
7121 label = fin.start_fin_label.Value;
7127 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7129 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7132 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7134 if (redirected_jumps == null) {
7135 redirected_jumps = new List<Label> ();
7137 // Add fallthrough label
7138 redirected_jumps.Add (ec.DefineLabel ());
7141 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7144 int index = redirected_jumps.IndexOf (label);
7146 redirected_jumps.Add (label);
7147 index = redirected_jumps.Count - 1;
7151 // Indicates we have captured exit jump
7153 if (setReturnState) {
7154 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7155 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7160 // Emits state table of jumps outside of try block and reload of return
7161 // value when try block returns value
7163 void EmitUnwindFinallyTable (EmitContext ec)
7165 if (redirected_jumps == null)
7168 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7169 initializer.HoistedReturnState.EmitLoad (ec);
7170 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7172 // Mark fallthrough label
7173 ec.MarkLabel (redirected_jumps [0]);
7176 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7178 var da = fc.BranchDefiniteAssignment ();
7180 var tf = fc.TryFinally;
7181 fc.TryFinally = this;
7183 var res_stmt = Statement.FlowAnalysis (fc);
7187 var try_da = fc.DefiniteAssignment;
7188 fc.DefiniteAssignment = da;
7190 var res_fin = fini.FlowAnalysis (fc);
7192 if (try_exit_dat != null) {
7194 // try block has global exit but we need to run definite assignment check
7195 // for parameter block out parameter after finally block because it's always
7196 // executed before exit
7198 foreach (var try_da_part in try_exit_dat)
7199 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7201 try_exit_dat = null;
7204 fc.DefiniteAssignment |= try_da;
7205 return res_stmt | res_fin;
7208 public override Reachability MarkReachable (Reachability rc)
7211 // Mark finally block first for any exit statement in try block
7212 // to know whether the code which follows finally is reachable
7214 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7217 protected override void CloneTo (CloneContext clonectx, Statement t)
7219 TryFinally target = (TryFinally) t;
7221 target.stmt = stmt.Clone (clonectx);
7223 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7226 public override object Accept (StructuralVisitor visitor)
7228 return visitor.Visit (this);
7232 public class TryCatch : ExceptionStatement
7235 List<Catch> clauses;
7236 readonly bool inside_try_finally;
7237 List<Catch> catch_sm;
7239 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7243 this.clauses = catch_clauses;
7244 this.inside_try_finally = inside_try_finally;
7247 public List<Catch> Clauses {
7253 public bool HasClauseWithAwait {
7255 return catch_sm != null;
7259 public bool IsTryCatchFinally {
7261 return inside_try_finally;
7265 public override bool Resolve (BlockContext bc)
7269 using (bc.Set (ResolveContext.Options.TryScope)) {
7271 parent_try_block = bc.CurrentTryBlock;
7273 if (IsTryCatchFinally) {
7274 ok = Block.Resolve (bc);
7276 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7277 bc.CurrentTryBlock = this;
7278 ok = Block.Resolve (bc);
7279 bc.CurrentTryBlock = parent_try_block;
7284 var prev_catch = bc.CurrentTryCatch;
7285 bc.CurrentTryCatch = this;
7287 for (int i = 0; i < clauses.Count; ++i) {
7290 ok &= c.Resolve (bc);
7292 if (c.Block.HasAwait) {
7293 if (catch_sm == null)
7294 catch_sm = new List<Catch> ();
7299 if (c.Filter != null)
7302 TypeSpec resolved_type = c.CatchType;
7303 if (resolved_type == null)
7306 for (int ii = 0; ii < clauses.Count; ++ii) {
7310 if (clauses[ii].Filter != null)
7313 if (clauses[ii].IsGeneral) {
7314 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7317 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7320 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7323 bc.Report.Warning (1058, 1, c.loc,
7324 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7332 var ct = clauses[ii].CatchType;
7336 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7337 bc.Report.Error (160, c.loc,
7338 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7339 ct.GetSignatureForError ());
7345 bc.CurrentTryCatch = prev_catch;
7347 return base.Resolve (bc) && ok;
7350 protected sealed override void DoEmit (EmitContext ec)
7352 if (!inside_try_finally)
7353 EmitTryBodyPrepare (ec);
7357 LocalBuilder state_variable = null;
7358 foreach (Catch c in clauses) {
7361 if (catch_sm != null) {
7362 if (state_variable == null) {
7364 // Cannot reuse temp variable because non-catch path assumes the value is 0
7365 // which may not be true for reused local variable
7367 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7370 var index = catch_sm.IndexOf (c);
7374 ec.EmitInt (index + 1);
7375 ec.Emit (OpCodes.Stloc, state_variable);
7379 if (state_variable == null) {
7380 if (!inside_try_finally)
7381 ec.EndExceptionBlock ();
7383 ec.EndExceptionBlock ();
7385 ec.Emit (OpCodes.Ldloc, state_variable);
7387 var labels = new Label [catch_sm.Count + 1];
7388 for (int i = 0; i < labels.Length; ++i) {
7389 labels [i] = ec.DefineLabel ();
7392 var end = ec.DefineLabel ();
7393 ec.Emit (OpCodes.Switch, labels);
7395 // 0 value is default label
7396 ec.MarkLabel (labels [0]);
7397 ec.Emit (OpCodes.Br, end);
7399 var atv = ec.AsyncThrowVariable;
7401 for (int i = 0; i < catch_sm.Count; ++i) {
7402 if (c != null && c.Block.HasReachableClosingBrace)
7403 ec.Emit (OpCodes.Br, end);
7405 ec.MarkLabel (labels [i + 1]);
7407 ec.AsyncThrowVariable = c.Variable;
7410 ec.AsyncThrowVariable = atv;
7416 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7418 var start_fc = fc.BranchDefiniteAssignment ();
7419 var res = Block.FlowAnalysis (fc);
7421 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7423 foreach (var c in clauses) {
7424 fc.BranchDefiniteAssignment (start_fc);
7425 if (!c.FlowAnalysis (fc)) {
7427 try_fc = fc.DefiniteAssignment;
7429 try_fc &= fc.DefiniteAssignment;
7435 fc.DefiniteAssignment = try_fc ?? start_fc;
7436 parent_try_block = null;
7440 public override Reachability MarkReachable (Reachability rc)
7442 if (rc.IsUnreachable)
7445 base.MarkReachable (rc);
7447 var tc_rc = Block.MarkReachable (rc);
7449 foreach (var c in clauses)
7450 tc_rc &= c.MarkReachable (rc);
7455 protected override void CloneTo (CloneContext clonectx, Statement t)
7457 TryCatch target = (TryCatch) t;
7459 target.Block = clonectx.LookupBlock (Block);
7460 if (clauses != null){
7461 target.clauses = new List<Catch> ();
7462 foreach (Catch c in clauses)
7463 target.clauses.Add ((Catch) c.Clone (clonectx));
7467 public override object Accept (StructuralVisitor visitor)
7469 return visitor.Visit (this);
7473 public class Using : TryFinallyBlock
7475 public class VariableDeclaration : BlockVariable
7477 Statement dispose_call;
7479 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7484 public VariableDeclaration (LocalVariable li, Location loc)
7491 public VariableDeclaration (Expression expr)
7494 loc = expr.Location;
7500 public bool IsNested { get; private set; }
7504 public void EmitDispose (EmitContext ec)
7506 dispose_call.Emit (ec);
7509 public override bool Resolve (BlockContext bc)
7514 return base.Resolve (bc, false);
7517 public Expression ResolveExpression (BlockContext bc)
7519 var e = Initializer.Resolve (bc);
7523 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7524 Initializer = ResolveInitializer (bc, Variable, e);
7528 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7530 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7531 initializer = initializer.Resolve (bc);
7532 if (initializer == null)
7535 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7536 Arguments args = new Arguments (1);
7537 args.Add (new Argument (initializer));
7538 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7539 if (initializer == null)
7542 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7543 dispose_call = CreateDisposeCall (bc, var);
7544 dispose_call.Resolve (bc);
7546 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7549 if (li == Variable) {
7550 CheckIDiposableConversion (bc, li, initializer);
7551 dispose_call = CreateDisposeCall (bc, li);
7552 dispose_call.Resolve (bc);
7555 return base.ResolveInitializer (bc, li, initializer);
7558 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7562 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7563 if (type.IsNullableType) {
7564 // it's handled in CreateDisposeCall
7568 if (type != InternalType.ErrorType) {
7569 bc.Report.SymbolRelatedToPreviousError (type);
7570 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7571 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7572 type.GetSignatureForError ());
7579 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7581 var target = bc.BuiltinTypes.IDisposable;
7582 var tp = type as TypeParameterSpec;
7584 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7586 return type.ImplementsInterface (target, false);
7589 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7591 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7593 var loc = lv.Location;
7595 var idt = bc.BuiltinTypes.IDisposable;
7596 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7598 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7599 dispose_mg.InstanceExpression = type.IsNullableType ?
7600 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7604 // Hide it from symbol file via null location
7606 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7608 // Add conditional call when disposing possible null variable
7609 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7610 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7615 public void ResolveDeclaratorInitializer (BlockContext bc)
7617 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7620 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7622 for (int i = declarators.Count - 1; i >= 0; --i) {
7623 var d = declarators [i];
7624 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7625 vd.Initializer = d.Initializer;
7627 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7628 vd.dispose_call.Resolve (bc);
7630 stmt = new Using (vd, stmt, d.Variable.Location);
7637 public override object Accept (StructuralVisitor visitor)
7639 return visitor.Visit (this);
7643 VariableDeclaration decl;
7645 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7651 public Using (Expression expr, Statement stmt, Location loc)
7654 this.decl = new VariableDeclaration (expr);
7659 public Expression Expr {
7661 return decl.Variable == null ? decl.Initializer : null;
7665 public BlockVariable Variables {
7673 public override void Emit (EmitContext ec)
7676 // Don't emit sequence point it will be set on variable declaration
7681 protected override void EmitTryBodyPrepare (EmitContext ec)
7684 base.EmitTryBodyPrepare (ec);
7687 protected override void EmitTryBody (EmitContext ec)
7692 public override void EmitFinallyBody (EmitContext ec)
7694 decl.EmitDispose (ec);
7697 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7699 decl.FlowAnalysis (fc);
7700 return stmt.FlowAnalysis (fc);
7703 public override Reachability MarkReachable (Reachability rc)
7705 decl.MarkReachable (rc);
7706 return base.MarkReachable (rc);
7709 public override bool Resolve (BlockContext ec)
7711 VariableReference vr;
7712 bool vr_locked = false;
7714 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7715 if (decl.Variable == null) {
7716 vr = decl.ResolveExpression (ec) as VariableReference;
7718 vr_locked = vr.IsLockedByStatement;
7719 vr.IsLockedByStatement = true;
7722 if (decl.IsNested) {
7723 decl.ResolveDeclaratorInitializer (ec);
7725 if (!decl.Resolve (ec))
7728 if (decl.Declarators != null) {
7729 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7737 var ok = base.Resolve (ec);
7740 vr.IsLockedByStatement = vr_locked;
7745 protected override void CloneTo (CloneContext clonectx, Statement t)
7747 Using target = (Using) t;
7749 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7750 target.stmt = stmt.Clone (clonectx);
7753 public override object Accept (StructuralVisitor visitor)
7755 return visitor.Visit (this);
7760 /// Implementation of the foreach C# statement
7762 public class Foreach : LoopStatement
7764 abstract class IteratorStatement : Statement
7766 protected readonly Foreach for_each;
7768 protected IteratorStatement (Foreach @foreach)
7770 this.for_each = @foreach;
7771 this.loc = @foreach.expr.Location;
7774 protected override void CloneTo (CloneContext clonectx, Statement target)
7776 throw new NotImplementedException ();
7779 public override void Emit (EmitContext ec)
7781 if (ec.EmitAccurateDebugInfo) {
7782 ec.Emit (OpCodes.Nop);
7788 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7790 throw new NotImplementedException ();
7794 sealed class ArrayForeach : IteratorStatement
7796 TemporaryVariableReference[] lengths;
7797 Expression [] length_exprs;
7798 StatementExpression[] counter;
7799 TemporaryVariableReference[] variables;
7801 TemporaryVariableReference copy;
7803 public ArrayForeach (Foreach @foreach, int rank)
7806 counter = new StatementExpression[rank];
7807 variables = new TemporaryVariableReference[rank];
7808 length_exprs = new Expression [rank];
7811 // Only use temporary length variables when dealing with
7812 // multi-dimensional arrays
7815 lengths = new TemporaryVariableReference [rank];
7818 public override bool Resolve (BlockContext ec)
7820 Block variables_block = for_each.variable.Block;
7821 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7824 int rank = length_exprs.Length;
7825 Arguments list = new Arguments (rank);
7826 for (int i = 0; i < rank; i++) {
7827 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7829 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7830 counter[i].Resolve (ec);
7833 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7835 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7836 lengths[i].Resolve (ec);
7838 Arguments args = new Arguments (1);
7839 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7840 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7843 list.Add (new Argument (v));
7846 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7851 if (for_each.type is VarExpr) {
7852 // Infer implicitly typed local variable from foreach array type
7853 var_type = access.Type;
7855 var_type = for_each.type.ResolveAsType (ec);
7857 if (var_type == null)
7860 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7865 for_each.variable.Type = var_type;
7867 var prev_block = ec.CurrentBlock;
7868 ec.CurrentBlock = variables_block;
7869 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7870 ec.CurrentBlock = prev_block;
7872 if (variable_ref == null)
7875 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7877 return for_each.body.Resolve (ec);
7880 protected override void DoEmit (EmitContext ec)
7882 copy.EmitAssign (ec, for_each.expr);
7884 int rank = length_exprs.Length;
7885 Label[] test = new Label [rank];
7886 Label[] loop = new Label [rank];
7888 for (int i = 0; i < rank; i++) {
7889 test [i] = ec.DefineLabel ();
7890 loop [i] = ec.DefineLabel ();
7892 if (lengths != null)
7893 lengths [i].EmitAssign (ec, length_exprs [i]);
7896 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7897 for (int i = 0; i < rank; i++) {
7898 variables [i].EmitAssign (ec, zero);
7900 ec.Emit (OpCodes.Br, test [i]);
7901 ec.MarkLabel (loop [i]);
7904 for_each.body.Emit (ec);
7906 ec.MarkLabel (ec.LoopBegin);
7907 ec.Mark (for_each.expr.Location);
7909 for (int i = rank - 1; i >= 0; i--){
7910 counter [i].Emit (ec);
7912 ec.MarkLabel (test [i]);
7913 variables [i].Emit (ec);
7915 if (lengths != null)
7916 lengths [i].Emit (ec);
7918 length_exprs [i].Emit (ec);
7920 ec.Emit (OpCodes.Blt, loop [i]);
7923 ec.MarkLabel (ec.LoopEnd);
7927 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7929 class RuntimeDispose : Using.VariableDeclaration
7931 public RuntimeDispose (LocalVariable lv, Location loc)
7937 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7939 // Defered to runtime check
7942 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7944 var idt = bc.BuiltinTypes.IDisposable;
7947 // Fabricates code like
7949 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7952 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7954 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7955 dispose_variable.CreateReferenceExpression (bc, loc),
7956 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7957 loc), new NullLiteral (loc));
7959 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7961 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7962 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7964 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7965 return new If (idisaposable_test, dispose, loc);
7969 LocalVariable variable;
7971 Statement statement;
7972 ExpressionStatement init;
7973 TemporaryVariableReference enumerator_variable;
7974 bool ambiguous_getenumerator_name;
7976 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7979 this.variable = var;
7983 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7985 rc.Report.SymbolRelatedToPreviousError (enumerator);
7986 rc.Report.Error (202, loc,
7987 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7988 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7991 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7994 // Option 1: Try to match by name GetEnumerator first
7996 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7997 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7999 var mg = mexpr as MethodGroupExpr;
8001 mg.InstanceExpression = expr;
8002 Arguments args = new Arguments (0);
8003 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8005 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8006 if (ambiguous_getenumerator_name)
8009 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8015 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8018 PredefinedMember<MethodSpec> iface_candidate = null;
8019 var ptypes = rc.Module.PredefinedTypes;
8020 var gen_ienumerable = ptypes.IEnumerableGeneric;
8021 if (!gen_ienumerable.Define ())
8022 gen_ienumerable = null;
8024 var ifaces = t.Interfaces;
8025 if (ifaces != null) {
8026 foreach (var iface in ifaces) {
8027 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8028 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8029 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8030 rc.Report.Error (1640, loc,
8031 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8032 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8037 // TODO: Cache this somehow
8038 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8039 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8044 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8045 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8050 if (iface_candidate == null) {
8051 if (expr.Type != InternalType.ErrorType) {
8052 rc.Report.Error (1579, loc,
8053 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8054 expr.Type.GetSignatureForError (), "GetEnumerator");
8060 var method = iface_candidate.Resolve (loc);
8064 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8065 mg.InstanceExpression = expr;
8069 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8071 var ms = MemberCache.FindMember (enumerator.ReturnType,
8072 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8073 BindingRestriction.InstanceOnly) as MethodSpec;
8075 if (ms == null || !ms.IsPublic) {
8076 Error_WrongEnumerator (rc, enumerator);
8080 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8083 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8085 var ps = MemberCache.FindMember (enumerator.ReturnType,
8086 MemberFilter.Property ("Current", null),
8087 BindingRestriction.InstanceOnly) as PropertySpec;
8089 if (ps == null || !ps.IsPublic) {
8090 Error_WrongEnumerator (rc, enumerator);
8097 public override bool Resolve (BlockContext ec)
8099 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8102 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8103 } else if (expr.Type.IsNullableType) {
8104 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8107 var get_enumerator_mg = ResolveGetEnumerator (ec);
8108 if (get_enumerator_mg == null) {
8112 var get_enumerator = get_enumerator_mg.BestCandidate;
8113 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8114 enumerator_variable.Resolve (ec);
8116 // Prepare bool MoveNext ()
8117 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8118 if (move_next_mg == null) {
8122 move_next_mg.InstanceExpression = enumerator_variable;
8124 // Prepare ~T~ Current { get; }
8125 var current_prop = ResolveCurrent (ec, get_enumerator);
8126 if (current_prop == null) {
8130 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8131 if (current_pe == null)
8134 VarExpr ve = for_each.type as VarExpr;
8138 // Source type is dynamic, set element type to dynamic too
8139 variable.Type = ec.BuiltinTypes.Dynamic;
8141 // Infer implicitly typed local variable from foreach enumerable type
8142 variable.Type = current_pe.Type;
8146 // Explicit cast of dynamic collection elements has to be done at runtime
8147 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8150 variable.Type = for_each.type.ResolveAsType (ec);
8152 if (variable.Type == null)
8155 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8156 if (current_pe == null)
8160 var prev_block = ec.CurrentBlock;
8161 ec.CurrentBlock = for_each.variable.Block;
8162 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8163 ec.CurrentBlock = prev_block;
8164 if (variable_ref == null)
8167 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8169 var init = new Invocation.Predefined (get_enumerator_mg, null);
8171 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8172 for_each.body, Location.Null);
8174 var enum_type = enumerator_variable.Type;
8177 // Add Dispose method call when enumerator can be IDisposable
8179 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8180 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8182 // Runtime Dispose check
8184 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8185 vd.Initializer = init;
8186 statement = new Using (vd, statement, Location.Null);
8189 // No Dispose call needed
8191 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8192 this.init.Resolve (ec);
8196 // Static Dispose check
8198 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8199 vd.Initializer = init;
8200 statement = new Using (vd, statement, Location.Null);
8203 return statement.Resolve (ec);
8206 protected override void DoEmit (EmitContext ec)
8208 enumerator_variable.LocalInfo.CreateBuilder (ec);
8211 init.EmitStatement (ec);
8213 statement.Emit (ec);
8216 #region IErrorHandler Members
8218 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8220 ec.Report.SymbolRelatedToPreviousError (best);
8221 ec.Report.Warning (278, 2, expr.Location,
8222 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8223 expr.Type.GetSignatureForError (), "enumerable",
8224 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8226 ambiguous_getenumerator_name = true;
8230 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8235 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8240 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8249 LocalVariable variable;
8253 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8257 this.variable = var;
8263 public Expression Expr {
8264 get { return expr; }
8267 public Expression TypeExpression {
8268 get { return type; }
8271 public LocalVariable Variable {
8272 get { return variable; }
8275 public override Reachability MarkReachable (Reachability rc)
8277 base.MarkReachable (rc);
8279 body.MarkReachable (rc);
8284 public override bool Resolve (BlockContext ec)
8286 expr = expr.Resolve (ec);
8291 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8295 body.AddStatement (Statement);
8297 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8298 Statement = new ArrayForeach (this, 1);
8299 } else if (expr.Type is ArrayContainer) {
8300 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8302 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8303 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8304 expr.ExprClassName);
8308 Statement = new CollectionForeach (this, variable, expr);
8311 return base.Resolve (ec);
8314 protected override void DoEmit (EmitContext ec)
8316 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8317 ec.LoopBegin = ec.DefineLabel ();
8318 ec.LoopEnd = ec.DefineLabel ();
8320 if (!(Statement is Block))
8321 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8323 variable.CreateBuilder (ec);
8325 Statement.Emit (ec);
8327 if (!(Statement is Block))
8330 ec.LoopBegin = old_begin;
8331 ec.LoopEnd = old_end;
8334 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8336 expr.FlowAnalysis (fc);
8338 var da = fc.BranchDefiniteAssignment ();
8339 body.FlowAnalysis (fc);
8340 fc.DefiniteAssignment = da;
8344 protected override void CloneTo (CloneContext clonectx, Statement t)
8346 Foreach target = (Foreach) t;
8348 target.type = type.Clone (clonectx);
8349 target.expr = expr.Clone (clonectx);
8350 target.body = (Block) body.Clone (clonectx);
8351 target.Statement = Statement.Clone (clonectx);
8354 public override object Accept (StructuralVisitor visitor)
8356 return visitor.Visit (this);
8360 class SentinelStatement: Statement
8362 protected override void CloneTo (CloneContext clonectx, Statement target)
8366 protected override void DoEmit (EmitContext ec)
8368 var l = ec.DefineLabel ();
8370 ec.Emit (OpCodes.Br_S, l);
8373 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8375 throw new NotImplementedException ();