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 if (!Variable.IsUsed)
2284 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2286 // Nothing to emit, not even sequence point
2289 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2291 initializer = initializer.Resolve (bc);
2292 if (initializer == null)
2295 var c = initializer as Constant;
2297 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2301 c = c.ConvertImplicitly (li.Type);
2303 if (TypeSpec.IsReferenceType (li.Type))
2304 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2306 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2311 li.ConstantValue = c;
2315 public override object Accept (StructuralVisitor visitor)
2317 return visitor.Visit (this);
2322 // The information about a user-perceived local variable
2324 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2331 AddressTaken = 1 << 2,
2332 CompilerGenerated = 1 << 3,
2334 ForeachVariable = 1 << 5,
2335 FixedVariable = 1 << 6,
2336 UsingVariable = 1 << 7,
2338 SymbolFileHidden = 1 << 9,
2340 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2344 readonly string name;
2345 readonly Location loc;
2346 readonly Block block;
2348 Constant const_value;
2350 public VariableInfo VariableInfo;
2351 HoistedVariable hoisted_variant;
2353 LocalBuilder builder;
2355 public LocalVariable (Block block, string name, Location loc)
2362 public LocalVariable (Block block, string name, Flags flags, Location loc)
2363 : this (block, name, loc)
2369 // Used by variable declarators
2371 public LocalVariable (LocalVariable li, string name, Location loc)
2372 : this (li.block, name, li.flags, loc)
2378 public bool AddressTaken {
2380 return (flags & Flags.AddressTaken) != 0;
2384 public Block Block {
2390 public Constant ConstantValue {
2395 const_value = value;
2400 // Hoisted local variable variant
2402 public HoistedVariable HoistedVariant {
2404 return hoisted_variant;
2407 hoisted_variant = value;
2411 public bool IsDeclared {
2413 return type != null;
2417 public bool IsCompilerGenerated {
2419 return (flags & Flags.CompilerGenerated) != 0;
2423 public bool IsConstant {
2425 return (flags & Flags.Constant) != 0;
2429 public bool IsLocked {
2431 return (flags & Flags.IsLocked) != 0;
2434 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2438 public bool IsThis {
2440 return (flags & Flags.IsThis) != 0;
2444 public bool IsUsed {
2446 return (flags & Flags.Used) != 0;
2450 public bool IsFixed {
2452 return (flags & Flags.FixedVariable) != 0;
2455 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2459 bool INamedBlockVariable.IsParameter {
2465 public bool IsReadonly {
2467 return (flags & Flags.ReadonlyMask) != 0;
2471 public Location Location {
2477 public string Name {
2483 public TypeSpec Type {
2494 public void CreateBuilder (EmitContext ec)
2496 if ((flags & Flags.Used) == 0) {
2497 if (VariableInfo == null) {
2498 // Missing flow analysis or wrong variable flags
2499 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2502 if (VariableInfo.IsEverAssigned)
2503 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2505 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2508 if (HoistedVariant != null)
2511 if (builder != null) {
2512 if ((flags & Flags.CompilerGenerated) != 0)
2515 // To avoid Used warning duplicates
2516 throw new InternalErrorException ("Already created variable `{0}'", name);
2520 // All fixed variabled are pinned, a slot has to be alocated
2522 builder = ec.DeclareLocal (Type, IsFixed);
2523 if ((flags & Flags.SymbolFileHidden) == 0)
2524 ec.DefineLocalVariable (name, builder);
2527 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2529 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2530 if (!writeToSymbolFile)
2531 li.flags |= Flags.SymbolFileHidden;
2537 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2539 if (IsConstant && const_value != null) {
2541 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2544 return new LocalVariableReference (this, loc);
2547 public void Emit (EmitContext ec)
2549 // TODO: Need something better for temporary variables
2550 if ((flags & Flags.CompilerGenerated) != 0)
2553 ec.Emit (OpCodes.Ldloc, builder);
2556 public void EmitAssign (EmitContext ec)
2558 // TODO: Need something better for temporary variables
2559 if ((flags & Flags.CompilerGenerated) != 0)
2562 ec.Emit (OpCodes.Stloc, builder);
2565 public void EmitAddressOf (EmitContext ec)
2567 // TODO: Need something better for temporary variables
2568 if ((flags & Flags.CompilerGenerated) != 0)
2571 ec.Emit (OpCodes.Ldloca, builder);
2574 public static string GetCompilerGeneratedName (Block block)
2576 // HACK: Debugger depends on the name semantics
2577 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2580 public string GetReadOnlyContext ()
2582 switch (flags & Flags.ReadonlyMask) {
2583 case Flags.FixedVariable:
2584 return "fixed variable";
2585 case Flags.ForeachVariable:
2586 return "foreach iteration variable";
2587 case Flags.UsingVariable:
2588 return "using variable";
2591 throw new InternalErrorException ("Variable is not readonly");
2594 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2596 if (VariableInfo == null)
2597 throw new Exception ();
2599 if (IsAssigned (fc))
2602 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2605 public bool IsAssigned (FlowAnalysisContext fc)
2607 return fc.IsDefinitelyAssigned (VariableInfo);
2610 public void PrepareAssignmentAnalysis (BlockContext bc)
2613 // No need to run assignment analysis for these guys
2615 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2618 VariableInfo = VariableInfo.Create (bc, this);
2622 // Mark the variables as referenced in the user code
2624 public void SetIsUsed ()
2626 flags |= Flags.Used;
2629 public void SetHasAddressTaken ()
2631 flags |= (Flags.AddressTaken | Flags.Used);
2634 public override string ToString ()
2636 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2641 /// Block represents a C# block.
2645 /// This class is used in a number of places: either to represent
2646 /// explicit blocks that the programmer places or implicit blocks.
2648 /// Implicit blocks are used as labels or to introduce variable
2651 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2652 /// they contain extra information that is not necessary on normal blocks.
2654 public class Block : Statement {
2661 HasCapturedVariable = 64,
2662 HasCapturedThis = 1 << 7,
2663 IsExpressionTree = 1 << 8,
2664 CompilerGenerated = 1 << 9,
2665 HasAsyncModifier = 1 << 10,
2667 YieldBlock = 1 << 12,
2668 AwaitBlock = 1 << 13,
2669 FinallyBlock = 1 << 14,
2670 CatchBlock = 1 << 15,
2671 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2673 NoFlowAnalysis = 1 << 21,
2674 InitializationEmitted = 1 << 22
2677 public Block Parent;
2678 public Location StartLocation;
2679 public Location EndLocation;
2681 public ExplicitBlock Explicit;
2682 public ParametersBlock ParametersBlock;
2684 protected Flags flags;
2687 // The statements in this block
2689 protected List<Statement> statements;
2691 protected List<Statement> scope_initializers;
2693 int? resolving_init_idx;
2699 public int ID = id++;
2701 static int clone_id_counter;
2705 // int assignable_slots;
2707 public Block (Block parent, Location start, Location end)
2708 : this (parent, 0, start, end)
2712 public Block (Block parent, Flags flags, Location start, Location end)
2714 if (parent != null) {
2715 // the appropriate constructors will fixup these fields
2716 ParametersBlock = parent.ParametersBlock;
2717 Explicit = parent.Explicit;
2720 this.Parent = parent;
2722 this.StartLocation = start;
2723 this.EndLocation = end;
2725 statements = new List<Statement> (4);
2727 this.original = this;
2732 public Block Original {
2741 public bool IsCompilerGenerated {
2742 get { return (flags & Flags.CompilerGenerated) != 0; }
2743 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2747 public bool IsCatchBlock {
2749 return (flags & Flags.CatchBlock) != 0;
2753 public bool IsFinallyBlock {
2755 return (flags & Flags.FinallyBlock) != 0;
2759 public bool Unchecked {
2760 get { return (flags & Flags.Unchecked) != 0; }
2761 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2764 public bool Unsafe {
2765 get { return (flags & Flags.Unsafe) != 0; }
2766 set { flags |= Flags.Unsafe; }
2769 public List<Statement> Statements {
2770 get { return statements; }
2775 public void SetEndLocation (Location loc)
2780 public void AddLabel (LabeledStatement target)
2782 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2785 public void AddLocalName (LocalVariable li)
2787 AddLocalName (li.Name, li);
2790 public void AddLocalName (string name, INamedBlockVariable li)
2792 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2795 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2797 if (reason == null) {
2798 Error_AlreadyDeclared (name, variable);
2802 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2803 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2804 "to `{0}', which is already used in a `{1}' scope to denote something else",
2808 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2810 var pi = variable as ParametersBlock.ParameterInfo;
2812 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2814 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2815 "A local variable named `{0}' is already defined in this scope", name);
2819 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2821 ParametersBlock.TopBlock.Report.Error (412, loc,
2822 "The type parameter name `{0}' is the same as local variable or parameter name",
2827 // It should be used by expressions which require to
2828 // register a statement during resolve process.
2830 public void AddScopeStatement (Statement s)
2832 if (scope_initializers == null)
2833 scope_initializers = new List<Statement> ();
2836 // Simple recursive helper, when resolve scope initializer another
2837 // new scope initializer can be added, this ensures it's initialized
2838 // before existing one. For now this can happen with expression trees
2839 // in base ctor initializer only
2841 if (resolving_init_idx.HasValue) {
2842 scope_initializers.Insert (resolving_init_idx.Value, s);
2843 ++resolving_init_idx;
2845 scope_initializers.Add (s);
2849 public void InsertStatement (int index, Statement s)
2851 statements.Insert (index, s);
2854 public void AddStatement (Statement s)
2859 public LabeledStatement LookupLabel (string name)
2861 return ParametersBlock.GetLabel (name, this);
2864 public override Reachability MarkReachable (Reachability rc)
2866 if (rc.IsUnreachable)
2869 MarkReachableScope (rc);
2871 foreach (var s in statements) {
2872 rc = s.MarkReachable (rc);
2873 if (rc.IsUnreachable) {
2874 if ((flags & Flags.ReachableEnd) != 0)
2875 return new Reachability ();
2881 flags |= Flags.ReachableEnd;
2886 public void MarkReachableScope (Reachability rc)
2888 base.MarkReachable (rc);
2890 if (scope_initializers != null) {
2891 foreach (var si in scope_initializers)
2892 si.MarkReachable (rc);
2896 public override bool Resolve (BlockContext bc)
2898 if ((flags & Flags.Resolved) != 0)
2901 Block prev_block = bc.CurrentBlock;
2902 bc.CurrentBlock = this;
2905 // Compiler generated scope statements
2907 if (scope_initializers != null) {
2908 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2909 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2912 resolving_init_idx = null;
2916 int statement_count = statements.Count;
2917 for (int ix = 0; ix < statement_count; ix++){
2918 Statement s = statements [ix];
2920 if (!s.Resolve (bc)) {
2922 statements [ix] = new EmptyStatement (s.loc);
2927 bc.CurrentBlock = prev_block;
2929 flags |= Flags.Resolved;
2933 protected override void DoEmit (EmitContext ec)
2935 for (int ix = 0; ix < statements.Count; ix++){
2936 statements [ix].Emit (ec);
2940 public override void Emit (EmitContext ec)
2942 if (scope_initializers != null)
2943 EmitScopeInitializers (ec);
2948 protected void EmitScopeInitializers (EmitContext ec)
2950 foreach (Statement s in scope_initializers)
2954 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2956 if (scope_initializers != null) {
2957 foreach (var si in scope_initializers)
2958 si.FlowAnalysis (fc);
2961 return DoFlowAnalysis (fc, 0);
2964 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2966 bool end_unreachable = !reachable;
2967 bool goto_flow_analysis = startIndex != 0;
2968 for (; startIndex < statements.Count; ++startIndex) {
2969 var s = statements[startIndex];
2971 end_unreachable = s.FlowAnalysis (fc);
2972 if (s.IsUnreachable) {
2973 statements [startIndex] = RewriteUnreachableStatement (s);
2978 // Statement end reachability is needed mostly due to goto support. Consider
2987 // X label is reachable only via goto not as another statement after if. We need
2988 // this for flow-analysis only to carry variable info correctly.
2990 if (end_unreachable) {
2991 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2993 var f = s as TryFinally;
2994 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
2996 // Special case for try-finally with unreachable code after
2997 // finally block. Try block has to include leave opcode but there is
2998 // no label to leave to after unreachable finally block closing
2999 // brace. This sentinel ensures there is always IL instruction to
3000 // leave to even if we know it'll never be reached.
3002 statements.Insert (startIndex + 1, new SentinelStatement ());
3004 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3005 s = statements [startIndex];
3006 if (s is SwitchLabel) {
3007 if (!after_goto_case)
3008 s.FlowAnalysis (fc);
3013 if (s.IsUnreachable) {
3014 s.FlowAnalysis (fc);
3015 statements [startIndex] = RewriteUnreachableStatement (s);
3021 // Idea is to stop after goto case because goto case will always have at least same
3022 // variable assigned as switch case label. This saves a lot for complex goto case tests
3024 if (after_goto_case)
3030 var lb = s as LabeledStatement;
3031 if (lb != null && fc.AddReachedLabel (lb))
3036 // The condition should be true unless there is forward jumping goto
3038 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3041 return !Explicit.HasReachableClosingBrace;
3044 static Statement RewriteUnreachableStatement (Statement s)
3046 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3047 // analysis. Even csc report unreachable warning for it but it's actually used hence
3048 // we try to emulate this behaviour
3056 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3059 return new EmptyStatement (s.loc);
3062 public void ScanGotoJump (Statement label)
3065 for (i = 0; i < statements.Count; ++i) {
3066 if (statements[i] == label)
3070 var rc = new Reachability ();
3071 for (++i; i < statements.Count; ++i) {
3072 var s = statements[i];
3073 rc = s.MarkReachable (rc);
3074 if (rc.IsUnreachable)
3078 flags |= Flags.ReachableEnd;
3081 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3084 for (i = 0; i < statements.Count; ++i) {
3085 if (statements[i] == label)
3089 DoFlowAnalysis (fc, ++i);
3093 public override string ToString ()
3095 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3099 protected override void CloneTo (CloneContext clonectx, Statement t)
3101 Block target = (Block) t;
3103 target.clone_id = ++clone_id_counter;
3106 clonectx.AddBlockMap (this, target);
3107 if (original != this)
3108 clonectx.AddBlockMap (original, target);
3110 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3111 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3114 target.Parent = clonectx.RemapBlockCopy (Parent);
3116 target.statements = new List<Statement> (statements.Count);
3117 foreach (Statement s in statements)
3118 target.statements.Add (s.Clone (clonectx));
3121 public override object Accept (StructuralVisitor visitor)
3123 return visitor.Visit (this);
3127 public class ExplicitBlock : Block
3129 protected AnonymousMethodStorey am_storey;
3130 int debug_scope_index;
3132 public ExplicitBlock (Block parent, Location start, Location end)
3133 : this (parent, (Flags) 0, start, end)
3137 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3138 : base (parent, flags, start, end)
3140 this.Explicit = this;
3145 public AnonymousMethodStorey AnonymousMethodStorey {
3151 public bool HasAwait {
3153 return (flags & Flags.AwaitBlock) != 0;
3157 public bool HasCapturedThis {
3159 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3162 return (flags & Flags.HasCapturedThis) != 0;
3167 // Used to indicate that the block has reference to parent
3168 // block and cannot be made static when defining anonymous method
3170 public bool HasCapturedVariable {
3172 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3175 return (flags & Flags.HasCapturedVariable) != 0;
3179 public bool HasReachableClosingBrace {
3181 return (flags & Flags.ReachableEnd) != 0;
3184 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3188 public bool HasYield {
3190 return (flags & Flags.YieldBlock) != 0;
3197 // Creates anonymous method storey in current block
3199 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3202 // Return same story for iterator and async blocks unless we are
3203 // in nested anonymous method
3205 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3206 return ec.CurrentAnonymousMethod.Storey;
3208 if (am_storey == null) {
3209 MemberBase mc = ec.MemberContext as MemberBase;
3212 // Creates anonymous method storey for this block
3214 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3220 public void EmitScopeInitialization (EmitContext ec)
3222 if ((flags & Flags.InitializationEmitted) != 0)
3225 if (am_storey != null) {
3226 DefineStoreyContainer (ec, am_storey);
3227 am_storey.EmitStoreyInstantiation (ec, this);
3230 if (scope_initializers != null)
3231 EmitScopeInitializers (ec);
3233 flags |= Flags.InitializationEmitted;
3236 public override void Emit (EmitContext ec)
3238 if (Parent != null) {
3239 // TODO: It's needed only when scope has variable (normal or lifted)
3240 ec.BeginScope (GetDebugSymbolScopeIndex ());
3243 EmitScopeInitialization (ec);
3245 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3246 ec.Emit (OpCodes.Nop);
3254 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3255 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3256 ec.Emit (OpCodes.Nop);
3260 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3262 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3263 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3264 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3268 // Creates anonymous method storey
3270 storey.CreateContainer ();
3271 storey.DefineContainer ();
3272 storey.ExpandBaseInterfaces ();
3274 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3277 // Only first storey in path will hold this reference. All children blocks will
3278 // reference it indirectly using $ref field
3280 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3281 if (b.Parent != null) {
3282 var s = b.Parent.Explicit.AnonymousMethodStorey;
3284 storey.HoistedThis = s.HoistedThis;
3289 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3290 if (storey.HoistedThis == null)
3291 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3293 if (storey.HoistedThis != null)
3299 // We are the first storey on path and 'this' has to be hoisted
3301 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3302 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3304 // ThisReferencesFromChildrenBlock holds all reference even if they
3305 // are not on this path. It saves some memory otherwise it'd have to
3306 // be in every explicit block. We run this check to see if the reference
3307 // is valid for this storey
3309 Block block_on_path = ref_block;
3310 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3312 if (block_on_path == null)
3315 if (storey.HoistedThis == null) {
3316 storey.AddCapturedThisField (ec, null);
3319 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3321 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3323 if (b_storey != null) {
3325 // Don't add storey cross reference for `this' when the storey ends up not
3326 // beeing attached to any parent
3328 if (b.ParametersBlock.StateMachine == null) {
3329 AnonymousMethodStorey s = null;
3330 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3331 s = ab.Explicit.AnonymousMethodStorey;
3336 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3338 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3339 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3346 // Stop propagation inside same top block
3348 if (b.ParametersBlock == ParametersBlock.Original) {
3349 b_storey.AddParentStoreyReference (ec, storey);
3350 // b_storey.HoistedThis = storey.HoistedThis;
3354 b = pb = b.ParametersBlock;
3356 pb = b as ParametersBlock;
3359 if (pb != null && pb.StateMachine != null) {
3360 if (pb.StateMachine == storey)
3364 // If we are state machine with no parent. We can hook into parent without additional
3365 // reference and capture this directly
3367 ExplicitBlock parent_storey_block = pb;
3368 while (parent_storey_block.Parent != null) {
3369 parent_storey_block = parent_storey_block.Parent.Explicit;
3370 if (parent_storey_block.AnonymousMethodStorey != null) {
3375 if (parent_storey_block.AnonymousMethodStorey == null) {
3376 if (pb.StateMachine.HoistedThis == null) {
3377 pb.StateMachine.AddCapturedThisField (ec, null);
3378 b.HasCapturedThis = true;
3384 var parent_this_block = pb;
3385 while (parent_this_block.Parent != null) {
3386 parent_this_block = parent_this_block.Parent.ParametersBlock;
3387 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3393 // Add reference to closest storey which holds captured this
3395 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3399 // Add parent storey reference only when this is not captured directly
3401 if (b_storey != null) {
3402 b_storey.AddParentStoreyReference (ec, storey);
3403 b_storey.HoistedThis = storey.HoistedThis;
3410 var ref_blocks = storey.ReferencesFromChildrenBlock;
3411 if (ref_blocks != null) {
3412 foreach (ExplicitBlock ref_block in ref_blocks) {
3413 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3414 if (b.AnonymousMethodStorey != null) {
3415 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3418 // Stop propagation inside same top block
3420 if (b.ParametersBlock == ParametersBlock.Original)
3423 b = b.ParametersBlock;
3426 var pb = b as ParametersBlock;
3427 if (pb != null && pb.StateMachine != null) {
3428 if (pb.StateMachine == storey)
3431 pb.StateMachine.AddParentStoreyReference (ec, storey);
3434 b.HasCapturedVariable = true;
3440 storey.PrepareEmit ();
3441 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3444 public int GetDebugSymbolScopeIndex ()
3446 if (debug_scope_index == 0)
3447 debug_scope_index = ++ParametersBlock.debug_scope_index;
3449 return debug_scope_index;
3452 public void RegisterAsyncAwait ()
3455 while ((block.flags & Flags.AwaitBlock) == 0) {
3456 block.flags |= Flags.AwaitBlock;
3458 if (block is ParametersBlock)
3461 block = block.Parent.Explicit;
3465 public void RegisterIteratorYield ()
3467 ParametersBlock.TopBlock.IsIterator = true;
3470 while ((block.flags & Flags.YieldBlock) == 0) {
3471 block.flags |= Flags.YieldBlock;
3473 if (block.Parent == null)
3476 block = block.Parent.Explicit;
3480 public void SetCatchBlock ()
3482 flags |= Flags.CatchBlock;
3485 public void SetFinallyBlock ()
3487 flags |= Flags.FinallyBlock;
3490 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3492 tryBlock.statements = statements;
3493 statements = new List<Statement> (1);
3494 statements.Add (tf);
3499 // ParametersBlock was introduced to support anonymous methods
3500 // and lambda expressions
3502 public class ParametersBlock : ExplicitBlock
3504 public class ParameterInfo : INamedBlockVariable
3506 readonly ParametersBlock block;
3508 public VariableInfo VariableInfo;
3511 public ParameterInfo (ParametersBlock block, int index)
3519 public ParametersBlock Block {
3525 Block INamedBlockVariable.Block {
3531 public bool IsDeclared {
3537 public bool IsParameter {
3543 public bool IsLocked {
3552 public Location Location {
3554 return Parameter.Location;
3558 public Parameter Parameter {
3560 return block.Parameters [index];
3564 public TypeSpec ParameterType {
3566 return Parameter.Type;
3572 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3574 return new ParameterReference (this, loc);
3579 // Block is converted into an expression
3581 sealed class BlockScopeExpression : Expression
3584 readonly ParametersBlock block;
3586 public BlockScopeExpression (Expression child, ParametersBlock block)
3592 public override bool ContainsEmitWithAwait ()
3594 return child.ContainsEmitWithAwait ();
3597 public override Expression CreateExpressionTree (ResolveContext ec)
3599 throw new NotSupportedException ();
3602 protected override Expression DoResolve (ResolveContext ec)
3607 child = child.Resolve (ec);
3611 eclass = child.eclass;
3616 public override void Emit (EmitContext ec)
3618 block.EmitScopeInitializers (ec);
3623 protected ParametersCompiled parameters;
3624 protected ParameterInfo[] parameter_info;
3625 protected bool resolved;
3626 protected ToplevelBlock top_block;
3627 protected StateMachine state_machine;
3628 protected Dictionary<string, object> labels;
3630 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3631 : base (parent, 0, start, start)
3633 if (parameters == null)
3634 throw new ArgumentNullException ("parameters");
3636 this.parameters = parameters;
3637 ParametersBlock = this;
3639 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3641 this.top_block = parent.ParametersBlock.top_block;
3642 ProcessParameters ();
3645 protected ParametersBlock (ParametersCompiled parameters, Location start)
3646 : base (null, 0, start, start)
3648 if (parameters == null)
3649 throw new ArgumentNullException ("parameters");
3651 this.parameters = parameters;
3652 ParametersBlock = this;
3656 // It's supposed to be used by method body implementation of anonymous methods
3658 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3659 : base (null, 0, source.StartLocation, source.EndLocation)
3661 this.parameters = parameters;
3662 this.statements = source.statements;
3663 this.scope_initializers = source.scope_initializers;
3665 this.resolved = true;
3666 this.reachable = source.reachable;
3667 this.am_storey = source.am_storey;
3668 this.state_machine = source.state_machine;
3669 this.flags = source.flags & Flags.ReachableEnd;
3671 ParametersBlock = this;
3674 // Overwrite original for comparison purposes when linking cross references
3675 // between anonymous methods
3677 Original = source.Original;
3682 public bool HasReferenceToStoreyForInstanceLambdas {
3684 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3687 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3691 public bool IsAsync {
3693 return (flags & Flags.HasAsyncModifier) != 0;
3696 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3701 // Block has been converted to expression tree
3703 public bool IsExpressionTree {
3705 return (flags & Flags.IsExpressionTree) != 0;
3710 // The parameters for the block.
3712 public ParametersCompiled Parameters {
3718 public StateMachine StateMachine {
3720 return state_machine;
3724 public ToplevelBlock TopBlock {
3733 public bool Resolved {
3735 return (flags & Flags.Resolved) != 0;
3739 public int TemporaryLocalsCount { get; set; }
3744 // Checks whether all `out' parameters have been assigned.
3746 public void CheckControlExit (FlowAnalysisContext fc)
3748 CheckControlExit (fc, fc.DefiniteAssignment);
3751 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3753 if (parameter_info == null)
3756 foreach (var p in parameter_info) {
3757 if (p.VariableInfo == null)
3760 if (p.VariableInfo.IsAssigned (dat))
3763 fc.Report.Error (177, p.Location,
3764 "The out parameter `{0}' must be assigned to before control leaves the current method",
3769 protected override void CloneTo (CloneContext clonectx, Statement t)
3771 base.CloneTo (clonectx, t);
3773 var target = (ParametersBlock) t;
3776 // Clone label statements as well as they contain block reference
3780 if (pb.labels != null) {
3781 target.labels = new Dictionary<string, object> ();
3783 foreach (var entry in pb.labels) {
3784 var list = entry.Value as List<LabeledStatement>;
3787 var list_clone = new List<LabeledStatement> ();
3788 foreach (var lentry in list) {
3789 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3792 target.labels.Add (entry.Key, list_clone);
3794 var labeled = (LabeledStatement) entry.Value;
3795 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3802 if (pb.Parent == null)
3805 pb = pb.Parent.ParametersBlock;
3809 public override Expression CreateExpressionTree (ResolveContext ec)
3811 if (statements.Count == 1) {
3812 Expression expr = statements[0].CreateExpressionTree (ec);
3813 if (scope_initializers != null)
3814 expr = new BlockScopeExpression (expr, this);
3819 return base.CreateExpressionTree (ec);
3822 public override void Emit (EmitContext ec)
3824 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3825 DefineStoreyContainer (ec, state_machine);
3826 state_machine.EmitStoreyInstantiation (ec, this);
3832 public void EmitEmbedded (EmitContext ec)
3834 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3835 DefineStoreyContainer (ec, state_machine);
3836 state_machine.EmitStoreyInstantiation (ec, this);
3842 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3844 var res = base.DoFlowAnalysis (fc);
3846 if (HasReachableClosingBrace)
3847 CheckControlExit (fc);
3852 public LabeledStatement GetLabel (string name, Block block)
3855 // Cloned parameters blocks can have their own cloned version of top-level labels
3857 if (labels == null) {
3859 return Parent.ParametersBlock.GetLabel (name, block);
3865 if (!labels.TryGetValue (name, out value)) {
3869 var label = value as LabeledStatement;
3871 if (label != null) {
3872 if (IsLabelVisible (label, b))
3876 List<LabeledStatement> list = (List<LabeledStatement>) value;
3877 for (int i = 0; i < list.Count; ++i) {
3879 if (IsLabelVisible (label, b))
3887 static bool IsLabelVisible (LabeledStatement label, Block b)
3890 if (label.Block == b)
3893 } while (b != null);
3898 public ParameterInfo GetParameterInfo (Parameter p)
3900 for (int i = 0; i < parameters.Count; ++i) {
3901 if (parameters[i] == p)
3902 return parameter_info[i];
3905 throw new ArgumentException ("Invalid parameter");
3908 public ParameterReference GetParameterReference (int index, Location loc)
3910 return new ParameterReference (parameter_info[index], loc);
3913 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3915 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3917 CloneContext clonectx = new CloneContext ();
3918 return Clone (clonectx);
3921 protected void ProcessParameters ()
3923 if (parameters.Count == 0)
3926 parameter_info = new ParameterInfo[parameters.Count];
3927 for (int i = 0; i < parameter_info.Length; ++i) {
3928 var p = parameters.FixedParameters[i];
3932 // TODO: Should use Parameter only and more block there
3933 parameter_info[i] = new ParameterInfo (this, i);
3935 AddLocalName (p.Name, parameter_info[i]);
3939 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3941 var src = stmt.Block;
3944 // Cannot remap label block if the label was not yet cloned which
3945 // can happen in case of anonymous method inside anoynymous method
3946 // with a label. But in this case we don't care because goto cannot
3947 // jump of out anonymous method
3949 if (src.ParametersBlock != this)
3952 var src_stmts = src.Statements;
3953 for (int i = 0; i < src_stmts.Count; ++i) {
3954 if (src_stmts[i] == stmt)
3955 return (LabeledStatement) dst.Statements[i];
3958 throw new InternalErrorException ("Should never be reached");
3961 public override bool Resolve (BlockContext bc)
3963 // TODO: if ((flags & Flags.Resolved) != 0)
3970 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3971 flags |= Flags.IsExpressionTree;
3974 PrepareAssignmentAnalysis (bc);
3976 if (!base.Resolve (bc))
3979 } catch (Exception e) {
3980 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3983 if (bc.CurrentBlock != null) {
3984 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3986 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3991 // If an asynchronous body of F is either an expression classified as nothing, or a
3992 // statement block where no return statements have expressions, the inferred return type is Task
3995 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3996 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3997 am.ReturnTypeInference = null;
3998 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4006 void PrepareAssignmentAnalysis (BlockContext bc)
4008 for (int i = 0; i < parameters.Count; ++i) {
4009 var par = parameters.FixedParameters[i];
4011 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4014 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4018 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4020 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4021 var stateMachine = new IteratorStorey (iterator);
4023 state_machine = stateMachine;
4024 iterator.SetStateMachine (stateMachine);
4026 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4027 tlb.Original = this;
4028 tlb.state_machine = stateMachine;
4029 tlb.AddStatement (new Return (iterator, iterator.Location));
4033 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4035 for (int i = 0; i < parameters.Count; i++) {
4036 Parameter p = parameters[i];
4037 Parameter.Modifier mod = p.ModFlags;
4038 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4039 host.Compiler.Report.Error (1988, p.Location,
4040 "Async methods cannot have ref or out parameters");
4044 if (p is ArglistParameter) {
4045 host.Compiler.Report.Error (4006, p.Location,
4046 "__arglist is not allowed in parameter list of async methods");
4050 if (parameters.Types[i].IsPointer) {
4051 host.Compiler.Report.Error (4005, p.Location,
4052 "Async methods cannot have unsafe parameters");
4058 host.Compiler.Report.Warning (1998, 1, loc,
4059 "Async block lacks `await' operator and will run synchronously");
4062 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4063 var initializer = new AsyncInitializer (this, host, block_type);
4064 initializer.Type = block_type;
4065 initializer.DelegateType = delegateType;
4067 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4069 state_machine = stateMachine;
4070 initializer.SetStateMachine (stateMachine);
4072 const Flags flags = Flags.CompilerGenerated;
4074 var b = this is ToplevelBlock ?
4075 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4076 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4079 b.state_machine = stateMachine;
4080 b.AddStatement (new AsyncInitializerStatement (initializer));
4088 public class ToplevelBlock : ParametersBlock
4090 LocalVariable this_variable;
4091 CompilerContext compiler;
4092 Dictionary<string, object> names;
4094 List<ExplicitBlock> this_references;
4096 public ToplevelBlock (CompilerContext ctx, Location loc)
4097 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4101 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4102 : base (parameters, start)
4104 this.compiler = ctx;
4108 ProcessParameters ();
4112 // Recreates a top level block from parameters block. Used for
4113 // compiler generated methods where the original block comes from
4114 // explicit child block. This works for already resolved blocks
4115 // only to ensure we resolve them in the correct flow order
4117 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4118 : base (source, parameters)
4120 this.compiler = source.TopBlock.compiler;
4124 public bool IsIterator {
4126 return (flags & Flags.Iterator) != 0;
4129 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4133 public Report Report {
4135 return compiler.Report;
4140 // Used by anonymous blocks to track references of `this' variable
4142 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4144 return this_references;
4149 // Returns the "this" instance variable of this block.
4150 // See AddThisVariable() for more information.
4152 public LocalVariable ThisVariable {
4154 return this_variable;
4158 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4161 names = new Dictionary<string, object> ();
4164 if (!names.TryGetValue (name, out value)) {
4165 names.Add (name, li);
4169 INamedBlockVariable existing = value as INamedBlockVariable;
4170 List<INamedBlockVariable> existing_list;
4171 if (existing != null) {
4172 existing_list = new List<INamedBlockVariable> ();
4173 existing_list.Add (existing);
4174 names[name] = existing_list;
4176 existing_list = (List<INamedBlockVariable>) value;
4180 // A collision checking between local names
4182 var variable_block = li.Block.Explicit;
4183 for (int i = 0; i < existing_list.Count; ++i) {
4184 existing = existing_list[i];
4185 Block b = existing.Block.Explicit;
4187 // Collision at same level
4188 if (variable_block == b) {
4189 li.Block.Error_AlreadyDeclared (name, li);
4193 // Collision with parent
4194 Block parent = variable_block;
4195 while ((parent = parent.Parent) != null) {
4197 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4198 i = existing_list.Count;
4203 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4204 // Collision with children
4205 while ((b = b.Parent) != null) {
4206 if (variable_block == b) {
4207 li.Block.Error_AlreadyDeclared (name, li, "child");
4208 i = existing_list.Count;
4215 existing_list.Add (li);
4218 public void AddLabel (string name, LabeledStatement label)
4221 labels = new Dictionary<string, object> ();
4224 if (!labels.TryGetValue (name, out value)) {
4225 labels.Add (name, label);
4229 LabeledStatement existing = value as LabeledStatement;
4230 List<LabeledStatement> existing_list;
4231 if (existing != null) {
4232 existing_list = new List<LabeledStatement> ();
4233 existing_list.Add (existing);
4234 labels[name] = existing_list;
4236 existing_list = (List<LabeledStatement>) value;
4240 // A collision checking between labels
4242 for (int i = 0; i < existing_list.Count; ++i) {
4243 existing = existing_list[i];
4244 Block b = existing.Block;
4246 // Collision at same level
4247 if (label.Block == b) {
4248 Report.SymbolRelatedToPreviousError (existing.loc, name);
4249 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4253 // Collision with parent
4255 while ((b = b.Parent) != null) {
4256 if (existing.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;
4264 // Collision with with children
4266 while ((b = b.Parent) != null) {
4267 if (label.Block == b) {
4268 Report.Error (158, label.loc,
4269 "The label `{0}' shadows another label by the same name in a contained scope", name);
4270 i = existing_list.Count;
4276 existing_list.Add (label);
4279 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4281 if (this_references == null)
4282 this_references = new List<ExplicitBlock> ();
4284 if (!this_references.Contains (block))
4285 this_references.Add (block);
4288 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4290 this_references.Remove (block);
4294 // Creates an arguments set from all parameters, useful for method proxy calls
4296 public Arguments GetAllParametersArguments ()
4298 int count = parameters.Count;
4299 Arguments args = new Arguments (count);
4300 for (int i = 0; i < count; ++i) {
4301 var pi = parameter_info[i];
4302 var arg_expr = GetParameterReference (i, pi.Location);
4304 Argument.AType atype_modifier;
4305 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4306 case Parameter.Modifier.REF:
4307 atype_modifier = Argument.AType.Ref;
4309 case Parameter.Modifier.OUT:
4310 atype_modifier = Argument.AType.Out;
4317 args.Add (new Argument (arg_expr, atype_modifier));
4324 // Lookup inside a block, the returned value can represent 3 states
4326 // true+variable: A local name was found and it's valid
4327 // false+variable: A local name was found in a child block only
4328 // false+null: No local name was found
4330 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4336 if (!names.TryGetValue (name, out value))
4339 variable = value as INamedBlockVariable;
4341 if (variable != null) {
4343 if (variable.Block == b.Original)
4347 } while (b != null);
4355 } while (b != null);
4357 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4358 for (int i = 0; i < list.Count; ++i) {
4361 if (variable.Block == b.Original)
4365 } while (b != null);
4373 } while (b != null);
4383 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4385 if (block.names != null) {
4386 foreach (var n in block.names) {
4387 var variable = n.Value as INamedBlockVariable;
4388 if (variable != null) {
4389 if (variable.Block.ParametersBlock == pb)
4390 AddLocalName (n.Key, variable, false);
4394 foreach (var v in (List<INamedBlockVariable>) n.Value)
4395 if (v.Block.ParametersBlock == pb)
4396 AddLocalName (n.Key, v, false);
4402 // This is used by non-static `struct' constructors which do not have an
4403 // initializer - in this case, the constructor must initialize all of the
4404 // struct's fields. To do this, we add a "this" variable and use the flow
4405 // analysis code to ensure that it's been fully initialized before control
4406 // leaves the constructor.
4408 public void AddThisVariable (BlockContext bc)
4410 if (this_variable != null)
4411 throw new InternalErrorException (StartLocation.ToString ());
4413 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4414 this_variable.Type = bc.CurrentType;
4415 this_variable.PrepareAssignmentAnalysis (bc);
4418 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4421 // If we're a non-static struct constructor which doesn't have an
4422 // initializer, then we must initialize all of the struct's fields.
4424 if (this_variable != null)
4425 this_variable.IsThisAssigned (fc, this);
4427 base.CheckControlExit (fc, dat);
4430 public HashSet<LocalVariable> GetUndeclaredVariables ()
4435 HashSet<LocalVariable> variables = null;
4437 foreach (var entry in names) {
4438 var complex = entry.Value as List<INamedBlockVariable>;
4439 if (complex != null) {
4440 foreach (var centry in complex) {
4441 if (IsUndeclaredVariable (centry)) {
4442 if (variables == null)
4443 variables = new HashSet<LocalVariable> ();
4445 variables.Add ((LocalVariable) centry);
4448 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4449 if (variables == null)
4450 variables = new HashSet<LocalVariable> ();
4452 variables.Add ((LocalVariable)entry.Value);
4459 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4461 var lv = namedBlockVariable as LocalVariable;
4462 return lv != null && !lv.IsDeclared;
4465 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4470 foreach (var entry in names) {
4471 var complex = entry.Value as List<INamedBlockVariable>;
4472 if (complex != null) {
4473 foreach (var centry in complex) {
4474 var lv = centry as LocalVariable;
4475 if (lv != null && undeclaredVariables.Contains (lv)) {
4480 var lv = entry.Value as LocalVariable;
4481 if (lv != null && undeclaredVariables.Contains (lv))
4487 public override void Emit (EmitContext ec)
4489 if (Report.Errors > 0)
4493 if (IsCompilerGenerated) {
4494 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4502 // If `HasReturnLabel' is set, then we already emitted a
4503 // jump to the end of the method, so we must emit a `ret'
4506 // Unfortunately, System.Reflection.Emit automatically emits
4507 // a leave to the end of a finally block. This is a problem
4508 // if no code is following the try/finally block since we may
4509 // jump to a point after the end of the method.
4510 // As a workaround, we're always creating a return label in
4513 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4514 if (ec.HasReturnLabel)
4515 ec.MarkLabel (ec.ReturnLabel);
4517 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4518 ec.Mark (EndLocation);
4520 if (ec.ReturnType.Kind != MemberKind.Void)
4521 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4523 ec.Emit (OpCodes.Ret);
4526 } catch (Exception e) {
4527 throw new InternalErrorException (e, StartLocation);
4531 public bool Resolve (BlockContext bc, IMethodData md)
4536 var errors = bc.Report.Errors;
4540 if (bc.Report.Errors > errors)
4543 MarkReachable (new Reachability ());
4545 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4546 // TODO: var md = bc.CurrentMemberDefinition;
4547 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4550 if ((flags & Flags.NoFlowAnalysis) != 0)
4553 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4556 } catch (Exception e) {
4557 throw new InternalErrorException (e, StartLocation);
4564 public class SwitchLabel : Statement
4572 // if expr == null, then it is the default case.
4574 public SwitchLabel (Expression expr, Location l)
4580 public bool IsDefault {
4582 return label == null;
4586 public Expression Label {
4592 public Location Location {
4598 public Constant Converted {
4607 public bool PatternMatching { get; set; }
4609 public bool SectionStart { get; set; }
4611 public Label GetILLabel (EmitContext ec)
4613 if (il_label == null){
4614 il_label = ec.DefineLabel ();
4617 return il_label.Value;
4620 protected override void DoEmit (EmitContext ec)
4622 ec.MarkLabel (GetILLabel (ec));
4625 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4630 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4634 public override bool Resolve (BlockContext bc)
4636 if (ResolveAndReduce (bc))
4637 bc.Switch.RegisterLabel (bc, this);
4643 // Resolves the expression, reduces it to a literal if possible
4644 // and then converts it to the requested type.
4646 bool ResolveAndReduce (BlockContext bc)
4651 var switch_statement = bc.Switch;
4653 if (PatternMatching) {
4654 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4655 return label != null;
4658 var c = label.ResolveLabelConstant (bc);
4662 if (switch_statement.IsNullable && c is NullLiteral) {
4667 if (switch_statement.IsPatternMatching) {
4668 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4672 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4673 return converted != null;
4676 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4678 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4679 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4682 protected override void CloneTo (CloneContext clonectx, Statement target)
4684 var t = (SwitchLabel) target;
4686 t.label = label.Clone (clonectx);
4689 public override object Accept (StructuralVisitor visitor)
4691 return visitor.Visit (this);
4694 public string GetSignatureForError ()
4697 if (converted == null)
4700 label = converted.GetValueAsLiteral ();
4702 return string.Format ("case {0}:", label);
4706 public class Switch : LoopStatement
4708 // structure used to hold blocks of keys while calculating table switch
4709 sealed class LabelsRange : IComparable<LabelsRange>
4711 public readonly long min;
4713 public readonly List<long> label_values;
4715 public LabelsRange (long value)
4718 label_values = new List<long> ();
4719 label_values.Add (value);
4722 public LabelsRange (long min, long max, ICollection<long> values)
4726 this.label_values = new List<long> (values);
4731 return max - min + 1;
4735 public bool AddValue (long value)
4737 var gap = value - min + 1;
4738 // Ensure the range has > 50% occupancy
4739 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4743 label_values.Add (value);
4747 public int CompareTo (LabelsRange other)
4749 int nLength = label_values.Count;
4750 int nLengthOther = other.label_values.Count;
4751 if (nLengthOther == nLength)
4752 return (int) (other.min - min);
4754 return nLength - nLengthOther;
4758 sealed class DispatchStatement : Statement
4760 readonly Switch body;
4762 public DispatchStatement (Switch body)
4767 protected override void CloneTo (CloneContext clonectx, Statement target)
4769 throw new NotImplementedException ();
4772 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4777 protected override void DoEmit (EmitContext ec)
4779 body.EmitDispatch (ec);
4783 class MissingBreak : Statement
4785 readonly SwitchLabel label;
4787 public MissingBreak (SwitchLabel sl)
4793 public bool FallOut { get; set; }
4795 protected override void DoEmit (EmitContext ec)
4799 protected override void CloneTo (CloneContext clonectx, Statement target)
4803 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4806 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4807 label.GetSignatureForError ());
4809 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4810 label.GetSignatureForError ());
4816 public Expression Expr;
4819 // Mapping of all labels to their SwitchLabels
4821 Dictionary<long, SwitchLabel> labels;
4822 Dictionary<string, SwitchLabel> string_labels;
4823 List<SwitchLabel> case_labels;
4825 List<Tuple<GotoCase, Constant>> goto_cases;
4826 List<DefiniteAssignmentBitSet> end_reachable_das;
4829 /// The governing switch type
4831 public TypeSpec SwitchType;
4833 Expression new_expr;
4835 SwitchLabel case_null;
4836 SwitchLabel case_default;
4838 Label defaultLabel, nullLabel;
4839 VariableReference value;
4840 ExpressionStatement string_dictionary;
4841 FieldExpr switch_cache_field;
4842 ExplicitBlock block;
4846 // Nullable Types support
4848 Nullable.Unwrap unwrap;
4850 public Switch (Expression e, ExplicitBlock block, Location l)
4858 public SwitchLabel ActiveLabel { get; set; }
4860 public ExplicitBlock Block {
4866 public SwitchLabel DefaultLabel {
4868 return case_default;
4872 public bool IsNullable {
4874 return unwrap != null;
4878 public bool IsPatternMatching {
4880 return new_expr == null && SwitchType != null;
4884 public List<SwitchLabel> RegisteredLabels {
4890 public VariableReference ExpressionValue {
4897 // Determines the governing type for a switch. The returned
4898 // expression might be the expression from the switch, or an
4899 // expression that includes any potential conversions to
4901 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4903 switch (expr.Type.BuiltinType) {
4904 case BuiltinTypeSpec.Type.Byte:
4905 case BuiltinTypeSpec.Type.SByte:
4906 case BuiltinTypeSpec.Type.UShort:
4907 case BuiltinTypeSpec.Type.Short:
4908 case BuiltinTypeSpec.Type.UInt:
4909 case BuiltinTypeSpec.Type.Int:
4910 case BuiltinTypeSpec.Type.ULong:
4911 case BuiltinTypeSpec.Type.Long:
4912 case BuiltinTypeSpec.Type.Char:
4913 case BuiltinTypeSpec.Type.String:
4914 case BuiltinTypeSpec.Type.Bool:
4918 if (expr.Type.IsEnum)
4922 // Try to find a *user* defined implicit conversion.
4924 // If there is no implicit conversion, or if there are multiple
4925 // conversions, we have to report an error
4927 Expression converted = null;
4928 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4930 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4933 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4934 Convert.UserConversionRestriction.ProbingOnly;
4937 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4939 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4944 // Ignore over-worked ImplicitUserConversions that do
4945 // an implicit conversion in addition to the user conversion.
4947 var uc = e as UserCast;
4951 if (converted != null){
4952 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4961 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4963 var types = module.Compiler.BuiltinTypes;
4965 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4966 TypeSpec[] stypes = new[] {
4979 if (nullable != null) {
4981 Array.Resize (ref stypes, stypes.Length + 9);
4983 for (int i = 0; i < 9; ++i) {
4984 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4991 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4993 case_labels.Add (sl);
4996 if (case_default != null) {
4997 sl.Error_AlreadyOccurs (rc, case_default);
5005 if (sl.Converted == null)
5009 if (string_labels != null) {
5010 string string_value = sl.Converted.GetValue () as string;
5011 if (string_value == null)
5014 string_labels.Add (string_value, sl);
5016 if (sl.Converted.IsNull) {
5019 labels.Add (sl.Converted.GetValueAsLong (), sl);
5022 } catch (ArgumentException) {
5023 if (string_labels != null)
5024 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5026 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5031 // This method emits code for a lookup-based switch statement (non-string)
5032 // Basically it groups the cases into blocks that are at least half full,
5033 // and then spits out individual lookup opcodes for each block.
5034 // It emits the longest blocks first, and short blocks are just
5035 // handled with direct compares.
5037 void EmitTableSwitch (EmitContext ec, Expression val)
5039 if (labels != null && labels.Count > 0) {
5040 List<LabelsRange> ranges;
5041 if (string_labels != null) {
5042 // We have done all hard work for string already
5043 // setup single range only
5044 ranges = new List<LabelsRange> (1);
5045 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5047 var element_keys = new long[labels.Count];
5048 labels.Keys.CopyTo (element_keys, 0);
5049 Array.Sort (element_keys);
5052 // Build possible ranges of switch labes to reduce number
5055 ranges = new List<LabelsRange> (element_keys.Length);
5056 var range = new LabelsRange (element_keys[0]);
5058 for (int i = 1; i < element_keys.Length; ++i) {
5059 var l = element_keys[i];
5060 if (range.AddValue (l))
5063 range = new LabelsRange (l);
5067 // sort the blocks so we can tackle the largest ones first
5071 Label lbl_default = defaultLabel;
5072 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5074 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5075 LabelsRange kb = ranges[range_index];
5076 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5078 // Optimize small ranges using simple equality check
5079 if (kb.Range <= 2) {
5080 foreach (var key in kb.label_values) {
5081 SwitchLabel sl = labels[key];
5082 if (sl == case_default || sl == case_null)
5085 if (sl.Converted.IsZeroInteger) {
5086 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5089 sl.Converted.Emit (ec);
5090 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5094 // TODO: if all the keys in the block are the same and there are
5095 // no gaps/defaults then just use a range-check.
5096 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5097 // TODO: optimize constant/I4 cases
5099 // check block range (could be > 2^31)
5101 ec.EmitLong (kb.min);
5102 ec.Emit (OpCodes.Blt, lbl_default);
5105 ec.EmitLong (kb.max);
5106 ec.Emit (OpCodes.Bgt, lbl_default);
5111 ec.EmitLong (kb.min);
5112 ec.Emit (OpCodes.Sub);
5115 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5119 int first = (int) kb.min;
5122 ec.Emit (OpCodes.Sub);
5123 } else if (first < 0) {
5124 ec.EmitInt (-first);
5125 ec.Emit (OpCodes.Add);
5129 // first, build the list of labels for the switch
5131 long cJumps = kb.Range;
5132 Label[] switch_labels = new Label[cJumps];
5133 for (int iJump = 0; iJump < cJumps; iJump++) {
5134 var key = kb.label_values[iKey];
5135 if (key == kb.min + iJump) {
5136 switch_labels[iJump] = labels[key].GetILLabel (ec);
5139 switch_labels[iJump] = lbl_default;
5143 // emit the switch opcode
5144 ec.Emit (OpCodes.Switch, switch_labels);
5147 // mark the default for this block
5148 if (range_index != 0)
5149 ec.MarkLabel (lbl_default);
5152 // the last default just goes to the end
5153 if (ranges.Count > 0)
5154 ec.Emit (OpCodes.Br, lbl_default);
5158 public SwitchLabel FindLabel (Constant value)
5160 SwitchLabel sl = null;
5162 if (string_labels != null) {
5163 string s = value.GetValue () as string;
5165 if (case_null != null)
5167 else if (case_default != null)
5170 string_labels.TryGetValue (s, out sl);
5173 if (value is NullLiteral) {
5176 labels.TryGetValue (value.GetValueAsLong (), out sl);
5180 if (sl == null || sl.SectionStart)
5184 // Always return section start, it simplifies handling of switch labels
5186 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5187 var cs = case_labels [idx];
5188 if (cs.SectionStart)
5193 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5195 Expr.FlowAnalysis (fc);
5197 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5198 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5199 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5201 block.FlowAnalysis (fc);
5203 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5205 if (end_reachable_das != null) {
5206 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5207 InitialDefinitiveAssignment |= sections_das;
5208 end_reachable_das = null;
5211 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5213 return case_default != null && !end_reachable;
5216 public override bool Resolve (BlockContext ec)
5218 Expr = Expr.Resolve (ec);
5223 // LAMESPEC: User conversion from non-nullable governing type has a priority
5225 new_expr = SwitchGoverningType (ec, Expr, false);
5227 if (new_expr == null) {
5228 if (Expr.Type.IsNullableType) {
5229 unwrap = Nullable.Unwrap.Create (Expr, false);
5234 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5235 // involving nullable Expr and nullable governing type is
5237 new_expr = SwitchGoverningType (ec, unwrap, true);
5241 Expression switch_expr;
5242 if (new_expr == null) {
5243 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5244 if (Expr.Type != InternalType.ErrorType) {
5245 ec.Report.Error (151, loc,
5246 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5247 Expr.Type.GetSignatureForError ());
5254 SwitchType = Expr.Type;
5256 switch_expr = new_expr;
5257 SwitchType = new_expr.Type;
5258 if (SwitchType.IsNullableType) {
5259 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5260 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5263 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5264 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5268 if (block.Statements.Count == 0)
5271 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5272 string_labels = new Dictionary<string, SwitchLabel> ();
5274 labels = new Dictionary<long, SwitchLabel> ();
5278 var constant = switch_expr as Constant;
5281 // Don't need extra variable for constant switch or switch with
5282 // only default case
5284 if (constant == null) {
5286 // Store switch expression for comparison purposes
5288 value = switch_expr as VariableReference;
5289 if (value == null && !HasOnlyDefaultSection ()) {
5290 var current_block = ec.CurrentBlock;
5291 ec.CurrentBlock = Block;
5292 // Create temporary variable inside switch scope
5293 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5295 ec.CurrentBlock = current_block;
5299 case_labels = new List<SwitchLabel> ();
5301 Switch old_switch = ec.Switch;
5303 var parent_los = ec.EnclosingLoopOrSwitch;
5304 ec.EnclosingLoopOrSwitch = this;
5306 var ok = Statement.Resolve (ec);
5308 ec.EnclosingLoopOrSwitch = parent_los;
5309 ec.Switch = old_switch;
5312 // Check if all goto cases are valid. Needs to be done after switch
5313 // is resolved because goto can jump forward in the scope.
5315 if (goto_cases != null) {
5316 foreach (var gc in goto_cases) {
5317 if (gc.Item1 == null) {
5318 if (DefaultLabel == null) {
5319 Goto.Error_UnknownLabel (ec, "default", loc);
5325 var sl = FindLabel (gc.Item2);
5327 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5329 gc.Item1.Label = sl;
5337 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5338 ResolveStringSwitchMap (ec);
5342 // Anonymous storey initialization has to happen before
5343 // any generated switch dispatch
5345 block.InsertStatement (0, new DispatchStatement (this));
5350 bool HasOnlyDefaultSection ()
5352 for (int i = 0; i < block.Statements.Count; ++i) {
5353 var s = block.Statements[i] as SwitchLabel;
5355 if (s == null || s.IsDefault)
5364 public override Reachability MarkReachable (Reachability rc)
5366 if (rc.IsUnreachable)
5369 base.MarkReachable (rc);
5371 block.MarkReachableScope (rc);
5373 if (block.Statements.Count == 0)
5376 SwitchLabel constant_label = null;
5377 var constant = new_expr as Constant;
5379 if (constant != null) {
5380 constant_label = FindLabel (constant) ?? case_default;
5381 if (constant_label == null) {
5382 block.Statements.RemoveAt (0);
5387 var section_rc = new Reachability ();
5388 SwitchLabel prev_label = null;
5390 for (int i = 0; i < block.Statements.Count; ++i) {
5391 var s = block.Statements[i];
5392 var sl = s as SwitchLabel;
5394 if (sl != null && sl.SectionStart) {
5396 // Section is marked already via goto case
5398 if (!sl.IsUnreachable) {
5399 section_rc = new Reachability ();
5403 if (section_rc.IsUnreachable) {
5405 // Common case. Previous label section end is unreachable as
5406 // it ends with break, return, etc. For next section revert
5407 // to reachable again unless we have constant switch block
5409 section_rc = constant_label != null && constant_label != sl ?
5410 Reachability.CreateUnreachable () :
5411 new Reachability ();
5412 } else if (prev_label != null) {
5414 // Error case as control cannot fall through from one case label
5416 sl.SectionStart = false;
5417 s = new MissingBreak (prev_label);
5418 s.MarkReachable (rc);
5419 block.Statements.Insert (i - 1, s);
5421 } else if (constant_label != null && constant_label != sl) {
5423 // Special case for the first unreachable label in constant
5426 section_rc = Reachability.CreateUnreachable ();
5432 section_rc = s.MarkReachable (section_rc);
5435 if (!section_rc.IsUnreachable && prev_label != null) {
5436 prev_label.SectionStart = false;
5437 var s = new MissingBreak (prev_label) {
5441 s.MarkReachable (rc);
5442 block.Statements.Add (s);
5446 // Reachability can affect parent only when all possible paths are handled but
5447 // we still need to run reachability check on switch body to check for fall-through
5449 if (case_default == null && constant_label == null)
5453 // We have at least one local exit from the switch
5458 return Reachability.CreateUnreachable ();
5461 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5463 if (goto_cases == null)
5464 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5466 goto_cases.Add (Tuple.Create (gotoCase, value));
5470 // Converts string switch into string hashtable
5472 void ResolveStringSwitchMap (ResolveContext ec)
5474 FullNamedExpression string_dictionary_type;
5475 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5476 string_dictionary_type = new TypeExpression (
5477 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5478 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5480 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5481 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5483 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5487 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5488 Field field = new Field (ctype, string_dictionary_type,
5489 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5490 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5491 if (!field.Define ())
5493 ctype.AddField (field);
5495 var init = new List<Expression> ();
5497 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5498 string value = null;
5500 foreach (SwitchLabel sl in case_labels) {
5502 if (sl.SectionStart)
5503 labels.Add (++counter, sl);
5505 if (sl == case_default || sl == case_null)
5508 value = (string) sl.Converted.GetValue ();
5509 var init_args = new List<Expression> (2);
5510 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5512 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5513 init_args.Add (sl.Converted);
5515 init.Add (new CollectionElementInitializer (init_args, loc));
5518 Arguments args = new Arguments (1);
5519 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5520 Expression initializer = new NewInitialize (string_dictionary_type, args,
5521 new CollectionOrObjectInitializers (init, loc), loc);
5523 switch_cache_field = new FieldExpr (field, loc);
5524 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5527 void DoEmitStringSwitch (EmitContext ec)
5529 Label l_initialized = ec.DefineLabel ();
5532 // Skip initialization when value is null
5534 value.EmitBranchable (ec, nullLabel, false);
5537 // Check if string dictionary is initialized and initialize
5539 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5540 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5541 string_dictionary.EmitStatement (ec);
5543 ec.MarkLabel (l_initialized);
5545 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5547 ResolveContext rc = new ResolveContext (ec.MemberContext);
5549 if (switch_cache_field.Type.IsGeneric) {
5550 Arguments get_value_args = new Arguments (2);
5551 get_value_args.Add (new Argument (value));
5552 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5553 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5554 if (get_item == null)
5558 // A value was not found, go to default case
5560 get_item.EmitBranchable (ec, defaultLabel, false);
5562 Arguments get_value_args = new Arguments (1);
5563 get_value_args.Add (new Argument (value));
5565 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5566 if (get_item == null)
5569 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5570 get_item_object.EmitAssign (ec, get_item, true, false);
5571 ec.Emit (OpCodes.Brfalse, defaultLabel);
5573 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5574 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5576 get_item_int.EmitStatement (ec);
5577 get_item_object.Release (ec);
5580 EmitTableSwitch (ec, string_switch_variable);
5581 string_switch_variable.Release (ec);
5585 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5587 void EmitShortSwitch (EmitContext ec)
5589 MethodSpec equal_method = null;
5590 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5591 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5594 if (equal_method != null) {
5595 value.EmitBranchable (ec, nullLabel, false);
5598 for (int i = 0; i < case_labels.Count; ++i) {
5599 var label = case_labels [i];
5600 if (label == case_default || label == case_null)
5603 var constant = label.Converted;
5605 if (constant == null) {
5606 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5610 if (equal_method != null) {
5614 var call = new CallEmitter ();
5615 call.EmitPredefined (ec, equal_method, new Arguments (0));
5616 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5620 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5621 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5627 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5630 ec.Emit (OpCodes.Br, defaultLabel);
5633 void EmitDispatch (EmitContext ec)
5635 if (IsPatternMatching) {
5636 EmitShortSwitch (ec);
5640 if (value == null) {
5642 // Constant switch, we've already done the work if there is only 1 label
5646 foreach (var sl in case_labels) {
5647 if (sl.IsUnreachable)
5650 if (reachable++ > 0) {
5651 var constant = (Constant) new_expr;
5652 var constant_label = FindLabel (constant) ?? case_default;
5654 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5662 if (string_dictionary != null) {
5663 DoEmitStringSwitch (ec);
5664 } else if (case_labels.Count < 4 || string_labels != null) {
5665 EmitShortSwitch (ec);
5667 EmitTableSwitch (ec, value);
5671 protected override void DoEmit (EmitContext ec)
5674 // Setup the codegen context
5676 Label old_end = ec.LoopEnd;
5677 Switch old_switch = ec.Switch;
5679 ec.LoopEnd = ec.DefineLabel ();
5682 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5683 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5685 if (value != null) {
5688 var switch_expr = new_expr ?? Expr;
5690 unwrap.EmitCheck (ec);
5691 ec.Emit (OpCodes.Brfalse, nullLabel);
5692 value.EmitAssign (ec, switch_expr, false, false);
5693 } else if (switch_expr != value) {
5694 value.EmitAssign (ec, switch_expr, false, false);
5699 // Next statement is compiler generated we don't need extra
5700 // nop when we can use the statement for sequence point
5702 ec.Mark (block.StartLocation);
5703 block.IsCompilerGenerated = true;
5705 new_expr.EmitSideEffect (ec);
5710 // Restore context state.
5711 ec.MarkLabel (ec.LoopEnd);
5714 // Restore the previous context
5716 ec.LoopEnd = old_end;
5717 ec.Switch = old_switch;
5720 protected override void CloneTo (CloneContext clonectx, Statement t)
5722 Switch target = (Switch) t;
5724 target.Expr = Expr.Clone (clonectx);
5725 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5728 public override object Accept (StructuralVisitor visitor)
5730 return visitor.Visit (this);
5733 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5735 if (case_default == null && !(new_expr is Constant))
5738 if (end_reachable_das == null)
5739 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5741 end_reachable_das.Add (fc.DefiniteAssignment);
5744 public override void SetEndReachable ()
5746 end_reachable = true;
5750 // A place where execution can restart in a state machine
5751 public abstract class ResumableStatement : Statement
5754 protected Label resume_point;
5756 public Label PrepareForEmit (EmitContext ec)
5760 resume_point = ec.DefineLabel ();
5762 return resume_point;
5765 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5770 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5775 public abstract class TryFinallyBlock : ExceptionStatement
5777 protected Statement stmt;
5778 Label dispose_try_block;
5779 bool prepared_for_dispose, emitted_dispose;
5780 Method finally_host;
5782 protected TryFinallyBlock (Statement stmt, Location loc)
5790 public Statement Statement {
5798 protected abstract void EmitTryBody (EmitContext ec);
5799 public abstract void EmitFinallyBody (EmitContext ec);
5801 public override Label PrepareForDispose (EmitContext ec, Label end)
5803 if (!prepared_for_dispose) {
5804 prepared_for_dispose = true;
5805 dispose_try_block = ec.DefineLabel ();
5807 return dispose_try_block;
5810 protected sealed override void DoEmit (EmitContext ec)
5812 EmitTryBodyPrepare (ec);
5815 bool beginFinally = EmitBeginFinallyBlock (ec);
5817 Label start_finally = ec.DefineLabel ();
5818 if (resume_points != null && beginFinally) {
5819 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5821 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5822 ec.Emit (OpCodes.Brfalse_S, start_finally);
5823 ec.Emit (OpCodes.Endfinally);
5826 ec.MarkLabel (start_finally);
5828 if (finally_host != null) {
5829 finally_host.Define ();
5830 finally_host.PrepareEmit ();
5831 finally_host.Emit ();
5833 // Now it's safe to add, to close it properly and emit sequence points
5834 finally_host.Parent.AddMember (finally_host);
5836 var ce = new CallEmitter ();
5837 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5838 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5840 EmitFinallyBody (ec);
5844 ec.EndExceptionBlock ();
5847 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5849 if (emitted_dispose)
5852 emitted_dispose = true;
5854 Label end_of_try = ec.DefineLabel ();
5856 // Ensure that the only way we can get into this code is through a dispatcher
5857 if (have_dispatcher)
5858 ec.Emit (OpCodes.Br, end);
5860 ec.BeginExceptionBlock ();
5862 ec.MarkLabel (dispose_try_block);
5864 Label[] labels = null;
5865 for (int i = 0; i < resume_points.Count; ++i) {
5866 ResumableStatement s = resume_points[i];
5867 Label ret = s.PrepareForDispose (ec, end_of_try);
5868 if (ret.Equals (end_of_try) && labels == null)
5870 if (labels == null) {
5871 labels = new Label[resume_points.Count];
5872 for (int j = 0; j < i; ++j)
5873 labels[j] = end_of_try;
5878 if (labels != null) {
5880 for (j = 1; j < labels.Length; ++j)
5881 if (!labels[0].Equals (labels[j]))
5883 bool emit_dispatcher = j < labels.Length;
5885 if (emit_dispatcher) {
5886 ec.Emit (OpCodes.Ldloc, pc);
5887 ec.EmitInt (first_resume_pc);
5888 ec.Emit (OpCodes.Sub);
5889 ec.Emit (OpCodes.Switch, labels);
5892 foreach (ResumableStatement s in resume_points)
5893 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5896 ec.MarkLabel (end_of_try);
5898 ec.BeginFinallyBlock ();
5900 if (finally_host != null) {
5901 var ce = new CallEmitter ();
5902 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5903 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5905 EmitFinallyBody (ec);
5908 ec.EndExceptionBlock ();
5911 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5913 var res = stmt.FlowAnalysis (fc);
5914 parent_try_block = null;
5918 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5920 ec.BeginFinallyBlock ();
5924 public override Reachability MarkReachable (Reachability rc)
5926 base.MarkReachable (rc);
5927 return Statement.MarkReachable (rc);
5930 public override bool Resolve (BlockContext bc)
5934 parent_try_block = bc.CurrentTryBlock;
5935 bc.CurrentTryBlock = this;
5937 if (stmt is TryCatch) {
5938 ok = stmt.Resolve (bc);
5940 using (bc.Set (ResolveContext.Options.TryScope)) {
5941 ok = stmt.Resolve (bc);
5945 bc.CurrentTryBlock = parent_try_block;
5948 // Finally block inside iterator is called from MoveNext and
5949 // Dispose methods that means we need to lift the block into
5950 // newly created host method to emit the body only once. The
5951 // original block then simply calls the newly generated method.
5953 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5954 var b = stmt as Block;
5955 if (b != null && b.Explicit.HasYield) {
5956 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5960 return base.Resolve (bc) && ok;
5965 // Base class for blocks using exception handling
5967 public abstract class ExceptionStatement : ResumableStatement
5969 protected List<ResumableStatement> resume_points;
5970 protected int first_resume_pc;
5971 protected ExceptionStatement parent_try_block;
5972 protected int first_catch_resume_pc = -1;
5974 protected ExceptionStatement (Location loc)
5979 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5981 StateMachineInitializer state_machine = null;
5982 if (resume_points != null) {
5983 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5985 ec.EmitInt ((int) IteratorStorey.State.Running);
5986 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5990 // The resume points in catch section when this is try-catch-finally
5992 if (IsRewrittenTryCatchFinally ()) {
5993 ec.BeginExceptionBlock ();
5995 if (first_catch_resume_pc >= 0) {
5997 ec.MarkLabel (resume_point);
5999 // For normal control flow, we want to fall-through the Switch
6000 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6001 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6002 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6003 ec.Emit (OpCodes.Sub);
6005 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6006 for (int i = 0; i < labels.Length; ++i)
6007 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6008 ec.Emit (OpCodes.Switch, labels);
6012 ec.BeginExceptionBlock ();
6015 // The resume points for try section
6017 if (resume_points != null && first_catch_resume_pc != 0) {
6018 if (first_catch_resume_pc < 0)
6019 ec.MarkLabel (resume_point);
6021 // For normal control flow, we want to fall-through the Switch
6022 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6023 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6024 ec.EmitInt (first_resume_pc);
6025 ec.Emit (OpCodes.Sub);
6027 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6028 for (int i = 0; i < labels.Length; ++i)
6029 labels[i] = resume_points[i].PrepareForEmit (ec);
6030 ec.Emit (OpCodes.Switch, labels);
6034 bool IsRewrittenTryCatchFinally ()
6036 var tf = this as TryFinally;
6040 var tc = tf.Statement as TryCatch;
6044 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6047 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6049 if (parent_try_block != null) {
6050 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6052 pc = stateMachine.AddResumePoint (this);
6055 if (resume_points == null) {
6056 resume_points = new List<ResumableStatement> ();
6057 first_resume_pc = pc;
6060 if (pc != first_resume_pc + resume_points.Count)
6061 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6063 var tf = this as TryFinally;
6064 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6065 first_catch_resume_pc = resume_points.Count;
6068 resume_points.Add (stmt);
6073 public class Lock : TryFinallyBlock
6076 TemporaryVariableReference expr_copy;
6077 TemporaryVariableReference lock_taken;
6079 public Lock (Expression expr, Statement stmt, Location loc)
6085 public Expression Expr {
6091 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6093 expr.FlowAnalysis (fc);
6094 return base.DoFlowAnalysis (fc);
6097 public override bool Resolve (BlockContext ec)
6099 expr = expr.Resolve (ec);
6103 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6104 ec.Report.Error (185, loc,
6105 "`{0}' is not a reference type as required by the lock statement",
6106 expr.Type.GetSignatureForError ());
6109 if (expr.Type.IsGenericParameter) {
6110 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6113 VariableReference lv = expr as VariableReference;
6116 locked = lv.IsLockedByStatement;
6117 lv.IsLockedByStatement = true;
6124 // Have to keep original lock value around to unlock same location
6125 // in the case of original value has changed or is null
6127 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6128 expr_copy.Resolve (ec);
6131 // Ensure Monitor methods are available
6133 if (ResolvePredefinedMethods (ec) > 1) {
6134 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6135 lock_taken.Resolve (ec);
6138 using (ec.Set (ResolveContext.Options.LockScope)) {
6143 lv.IsLockedByStatement = locked;
6149 protected override void EmitTryBodyPrepare (EmitContext ec)
6151 expr_copy.EmitAssign (ec, expr);
6153 if (lock_taken != null) {
6155 // Initialize ref variable
6157 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6160 // Monitor.Enter (expr_copy)
6162 expr_copy.Emit (ec);
6163 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6166 base.EmitTryBodyPrepare (ec);
6169 protected override void EmitTryBody (EmitContext ec)
6172 // Monitor.Enter (expr_copy, ref lock_taken)
6174 if (lock_taken != null) {
6175 expr_copy.Emit (ec);
6176 lock_taken.LocalInfo.CreateBuilder (ec);
6177 lock_taken.AddressOf (ec, AddressOp.Load);
6178 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6181 Statement.Emit (ec);
6184 public override void EmitFinallyBody (EmitContext ec)
6187 // if (lock_taken) Monitor.Exit (expr_copy)
6189 Label skip = ec.DefineLabel ();
6191 if (lock_taken != null) {
6192 lock_taken.Emit (ec);
6193 ec.Emit (OpCodes.Brfalse_S, skip);
6196 expr_copy.Emit (ec);
6197 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6199 ec.Emit (OpCodes.Call, m);
6201 ec.MarkLabel (skip);
6204 int ResolvePredefinedMethods (ResolveContext rc)
6206 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6207 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6211 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6215 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6219 protected override void CloneTo (CloneContext clonectx, Statement t)
6221 Lock target = (Lock) t;
6223 target.expr = expr.Clone (clonectx);
6224 target.stmt = Statement.Clone (clonectx);
6227 public override object Accept (StructuralVisitor visitor)
6229 return visitor.Visit (this);
6234 public class Unchecked : Statement {
6237 public Unchecked (Block b, Location loc)
6244 public override bool Resolve (BlockContext ec)
6246 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6247 return Block.Resolve (ec);
6250 protected override void DoEmit (EmitContext ec)
6252 using (ec.With (EmitContext.Options.CheckedScope, false))
6256 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6258 return Block.FlowAnalysis (fc);
6261 public override Reachability MarkReachable (Reachability rc)
6263 base.MarkReachable (rc);
6264 return Block.MarkReachable (rc);
6267 protected override void CloneTo (CloneContext clonectx, Statement t)
6269 Unchecked target = (Unchecked) t;
6271 target.Block = clonectx.LookupBlock (Block);
6274 public override object Accept (StructuralVisitor visitor)
6276 return visitor.Visit (this);
6280 public class Checked : Statement {
6283 public Checked (Block b, Location loc)
6286 b.Unchecked = false;
6290 public override bool Resolve (BlockContext ec)
6292 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6293 return Block.Resolve (ec);
6296 protected override void DoEmit (EmitContext ec)
6298 using (ec.With (EmitContext.Options.CheckedScope, true))
6302 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6304 return Block.FlowAnalysis (fc);
6307 public override Reachability MarkReachable (Reachability rc)
6309 base.MarkReachable (rc);
6310 return Block.MarkReachable (rc);
6313 protected override void CloneTo (CloneContext clonectx, Statement t)
6315 Checked target = (Checked) t;
6317 target.Block = clonectx.LookupBlock (Block);
6320 public override object Accept (StructuralVisitor visitor)
6322 return visitor.Visit (this);
6326 public class Unsafe : Statement {
6329 public Unsafe (Block b, Location loc)
6332 Block.Unsafe = true;
6336 public override bool Resolve (BlockContext ec)
6338 if (ec.CurrentIterator != null)
6339 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6341 using (ec.Set (ResolveContext.Options.UnsafeScope))
6342 return Block.Resolve (ec);
6345 protected override void DoEmit (EmitContext ec)
6350 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6352 return Block.FlowAnalysis (fc);
6355 public override Reachability MarkReachable (Reachability rc)
6357 base.MarkReachable (rc);
6358 return Block.MarkReachable (rc);
6361 protected override void CloneTo (CloneContext clonectx, Statement t)
6363 Unsafe target = (Unsafe) t;
6365 target.Block = clonectx.LookupBlock (Block);
6368 public override object Accept (StructuralVisitor visitor)
6370 return visitor.Visit (this);
6377 public class Fixed : Statement
6379 abstract class Emitter : ShimExpression
6381 protected LocalVariable vi;
6383 protected Emitter (Expression expr, LocalVariable li)
6389 public abstract void EmitExit (EmitContext ec);
6391 public override void FlowAnalysis (FlowAnalysisContext fc)
6393 expr.FlowAnalysis (fc);
6397 sealed class ExpressionEmitter : Emitter {
6398 public ExpressionEmitter (Expression converted, LocalVariable li)
6399 : base (converted, li)
6403 protected override Expression DoResolve (ResolveContext rc)
6405 throw new NotImplementedException ();
6408 public override void Emit (EmitContext ec) {
6410 // Store pointer in pinned location
6416 public override void EmitExit (EmitContext ec)
6419 ec.Emit (OpCodes.Conv_U);
6424 class StringEmitter : Emitter
6426 LocalVariable pinned_string;
6428 public StringEmitter (Expression expr, LocalVariable li)
6433 protected override Expression DoResolve (ResolveContext rc)
6435 pinned_string = new LocalVariable (vi.Block, "$pinned",
6436 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6438 pinned_string.Type = rc.BuiltinTypes.String;
6441 eclass = ExprClass.Variable;
6442 type = rc.BuiltinTypes.Int;
6446 public override void Emit (EmitContext ec)
6448 pinned_string.CreateBuilder (ec);
6451 pinned_string.EmitAssign (ec);
6453 // TODO: Should use Binary::Add
6454 pinned_string.Emit (ec);
6455 ec.Emit (OpCodes.Conv_I);
6457 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6461 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6462 //pe.InstanceExpression = pinned_string;
6463 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6465 ec.Emit (OpCodes.Add);
6469 public override void EmitExit (EmitContext ec)
6472 pinned_string.EmitAssign (ec);
6476 public class VariableDeclaration : BlockVariable
6478 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6483 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6485 if (!Variable.Type.IsPointer && li == Variable) {
6486 bc.Report.Error (209, TypeExpression.Location,
6487 "The type of locals declared in a fixed statement must be a pointer type");
6491 var res = initializer.Resolve (bc);
6498 var ac = res.Type as ArrayContainer;
6500 TypeSpec array_type = ac.Element;
6503 // Provided that array_type is unmanaged,
6505 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6508 Expression res_init;
6509 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6512 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6513 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6514 res = expr_variable.CreateReferenceExpression (bc, loc);
6518 // and T* is implicitly convertible to the
6519 // pointer type given in the fixed statement.
6521 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6523 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6524 if (converted == null)
6528 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6530 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6531 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6532 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6533 new NullLiteral (loc),
6536 converted = converted.Resolve (bc);
6538 return new ExpressionEmitter (converted, li);
6544 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6545 return new StringEmitter (res, li).Resolve (bc);
6548 // Case 3: fixed buffer
6549 if (res is FixedBufferPtr) {
6550 return new ExpressionEmitter (res, li);
6553 bool already_fixed = true;
6556 // Case 4: & object.
6558 Unary u = res as Unary;
6560 if (u.Oper == Unary.Operator.AddressOf) {
6561 IVariableReference vr = u.Expr as IVariableReference;
6562 if (vr == null || !vr.IsFixed) {
6563 already_fixed = false;
6566 } else if (initializer is Cast) {
6567 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6571 if (already_fixed) {
6572 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6575 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6576 return new ExpressionEmitter (res, li);
6581 VariableDeclaration decl;
6582 Statement statement;
6585 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6594 public Statement Statement {
6600 public BlockVariable Variables {
6608 public override bool Resolve (BlockContext bc)
6610 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6611 if (!decl.Resolve (bc))
6615 return statement.Resolve (bc);
6618 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6620 decl.FlowAnalysis (fc);
6621 return statement.FlowAnalysis (fc);
6624 protected override void DoEmit (EmitContext ec)
6626 decl.Variable.CreateBuilder (ec);
6627 decl.Initializer.Emit (ec);
6628 if (decl.Declarators != null) {
6629 foreach (var d in decl.Declarators) {
6630 d.Variable.CreateBuilder (ec);
6631 d.Initializer.Emit (ec);
6635 statement.Emit (ec);
6641 // Clear the pinned variable
6643 ((Emitter) decl.Initializer).EmitExit (ec);
6644 if (decl.Declarators != null) {
6645 foreach (var d in decl.Declarators) {
6646 ((Emitter)d.Initializer).EmitExit (ec);
6651 public override Reachability MarkReachable (Reachability rc)
6653 base.MarkReachable (rc);
6655 decl.MarkReachable (rc);
6657 rc = statement.MarkReachable (rc);
6659 // TODO: What if there is local exit?
6660 has_ret = rc.IsUnreachable;
6664 protected override void CloneTo (CloneContext clonectx, Statement t)
6666 Fixed target = (Fixed) t;
6668 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6669 target.statement = statement.Clone (clonectx);
6672 public override object Accept (StructuralVisitor visitor)
6674 return visitor.Visit (this);
6678 public class Catch : Statement
6680 class CatchVariableStore : Statement
6682 readonly Catch ctch;
6684 public CatchVariableStore (Catch ctch)
6689 protected override void CloneTo (CloneContext clonectx, Statement target)
6693 protected override void DoEmit (EmitContext ec)
6695 // Emits catch variable debug information inside correct block
6696 ctch.EmitCatchVariableStore (ec);
6699 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6705 class FilterStatement : Statement
6707 readonly Catch ctch;
6709 public FilterStatement (Catch ctch)
6714 protected override void CloneTo (CloneContext clonectx, Statement target)
6718 protected override void DoEmit (EmitContext ec)
6720 if (ctch.li != null) {
6721 if (ctch.hoisted_temp != null)
6722 ctch.hoisted_temp.Emit (ec);
6726 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6727 ec.Emit (OpCodes.Box, ctch.type);
6730 var expr_start = ec.DefineLabel ();
6731 var end = ec.DefineLabel ();
6733 ec.Emit (OpCodes.Brtrue_S, expr_start);
6735 ec.Emit (OpCodes.Br, end);
6736 ec.MarkLabel (expr_start);
6738 ctch.Filter.Emit (ec);
6741 ec.Emit (OpCodes.Endfilter);
6742 ec.BeginFilterHandler ();
6743 ec.Emit (OpCodes.Pop);
6746 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6748 ctch.Filter.FlowAnalysis (fc);
6752 public override bool Resolve (BlockContext bc)
6754 ctch.Filter = ctch.Filter.Resolve (bc);
6756 if (ctch.Filter != null) {
6757 if (ctch.Filter.ContainsEmitWithAwait ()) {
6758 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6761 var c = ctch.Filter as Constant;
6762 if (c != null && !c.IsDefaultValue) {
6763 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6771 ExplicitBlock block;
6773 FullNamedExpression type_expr;
6774 CompilerAssign assign;
6776 LocalTemporary hoisted_temp;
6778 public Catch (ExplicitBlock block, Location loc)
6786 public ExplicitBlock Block {
6792 public TypeSpec CatchType {
6798 public Expression Filter {
6802 public bool IsGeneral {
6804 return type_expr == null;
6808 public FullNamedExpression TypeExpression {
6817 public LocalVariable Variable {
6828 protected override void DoEmit (EmitContext ec)
6830 if (Filter != null) {
6831 ec.BeginExceptionFilterBlock ();
6832 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6834 if (Block.HasAwait) {
6835 Block.EmitScopeInitialization (ec);
6844 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6846 ec.BeginCatchBlock (CatchType);
6849 ec.Emit (OpCodes.Pop);
6851 if (Block.HasAwait) {
6853 EmitCatchVariableStore (ec);
6859 void EmitCatchVariableStore (EmitContext ec)
6861 li.CreateBuilder (ec);
6864 // For hoisted catch variable we have to use a temporary local variable
6865 // for captured variable initialization during storey setup because variable
6866 // needs to be on the stack after storey instance for stfld operation
6868 if (li.HoistedVariant != null) {
6869 hoisted_temp = new LocalTemporary (li.Type);
6870 hoisted_temp.Store (ec);
6872 // switch to assignment from temporary variable and not from top of the stack
6873 assign.UpdateSource (hoisted_temp);
6877 public override bool Resolve (BlockContext bc)
6879 using (bc.Set (ResolveContext.Options.CatchScope)) {
6880 if (type_expr == null) {
6881 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6882 if (!block.HasAwait || Filter != null)
6883 block.AddScopeStatement (new CatchVariableStore (this));
6885 Expression source = new EmptyExpression (li.Type);
6886 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6887 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6890 type = type_expr.ResolveAsType (bc);
6895 CreateExceptionVariable (type);
6897 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6898 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6899 } else if (li != null) {
6901 li.PrepareAssignmentAnalysis (bc);
6903 // source variable is at the top of the stack
6904 Expression source = new EmptyExpression (li.Type);
6905 if (li.Type.IsGenericParameter)
6906 source = new UnboxCast (source, li.Type);
6908 if (!block.HasAwait || Filter != null)
6909 block.AddScopeStatement (new CatchVariableStore (this));
6912 // Uses Location.Null to hide from symbol file
6914 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6915 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6919 if (Filter != null) {
6920 Block.AddScopeStatement (new FilterStatement (this));
6923 Block.SetCatchBlock ();
6924 return Block.Resolve (bc);
6928 bool CreateExceptionVariable (TypeSpec type)
6930 if (!Block.HasAwait)
6933 // TODO: Scan the block for rethrow expression
6934 //if (!Block.HasRethrow)
6937 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6941 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6943 if (li != null && !li.IsCompilerGenerated) {
6944 fc.SetVariableAssigned (li.VariableInfo, true);
6947 return block.FlowAnalysis (fc);
6950 public override Reachability MarkReachable (Reachability rc)
6952 base.MarkReachable (rc);
6954 var c = Filter as Constant;
6955 if (c != null && c.IsDefaultValue)
6956 return Reachability.CreateUnreachable ();
6958 return block.MarkReachable (rc);
6961 protected override void CloneTo (CloneContext clonectx, Statement t)
6963 Catch target = (Catch) t;
6965 if (type_expr != null)
6966 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6969 target.Filter = Filter.Clone (clonectx);
6971 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6975 public class TryFinally : TryFinallyBlock
6978 List<DefiniteAssignmentBitSet> try_exit_dat;
6979 List<Label> redirected_jumps;
6980 Label? start_fin_label;
6982 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6988 public ExplicitBlock FinallyBlock {
6994 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6996 if (try_exit_dat == null)
6997 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6999 try_exit_dat.Add (vector);
7002 public override bool Resolve (BlockContext bc)
7004 bool ok = base.Resolve (bc);
7006 fini.SetFinallyBlock ();
7007 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7008 ok &= fini.Resolve (bc);
7014 protected override void EmitTryBody (EmitContext ec)
7016 if (fini.HasAwait) {
7017 if (ec.TryFinallyUnwind == null)
7018 ec.TryFinallyUnwind = new List<TryFinally> ();
7020 ec.TryFinallyUnwind.Add (this);
7023 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7024 ec.EndExceptionBlock ();
7026 ec.TryFinallyUnwind.Remove (this);
7028 if (start_fin_label != null)
7029 ec.MarkLabel (start_fin_label.Value);
7037 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7042 return base.EmitBeginFinallyBlock (ec);
7045 public override void EmitFinallyBody (EmitContext ec)
7047 if (!fini.HasAwait) {
7053 // Emits catch block like
7055 // catch (object temp) {
7056 // this.exception_field = temp;
7059 var type = ec.BuiltinTypes.Object;
7060 ec.BeginCatchBlock (type);
7062 var temp = ec.GetTemporaryLocal (type);
7063 ec.Emit (OpCodes.Stloc, temp);
7065 var exception_field = ec.GetTemporaryField (type);
7066 exception_field.AutomaticallyReuse = false;
7068 ec.Emit (OpCodes.Ldloc, temp);
7069 exception_field.EmitAssignFromStack (ec);
7071 ec.EndExceptionBlock ();
7073 ec.FreeTemporaryLocal (temp, type);
7078 // Emits exception rethrow
7080 // if (this.exception_field != null)
7081 // throw this.exception_field;
7083 exception_field.Emit (ec);
7084 var skip_throw = ec.DefineLabel ();
7085 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7086 exception_field.Emit (ec);
7087 ec.Emit (OpCodes.Throw);
7088 ec.MarkLabel (skip_throw);
7090 exception_field.PrepareCleanup (ec);
7092 EmitUnwindFinallyTable (ec);
7095 bool IsParentBlock (Block block)
7097 for (Block b = fini; b != null; b = b.Parent) {
7105 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7108 if (labelBlock != null) {
7109 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7110 var fin = ec.TryFinallyUnwind [idx - 1];
7111 if (!fin.IsParentBlock (labelBlock))
7118 bool set_return_state = true;
7120 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7121 var fin = ec.TryFinallyUnwind [idx];
7122 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7125 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7126 set_return_state = false;
7128 if (fin.start_fin_label == null) {
7129 fin.start_fin_label = ec.DefineLabel ();
7132 label = fin.start_fin_label.Value;
7138 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7140 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7143 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7145 if (redirected_jumps == null) {
7146 redirected_jumps = new List<Label> ();
7148 // Add fallthrough label
7149 redirected_jumps.Add (ec.DefineLabel ());
7152 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7155 int index = redirected_jumps.IndexOf (label);
7157 redirected_jumps.Add (label);
7158 index = redirected_jumps.Count - 1;
7162 // Indicates we have captured exit jump
7164 if (setReturnState) {
7165 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7166 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7171 // Emits state table of jumps outside of try block and reload of return
7172 // value when try block returns value
7174 void EmitUnwindFinallyTable (EmitContext ec)
7176 if (redirected_jumps == null)
7179 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7180 initializer.HoistedReturnState.EmitLoad (ec);
7181 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7183 // Mark fallthrough label
7184 ec.MarkLabel (redirected_jumps [0]);
7187 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7189 var da = fc.BranchDefiniteAssignment ();
7191 var tf = fc.TryFinally;
7192 fc.TryFinally = this;
7194 var res_stmt = Statement.FlowAnalysis (fc);
7198 var try_da = fc.DefiniteAssignment;
7199 fc.DefiniteAssignment = da;
7201 var res_fin = fini.FlowAnalysis (fc);
7203 if (try_exit_dat != null) {
7205 // try block has global exit but we need to run definite assignment check
7206 // for parameter block out parameter after finally block because it's always
7207 // executed before exit
7209 foreach (var try_da_part in try_exit_dat)
7210 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7212 try_exit_dat = null;
7215 fc.DefiniteAssignment |= try_da;
7216 return res_stmt | res_fin;
7219 public override Reachability MarkReachable (Reachability rc)
7222 // Mark finally block first for any exit statement in try block
7223 // to know whether the code which follows finally is reachable
7225 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7228 protected override void CloneTo (CloneContext clonectx, Statement t)
7230 TryFinally target = (TryFinally) t;
7232 target.stmt = stmt.Clone (clonectx);
7234 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7237 public override object Accept (StructuralVisitor visitor)
7239 return visitor.Visit (this);
7243 public class TryCatch : ExceptionStatement
7246 List<Catch> clauses;
7247 readonly bool inside_try_finally;
7248 List<Catch> catch_sm;
7250 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7254 this.clauses = catch_clauses;
7255 this.inside_try_finally = inside_try_finally;
7258 public List<Catch> Clauses {
7264 public bool HasClauseWithAwait {
7266 return catch_sm != null;
7270 public bool IsTryCatchFinally {
7272 return inside_try_finally;
7276 public override bool Resolve (BlockContext bc)
7280 using (bc.Set (ResolveContext.Options.TryScope)) {
7282 parent_try_block = bc.CurrentTryBlock;
7284 if (IsTryCatchFinally) {
7285 ok = Block.Resolve (bc);
7287 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7288 bc.CurrentTryBlock = this;
7289 ok = Block.Resolve (bc);
7290 bc.CurrentTryBlock = parent_try_block;
7295 var prev_catch = bc.CurrentTryCatch;
7296 bc.CurrentTryCatch = this;
7298 for (int i = 0; i < clauses.Count; ++i) {
7301 ok &= c.Resolve (bc);
7303 if (c.Block.HasAwait) {
7304 if (catch_sm == null)
7305 catch_sm = new List<Catch> ();
7310 if (c.Filter != null)
7313 TypeSpec resolved_type = c.CatchType;
7314 if (resolved_type == null)
7317 for (int ii = 0; ii < clauses.Count; ++ii) {
7321 if (clauses[ii].Filter != null)
7324 if (clauses[ii].IsGeneral) {
7325 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7328 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7331 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7334 bc.Report.Warning (1058, 1, c.loc,
7335 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7343 var ct = clauses[ii].CatchType;
7347 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7348 bc.Report.Error (160, c.loc,
7349 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7350 ct.GetSignatureForError ());
7356 bc.CurrentTryCatch = prev_catch;
7358 return base.Resolve (bc) && ok;
7361 protected sealed override void DoEmit (EmitContext ec)
7363 if (!inside_try_finally)
7364 EmitTryBodyPrepare (ec);
7368 LocalBuilder state_variable = null;
7369 foreach (Catch c in clauses) {
7372 if (catch_sm != null) {
7373 if (state_variable == null) {
7375 // Cannot reuse temp variable because non-catch path assumes the value is 0
7376 // which may not be true for reused local variable
7378 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7381 var index = catch_sm.IndexOf (c);
7385 ec.EmitInt (index + 1);
7386 ec.Emit (OpCodes.Stloc, state_variable);
7390 if (state_variable == null) {
7391 if (!inside_try_finally)
7392 ec.EndExceptionBlock ();
7394 ec.EndExceptionBlock ();
7396 ec.Emit (OpCodes.Ldloc, state_variable);
7398 var labels = new Label [catch_sm.Count + 1];
7399 for (int i = 0; i < labels.Length; ++i) {
7400 labels [i] = ec.DefineLabel ();
7403 var end = ec.DefineLabel ();
7404 ec.Emit (OpCodes.Switch, labels);
7406 // 0 value is default label
7407 ec.MarkLabel (labels [0]);
7408 ec.Emit (OpCodes.Br, end);
7410 var atv = ec.AsyncThrowVariable;
7412 for (int i = 0; i < catch_sm.Count; ++i) {
7413 if (c != null && c.Block.HasReachableClosingBrace)
7414 ec.Emit (OpCodes.Br, end);
7416 ec.MarkLabel (labels [i + 1]);
7418 ec.AsyncThrowVariable = c.Variable;
7421 ec.AsyncThrowVariable = atv;
7427 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7429 var start_fc = fc.BranchDefiniteAssignment ();
7430 var res = Block.FlowAnalysis (fc);
7432 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7434 foreach (var c in clauses) {
7435 fc.BranchDefiniteAssignment (start_fc);
7436 if (!c.FlowAnalysis (fc)) {
7438 try_fc = fc.DefiniteAssignment;
7440 try_fc &= fc.DefiniteAssignment;
7446 fc.DefiniteAssignment = try_fc ?? start_fc;
7447 parent_try_block = null;
7451 public override Reachability MarkReachable (Reachability rc)
7453 if (rc.IsUnreachable)
7456 base.MarkReachable (rc);
7458 var tc_rc = Block.MarkReachable (rc);
7460 foreach (var c in clauses)
7461 tc_rc &= c.MarkReachable (rc);
7466 protected override void CloneTo (CloneContext clonectx, Statement t)
7468 TryCatch target = (TryCatch) t;
7470 target.Block = clonectx.LookupBlock (Block);
7471 if (clauses != null){
7472 target.clauses = new List<Catch> ();
7473 foreach (Catch c in clauses)
7474 target.clauses.Add ((Catch) c.Clone (clonectx));
7478 public override object Accept (StructuralVisitor visitor)
7480 return visitor.Visit (this);
7484 public class Using : TryFinallyBlock
7486 public class VariableDeclaration : BlockVariable
7488 Statement dispose_call;
7490 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7495 public VariableDeclaration (LocalVariable li, Location loc)
7502 public VariableDeclaration (Expression expr)
7505 loc = expr.Location;
7511 public bool IsNested { get; private set; }
7515 public void EmitDispose (EmitContext ec)
7517 dispose_call.Emit (ec);
7520 public override bool Resolve (BlockContext bc)
7525 return base.Resolve (bc, false);
7528 public Expression ResolveExpression (BlockContext bc)
7530 var e = Initializer.Resolve (bc);
7534 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7535 Initializer = ResolveInitializer (bc, Variable, e);
7539 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7541 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7542 initializer = initializer.Resolve (bc);
7543 if (initializer == null)
7546 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7547 Arguments args = new Arguments (1);
7548 args.Add (new Argument (initializer));
7549 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7550 if (initializer == null)
7553 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7554 dispose_call = CreateDisposeCall (bc, var);
7555 dispose_call.Resolve (bc);
7557 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7560 if (li == Variable) {
7561 CheckIDiposableConversion (bc, li, initializer);
7562 dispose_call = CreateDisposeCall (bc, li);
7563 dispose_call.Resolve (bc);
7566 return base.ResolveInitializer (bc, li, initializer);
7569 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7573 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7574 if (type.IsNullableType) {
7575 // it's handled in CreateDisposeCall
7579 if (type != InternalType.ErrorType) {
7580 bc.Report.SymbolRelatedToPreviousError (type);
7581 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7582 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7583 type.GetSignatureForError ());
7590 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7592 var target = bc.BuiltinTypes.IDisposable;
7593 var tp = type as TypeParameterSpec;
7595 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7597 return type.ImplementsInterface (target, false);
7600 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7602 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7604 var loc = lv.Location;
7606 var idt = bc.BuiltinTypes.IDisposable;
7607 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7609 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7610 dispose_mg.InstanceExpression = type.IsNullableType ?
7611 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7615 // Hide it from symbol file via null location
7617 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7619 // Add conditional call when disposing possible null variable
7620 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7621 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7626 public void ResolveDeclaratorInitializer (BlockContext bc)
7628 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7631 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7633 for (int i = declarators.Count - 1; i >= 0; --i) {
7634 var d = declarators [i];
7635 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7636 vd.Initializer = d.Initializer;
7638 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7639 vd.dispose_call.Resolve (bc);
7641 stmt = new Using (vd, stmt, d.Variable.Location);
7648 public override object Accept (StructuralVisitor visitor)
7650 return visitor.Visit (this);
7654 VariableDeclaration decl;
7656 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7662 public Using (Expression expr, Statement stmt, Location loc)
7665 this.decl = new VariableDeclaration (expr);
7670 public Expression Expr {
7672 return decl.Variable == null ? decl.Initializer : null;
7676 public BlockVariable Variables {
7684 public override void Emit (EmitContext ec)
7687 // Don't emit sequence point it will be set on variable declaration
7692 protected override void EmitTryBodyPrepare (EmitContext ec)
7695 base.EmitTryBodyPrepare (ec);
7698 protected override void EmitTryBody (EmitContext ec)
7703 public override void EmitFinallyBody (EmitContext ec)
7705 decl.EmitDispose (ec);
7708 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7710 decl.FlowAnalysis (fc);
7711 return stmt.FlowAnalysis (fc);
7714 public override Reachability MarkReachable (Reachability rc)
7716 decl.MarkReachable (rc);
7717 return base.MarkReachable (rc);
7720 public override bool Resolve (BlockContext ec)
7722 VariableReference vr;
7723 bool vr_locked = false;
7725 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7726 if (decl.Variable == null) {
7727 vr = decl.ResolveExpression (ec) as VariableReference;
7729 vr_locked = vr.IsLockedByStatement;
7730 vr.IsLockedByStatement = true;
7733 if (decl.IsNested) {
7734 decl.ResolveDeclaratorInitializer (ec);
7736 if (!decl.Resolve (ec))
7739 if (decl.Declarators != null) {
7740 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7748 var ok = base.Resolve (ec);
7751 vr.IsLockedByStatement = vr_locked;
7756 protected override void CloneTo (CloneContext clonectx, Statement t)
7758 Using target = (Using) t;
7760 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7761 target.stmt = stmt.Clone (clonectx);
7764 public override object Accept (StructuralVisitor visitor)
7766 return visitor.Visit (this);
7771 /// Implementation of the foreach C# statement
7773 public class Foreach : LoopStatement
7775 abstract class IteratorStatement : Statement
7777 protected readonly Foreach for_each;
7779 protected IteratorStatement (Foreach @foreach)
7781 this.for_each = @foreach;
7782 this.loc = @foreach.expr.Location;
7785 protected override void CloneTo (CloneContext clonectx, Statement target)
7787 throw new NotImplementedException ();
7790 public override void Emit (EmitContext ec)
7792 if (ec.EmitAccurateDebugInfo) {
7793 ec.Emit (OpCodes.Nop);
7799 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7801 throw new NotImplementedException ();
7805 sealed class ArrayForeach : IteratorStatement
7807 TemporaryVariableReference[] lengths;
7808 Expression [] length_exprs;
7809 StatementExpression[] counter;
7810 TemporaryVariableReference[] variables;
7812 TemporaryVariableReference copy;
7814 public ArrayForeach (Foreach @foreach, int rank)
7817 counter = new StatementExpression[rank];
7818 variables = new TemporaryVariableReference[rank];
7819 length_exprs = new Expression [rank];
7822 // Only use temporary length variables when dealing with
7823 // multi-dimensional arrays
7826 lengths = new TemporaryVariableReference [rank];
7829 public override bool Resolve (BlockContext ec)
7831 Block variables_block = for_each.variable.Block;
7832 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7835 int rank = length_exprs.Length;
7836 Arguments list = new Arguments (rank);
7837 for (int i = 0; i < rank; i++) {
7838 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7840 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7841 counter[i].Resolve (ec);
7844 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7846 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7847 lengths[i].Resolve (ec);
7849 Arguments args = new Arguments (1);
7850 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7851 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7854 list.Add (new Argument (v));
7857 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7862 if (for_each.type is VarExpr) {
7863 // Infer implicitly typed local variable from foreach array type
7864 var_type = access.Type;
7866 var_type = for_each.type.ResolveAsType (ec);
7868 if (var_type == null)
7871 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7876 for_each.variable.Type = var_type;
7878 var prev_block = ec.CurrentBlock;
7879 ec.CurrentBlock = variables_block;
7880 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7881 ec.CurrentBlock = prev_block;
7883 if (variable_ref == null)
7886 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7888 return for_each.body.Resolve (ec);
7891 protected override void DoEmit (EmitContext ec)
7893 copy.EmitAssign (ec, for_each.expr);
7895 int rank = length_exprs.Length;
7896 Label[] test = new Label [rank];
7897 Label[] loop = new Label [rank];
7899 for (int i = 0; i < rank; i++) {
7900 test [i] = ec.DefineLabel ();
7901 loop [i] = ec.DefineLabel ();
7903 if (lengths != null)
7904 lengths [i].EmitAssign (ec, length_exprs [i]);
7907 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7908 for (int i = 0; i < rank; i++) {
7909 variables [i].EmitAssign (ec, zero);
7911 ec.Emit (OpCodes.Br, test [i]);
7912 ec.MarkLabel (loop [i]);
7915 for_each.body.Emit (ec);
7917 ec.MarkLabel (ec.LoopBegin);
7918 ec.Mark (for_each.expr.Location);
7920 for (int i = rank - 1; i >= 0; i--){
7921 counter [i].Emit (ec);
7923 ec.MarkLabel (test [i]);
7924 variables [i].Emit (ec);
7926 if (lengths != null)
7927 lengths [i].Emit (ec);
7929 length_exprs [i].Emit (ec);
7931 ec.Emit (OpCodes.Blt, loop [i]);
7934 ec.MarkLabel (ec.LoopEnd);
7938 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7940 class RuntimeDispose : Using.VariableDeclaration
7942 public RuntimeDispose (LocalVariable lv, Location loc)
7948 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7950 // Defered to runtime check
7953 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7955 var idt = bc.BuiltinTypes.IDisposable;
7958 // Fabricates code like
7960 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7963 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7965 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7966 dispose_variable.CreateReferenceExpression (bc, loc),
7967 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7968 loc), new NullLiteral (loc));
7970 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7972 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7973 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7975 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7976 return new If (idisaposable_test, dispose, loc);
7980 LocalVariable variable;
7982 Statement statement;
7983 ExpressionStatement init;
7984 TemporaryVariableReference enumerator_variable;
7985 bool ambiguous_getenumerator_name;
7987 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7990 this.variable = var;
7994 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7996 rc.Report.SymbolRelatedToPreviousError (enumerator);
7997 rc.Report.Error (202, loc,
7998 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7999 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8002 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8005 // Option 1: Try to match by name GetEnumerator first
8007 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8008 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8010 var mg = mexpr as MethodGroupExpr;
8012 mg.InstanceExpression = expr;
8013 Arguments args = new Arguments (0);
8014 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8016 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8017 if (ambiguous_getenumerator_name)
8020 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8026 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8029 PredefinedMember<MethodSpec> iface_candidate = null;
8030 var ptypes = rc.Module.PredefinedTypes;
8031 var gen_ienumerable = ptypes.IEnumerableGeneric;
8032 if (!gen_ienumerable.Define ())
8033 gen_ienumerable = null;
8035 var ifaces = t.Interfaces;
8036 if (ifaces != null) {
8037 foreach (var iface in ifaces) {
8038 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8039 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8040 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8041 rc.Report.Error (1640, loc,
8042 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8043 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8048 // TODO: Cache this somehow
8049 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8050 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8055 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8056 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8061 if (iface_candidate == null) {
8062 if (expr.Type != InternalType.ErrorType) {
8063 rc.Report.Error (1579, loc,
8064 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8065 expr.Type.GetSignatureForError (), "GetEnumerator");
8071 var method = iface_candidate.Resolve (loc);
8075 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8076 mg.InstanceExpression = expr;
8080 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8082 var ms = MemberCache.FindMember (enumerator.ReturnType,
8083 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8084 BindingRestriction.InstanceOnly) as MethodSpec;
8086 if (ms == null || !ms.IsPublic) {
8087 Error_WrongEnumerator (rc, enumerator);
8091 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8094 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8096 var ps = MemberCache.FindMember (enumerator.ReturnType,
8097 MemberFilter.Property ("Current", null),
8098 BindingRestriction.InstanceOnly) as PropertySpec;
8100 if (ps == null || !ps.IsPublic) {
8101 Error_WrongEnumerator (rc, enumerator);
8108 public override bool Resolve (BlockContext ec)
8110 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8113 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8114 } else if (expr.Type.IsNullableType) {
8115 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8118 var get_enumerator_mg = ResolveGetEnumerator (ec);
8119 if (get_enumerator_mg == null) {
8123 var get_enumerator = get_enumerator_mg.BestCandidate;
8124 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8125 enumerator_variable.Resolve (ec);
8127 // Prepare bool MoveNext ()
8128 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8129 if (move_next_mg == null) {
8133 move_next_mg.InstanceExpression = enumerator_variable;
8135 // Prepare ~T~ Current { get; }
8136 var current_prop = ResolveCurrent (ec, get_enumerator);
8137 if (current_prop == null) {
8141 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8142 if (current_pe == null)
8145 VarExpr ve = for_each.type as VarExpr;
8149 // Source type is dynamic, set element type to dynamic too
8150 variable.Type = ec.BuiltinTypes.Dynamic;
8152 // Infer implicitly typed local variable from foreach enumerable type
8153 variable.Type = current_pe.Type;
8157 // Explicit cast of dynamic collection elements has to be done at runtime
8158 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8161 variable.Type = for_each.type.ResolveAsType (ec);
8163 if (variable.Type == null)
8166 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8167 if (current_pe == null)
8171 var prev_block = ec.CurrentBlock;
8172 ec.CurrentBlock = for_each.variable.Block;
8173 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8174 ec.CurrentBlock = prev_block;
8175 if (variable_ref == null)
8178 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8180 var init = new Invocation.Predefined (get_enumerator_mg, null);
8182 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8183 for_each.body, Location.Null);
8185 var enum_type = enumerator_variable.Type;
8188 // Add Dispose method call when enumerator can be IDisposable
8190 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8191 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8193 // Runtime Dispose check
8195 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8196 vd.Initializer = init;
8197 statement = new Using (vd, statement, Location.Null);
8200 // No Dispose call needed
8202 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8203 this.init.Resolve (ec);
8207 // Static Dispose check
8209 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8210 vd.Initializer = init;
8211 statement = new Using (vd, statement, Location.Null);
8214 return statement.Resolve (ec);
8217 protected override void DoEmit (EmitContext ec)
8219 enumerator_variable.LocalInfo.CreateBuilder (ec);
8222 init.EmitStatement (ec);
8224 statement.Emit (ec);
8227 #region IErrorHandler Members
8229 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8231 ec.Report.SymbolRelatedToPreviousError (best);
8232 ec.Report.Warning (278, 2, expr.Location,
8233 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8234 expr.Type.GetSignatureForError (), "enumerable",
8235 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8237 ambiguous_getenumerator_name = true;
8241 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8246 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8251 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8260 LocalVariable variable;
8264 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8268 this.variable = var;
8274 public Expression Expr {
8275 get { return expr; }
8278 public Expression TypeExpression {
8279 get { return type; }
8282 public LocalVariable Variable {
8283 get { return variable; }
8286 public override Reachability MarkReachable (Reachability rc)
8288 base.MarkReachable (rc);
8290 body.MarkReachable (rc);
8295 public override bool Resolve (BlockContext ec)
8297 expr = expr.Resolve (ec);
8302 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8306 body.AddStatement (Statement);
8308 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8309 Statement = new ArrayForeach (this, 1);
8310 } else if (expr.Type is ArrayContainer) {
8311 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8313 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8314 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8315 expr.ExprClassName);
8319 Statement = new CollectionForeach (this, variable, expr);
8322 return base.Resolve (ec);
8325 protected override void DoEmit (EmitContext ec)
8327 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8328 ec.LoopBegin = ec.DefineLabel ();
8329 ec.LoopEnd = ec.DefineLabel ();
8331 if (!(Statement is Block))
8332 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8334 variable.CreateBuilder (ec);
8336 Statement.Emit (ec);
8338 if (!(Statement is Block))
8341 ec.LoopBegin = old_begin;
8342 ec.LoopEnd = old_end;
8345 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8347 expr.FlowAnalysis (fc);
8349 var da = fc.BranchDefiniteAssignment ();
8350 body.FlowAnalysis (fc);
8351 fc.DefiniteAssignment = da;
8355 protected override void CloneTo (CloneContext clonectx, Statement t)
8357 Foreach target = (Foreach) t;
8359 target.type = type.Clone (clonectx);
8360 target.expr = expr.Clone (clonectx);
8361 target.body = (Block) body.Clone (clonectx);
8362 target.Statement = Statement.Clone (clonectx);
8365 public override object Accept (StructuralVisitor visitor)
8367 return visitor.Visit (this);
8371 class SentinelStatement: Statement
8373 protected override void CloneTo (CloneContext clonectx, Statement target)
8377 protected override void DoEmit (EmitContext ec)
8379 var l = ec.DefineLabel ();
8381 ec.Emit (OpCodes.Br_S, l);
8384 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8386 throw new NotImplementedException ();