2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
27 protected bool reachable;
29 public bool IsUnreachable {
36 /// Resolves the statement, true means that all sub-statements
39 public virtual bool Resolve (BlockContext bc)
45 /// Return value indicates whether all code paths emitted return.
47 protected abstract void DoEmit (EmitContext ec);
49 public virtual void Emit (EmitContext ec)
54 if (ec.StatementEpilogue != null) {
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
63 protected abstract void CloneTo (CloneContext clonectx, Statement target);
65 public Statement Clone (CloneContext clonectx)
67 Statement s = (Statement) this.MemberwiseClone ();
68 CloneTo (clonectx, s);
72 public virtual Expression CreateExpressionTree (ResolveContext ec)
74 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
78 public virtual object Accept (StructuralVisitor visitor)
80 return visitor.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
88 public bool FlowAnalysis (FlowAnalysisContext fc)
91 fc.UnreachableReported = false;
92 var res = DoFlowAnalysis (fc);
97 // Special handling cases
100 return DoFlowAnalysis (fc);
103 if (this is EmptyStatement || loc.IsNull)
106 if (fc.UnreachableReported)
109 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
110 fc.UnreachableReported = true;
114 public virtual Reachability MarkReachable (Reachability rc)
116 if (!rc.IsUnreachable)
122 protected void CheckExitBoundaries (BlockContext bc, Block scope)
124 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
125 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
129 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
130 if (b.IsFinallyBlock) {
131 Error_FinallyClauseExit (bc);
137 protected void Error_FinallyClauseExit (BlockContext bc)
139 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
143 public sealed class EmptyStatement : Statement
145 public EmptyStatement (Location loc)
150 public override bool Resolve (BlockContext ec)
155 public override void Emit (EmitContext ec)
159 protected override void DoEmit (EmitContext ec)
161 throw new NotSupportedException ();
164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
169 protected override void CloneTo (CloneContext clonectx, Statement target)
174 public override object Accept (StructuralVisitor visitor)
176 return visitor.Visit (this);
180 public class If : Statement {
182 public Statement TrueStatement;
183 public Statement FalseStatement;
185 bool true_returns, false_returns;
187 public If (Expression bool_expr, Statement true_statement, Location l)
188 : this (bool_expr, true_statement, null, l)
192 public If (Expression bool_expr,
193 Statement true_statement,
194 Statement false_statement,
197 this.expr = bool_expr;
198 TrueStatement = true_statement;
199 FalseStatement = false_statement;
203 public Expression Expr {
209 public override bool Resolve (BlockContext ec)
211 expr = expr.Resolve (ec);
213 var ok = TrueStatement.Resolve (ec);
215 if (FalseStatement != null) {
216 ok &= FalseStatement.Resolve (ec);
222 protected override void DoEmit (EmitContext ec)
224 Label false_target = ec.DefineLabel ();
228 // If we're a boolean constant, Resolve() already
229 // eliminated dead code for us.
231 Constant c = expr as Constant;
233 c.EmitSideEffect (ec);
235 if (!c.IsDefaultValue)
236 TrueStatement.Emit (ec);
237 else if (FalseStatement != null)
238 FalseStatement.Emit (ec);
243 expr.EmitBranchable (ec, false_target, false);
245 TrueStatement.Emit (ec);
247 if (FalseStatement != null){
248 bool branch_emitted = false;
250 end = ec.DefineLabel ();
252 ec.Emit (OpCodes.Br, end);
253 branch_emitted = true;
256 ec.MarkLabel (false_target);
257 FalseStatement.Emit (ec);
262 ec.MarkLabel (false_target);
266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
268 expr.FlowAnalysisConditional (fc);
270 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
272 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
274 var res = TrueStatement.FlowAnalysis (fc);
276 if (FalseStatement == null) {
277 var c = expr as Constant;
278 if (c != null && !c.IsDefaultValue)
282 fc.DefiniteAssignment = da_false;
284 fc.DefiniteAssignment &= da_false;
290 fc.DefiniteAssignment = da_false;
291 return FalseStatement.FlowAnalysis (fc);
294 var da_true = fc.DefiniteAssignment;
296 fc.DefiniteAssignment = da_false;
297 res &= FalseStatement.FlowAnalysis (fc);
299 if (!TrueStatement.IsUnreachable) {
300 if (false_returns || FalseStatement.IsUnreachable)
301 fc.DefiniteAssignment = da_true;
303 fc.DefiniteAssignment &= da_true;
309 public override Reachability MarkReachable (Reachability rc)
311 if (rc.IsUnreachable)
314 base.MarkReachable (rc);
316 var c = expr as Constant;
318 bool take = !c.IsDefaultValue;
320 rc = TrueStatement.MarkReachable (rc);
322 if (FalseStatement != null)
323 rc = FalseStatement.MarkReachable (rc);
329 var true_rc = TrueStatement.MarkReachable (rc);
330 true_returns = true_rc.IsUnreachable;
332 if (FalseStatement == null)
335 var false_rc = FalseStatement.MarkReachable (rc);
336 false_returns = false_rc.IsUnreachable;
338 return true_rc & false_rc;
341 protected override void CloneTo (CloneContext clonectx, Statement t)
345 target.expr = expr.Clone (clonectx);
346 target.TrueStatement = TrueStatement.Clone (clonectx);
347 if (FalseStatement != null)
348 target.FalseStatement = FalseStatement.Clone (clonectx);
351 public override object Accept (StructuralVisitor visitor)
353 return visitor.Visit (this);
357 public class Do : LoopStatement
359 public Expression expr;
360 bool iterator_reachable, end_reachable;
362 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
367 WhileLocation = whileLocation;
370 public Location WhileLocation {
374 public override bool Resolve (BlockContext bc)
376 var ok = base.Resolve (bc);
378 expr = expr.Resolve (bc);
383 protected override void DoEmit (EmitContext ec)
385 Label loop = ec.DefineLabel ();
386 Label old_begin = ec.LoopBegin;
387 Label old_end = ec.LoopEnd;
389 ec.LoopBegin = ec.DefineLabel ();
390 ec.LoopEnd = ec.DefineLabel ();
394 ec.MarkLabel (ec.LoopBegin);
396 // Mark start of while condition
397 ec.Mark (WhileLocation);
400 // Dead code elimination
402 if (expr is Constant) {
403 bool res = !((Constant) expr).IsDefaultValue;
405 expr.EmitSideEffect (ec);
407 ec.Emit (OpCodes.Br, loop);
409 expr.EmitBranchable (ec, loop, true);
412 ec.MarkLabel (ec.LoopEnd);
414 ec.LoopBegin = old_begin;
415 ec.LoopEnd = old_end;
418 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
420 var res = Statement.FlowAnalysis (fc);
422 expr.FlowAnalysisConditional (fc);
424 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
426 if (res && !iterator_reachable)
427 return !end_reachable;
429 if (!end_reachable) {
430 var c = expr as Constant;
431 if (c != null && !c.IsDefaultValue)
438 public override Reachability MarkReachable (Reachability rc)
440 base.MarkReachable (rc);
442 var body_rc = Statement.MarkReachable (rc);
444 if (body_rc.IsUnreachable && !iterator_reachable) {
445 expr = new UnreachableExpression (expr);
446 return end_reachable ? rc : Reachability.CreateUnreachable ();
449 if (!end_reachable) {
450 var c = expr as Constant;
451 if (c != null && !c.IsDefaultValue)
452 return Reachability.CreateUnreachable ();
458 protected override void CloneTo (CloneContext clonectx, Statement t)
462 target.Statement = Statement.Clone (clonectx);
463 target.expr = expr.Clone (clonectx);
466 public override object Accept (StructuralVisitor visitor)
468 return visitor.Visit (this);
471 public override void SetEndReachable ()
473 end_reachable = true;
476 public override void SetIteratorReachable ()
478 iterator_reachable = true;
482 public class While : LoopStatement
484 public Expression expr;
485 bool empty, infinite, end_reachable;
486 List<DefiniteAssignmentBitSet> end_reachable_das;
488 public While (BooleanExpression bool_expr, Statement statement, Location l)
491 this.expr = bool_expr;
495 public override bool Resolve (BlockContext bc)
499 expr = expr.Resolve (bc);
503 var c = expr as Constant;
505 empty = c.IsDefaultValue;
509 ok &= base.Resolve (bc);
513 protected override void DoEmit (EmitContext ec)
516 expr.EmitSideEffect (ec);
520 Label old_begin = ec.LoopBegin;
521 Label old_end = ec.LoopEnd;
523 ec.LoopBegin = ec.DefineLabel ();
524 ec.LoopEnd = ec.DefineLabel ();
527 // Inform whether we are infinite or not
529 if (expr is Constant) {
530 // expr is 'true', since the 'empty' case above handles the 'false' case
531 ec.MarkLabel (ec.LoopBegin);
533 if (ec.EmitAccurateDebugInfo)
534 ec.Emit (OpCodes.Nop);
536 expr.EmitSideEffect (ec);
538 ec.Emit (OpCodes.Br, ec.LoopBegin);
541 // Inform that we are infinite (ie, `we return'), only
542 // if we do not `break' inside the code.
544 ec.MarkLabel (ec.LoopEnd);
546 Label while_loop = ec.DefineLabel ();
548 ec.Emit (OpCodes.Br, ec.LoopBegin);
549 ec.MarkLabel (while_loop);
553 ec.MarkLabel (ec.LoopBegin);
556 expr.EmitBranchable (ec, while_loop, true);
558 ec.MarkLabel (ec.LoopEnd);
561 ec.LoopBegin = old_begin;
562 ec.LoopEnd = old_end;
565 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
567 expr.FlowAnalysisConditional (fc);
569 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
570 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
572 Statement.FlowAnalysis (fc);
575 // Special case infinite while with breaks
577 if (end_reachable_das != null) {
578 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
579 end_reachable_das = null;
582 fc.DefiniteAssignment = da_false;
584 if (infinite && !end_reachable)
590 public override Reachability MarkReachable (Reachability rc)
592 if (rc.IsUnreachable)
595 base.MarkReachable (rc);
598 // Special case unreachable while body
601 Statement.MarkReachable (Reachability.CreateUnreachable ());
605 Statement.MarkReachable (rc);
608 // When infinite while end is unreachable via break anything what follows is unreachable too
610 if (infinite && !end_reachable)
611 return Reachability.CreateUnreachable ();
616 protected override void CloneTo (CloneContext clonectx, Statement t)
618 While target = (While) t;
620 target.expr = expr.Clone (clonectx);
621 target.Statement = Statement.Clone (clonectx);
624 public override object Accept (StructuralVisitor visitor)
626 return visitor.Visit (this);
629 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
634 if (end_reachable_das == null)
635 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
637 end_reachable_das.Add (fc.DefiniteAssignment);
640 public override void SetEndReachable ()
642 end_reachable = true;
646 public class For : LoopStatement
648 bool infinite, empty, iterator_reachable, end_reachable;
649 List<DefiniteAssignmentBitSet> end_reachable_das;
651 public For (Location l)
657 public Statement Initializer {
661 public Expression Condition {
665 public Statement Iterator {
669 public override bool Resolve (BlockContext bc)
671 Initializer.Resolve (bc);
673 if (Condition != null) {
674 Condition = Condition.Resolve (bc);
675 var condition_constant = Condition as Constant;
676 if (condition_constant != null) {
677 if (condition_constant.IsDefaultValue) {
687 return base.Resolve (bc) && Iterator.Resolve (bc);
690 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
692 Initializer.FlowAnalysis (fc);
694 DefiniteAssignmentBitSet da_false;
695 if (Condition != null) {
696 Condition.FlowAnalysisConditional (fc);
697 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
698 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
700 da_false = fc.BranchDefiniteAssignment ();
703 Statement.FlowAnalysis (fc);
705 Iterator.FlowAnalysis (fc);
708 // Special case infinite for with breaks
710 if (end_reachable_das != null) {
711 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
712 end_reachable_das = null;
715 fc.DefiniteAssignment = da_false;
717 if (infinite && !end_reachable)
723 public override Reachability MarkReachable (Reachability rc)
725 base.MarkReachable (rc);
727 Initializer.MarkReachable (rc);
729 var body_rc = Statement.MarkReachable (rc);
730 if (!body_rc.IsUnreachable || iterator_reachable) {
731 Iterator.MarkReachable (rc);
735 // When infinite for end is unreachable via break anything what follows is unreachable too
737 if (infinite && !end_reachable) {
738 return Reachability.CreateUnreachable ();
744 protected override void DoEmit (EmitContext ec)
746 if (Initializer != null)
747 Initializer.Emit (ec);
750 Condition.EmitSideEffect (ec);
754 Label old_begin = ec.LoopBegin;
755 Label old_end = ec.LoopEnd;
756 Label loop = ec.DefineLabel ();
757 Label test = ec.DefineLabel ();
759 ec.LoopBegin = ec.DefineLabel ();
760 ec.LoopEnd = ec.DefineLabel ();
762 ec.Emit (OpCodes.Br, test);
766 ec.MarkLabel (ec.LoopBegin);
771 // If test is null, there is no test, and we are just
774 if (Condition != null) {
775 ec.Mark (Condition.Location);
778 // The Resolve code already catches the case for
779 // Test == Constant (false) so we know that
782 if (Condition is Constant) {
783 Condition.EmitSideEffect (ec);
784 ec.Emit (OpCodes.Br, loop);
786 Condition.EmitBranchable (ec, loop, true);
790 ec.Emit (OpCodes.Br, loop);
791 ec.MarkLabel (ec.LoopEnd);
793 ec.LoopBegin = old_begin;
794 ec.LoopEnd = old_end;
797 protected override void CloneTo (CloneContext clonectx, Statement t)
799 For target = (For) t;
801 if (Initializer != null)
802 target.Initializer = Initializer.Clone (clonectx);
803 if (Condition != null)
804 target.Condition = Condition.Clone (clonectx);
805 if (Iterator != null)
806 target.Iterator = Iterator.Clone (clonectx);
807 target.Statement = Statement.Clone (clonectx);
810 public override object Accept (StructuralVisitor visitor)
812 return visitor.Visit (this);
815 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
820 if (end_reachable_das == null)
821 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
823 end_reachable_das.Add (fc.DefiniteAssignment);
826 public override void SetEndReachable ()
828 end_reachable = true;
831 public override void SetIteratorReachable ()
833 iterator_reachable = true;
837 public abstract class LoopStatement : Statement
839 protected LoopStatement (Statement statement)
841 Statement = statement;
844 public Statement Statement { get; set; }
846 public override bool Resolve (BlockContext bc)
848 var prev_loop = bc.EnclosingLoop;
849 var prev_los = bc.EnclosingLoopOrSwitch;
850 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
851 var ok = Statement.Resolve (bc);
852 bc.EnclosingLoopOrSwitch = prev_los;
853 bc.EnclosingLoop = prev_loop;
859 // Needed by possibly infinite loops statements (for, while) and switch statment
861 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
865 public virtual void SetEndReachable ()
869 public virtual void SetIteratorReachable ()
874 public class StatementExpression : Statement
876 ExpressionStatement expr;
878 public StatementExpression (ExpressionStatement expr)
881 loc = expr.StartLocation;
884 public StatementExpression (ExpressionStatement expr, Location loc)
890 public ExpressionStatement Expr {
896 protected override void CloneTo (CloneContext clonectx, Statement t)
898 StatementExpression target = (StatementExpression) t;
899 target.expr = (ExpressionStatement) expr.Clone (clonectx);
902 protected override void DoEmit (EmitContext ec)
904 expr.EmitStatement (ec);
907 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
909 expr.FlowAnalysis (fc);
913 public override Reachability MarkReachable (Reachability rc)
915 base.MarkReachable (rc);
916 expr.MarkReachable (rc);
920 public override bool Resolve (BlockContext ec)
922 expr = expr.ResolveStatement (ec);
926 public override object Accept (StructuralVisitor visitor)
928 return visitor.Visit (this);
932 public class StatementErrorExpression : Statement
936 public StatementErrorExpression (Expression expr)
939 this.loc = expr.StartLocation;
942 public Expression Expr {
948 public override bool Resolve (BlockContext bc)
950 expr.Error_InvalidExpressionStatement (bc);
954 protected override void DoEmit (EmitContext ec)
956 throw new NotSupportedException ();
959 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
964 protected override void CloneTo (CloneContext clonectx, Statement target)
966 var t = (StatementErrorExpression) target;
968 t.expr = expr.Clone (clonectx);
971 public override object Accept (StructuralVisitor visitor)
973 return visitor.Visit (this);
978 // Simple version of statement list not requiring a block
980 public class StatementList : Statement
982 List<Statement> statements;
984 public StatementList (Statement first, Statement second)
986 statements = new List<Statement> { first, second };
990 public IList<Statement> Statements {
997 public void Add (Statement statement)
999 statements.Add (statement);
1002 public override bool Resolve (BlockContext ec)
1004 foreach (var s in statements)
1010 protected override void DoEmit (EmitContext ec)
1012 foreach (var s in statements)
1016 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1018 foreach (var s in statements)
1019 s.FlowAnalysis (fc);
1024 public override Reachability MarkReachable (Reachability rc)
1026 base.MarkReachable (rc);
1028 Reachability res = rc;
1029 foreach (var s in statements)
1030 res = s.MarkReachable (rc);
1035 protected override void CloneTo (CloneContext clonectx, Statement target)
1037 StatementList t = (StatementList) target;
1039 t.statements = new List<Statement> (statements.Count);
1040 foreach (Statement s in statements)
1041 t.statements.Add (s.Clone (clonectx));
1044 public override object Accept (StructuralVisitor visitor)
1046 return visitor.Visit (this);
1051 // For statements which require special handling when inside try or catch block
1053 public abstract class ExitStatement : Statement
1055 protected bool unwind_protect;
1057 protected abstract bool DoResolve (BlockContext bc);
1058 protected abstract bool IsLocalExit { get; }
1060 public override bool Resolve (BlockContext bc)
1062 var res = DoResolve (bc);
1066 // We are inside finally scope but is it the scope we are exiting
1068 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1070 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1071 if (b.IsFinallyBlock) {
1072 Error_FinallyClauseExit (bc);
1076 if (b is ParametersBlock)
1082 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1086 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1091 if (fc.TryFinally != null) {
1092 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1094 fc.ParametersBlock.CheckControlExit (fc);
1102 /// Implements the return statement
1104 public class Return : ExitStatement
1108 public Return (Expression expr, Location l)
1116 public Expression Expr {
1125 protected override bool IsLocalExit {
1133 protected override bool DoResolve (BlockContext ec)
1135 var block_return_type = ec.ReturnType;
1138 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1142 // Return must not be followed by an expression when
1143 // the method return type is Task
1145 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1146 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1147 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1149 // Extra trick not to emit ret/leave inside awaiter body
1151 expr = EmptyExpression.Null;
1155 if (storey.ReturnType.IsGenericTask)
1156 block_return_type = storey.ReturnType.TypeArguments[0];
1159 if (ec.CurrentIterator != null) {
1160 Error_ReturnFromIterator (ec);
1161 } else if (block_return_type != InternalType.ErrorType) {
1162 ec.Report.Error (126, loc,
1163 "An object of a type convertible to `{0}' is required for the return statement",
1164 block_return_type.GetSignatureForError ());
1170 expr = expr.Resolve (ec);
1172 AnonymousExpression am = ec.CurrentAnonymousMethod;
1174 if (block_return_type.Kind == MemberKind.Void) {
1175 ec.Report.Error (127, loc,
1176 "`{0}': A return keyword must not be followed by any expression when method returns void",
1177 ec.GetSignatureForError ());
1182 if (am.IsIterator) {
1183 Error_ReturnFromIterator (ec);
1187 var async_block = am as AsyncInitializer;
1188 if (async_block != null) {
1190 var storey = (AsyncTaskStorey) am.Storey;
1191 var async_type = storey.ReturnType;
1193 if (async_type == null && async_block.ReturnTypeInference != null) {
1194 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1195 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1197 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1201 if (async_type.Kind == MemberKind.Void) {
1202 ec.Report.Error (8030, loc,
1203 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1207 if (!async_type.IsGenericTask) {
1208 if (this is ContextualReturn)
1211 if (async_block.DelegateType != null) {
1212 ec.Report.Error (8031, loc,
1213 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1215 ec.Report.Error (1997, loc,
1216 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1217 ec.GetSignatureForError ());
1223 // The return type is actually Task<T> type argument
1225 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1226 ec.Report.Error (4016, loc,
1227 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1228 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1230 block_return_type = async_type.TypeArguments[0];
1234 if (block_return_type.Kind == MemberKind.Void) {
1235 ec.Report.Error (8030, loc,
1236 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1240 var l = am as AnonymousMethodBody;
1241 if (l != null && expr != null) {
1242 if (l.ReturnTypeInference != null) {
1243 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1248 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1249 // unexpected debugging experience
1251 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1252 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1261 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1262 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1265 if (am != null && block_return_type == ec.ReturnType) {
1266 ec.Report.Error (1662, loc,
1267 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
1268 am.ContainerType, am.GetSignatureForError ());
1277 protected override void DoEmit (EmitContext ec)
1281 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1282 if (async_body != null) {
1283 var storey = (AsyncTaskStorey)async_body.Storey;
1284 Label exit_label = async_body.BodyEnd;
1287 // It's null for await without async
1289 if (storey.HoistedReturnValue != null) {
1291 // Special case hoisted return value (happens in try/finally scenario)
1293 if (ec.TryFinallyUnwind != null) {
1294 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1297 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1298 async_return.EmitAssign (ec, expr, false, false);
1303 if (ec.TryFinallyUnwind != null)
1304 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1307 ec.Emit (OpCodes.Leave, exit_label);
1314 if (unwind_protect || ec.EmitAccurateDebugInfo)
1315 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1318 if (unwind_protect) {
1319 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1320 } else if (ec.EmitAccurateDebugInfo) {
1321 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1323 ec.Emit (OpCodes.Ret);
1327 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1330 expr.FlowAnalysis (fc);
1332 base.DoFlowAnalysis (fc);
1336 void Error_ReturnFromIterator (ResolveContext rc)
1338 rc.Report.Error (1622, loc,
1339 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1342 public override Reachability MarkReachable (Reachability rc)
1344 base.MarkReachable (rc);
1345 return Reachability.CreateUnreachable ();
1348 protected override void CloneTo (CloneContext clonectx, Statement t)
1350 Return target = (Return) t;
1351 // It's null for simple return;
1353 target.expr = expr.Clone (clonectx);
1356 public override object Accept (StructuralVisitor visitor)
1358 return visitor.Visit (this);
1362 public class Goto : ExitStatement
1365 LabeledStatement label;
1366 TryFinally try_finally;
1368 public Goto (string label, Location l)
1374 public string Target {
1375 get { return target; }
1378 protected override bool IsLocalExit {
1384 protected override bool DoResolve (BlockContext bc)
1386 label = bc.CurrentBlock.LookupLabel (target);
1387 if (label == null) {
1388 Error_UnknownLabel (bc, target, loc);
1392 try_finally = bc.CurrentTryBlock as TryFinally;
1394 CheckExitBoundaries (bc, label.Block);
1399 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1401 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1405 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1407 // Goto to unreachable label
1411 if (fc.AddReachedLabel (label))
1414 label.Block.ScanGotoJump (label, fc);
1418 public override Reachability MarkReachable (Reachability rc)
1420 if (rc.IsUnreachable)
1423 base.MarkReachable (rc);
1425 if (try_finally != null) {
1426 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1427 label.AddGotoReference (rc);
1432 label.AddGotoReference (rc);
1435 return Reachability.CreateUnreachable ();
1438 protected override void CloneTo (CloneContext clonectx, Statement target)
1443 protected override void DoEmit (EmitContext ec)
1445 // This should only happen for goto from try block to unrechable label
1449 Label l = label.LabelTarget (ec);
1451 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1452 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1453 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1456 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1459 bool IsLeavingFinally (Block labelBlock)
1461 var b = try_finally.Statement as Block;
1463 if (b == labelBlock)
1472 public override object Accept (StructuralVisitor visitor)
1474 return visitor.Visit (this);
1478 public class LabeledStatement : Statement {
1485 public LabeledStatement (string name, Block block, Location l)
1492 public Label LabelTarget (EmitContext ec)
1497 label = ec.DefineLabel ();
1502 public Block Block {
1508 public string Name {
1509 get { return name; }
1512 protected override void CloneTo (CloneContext clonectx, Statement target)
1514 var t = (LabeledStatement) target;
1516 t.block = clonectx.RemapBlockCopy (block);
1519 public override bool Resolve (BlockContext bc)
1524 protected override void DoEmit (EmitContext ec)
1527 ec.MarkLabel (label);
1530 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1533 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1539 public override Reachability MarkReachable (Reachability rc)
1541 base.MarkReachable (rc);
1544 rc = new Reachability ();
1549 public void AddGotoReference (Reachability rc)
1557 block.ScanGotoJump (this);
1560 public override object Accept (StructuralVisitor visitor)
1562 return visitor.Visit (this);
1568 /// `goto default' statement
1570 public class GotoDefault : SwitchGoto
1572 public GotoDefault (Location l)
1577 public override bool Resolve (BlockContext bc)
1579 if (bc.Switch == null) {
1580 Error_GotoCaseRequiresSwitchBlock (bc);
1584 bc.Switch.RegisterGotoCase (null, null);
1590 protected override void DoEmit (EmitContext ec)
1592 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1595 public override Reachability MarkReachable (Reachability rc)
1597 if (!rc.IsUnreachable) {
1598 var label = switch_statement.DefaultLabel;
1599 if (label.IsUnreachable) {
1600 label.MarkReachable (rc);
1601 switch_statement.Block.ScanGotoJump (label);
1605 return base.MarkReachable (rc);
1608 public override object Accept (StructuralVisitor visitor)
1610 return visitor.Visit (this);
1615 /// `goto case' statement
1617 public class GotoCase : SwitchGoto
1621 public GotoCase (Expression e, Location l)
1627 public Expression Expr {
1633 public SwitchLabel Label { get; set; }
1635 public override bool Resolve (BlockContext ec)
1637 if (ec.Switch == null) {
1638 Error_GotoCaseRequiresSwitchBlock (ec);
1642 Constant c = expr.ResolveLabelConstant (ec);
1648 if (ec.Switch.IsNullable && c is NullLiteral) {
1651 TypeSpec type = ec.Switch.SwitchType;
1652 res = c.Reduce (ec, type);
1654 c.Error_ValueCannotBeConverted (ec, type, true);
1658 if (!Convert.ImplicitStandardConversionExists (c, type))
1659 ec.Report.Warning (469, 2, loc,
1660 "The `goto case' value is not implicitly convertible to type `{0}'",
1661 type.GetSignatureForError ());
1665 ec.Switch.RegisterGotoCase (this, res);
1672 protected override void DoEmit (EmitContext ec)
1674 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1677 protected override void CloneTo (CloneContext clonectx, Statement t)
1679 GotoCase target = (GotoCase) t;
1681 target.expr = expr.Clone (clonectx);
1684 public override Reachability MarkReachable (Reachability rc)
1686 if (!rc.IsUnreachable) {
1687 var label = switch_statement.FindLabel ((Constant) expr);
1688 if (label.IsUnreachable) {
1689 label.MarkReachable (rc);
1690 switch_statement.Block.ScanGotoJump (label);
1694 return base.MarkReachable (rc);
1697 public override object Accept (StructuralVisitor visitor)
1699 return visitor.Visit (this);
1703 public abstract class SwitchGoto : Statement
1705 protected bool unwind_protect;
1706 protected Switch switch_statement;
1708 protected SwitchGoto (Location loc)
1713 protected override void CloneTo (CloneContext clonectx, Statement target)
1718 public override bool Resolve (BlockContext bc)
1720 CheckExitBoundaries (bc, bc.Switch.Block);
1722 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1723 switch_statement = bc.Switch;
1728 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1733 public override Reachability MarkReachable (Reachability rc)
1735 base.MarkReachable (rc);
1736 return Reachability.CreateUnreachable ();
1739 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1741 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1745 public class Throw : Statement {
1748 public Throw (Expression expr, Location l)
1754 public Expression Expr {
1760 public override bool Resolve (BlockContext ec)
1763 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1764 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1765 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1766 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1767 if (b.IsFinallyBlock) {
1768 ec.Report.Error (724, loc,
1769 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1778 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1783 var et = ec.BuiltinTypes.Exception;
1784 if (Convert.ImplicitConversionExists (ec, expr, et))
1785 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1787 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1792 protected override void DoEmit (EmitContext ec)
1795 var atv = ec.AsyncThrowVariable;
1797 if (atv.HoistedVariant != null) {
1798 atv.HoistedVariant.Emit (ec);
1803 ec.Emit (OpCodes.Throw);
1805 ec.Emit (OpCodes.Rethrow);
1810 ec.Emit (OpCodes.Throw);
1814 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1817 expr.FlowAnalysis (fc);
1822 public override Reachability MarkReachable (Reachability rc)
1824 base.MarkReachable (rc);
1825 return Reachability.CreateUnreachable ();
1828 protected override void CloneTo (CloneContext clonectx, Statement t)
1830 Throw target = (Throw) t;
1833 target.expr = expr.Clone (clonectx);
1836 public override object Accept (StructuralVisitor visitor)
1838 return visitor.Visit (this);
1842 public class Break : LocalExitStatement
1844 public Break (Location l)
1849 public override object Accept (StructuralVisitor visitor)
1851 return visitor.Visit (this);
1854 protected override void DoEmit (EmitContext ec)
1858 if (ec.TryFinallyUnwind != null) {
1859 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1860 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1863 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1866 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1868 enclosing_loop.AddEndDefiniteAssignment (fc);
1872 protected override bool DoResolve (BlockContext bc)
1874 enclosing_loop = bc.EnclosingLoopOrSwitch;
1875 return base.DoResolve (bc);
1878 public override Reachability MarkReachable (Reachability rc)
1880 base.MarkReachable (rc);
1882 if (!rc.IsUnreachable)
1883 enclosing_loop.SetEndReachable ();
1885 return Reachability.CreateUnreachable ();
1889 public class Continue : LocalExitStatement
1891 public Continue (Location l)
1896 public override object Accept (StructuralVisitor visitor)
1898 return visitor.Visit (this);
1902 protected override void DoEmit (EmitContext ec)
1904 var l = ec.LoopBegin;
1906 if (ec.TryFinallyUnwind != null) {
1907 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1908 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1911 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1914 protected override bool DoResolve (BlockContext bc)
1916 enclosing_loop = bc.EnclosingLoop;
1917 return base.DoResolve (bc);
1920 public override Reachability MarkReachable (Reachability rc)
1922 base.MarkReachable (rc);
1924 if (!rc.IsUnreachable)
1925 enclosing_loop.SetIteratorReachable ();
1927 return Reachability.CreateUnreachable ();
1931 public abstract class LocalExitStatement : ExitStatement
1933 protected LoopStatement enclosing_loop;
1935 protected LocalExitStatement (Location loc)
1940 protected override bool IsLocalExit {
1946 protected override void CloneTo (CloneContext clonectx, Statement t)
1951 protected override bool DoResolve (BlockContext bc)
1953 if (enclosing_loop == null) {
1954 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1958 var block = enclosing_loop.Statement as Block;
1960 // Don't need to do extra checks for simple statements loops
1961 if (block != null) {
1962 CheckExitBoundaries (bc, block);
1969 public interface ILocalVariable
1971 void Emit (EmitContext ec);
1972 void EmitAssign (EmitContext ec);
1973 void EmitAddressOf (EmitContext ec);
1976 public interface INamedBlockVariable
1978 Block Block { get; }
1979 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1980 bool IsDeclared { get; }
1981 bool IsParameter { get; }
1982 Location Location { get; }
1985 public class BlockVariableDeclarator
1988 Expression initializer;
1990 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1992 if (li.Type != null)
1993 throw new ArgumentException ("Expected null variable type");
1996 this.initializer = initializer;
2001 public LocalVariable Variable {
2007 public Expression Initializer {
2012 initializer = value;
2018 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2020 var t = (BlockVariableDeclarator) MemberwiseClone ();
2021 if (initializer != null)
2022 t.initializer = initializer.Clone (cloneCtx);
2028 public class BlockVariable : Statement
2030 Expression initializer;
2031 protected FullNamedExpression type_expr;
2032 protected LocalVariable li;
2033 protected List<BlockVariableDeclarator> declarators;
2036 public BlockVariable (FullNamedExpression type, LocalVariable li)
2038 this.type_expr = type;
2040 this.loc = type_expr.Location;
2043 protected BlockVariable (LocalVariable li)
2050 public List<BlockVariableDeclarator> Declarators {
2056 public Expression Initializer {
2061 initializer = value;
2065 public FullNamedExpression TypeExpression {
2071 public LocalVariable Variable {
2079 public void AddDeclarator (BlockVariableDeclarator decl)
2081 if (declarators == null)
2082 declarators = new List<BlockVariableDeclarator> ();
2084 declarators.Add (decl);
2087 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2089 if (bc.Report.Errors != 0)
2092 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2094 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2095 new MemberName (li.Name, li.Location), null);
2097 container.AddField (f);
2100 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2104 public override bool Resolve (BlockContext bc)
2106 return Resolve (bc, true);
2109 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2111 if (type == null && !li.IsCompilerGenerated) {
2112 var vexpr = type_expr as VarExpr;
2115 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2116 // same name exists or as a keyword when no type was found
2118 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2119 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2120 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2123 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2127 if (li.IsConstant) {
2128 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2132 if (Initializer == null) {
2133 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2137 if (declarators != null) {
2138 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2142 Initializer = Initializer.Resolve (bc);
2143 if (Initializer != null) {
2144 ((VarExpr) type_expr).InferType (bc, Initializer);
2145 type = type_expr.Type;
2147 // Set error type to indicate the var was placed correctly but could
2150 // var a = missing ();
2152 type = InternalType.ErrorType;
2157 type = type_expr.ResolveAsType (bc);
2161 if (li.IsConstant && !type.IsConstantCompatible) {
2162 Const.Error_InvalidConstantType (type, loc, bc.Report);
2167 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2172 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2174 CreateEvaluatorVariable (bc, li);
2175 } else if (type != InternalType.ErrorType) {
2176 li.PrepareAssignmentAnalysis (bc);
2179 if (initializer != null) {
2180 initializer = ResolveInitializer (bc, li, initializer);
2181 // li.Variable.DefinitelyAssigned
2184 if (declarators != null) {
2185 foreach (var d in declarators) {
2186 d.Variable.Type = li.Type;
2188 CreateEvaluatorVariable (bc, d.Variable);
2189 } else if (type != InternalType.ErrorType) {
2190 d.Variable.PrepareAssignmentAnalysis (bc);
2193 if (d.Initializer != null && resolveDeclaratorInitializers) {
2194 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2195 // d.Variable.DefinitelyAssigned
2203 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2205 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2206 return a.ResolveStatement (bc);
2209 protected override void DoEmit (EmitContext ec)
2211 li.CreateBuilder (ec);
2213 if (Initializer != null && !IsUnreachable)
2214 ((ExpressionStatement) Initializer).EmitStatement (ec);
2216 if (declarators != null) {
2217 foreach (var d in declarators) {
2218 d.Variable.CreateBuilder (ec);
2219 if (d.Initializer != null && !IsUnreachable) {
2220 ec.Mark (d.Variable.Location);
2221 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2227 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2229 if (Initializer != null)
2230 Initializer.FlowAnalysis (fc);
2232 if (declarators != null) {
2233 foreach (var d in declarators) {
2234 if (d.Initializer != null)
2235 d.Initializer.FlowAnalysis (fc);
2242 public override Reachability MarkReachable (Reachability rc)
2244 var init = initializer as ExpressionStatement;
2246 init.MarkReachable (rc);
2248 return base.MarkReachable (rc);
2251 protected override void CloneTo (CloneContext clonectx, Statement target)
2253 BlockVariable t = (BlockVariable) target;
2255 if (type_expr != null)
2256 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2258 if (initializer != null)
2259 t.initializer = initializer.Clone (clonectx);
2261 if (declarators != null) {
2262 t.declarators = null;
2263 foreach (var d in declarators)
2264 t.AddDeclarator (d.Clone (clonectx));
2268 public override object Accept (StructuralVisitor visitor)
2270 return visitor.Visit (this);
2274 public class BlockConstant : BlockVariable
2276 public BlockConstant (FullNamedExpression type, LocalVariable li)
2281 public override void Emit (EmitContext ec)
2283 // Nothing to emit, not even sequence point
2286 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2288 initializer = initializer.Resolve (bc);
2289 if (initializer == null)
2292 var c = initializer as Constant;
2294 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2298 c = c.ConvertImplicitly (li.Type);
2300 if (TypeSpec.IsReferenceType (li.Type))
2301 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2303 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2308 li.ConstantValue = c;
2312 public override object Accept (StructuralVisitor visitor)
2314 return visitor.Visit (this);
2319 // The information about a user-perceived local variable
2321 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2328 AddressTaken = 1 << 2,
2329 CompilerGenerated = 1 << 3,
2331 ForeachVariable = 1 << 5,
2332 FixedVariable = 1 << 6,
2333 UsingVariable = 1 << 7,
2335 SymbolFileHidden = 1 << 9,
2337 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2341 readonly string name;
2342 readonly Location loc;
2343 readonly Block block;
2345 Constant const_value;
2347 public VariableInfo VariableInfo;
2348 HoistedVariable hoisted_variant;
2350 LocalBuilder builder;
2352 public LocalVariable (Block block, string name, Location loc)
2359 public LocalVariable (Block block, string name, Flags flags, Location loc)
2360 : this (block, name, loc)
2366 // Used by variable declarators
2368 public LocalVariable (LocalVariable li, string name, Location loc)
2369 : this (li.block, name, li.flags, loc)
2375 public bool AddressTaken {
2377 return (flags & Flags.AddressTaken) != 0;
2381 public Block Block {
2387 public Constant ConstantValue {
2392 const_value = value;
2397 // Hoisted local variable variant
2399 public HoistedVariable HoistedVariant {
2401 return hoisted_variant;
2404 hoisted_variant = value;
2408 public bool IsDeclared {
2410 return type != null;
2414 public bool IsCompilerGenerated {
2416 return (flags & Flags.CompilerGenerated) != 0;
2420 public bool IsConstant {
2422 return (flags & Flags.Constant) != 0;
2426 public bool IsLocked {
2428 return (flags & Flags.IsLocked) != 0;
2431 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2435 public bool IsThis {
2437 return (flags & Flags.IsThis) != 0;
2441 public bool IsFixed {
2443 return (flags & Flags.FixedVariable) != 0;
2446 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2450 bool INamedBlockVariable.IsParameter {
2456 public bool IsReadonly {
2458 return (flags & Flags.ReadonlyMask) != 0;
2462 public Location Location {
2468 public string Name {
2474 public TypeSpec Type {
2485 public void CreateBuilder (EmitContext ec)
2487 if ((flags & Flags.Used) == 0) {
2488 if (VariableInfo == null) {
2489 // Missing flow analysis or wrong variable flags
2490 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2493 if (VariableInfo.IsEverAssigned)
2494 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2496 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2499 if (HoistedVariant != null)
2502 if (builder != null) {
2503 if ((flags & Flags.CompilerGenerated) != 0)
2506 // To avoid Used warning duplicates
2507 throw new InternalErrorException ("Already created variable `{0}'", name);
2511 // All fixed variabled are pinned, a slot has to be alocated
2513 builder = ec.DeclareLocal (Type, IsFixed);
2514 if ((flags & Flags.SymbolFileHidden) == 0)
2515 ec.DefineLocalVariable (name, builder);
2518 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2520 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2521 if (!writeToSymbolFile)
2522 li.flags |= Flags.SymbolFileHidden;
2528 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2530 if (IsConstant && const_value != null)
2531 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2533 return new LocalVariableReference (this, loc);
2536 public void Emit (EmitContext ec)
2538 // TODO: Need something better for temporary variables
2539 if ((flags & Flags.CompilerGenerated) != 0)
2542 ec.Emit (OpCodes.Ldloc, builder);
2545 public void EmitAssign (EmitContext ec)
2547 // TODO: Need something better for temporary variables
2548 if ((flags & Flags.CompilerGenerated) != 0)
2551 ec.Emit (OpCodes.Stloc, builder);
2554 public void EmitAddressOf (EmitContext ec)
2556 // TODO: Need something better for temporary variables
2557 if ((flags & Flags.CompilerGenerated) != 0)
2560 ec.Emit (OpCodes.Ldloca, builder);
2563 public static string GetCompilerGeneratedName (Block block)
2565 // HACK: Debugger depends on the name semantics
2566 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2569 public string GetReadOnlyContext ()
2571 switch (flags & Flags.ReadonlyMask) {
2572 case Flags.FixedVariable:
2573 return "fixed variable";
2574 case Flags.ForeachVariable:
2575 return "foreach iteration variable";
2576 case Flags.UsingVariable:
2577 return "using variable";
2580 throw new InternalErrorException ("Variable is not readonly");
2583 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2585 if (VariableInfo == null)
2586 throw new Exception ();
2588 if (IsAssigned (fc))
2591 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2594 public bool IsAssigned (FlowAnalysisContext fc)
2596 return fc.IsDefinitelyAssigned (VariableInfo);
2599 public void PrepareAssignmentAnalysis (BlockContext bc)
2602 // No need to run assignment analysis for these guys
2604 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2607 VariableInfo = VariableInfo.Create (bc, this);
2611 // Mark the variables as referenced in the user code
2613 public void SetIsUsed ()
2615 flags |= Flags.Used;
2618 public void SetHasAddressTaken ()
2620 flags |= (Flags.AddressTaken | Flags.Used);
2623 public override string ToString ()
2625 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2630 /// Block represents a C# block.
2634 /// This class is used in a number of places: either to represent
2635 /// explicit blocks that the programmer places or implicit blocks.
2637 /// Implicit blocks are used as labels or to introduce variable
2640 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2641 /// they contain extra information that is not necessary on normal blocks.
2643 public class Block : Statement {
2650 HasCapturedVariable = 64,
2651 HasCapturedThis = 1 << 7,
2652 IsExpressionTree = 1 << 8,
2653 CompilerGenerated = 1 << 9,
2654 HasAsyncModifier = 1 << 10,
2656 YieldBlock = 1 << 12,
2657 AwaitBlock = 1 << 13,
2658 FinallyBlock = 1 << 14,
2659 CatchBlock = 1 << 15,
2660 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2662 NoFlowAnalysis = 1 << 21,
2663 InitializationEmitted = 1 << 22
2666 public Block Parent;
2667 public Location StartLocation;
2668 public Location EndLocation;
2670 public ExplicitBlock Explicit;
2671 public ParametersBlock ParametersBlock;
2673 protected Flags flags;
2676 // The statements in this block
2678 protected List<Statement> statements;
2680 protected List<Statement> scope_initializers;
2682 int? resolving_init_idx;
2688 public int ID = id++;
2690 static int clone_id_counter;
2694 // int assignable_slots;
2696 public Block (Block parent, Location start, Location end)
2697 : this (parent, 0, start, end)
2701 public Block (Block parent, Flags flags, Location start, Location end)
2703 if (parent != null) {
2704 // the appropriate constructors will fixup these fields
2705 ParametersBlock = parent.ParametersBlock;
2706 Explicit = parent.Explicit;
2709 this.Parent = parent;
2711 this.StartLocation = start;
2712 this.EndLocation = end;
2714 statements = new List<Statement> (4);
2716 this.original = this;
2721 public Block Original {
2730 public bool IsCompilerGenerated {
2731 get { return (flags & Flags.CompilerGenerated) != 0; }
2732 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2736 public bool IsCatchBlock {
2738 return (flags & Flags.CatchBlock) != 0;
2742 public bool IsFinallyBlock {
2744 return (flags & Flags.FinallyBlock) != 0;
2748 public bool Unchecked {
2749 get { return (flags & Flags.Unchecked) != 0; }
2750 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2753 public bool Unsafe {
2754 get { return (flags & Flags.Unsafe) != 0; }
2755 set { flags |= Flags.Unsafe; }
2758 public List<Statement> Statements {
2759 get { return statements; }
2764 public void SetEndLocation (Location loc)
2769 public void AddLabel (LabeledStatement target)
2771 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2774 public void AddLocalName (LocalVariable li)
2776 AddLocalName (li.Name, li);
2779 public void AddLocalName (string name, INamedBlockVariable li)
2781 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2784 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2786 if (reason == null) {
2787 Error_AlreadyDeclared (name, variable);
2791 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2792 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2793 "to `{0}', which is already used in a `{1}' scope to denote something else",
2797 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2799 var pi = variable as ParametersBlock.ParameterInfo;
2801 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2803 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2804 "A local variable named `{0}' is already defined in this scope", name);
2808 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2810 ParametersBlock.TopBlock.Report.Error (412, loc,
2811 "The type parameter name `{0}' is the same as local variable or parameter name",
2816 // It should be used by expressions which require to
2817 // register a statement during resolve process.
2819 public void AddScopeStatement (Statement s)
2821 if (scope_initializers == null)
2822 scope_initializers = new List<Statement> ();
2825 // Simple recursive helper, when resolve scope initializer another
2826 // new scope initializer can be added, this ensures it's initialized
2827 // before existing one. For now this can happen with expression trees
2828 // in base ctor initializer only
2830 if (resolving_init_idx.HasValue) {
2831 scope_initializers.Insert (resolving_init_idx.Value, s);
2832 ++resolving_init_idx;
2834 scope_initializers.Add (s);
2838 public void InsertStatement (int index, Statement s)
2840 statements.Insert (index, s);
2843 public void AddStatement (Statement s)
2848 public LabeledStatement LookupLabel (string name)
2850 return ParametersBlock.GetLabel (name, this);
2853 public override Reachability MarkReachable (Reachability rc)
2855 if (rc.IsUnreachable)
2858 MarkReachableScope (rc);
2860 foreach (var s in statements) {
2861 rc = s.MarkReachable (rc);
2862 if (rc.IsUnreachable) {
2863 if ((flags & Flags.ReachableEnd) != 0)
2864 return new Reachability ();
2870 flags |= Flags.ReachableEnd;
2875 public void MarkReachableScope (Reachability rc)
2877 base.MarkReachable (rc);
2879 if (scope_initializers != null) {
2880 foreach (var si in scope_initializers)
2881 si.MarkReachable (rc);
2885 public override bool Resolve (BlockContext bc)
2887 if ((flags & Flags.Resolved) != 0)
2890 Block prev_block = bc.CurrentBlock;
2891 bc.CurrentBlock = this;
2894 // Compiler generated scope statements
2896 if (scope_initializers != null) {
2897 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2898 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2901 resolving_init_idx = null;
2905 int statement_count = statements.Count;
2906 for (int ix = 0; ix < statement_count; ix++){
2907 Statement s = statements [ix];
2909 if (!s.Resolve (bc)) {
2911 statements [ix] = new EmptyStatement (s.loc);
2916 bc.CurrentBlock = prev_block;
2918 flags |= Flags.Resolved;
2922 protected override void DoEmit (EmitContext ec)
2924 for (int ix = 0; ix < statements.Count; ix++){
2925 statements [ix].Emit (ec);
2929 public override void Emit (EmitContext ec)
2931 if (scope_initializers != null)
2932 EmitScopeInitializers (ec);
2937 protected void EmitScopeInitializers (EmitContext ec)
2939 foreach (Statement s in scope_initializers)
2943 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2945 if (scope_initializers != null) {
2946 foreach (var si in scope_initializers)
2947 si.FlowAnalysis (fc);
2950 return DoFlowAnalysis (fc, 0);
2953 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2955 bool end_unreachable = !reachable;
2956 bool goto_flow_analysis = startIndex != 0;
2957 for (; startIndex < statements.Count; ++startIndex) {
2958 var s = statements[startIndex];
2960 end_unreachable = s.FlowAnalysis (fc);
2961 if (s.IsUnreachable) {
2962 statements [startIndex] = RewriteUnreachableStatement (s);
2967 // Statement end reachability is needed mostly due to goto support. Consider
2976 // X label is reachable only via goto not as another statement after if. We need
2977 // this for flow-analysis only to carry variable info correctly.
2979 if (end_unreachable) {
2980 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2982 var f = s as TryFinally;
2983 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
2985 // Special case for try-finally with unreachable code after
2986 // finally block. Try block has to include leave opcode but there is
2987 // no label to leave to after unreachable finally block closing
2988 // brace. This sentinel ensures there is always IL instruction to
2989 // leave to even if we know it'll never be reached.
2991 statements.Insert (startIndex + 1, new SentinelStatement ());
2993 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2994 s = statements [startIndex];
2995 if (s is SwitchLabel) {
2996 if (!after_goto_case)
2997 s.FlowAnalysis (fc);
3002 if (s.IsUnreachable) {
3003 s.FlowAnalysis (fc);
3004 statements [startIndex] = RewriteUnreachableStatement (s);
3010 // Idea is to stop after goto case because goto case will always have at least same
3011 // variable assigned as switch case label. This saves a lot for complex goto case tests
3013 if (after_goto_case)
3019 var lb = s as LabeledStatement;
3020 if (lb != null && fc.AddReachedLabel (lb))
3025 // The condition should be true unless there is forward jumping goto
3027 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3030 return !Explicit.HasReachableClosingBrace;
3033 static Statement RewriteUnreachableStatement (Statement s)
3035 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3036 // analysis. Even csc report unreachable warning for it but it's actually used hence
3037 // we try to emulate this behaviour
3045 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3048 return new EmptyStatement (s.loc);
3051 public void ScanGotoJump (Statement label)
3054 for (i = 0; i < statements.Count; ++i) {
3055 if (statements[i] == label)
3059 var rc = new Reachability ();
3060 for (++i; i < statements.Count; ++i) {
3061 var s = statements[i];
3062 rc = s.MarkReachable (rc);
3063 if (rc.IsUnreachable)
3067 flags |= Flags.ReachableEnd;
3070 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3073 for (i = 0; i < statements.Count; ++i) {
3074 if (statements[i] == label)
3078 DoFlowAnalysis (fc, ++i);
3082 public override string ToString ()
3084 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3088 protected override void CloneTo (CloneContext clonectx, Statement t)
3090 Block target = (Block) t;
3092 target.clone_id = ++clone_id_counter;
3095 clonectx.AddBlockMap (this, target);
3096 if (original != this)
3097 clonectx.AddBlockMap (original, target);
3099 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3100 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3103 target.Parent = clonectx.RemapBlockCopy (Parent);
3105 target.statements = new List<Statement> (statements.Count);
3106 foreach (Statement s in statements)
3107 target.statements.Add (s.Clone (clonectx));
3110 public override object Accept (StructuralVisitor visitor)
3112 return visitor.Visit (this);
3116 public class ExplicitBlock : Block
3118 protected AnonymousMethodStorey am_storey;
3119 int debug_scope_index;
3121 public ExplicitBlock (Block parent, Location start, Location end)
3122 : this (parent, (Flags) 0, start, end)
3126 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3127 : base (parent, flags, start, end)
3129 this.Explicit = this;
3134 public AnonymousMethodStorey AnonymousMethodStorey {
3140 public bool HasAwait {
3142 return (flags & Flags.AwaitBlock) != 0;
3146 public bool HasCapturedThis {
3148 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3151 return (flags & Flags.HasCapturedThis) != 0;
3156 // Used to indicate that the block has reference to parent
3157 // block and cannot be made static when defining anonymous method
3159 public bool HasCapturedVariable {
3161 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3164 return (flags & Flags.HasCapturedVariable) != 0;
3168 public bool HasReachableClosingBrace {
3170 return (flags & Flags.ReachableEnd) != 0;
3173 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3177 public bool HasYield {
3179 return (flags & Flags.YieldBlock) != 0;
3186 // Creates anonymous method storey in current block
3188 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3191 // Return same story for iterator and async blocks unless we are
3192 // in nested anonymous method
3194 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3195 return ec.CurrentAnonymousMethod.Storey;
3197 if (am_storey == null) {
3198 MemberBase mc = ec.MemberContext as MemberBase;
3201 // Creates anonymous method storey for this block
3203 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3209 public void EmitScopeInitialization (EmitContext ec)
3211 if ((flags & Flags.InitializationEmitted) != 0)
3214 if (am_storey != null) {
3215 DefineStoreyContainer (ec, am_storey);
3216 am_storey.EmitStoreyInstantiation (ec, this);
3219 if (scope_initializers != null)
3220 EmitScopeInitializers (ec);
3222 flags |= Flags.InitializationEmitted;
3225 public override void Emit (EmitContext ec)
3227 if (Parent != null) {
3228 // TODO: It's needed only when scope has variable (normal or lifted)
3229 ec.BeginScope (GetDebugSymbolScopeIndex ());
3232 EmitScopeInitialization (ec);
3234 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3235 ec.Emit (OpCodes.Nop);
3243 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3244 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3245 ec.Emit (OpCodes.Nop);
3249 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3251 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3252 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3253 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3257 // Creates anonymous method storey
3259 storey.CreateContainer ();
3260 storey.DefineContainer ();
3262 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3265 // Only first storey in path will hold this reference. All children blocks will
3266 // reference it indirectly using $ref field
3268 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3269 if (b.Parent != null) {
3270 var s = b.Parent.Explicit.AnonymousMethodStorey;
3272 storey.HoistedThis = s.HoistedThis;
3277 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3278 if (storey.HoistedThis == null)
3279 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3281 if (storey.HoistedThis != null)
3287 // We are the first storey on path and 'this' has to be hoisted
3289 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3290 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3292 // ThisReferencesFromChildrenBlock holds all reference even if they
3293 // are not on this path. It saves some memory otherwise it'd have to
3294 // be in every explicit block. We run this check to see if the reference
3295 // is valid for this storey
3297 Block block_on_path = ref_block;
3298 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3300 if (block_on_path == null)
3303 if (storey.HoistedThis == null) {
3304 storey.AddCapturedThisField (ec, null);
3307 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3309 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3311 if (b_storey != null) {
3313 // Don't add storey cross reference for `this' when the storey ends up not
3314 // beeing attached to any parent
3316 if (b.ParametersBlock.StateMachine == null) {
3317 AnonymousMethodStorey s = null;
3318 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3319 s = ab.Explicit.AnonymousMethodStorey;
3324 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3326 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3327 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3334 // Stop propagation inside same top block
3336 if (b.ParametersBlock == ParametersBlock.Original) {
3337 b_storey.AddParentStoreyReference (ec, storey);
3338 // b_storey.HoistedThis = storey.HoistedThis;
3342 b = pb = b.ParametersBlock;
3344 pb = b as ParametersBlock;
3347 if (pb != null && pb.StateMachine != null) {
3348 if (pb.StateMachine == storey)
3352 // If we are state machine with no parent. We can hook into parent without additional
3353 // reference and capture this directly
3355 ExplicitBlock parent_storey_block = pb;
3356 while (parent_storey_block.Parent != null) {
3357 parent_storey_block = parent_storey_block.Parent.Explicit;
3358 if (parent_storey_block.AnonymousMethodStorey != null) {
3363 if (parent_storey_block.AnonymousMethodStorey == null) {
3364 if (pb.StateMachine.HoistedThis == null) {
3365 pb.StateMachine.AddCapturedThisField (ec, null);
3366 b.HasCapturedThis = true;
3372 var parent_this_block = pb;
3373 while (parent_this_block.Parent != null) {
3374 parent_this_block = parent_this_block.Parent.ParametersBlock;
3375 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3381 // Add reference to closest storey which holds captured this
3383 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3387 // Add parent storey reference only when this is not captured directly
3389 if (b_storey != null) {
3390 b_storey.AddParentStoreyReference (ec, storey);
3391 b_storey.HoistedThis = storey.HoistedThis;
3398 var ref_blocks = storey.ReferencesFromChildrenBlock;
3399 if (ref_blocks != null) {
3400 foreach (ExplicitBlock ref_block in ref_blocks) {
3401 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3402 if (b.AnonymousMethodStorey != null) {
3403 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3406 // Stop propagation inside same top block
3408 if (b.ParametersBlock == ParametersBlock.Original)
3411 b = b.ParametersBlock;
3414 var pb = b as ParametersBlock;
3415 if (pb != null && pb.StateMachine != null) {
3416 if (pb.StateMachine == storey)
3419 pb.StateMachine.AddParentStoreyReference (ec, storey);
3422 b.HasCapturedVariable = true;
3428 storey.PrepareEmit ();
3429 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3432 public int GetDebugSymbolScopeIndex ()
3434 if (debug_scope_index == 0)
3435 debug_scope_index = ++ParametersBlock.debug_scope_index;
3437 return debug_scope_index;
3440 public void RegisterAsyncAwait ()
3443 while ((block.flags & Flags.AwaitBlock) == 0) {
3444 block.flags |= Flags.AwaitBlock;
3446 if (block is ParametersBlock)
3449 block = block.Parent.Explicit;
3453 public void RegisterIteratorYield ()
3455 ParametersBlock.TopBlock.IsIterator = true;
3458 while ((block.flags & Flags.YieldBlock) == 0) {
3459 block.flags |= Flags.YieldBlock;
3461 if (block.Parent == null)
3464 block = block.Parent.Explicit;
3468 public void SetCatchBlock ()
3470 flags |= Flags.CatchBlock;
3473 public void SetFinallyBlock ()
3475 flags |= Flags.FinallyBlock;
3478 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3480 tryBlock.statements = statements;
3481 statements = new List<Statement> (1);
3482 statements.Add (tf);
3487 // ParametersBlock was introduced to support anonymous methods
3488 // and lambda expressions
3490 public class ParametersBlock : ExplicitBlock
3492 public class ParameterInfo : INamedBlockVariable
3494 readonly ParametersBlock block;
3496 public VariableInfo VariableInfo;
3499 public ParameterInfo (ParametersBlock block, int index)
3507 public ParametersBlock Block {
3513 Block INamedBlockVariable.Block {
3519 public bool IsDeclared {
3525 public bool IsParameter {
3531 public bool IsLocked {
3540 public Location Location {
3542 return Parameter.Location;
3546 public Parameter Parameter {
3548 return block.Parameters [index];
3552 public TypeSpec ParameterType {
3554 return Parameter.Type;
3560 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3562 return new ParameterReference (this, loc);
3567 // Block is converted into an expression
3569 sealed class BlockScopeExpression : Expression
3572 readonly ParametersBlock block;
3574 public BlockScopeExpression (Expression child, ParametersBlock block)
3580 public override bool ContainsEmitWithAwait ()
3582 return child.ContainsEmitWithAwait ();
3585 public override Expression CreateExpressionTree (ResolveContext ec)
3587 throw new NotSupportedException ();
3590 protected override Expression DoResolve (ResolveContext ec)
3595 child = child.Resolve (ec);
3599 eclass = child.eclass;
3604 public override void Emit (EmitContext ec)
3606 block.EmitScopeInitializers (ec);
3611 protected ParametersCompiled parameters;
3612 protected ParameterInfo[] parameter_info;
3613 protected bool resolved;
3614 protected ToplevelBlock top_block;
3615 protected StateMachine state_machine;
3616 protected Dictionary<string, object> labels;
3618 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3619 : base (parent, 0, start, start)
3621 if (parameters == null)
3622 throw new ArgumentNullException ("parameters");
3624 this.parameters = parameters;
3625 ParametersBlock = this;
3627 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3629 this.top_block = parent.ParametersBlock.top_block;
3630 ProcessParameters ();
3633 protected ParametersBlock (ParametersCompiled parameters, Location start)
3634 : base (null, 0, start, start)
3636 if (parameters == null)
3637 throw new ArgumentNullException ("parameters");
3639 this.parameters = parameters;
3640 ParametersBlock = this;
3644 // It's supposed to be used by method body implementation of anonymous methods
3646 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3647 : base (null, 0, source.StartLocation, source.EndLocation)
3649 this.parameters = parameters;
3650 this.statements = source.statements;
3651 this.scope_initializers = source.scope_initializers;
3653 this.resolved = true;
3654 this.reachable = source.reachable;
3655 this.am_storey = source.am_storey;
3656 this.state_machine = source.state_machine;
3657 this.flags = source.flags & Flags.ReachableEnd;
3659 ParametersBlock = this;
3662 // Overwrite original for comparison purposes when linking cross references
3663 // between anonymous methods
3665 Original = source.Original;
3670 public bool HasReferenceToStoreyForInstanceLambdas {
3672 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3675 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3679 public bool IsAsync {
3681 return (flags & Flags.HasAsyncModifier) != 0;
3684 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3689 // Block has been converted to expression tree
3691 public bool IsExpressionTree {
3693 return (flags & Flags.IsExpressionTree) != 0;
3698 // The parameters for the block.
3700 public ParametersCompiled Parameters {
3706 public StateMachine StateMachine {
3708 return state_machine;
3712 public ToplevelBlock TopBlock {
3721 public bool Resolved {
3723 return (flags & Flags.Resolved) != 0;
3727 public int TemporaryLocalsCount { get; set; }
3732 // Checks whether all `out' parameters have been assigned.
3734 public void CheckControlExit (FlowAnalysisContext fc)
3736 CheckControlExit (fc, fc.DefiniteAssignment);
3739 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3741 if (parameter_info == null)
3744 foreach (var p in parameter_info) {
3745 if (p.VariableInfo == null)
3748 if (p.VariableInfo.IsAssigned (dat))
3751 fc.Report.Error (177, p.Location,
3752 "The out parameter `{0}' must be assigned to before control leaves the current method",
3757 protected override void CloneTo (CloneContext clonectx, Statement t)
3759 base.CloneTo (clonectx, t);
3761 var target = (ParametersBlock) t;
3764 // Clone label statements as well as they contain block reference
3768 if (pb.labels != null) {
3769 target.labels = new Dictionary<string, object> ();
3771 foreach (var entry in pb.labels) {
3772 var list = entry.Value as List<LabeledStatement>;
3775 var list_clone = new List<LabeledStatement> ();
3776 foreach (var lentry in list) {
3777 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3780 target.labels.Add (entry.Key, list_clone);
3782 var labeled = (LabeledStatement) entry.Value;
3783 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3790 if (pb.Parent == null)
3793 pb = pb.Parent.ParametersBlock;
3797 public override Expression CreateExpressionTree (ResolveContext ec)
3799 if (statements.Count == 1) {
3800 Expression expr = statements[0].CreateExpressionTree (ec);
3801 if (scope_initializers != null)
3802 expr = new BlockScopeExpression (expr, this);
3807 return base.CreateExpressionTree (ec);
3810 public override void Emit (EmitContext ec)
3812 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3813 DefineStoreyContainer (ec, state_machine);
3814 state_machine.EmitStoreyInstantiation (ec, this);
3820 public void EmitEmbedded (EmitContext ec)
3822 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3823 DefineStoreyContainer (ec, state_machine);
3824 state_machine.EmitStoreyInstantiation (ec, this);
3830 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3832 var res = base.DoFlowAnalysis (fc);
3834 if (HasReachableClosingBrace)
3835 CheckControlExit (fc);
3840 public LabeledStatement GetLabel (string name, Block block)
3843 // Cloned parameters blocks can have their own cloned version of top-level labels
3845 if (labels == null) {
3847 return Parent.ParametersBlock.GetLabel (name, block);
3853 if (!labels.TryGetValue (name, out value)) {
3857 var label = value as LabeledStatement;
3859 if (label != null) {
3860 if (IsLabelVisible (label, b))
3864 List<LabeledStatement> list = (List<LabeledStatement>) value;
3865 for (int i = 0; i < list.Count; ++i) {
3867 if (IsLabelVisible (label, b))
3875 static bool IsLabelVisible (LabeledStatement label, Block b)
3878 if (label.Block == b)
3881 } while (b != null);
3886 public ParameterInfo GetParameterInfo (Parameter p)
3888 for (int i = 0; i < parameters.Count; ++i) {
3889 if (parameters[i] == p)
3890 return parameter_info[i];
3893 throw new ArgumentException ("Invalid parameter");
3896 public ParameterReference GetParameterReference (int index, Location loc)
3898 return new ParameterReference (parameter_info[index], loc);
3901 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3903 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3905 CloneContext clonectx = new CloneContext ();
3906 return Clone (clonectx);
3909 protected void ProcessParameters ()
3911 if (parameters.Count == 0)
3914 parameter_info = new ParameterInfo[parameters.Count];
3915 for (int i = 0; i < parameter_info.Length; ++i) {
3916 var p = parameters.FixedParameters[i];
3920 // TODO: Should use Parameter only and more block there
3921 parameter_info[i] = new ParameterInfo (this, i);
3923 AddLocalName (p.Name, parameter_info[i]);
3927 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3929 var src = stmt.Block;
3932 // Cannot remap label block if the label was not yet cloned which
3933 // can happen in case of anonymous method inside anoynymous method
3934 // with a label. But in this case we don't care because goto cannot
3935 // jump of out anonymous method
3937 if (src.ParametersBlock != this)
3940 var src_stmts = src.Statements;
3941 for (int i = 0; i < src_stmts.Count; ++i) {
3942 if (src_stmts[i] == stmt)
3943 return (LabeledStatement) dst.Statements[i];
3946 throw new InternalErrorException ("Should never be reached");
3949 public override bool Resolve (BlockContext bc)
3951 // TODO: if ((flags & Flags.Resolved) != 0)
3958 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3959 flags |= Flags.IsExpressionTree;
3962 PrepareAssignmentAnalysis (bc);
3964 if (!base.Resolve (bc))
3967 } catch (Exception e) {
3968 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3971 if (bc.CurrentBlock != null) {
3972 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3974 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3979 // If an asynchronous body of F is either an expression classified as nothing, or a
3980 // statement block where no return statements have expressions, the inferred return type is Task
3983 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3984 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3985 am.ReturnTypeInference = null;
3986 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3994 void PrepareAssignmentAnalysis (BlockContext bc)
3996 for (int i = 0; i < parameters.Count; ++i) {
3997 var par = parameters.FixedParameters[i];
3999 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4002 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4006 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4008 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4009 var stateMachine = new IteratorStorey (iterator);
4011 state_machine = stateMachine;
4012 iterator.SetStateMachine (stateMachine);
4014 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4015 tlb.Original = this;
4016 tlb.state_machine = stateMachine;
4017 tlb.AddStatement (new Return (iterator, iterator.Location));
4021 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4023 for (int i = 0; i < parameters.Count; i++) {
4024 Parameter p = parameters[i];
4025 Parameter.Modifier mod = p.ModFlags;
4026 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4027 host.Compiler.Report.Error (1988, p.Location,
4028 "Async methods cannot have ref or out parameters");
4032 if (p is ArglistParameter) {
4033 host.Compiler.Report.Error (4006, p.Location,
4034 "__arglist is not allowed in parameter list of async methods");
4038 if (parameters.Types[i].IsPointer) {
4039 host.Compiler.Report.Error (4005, p.Location,
4040 "Async methods cannot have unsafe parameters");
4046 host.Compiler.Report.Warning (1998, 1, loc,
4047 "Async block lacks `await' operator and will run synchronously");
4050 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4051 var initializer = new AsyncInitializer (this, host, block_type);
4052 initializer.Type = block_type;
4053 initializer.DelegateType = delegateType;
4055 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4057 state_machine = stateMachine;
4058 initializer.SetStateMachine (stateMachine);
4060 const Flags flags = Flags.CompilerGenerated;
4062 var b = this is ToplevelBlock ?
4063 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4064 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4067 b.state_machine = stateMachine;
4068 b.AddStatement (new AsyncInitializerStatement (initializer));
4076 public class ToplevelBlock : ParametersBlock
4078 LocalVariable this_variable;
4079 CompilerContext compiler;
4080 Dictionary<string, object> names;
4082 List<ExplicitBlock> this_references;
4084 public ToplevelBlock (CompilerContext ctx, Location loc)
4085 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4089 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4090 : base (parameters, start)
4092 this.compiler = ctx;
4096 ProcessParameters ();
4100 // Recreates a top level block from parameters block. Used for
4101 // compiler generated methods where the original block comes from
4102 // explicit child block. This works for already resolved blocks
4103 // only to ensure we resolve them in the correct flow order
4105 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4106 : base (source, parameters)
4108 this.compiler = source.TopBlock.compiler;
4112 public bool IsIterator {
4114 return (flags & Flags.Iterator) != 0;
4117 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4121 public Report Report {
4123 return compiler.Report;
4128 // Used by anonymous blocks to track references of `this' variable
4130 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4132 return this_references;
4137 // Returns the "this" instance variable of this block.
4138 // See AddThisVariable() for more information.
4140 public LocalVariable ThisVariable {
4142 return this_variable;
4146 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4149 names = new Dictionary<string, object> ();
4152 if (!names.TryGetValue (name, out value)) {
4153 names.Add (name, li);
4157 INamedBlockVariable existing = value as INamedBlockVariable;
4158 List<INamedBlockVariable> existing_list;
4159 if (existing != null) {
4160 existing_list = new List<INamedBlockVariable> ();
4161 existing_list.Add (existing);
4162 names[name] = existing_list;
4164 existing_list = (List<INamedBlockVariable>) value;
4168 // A collision checking between local names
4170 var variable_block = li.Block.Explicit;
4171 for (int i = 0; i < existing_list.Count; ++i) {
4172 existing = existing_list[i];
4173 Block b = existing.Block.Explicit;
4175 // Collision at same level
4176 if (variable_block == b) {
4177 li.Block.Error_AlreadyDeclared (name, li);
4181 // Collision with parent
4182 Block parent = variable_block;
4183 while ((parent = parent.Parent) != null) {
4185 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4186 i = existing_list.Count;
4191 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4192 // Collision with children
4193 while ((b = b.Parent) != null) {
4194 if (variable_block == b) {
4195 li.Block.Error_AlreadyDeclared (name, li, "child");
4196 i = existing_list.Count;
4203 existing_list.Add (li);
4206 public void AddLabel (string name, LabeledStatement label)
4209 labels = new Dictionary<string, object> ();
4212 if (!labels.TryGetValue (name, out value)) {
4213 labels.Add (name, label);
4217 LabeledStatement existing = value as LabeledStatement;
4218 List<LabeledStatement> existing_list;
4219 if (existing != null) {
4220 existing_list = new List<LabeledStatement> ();
4221 existing_list.Add (existing);
4222 labels[name] = existing_list;
4224 existing_list = (List<LabeledStatement>) value;
4228 // A collision checking between labels
4230 for (int i = 0; i < existing_list.Count; ++i) {
4231 existing = existing_list[i];
4232 Block b = existing.Block;
4234 // Collision at same level
4235 if (label.Block == b) {
4236 Report.SymbolRelatedToPreviousError (existing.loc, name);
4237 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4241 // Collision with parent
4243 while ((b = b.Parent) != null) {
4244 if (existing.Block == b) {
4245 Report.Error (158, label.loc,
4246 "The label `{0}' shadows another label by the same name in a contained scope", name);
4247 i = existing_list.Count;
4252 // Collision with with children
4254 while ((b = b.Parent) != null) {
4255 if (label.Block == b) {
4256 Report.Error (158, label.loc,
4257 "The label `{0}' shadows another label by the same name in a contained scope", name);
4258 i = existing_list.Count;
4264 existing_list.Add (label);
4267 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4269 if (this_references == null)
4270 this_references = new List<ExplicitBlock> ();
4272 if (!this_references.Contains (block))
4273 this_references.Add (block);
4276 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4278 this_references.Remove (block);
4282 // Creates an arguments set from all parameters, useful for method proxy calls
4284 public Arguments GetAllParametersArguments ()
4286 int count = parameters.Count;
4287 Arguments args = new Arguments (count);
4288 for (int i = 0; i < count; ++i) {
4289 var pi = parameter_info[i];
4290 var arg_expr = GetParameterReference (i, pi.Location);
4292 Argument.AType atype_modifier;
4293 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4294 case Parameter.Modifier.REF:
4295 atype_modifier = Argument.AType.Ref;
4297 case Parameter.Modifier.OUT:
4298 atype_modifier = Argument.AType.Out;
4305 args.Add (new Argument (arg_expr, atype_modifier));
4312 // Lookup inside a block, the returned value can represent 3 states
4314 // true+variable: A local name was found and it's valid
4315 // false+variable: A local name was found in a child block only
4316 // false+null: No local name was found
4318 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4324 if (!names.TryGetValue (name, out value))
4327 variable = value as INamedBlockVariable;
4329 if (variable != null) {
4331 if (variable.Block == b.Original)
4335 } while (b != null);
4343 } while (b != null);
4345 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4346 for (int i = 0; i < list.Count; ++i) {
4349 if (variable.Block == b.Original)
4353 } while (b != null);
4361 } while (b != null);
4371 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4373 if (block.names != null) {
4374 foreach (var n in block.names) {
4375 var variable = n.Value as INamedBlockVariable;
4376 if (variable != null) {
4377 if (variable.Block.ParametersBlock == pb)
4378 AddLocalName (n.Key, variable, false);
4382 foreach (var v in (List<INamedBlockVariable>) n.Value)
4383 if (v.Block.ParametersBlock == pb)
4384 AddLocalName (n.Key, v, false);
4390 // This is used by non-static `struct' constructors which do not have an
4391 // initializer - in this case, the constructor must initialize all of the
4392 // struct's fields. To do this, we add a "this" variable and use the flow
4393 // analysis code to ensure that it's been fully initialized before control
4394 // leaves the constructor.
4396 public void AddThisVariable (BlockContext bc)
4398 if (this_variable != null)
4399 throw new InternalErrorException (StartLocation.ToString ());
4401 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4402 this_variable.Type = bc.CurrentType;
4403 this_variable.PrepareAssignmentAnalysis (bc);
4406 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4409 // If we're a non-static struct constructor which doesn't have an
4410 // initializer, then we must initialize all of the struct's fields.
4412 if (this_variable != null)
4413 this_variable.IsThisAssigned (fc, this);
4415 base.CheckControlExit (fc, dat);
4418 public HashSet<LocalVariable> GetUndeclaredVariables ()
4423 HashSet<LocalVariable> variables = null;
4425 foreach (var entry in names) {
4426 var complex = entry.Value as List<INamedBlockVariable>;
4427 if (complex != null) {
4428 foreach (var centry in complex) {
4429 if (IsUndeclaredVariable (centry)) {
4430 if (variables == null)
4431 variables = new HashSet<LocalVariable> ();
4433 variables.Add ((LocalVariable) centry);
4436 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4437 if (variables == null)
4438 variables = new HashSet<LocalVariable> ();
4440 variables.Add ((LocalVariable)entry.Value);
4447 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4449 var lv = namedBlockVariable as LocalVariable;
4450 return lv != null && !lv.IsDeclared;
4453 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4458 foreach (var entry in names) {
4459 var complex = entry.Value as List<INamedBlockVariable>;
4460 if (complex != null) {
4461 foreach (var centry in complex) {
4462 var lv = centry as LocalVariable;
4463 if (lv != null && undeclaredVariables.Contains (lv)) {
4468 var lv = entry.Value as LocalVariable;
4469 if (lv != null && undeclaredVariables.Contains (lv))
4475 public override void Emit (EmitContext ec)
4477 if (Report.Errors > 0)
4481 if (IsCompilerGenerated) {
4482 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4490 // If `HasReturnLabel' is set, then we already emitted a
4491 // jump to the end of the method, so we must emit a `ret'
4494 // Unfortunately, System.Reflection.Emit automatically emits
4495 // a leave to the end of a finally block. This is a problem
4496 // if no code is following the try/finally block since we may
4497 // jump to a point after the end of the method.
4498 // As a workaround, we're always creating a return label in
4501 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4502 if (ec.HasReturnLabel)
4503 ec.MarkLabel (ec.ReturnLabel);
4505 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4506 ec.Mark (EndLocation);
4508 if (ec.ReturnType.Kind != MemberKind.Void)
4509 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4511 ec.Emit (OpCodes.Ret);
4514 } catch (Exception e) {
4515 throw new InternalErrorException (e, StartLocation);
4519 public bool Resolve (BlockContext bc, IMethodData md)
4524 var errors = bc.Report.Errors;
4528 if (bc.Report.Errors > errors)
4531 MarkReachable (new Reachability ());
4533 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4534 // TODO: var md = bc.CurrentMemberDefinition;
4535 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4538 if ((flags & Flags.NoFlowAnalysis) != 0)
4541 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4544 } catch (Exception e) {
4545 throw new InternalErrorException (e, StartLocation);
4552 public class SwitchLabel : Statement
4560 // if expr == null, then it is the default case.
4562 public SwitchLabel (Expression expr, Location l)
4568 public bool IsDefault {
4570 return label == null;
4574 public Expression Label {
4580 public Location Location {
4586 public Constant Converted {
4595 public bool PatternMatching { get; set; }
4597 public bool SectionStart { get; set; }
4599 public Label GetILLabel (EmitContext ec)
4601 if (il_label == null){
4602 il_label = ec.DefineLabel ();
4605 return il_label.Value;
4608 protected override void DoEmit (EmitContext ec)
4610 ec.MarkLabel (GetILLabel (ec));
4613 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4618 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4622 public override bool Resolve (BlockContext bc)
4624 if (ResolveAndReduce (bc))
4625 bc.Switch.RegisterLabel (bc, this);
4631 // Resolves the expression, reduces it to a literal if possible
4632 // and then converts it to the requested type.
4634 bool ResolveAndReduce (BlockContext bc)
4639 var switch_statement = bc.Switch;
4641 if (PatternMatching) {
4642 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4643 return label != null;
4646 var c = label.ResolveLabelConstant (bc);
4650 if (switch_statement.IsNullable && c is NullLiteral) {
4655 if (switch_statement.IsPatternMatching) {
4656 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4660 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4661 return converted != null;
4664 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4666 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4667 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4670 protected override void CloneTo (CloneContext clonectx, Statement target)
4672 var t = (SwitchLabel) target;
4674 t.label = label.Clone (clonectx);
4677 public override object Accept (StructuralVisitor visitor)
4679 return visitor.Visit (this);
4682 public string GetSignatureForError ()
4685 if (converted == null)
4688 label = converted.GetValueAsLiteral ();
4690 return string.Format ("case {0}:", label);
4694 public class Switch : LoopStatement
4696 // structure used to hold blocks of keys while calculating table switch
4697 sealed class LabelsRange : IComparable<LabelsRange>
4699 public readonly long min;
4701 public readonly List<long> label_values;
4703 public LabelsRange (long value)
4706 label_values = new List<long> ();
4707 label_values.Add (value);
4710 public LabelsRange (long min, long max, ICollection<long> values)
4714 this.label_values = new List<long> (values);
4719 return max - min + 1;
4723 public bool AddValue (long value)
4725 var gap = value - min + 1;
4726 // Ensure the range has > 50% occupancy
4727 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4731 label_values.Add (value);
4735 public int CompareTo (LabelsRange other)
4737 int nLength = label_values.Count;
4738 int nLengthOther = other.label_values.Count;
4739 if (nLengthOther == nLength)
4740 return (int) (other.min - min);
4742 return nLength - nLengthOther;
4746 sealed class DispatchStatement : Statement
4748 readonly Switch body;
4750 public DispatchStatement (Switch body)
4755 protected override void CloneTo (CloneContext clonectx, Statement target)
4757 throw new NotImplementedException ();
4760 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4765 protected override void DoEmit (EmitContext ec)
4767 body.EmitDispatch (ec);
4771 class MissingBreak : Statement
4773 readonly SwitchLabel label;
4775 public MissingBreak (SwitchLabel sl)
4781 public bool FallOut { get; set; }
4783 protected override void DoEmit (EmitContext ec)
4787 protected override void CloneTo (CloneContext clonectx, Statement target)
4791 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4794 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4795 label.GetSignatureForError ());
4797 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4798 label.GetSignatureForError ());
4804 public Expression Expr;
4807 // Mapping of all labels to their SwitchLabels
4809 Dictionary<long, SwitchLabel> labels;
4810 Dictionary<string, SwitchLabel> string_labels;
4811 List<SwitchLabel> case_labels;
4813 List<Tuple<GotoCase, Constant>> goto_cases;
4814 List<DefiniteAssignmentBitSet> end_reachable_das;
4817 /// The governing switch type
4819 public TypeSpec SwitchType;
4821 Expression new_expr;
4823 SwitchLabel case_null;
4824 SwitchLabel case_default;
4826 Label defaultLabel, nullLabel;
4827 VariableReference value;
4828 ExpressionStatement string_dictionary;
4829 FieldExpr switch_cache_field;
4830 ExplicitBlock block;
4834 // Nullable Types support
4836 Nullable.Unwrap unwrap;
4838 public Switch (Expression e, ExplicitBlock block, Location l)
4846 public SwitchLabel ActiveLabel { get; set; }
4848 public ExplicitBlock Block {
4854 public SwitchLabel DefaultLabel {
4856 return case_default;
4860 public bool IsNullable {
4862 return unwrap != null;
4866 public bool IsPatternMatching {
4868 return new_expr == null && SwitchType != null;
4872 public List<SwitchLabel> RegisteredLabels {
4878 public VariableReference ExpressionValue {
4885 // Determines the governing type for a switch. The returned
4886 // expression might be the expression from the switch, or an
4887 // expression that includes any potential conversions to
4889 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4891 switch (expr.Type.BuiltinType) {
4892 case BuiltinTypeSpec.Type.Byte:
4893 case BuiltinTypeSpec.Type.SByte:
4894 case BuiltinTypeSpec.Type.UShort:
4895 case BuiltinTypeSpec.Type.Short:
4896 case BuiltinTypeSpec.Type.UInt:
4897 case BuiltinTypeSpec.Type.Int:
4898 case BuiltinTypeSpec.Type.ULong:
4899 case BuiltinTypeSpec.Type.Long:
4900 case BuiltinTypeSpec.Type.Char:
4901 case BuiltinTypeSpec.Type.String:
4902 case BuiltinTypeSpec.Type.Bool:
4906 if (expr.Type.IsEnum)
4910 // Try to find a *user* defined implicit conversion.
4912 // If there is no implicit conversion, or if there are multiple
4913 // conversions, we have to report an error
4915 Expression converted = null;
4916 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4918 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4921 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4922 Convert.UserConversionRestriction.ProbingOnly;
4925 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4927 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4932 // Ignore over-worked ImplicitUserConversions that do
4933 // an implicit conversion in addition to the user conversion.
4935 var uc = e as UserCast;
4939 if (converted != null){
4940 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4949 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4951 var types = module.Compiler.BuiltinTypes;
4953 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4954 TypeSpec[] stypes = new[] {
4967 if (nullable != null) {
4969 Array.Resize (ref stypes, stypes.Length + 9);
4971 for (int i = 0; i < 9; ++i) {
4972 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4979 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4981 case_labels.Add (sl);
4984 if (case_default != null) {
4985 sl.Error_AlreadyOccurs (rc, case_default);
4993 if (sl.Converted == null)
4997 if (string_labels != null) {
4998 string string_value = sl.Converted.GetValue () as string;
4999 if (string_value == null)
5002 string_labels.Add (string_value, sl);
5004 if (sl.Converted.IsNull) {
5007 labels.Add (sl.Converted.GetValueAsLong (), sl);
5010 } catch (ArgumentException) {
5011 if (string_labels != null)
5012 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5014 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5019 // This method emits code for a lookup-based switch statement (non-string)
5020 // Basically it groups the cases into blocks that are at least half full,
5021 // and then spits out individual lookup opcodes for each block.
5022 // It emits the longest blocks first, and short blocks are just
5023 // handled with direct compares.
5025 void EmitTableSwitch (EmitContext ec, Expression val)
5027 if (labels != null && labels.Count > 0) {
5028 List<LabelsRange> ranges;
5029 if (string_labels != null) {
5030 // We have done all hard work for string already
5031 // setup single range only
5032 ranges = new List<LabelsRange> (1);
5033 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5035 var element_keys = new long[labels.Count];
5036 labels.Keys.CopyTo (element_keys, 0);
5037 Array.Sort (element_keys);
5040 // Build possible ranges of switch labes to reduce number
5043 ranges = new List<LabelsRange> (element_keys.Length);
5044 var range = new LabelsRange (element_keys[0]);
5046 for (int i = 1; i < element_keys.Length; ++i) {
5047 var l = element_keys[i];
5048 if (range.AddValue (l))
5051 range = new LabelsRange (l);
5055 // sort the blocks so we can tackle the largest ones first
5059 Label lbl_default = defaultLabel;
5060 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5062 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5063 LabelsRange kb = ranges[range_index];
5064 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5066 // Optimize small ranges using simple equality check
5067 if (kb.Range <= 2) {
5068 foreach (var key in kb.label_values) {
5069 SwitchLabel sl = labels[key];
5070 if (sl == case_default || sl == case_null)
5073 if (sl.Converted.IsZeroInteger) {
5074 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5077 sl.Converted.Emit (ec);
5078 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5082 // TODO: if all the keys in the block are the same and there are
5083 // no gaps/defaults then just use a range-check.
5084 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5085 // TODO: optimize constant/I4 cases
5087 // check block range (could be > 2^31)
5089 ec.EmitLong (kb.min);
5090 ec.Emit (OpCodes.Blt, lbl_default);
5093 ec.EmitLong (kb.max);
5094 ec.Emit (OpCodes.Bgt, lbl_default);
5099 ec.EmitLong (kb.min);
5100 ec.Emit (OpCodes.Sub);
5103 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5107 int first = (int) kb.min;
5110 ec.Emit (OpCodes.Sub);
5111 } else if (first < 0) {
5112 ec.EmitInt (-first);
5113 ec.Emit (OpCodes.Add);
5117 // first, build the list of labels for the switch
5119 long cJumps = kb.Range;
5120 Label[] switch_labels = new Label[cJumps];
5121 for (int iJump = 0; iJump < cJumps; iJump++) {
5122 var key = kb.label_values[iKey];
5123 if (key == kb.min + iJump) {
5124 switch_labels[iJump] = labels[key].GetILLabel (ec);
5127 switch_labels[iJump] = lbl_default;
5131 // emit the switch opcode
5132 ec.Emit (OpCodes.Switch, switch_labels);
5135 // mark the default for this block
5136 if (range_index != 0)
5137 ec.MarkLabel (lbl_default);
5140 // the last default just goes to the end
5141 if (ranges.Count > 0)
5142 ec.Emit (OpCodes.Br, lbl_default);
5146 public SwitchLabel FindLabel (Constant value)
5148 SwitchLabel sl = null;
5150 if (string_labels != null) {
5151 string s = value.GetValue () as string;
5153 if (case_null != null)
5155 else if (case_default != null)
5158 string_labels.TryGetValue (s, out sl);
5161 if (value is NullLiteral) {
5164 labels.TryGetValue (value.GetValueAsLong (), out sl);
5168 if (sl == null || sl.SectionStart)
5172 // Always return section start, it simplifies handling of switch labels
5174 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5175 var cs = case_labels [idx];
5176 if (cs.SectionStart)
5181 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5183 Expr.FlowAnalysis (fc);
5185 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5186 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5187 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5189 block.FlowAnalysis (fc);
5191 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5193 if (end_reachable_das != null) {
5194 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5195 InitialDefinitiveAssignment |= sections_das;
5196 end_reachable_das = null;
5199 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5201 return case_default != null && !end_reachable;
5204 public override bool Resolve (BlockContext ec)
5206 Expr = Expr.Resolve (ec);
5211 // LAMESPEC: User conversion from non-nullable governing type has a priority
5213 new_expr = SwitchGoverningType (ec, Expr, false);
5215 if (new_expr == null) {
5216 if (Expr.Type.IsNullableType) {
5217 unwrap = Nullable.Unwrap.Create (Expr, false);
5222 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5223 // involving nullable Expr and nullable governing type is
5225 new_expr = SwitchGoverningType (ec, unwrap, true);
5229 Expression switch_expr;
5230 if (new_expr == null) {
5231 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5232 if (Expr.Type != InternalType.ErrorType) {
5233 ec.Report.Error (151, loc,
5234 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5235 Expr.Type.GetSignatureForError ());
5242 SwitchType = Expr.Type;
5244 switch_expr = new_expr;
5245 SwitchType = new_expr.Type;
5246 if (SwitchType.IsNullableType) {
5247 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5248 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5251 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5252 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5256 if (block.Statements.Count == 0)
5259 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5260 string_labels = new Dictionary<string, SwitchLabel> ();
5262 labels = new Dictionary<long, SwitchLabel> ();
5266 var constant = switch_expr as Constant;
5269 // Don't need extra variable for constant switch or switch with
5270 // only default case
5272 if (constant == null) {
5274 // Store switch expression for comparison purposes
5276 value = switch_expr as VariableReference;
5277 if (value == null && !HasOnlyDefaultSection ()) {
5278 var current_block = ec.CurrentBlock;
5279 ec.CurrentBlock = Block;
5280 // Create temporary variable inside switch scope
5281 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5283 ec.CurrentBlock = current_block;
5287 case_labels = new List<SwitchLabel> ();
5289 Switch old_switch = ec.Switch;
5291 var parent_los = ec.EnclosingLoopOrSwitch;
5292 ec.EnclosingLoopOrSwitch = this;
5294 var ok = Statement.Resolve (ec);
5296 ec.EnclosingLoopOrSwitch = parent_los;
5297 ec.Switch = old_switch;
5300 // Check if all goto cases are valid. Needs to be done after switch
5301 // is resolved because goto can jump forward in the scope.
5303 if (goto_cases != null) {
5304 foreach (var gc in goto_cases) {
5305 if (gc.Item1 == null) {
5306 if (DefaultLabel == null) {
5307 Goto.Error_UnknownLabel (ec, "default", loc);
5313 var sl = FindLabel (gc.Item2);
5315 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5317 gc.Item1.Label = sl;
5325 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5326 ResolveStringSwitchMap (ec);
5330 // Anonymous storey initialization has to happen before
5331 // any generated switch dispatch
5333 block.InsertStatement (0, new DispatchStatement (this));
5338 bool HasOnlyDefaultSection ()
5340 for (int i = 0; i < block.Statements.Count; ++i) {
5341 var s = block.Statements[i] as SwitchLabel;
5343 if (s == null || s.IsDefault)
5352 public override Reachability MarkReachable (Reachability rc)
5354 if (rc.IsUnreachable)
5357 base.MarkReachable (rc);
5359 block.MarkReachableScope (rc);
5361 if (block.Statements.Count == 0)
5364 SwitchLabel constant_label = null;
5365 var constant = new_expr as Constant;
5367 if (constant != null) {
5368 constant_label = FindLabel (constant) ?? case_default;
5369 if (constant_label == null) {
5370 block.Statements.RemoveAt (0);
5375 var section_rc = new Reachability ();
5376 SwitchLabel prev_label = null;
5378 for (int i = 0; i < block.Statements.Count; ++i) {
5379 var s = block.Statements[i];
5380 var sl = s as SwitchLabel;
5382 if (sl != null && sl.SectionStart) {
5384 // Section is marked already via goto case
5386 if (!sl.IsUnreachable) {
5387 section_rc = new Reachability ();
5391 if (section_rc.IsUnreachable) {
5393 // Common case. Previous label section end is unreachable as
5394 // it ends with break, return, etc. For next section revert
5395 // to reachable again unless we have constant switch block
5397 section_rc = constant_label != null && constant_label != sl ?
5398 Reachability.CreateUnreachable () :
5399 new Reachability ();
5400 } else if (prev_label != null) {
5402 // Error case as control cannot fall through from one case label
5404 sl.SectionStart = false;
5405 s = new MissingBreak (prev_label);
5406 s.MarkReachable (rc);
5407 block.Statements.Insert (i - 1, s);
5409 } else if (constant_label != null && constant_label != sl) {
5411 // Special case for the first unreachable label in constant
5414 section_rc = Reachability.CreateUnreachable ();
5420 section_rc = s.MarkReachable (section_rc);
5423 if (!section_rc.IsUnreachable && prev_label != null) {
5424 prev_label.SectionStart = false;
5425 var s = new MissingBreak (prev_label) {
5429 s.MarkReachable (rc);
5430 block.Statements.Add (s);
5434 // Reachability can affect parent only when all possible paths are handled but
5435 // we still need to run reachability check on switch body to check for fall-through
5437 if (case_default == null && constant_label == null)
5441 // We have at least one local exit from the switch
5446 return Reachability.CreateUnreachable ();
5449 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5451 if (goto_cases == null)
5452 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5454 goto_cases.Add (Tuple.Create (gotoCase, value));
5458 // Converts string switch into string hashtable
5460 void ResolveStringSwitchMap (ResolveContext ec)
5462 FullNamedExpression string_dictionary_type;
5463 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5464 string_dictionary_type = new TypeExpression (
5465 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5466 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5468 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5469 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5471 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5475 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5476 Field field = new Field (ctype, string_dictionary_type,
5477 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5478 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5479 if (!field.Define ())
5481 ctype.AddField (field);
5483 var init = new List<Expression> ();
5485 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5486 string value = null;
5488 foreach (SwitchLabel sl in case_labels) {
5490 if (sl.SectionStart)
5491 labels.Add (++counter, sl);
5493 if (sl == case_default || sl == case_null)
5496 value = (string) sl.Converted.GetValue ();
5497 var init_args = new List<Expression> (2);
5498 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5500 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5501 init_args.Add (sl.Converted);
5503 init.Add (new CollectionElementInitializer (init_args, loc));
5506 Arguments args = new Arguments (1);
5507 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5508 Expression initializer = new NewInitialize (string_dictionary_type, args,
5509 new CollectionOrObjectInitializers (init, loc), loc);
5511 switch_cache_field = new FieldExpr (field, loc);
5512 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5515 void DoEmitStringSwitch (EmitContext ec)
5517 Label l_initialized = ec.DefineLabel ();
5520 // Skip initialization when value is null
5522 value.EmitBranchable (ec, nullLabel, false);
5525 // Check if string dictionary is initialized and initialize
5527 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5528 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5529 string_dictionary.EmitStatement (ec);
5531 ec.MarkLabel (l_initialized);
5533 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5535 ResolveContext rc = new ResolveContext (ec.MemberContext);
5537 if (switch_cache_field.Type.IsGeneric) {
5538 Arguments get_value_args = new Arguments (2);
5539 get_value_args.Add (new Argument (value));
5540 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5541 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5542 if (get_item == null)
5546 // A value was not found, go to default case
5548 get_item.EmitBranchable (ec, defaultLabel, false);
5550 Arguments get_value_args = new Arguments (1);
5551 get_value_args.Add (new Argument (value));
5553 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5554 if (get_item == null)
5557 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5558 get_item_object.EmitAssign (ec, get_item, true, false);
5559 ec.Emit (OpCodes.Brfalse, defaultLabel);
5561 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5562 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5564 get_item_int.EmitStatement (ec);
5565 get_item_object.Release (ec);
5568 EmitTableSwitch (ec, string_switch_variable);
5569 string_switch_variable.Release (ec);
5573 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5575 void EmitShortSwitch (EmitContext ec)
5577 MethodSpec equal_method = null;
5578 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5579 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5582 if (equal_method != null) {
5583 value.EmitBranchable (ec, nullLabel, false);
5586 for (int i = 0; i < case_labels.Count; ++i) {
5587 var label = case_labels [i];
5588 if (label == case_default || label == case_null)
5591 var constant = label.Converted;
5593 if (constant == null) {
5594 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5598 if (equal_method != null) {
5602 var call = new CallEmitter ();
5603 call.EmitPredefined (ec, equal_method, new Arguments (0));
5604 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5608 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5609 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5615 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5618 ec.Emit (OpCodes.Br, defaultLabel);
5621 void EmitDispatch (EmitContext ec)
5623 if (IsPatternMatching) {
5624 EmitShortSwitch (ec);
5628 if (value == null) {
5630 // Constant switch, we've already done the work if there is only 1 label
5634 foreach (var sl in case_labels) {
5635 if (sl.IsUnreachable)
5638 if (reachable++ > 0) {
5639 var constant = (Constant) new_expr;
5640 var constant_label = FindLabel (constant) ?? case_default;
5642 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5650 if (string_dictionary != null) {
5651 DoEmitStringSwitch (ec);
5652 } else if (case_labels.Count < 4 || string_labels != null) {
5653 EmitShortSwitch (ec);
5655 EmitTableSwitch (ec, value);
5659 protected override void DoEmit (EmitContext ec)
5662 // Setup the codegen context
5664 Label old_end = ec.LoopEnd;
5665 Switch old_switch = ec.Switch;
5667 ec.LoopEnd = ec.DefineLabel ();
5670 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5671 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5673 if (value != null) {
5676 var switch_expr = new_expr ?? Expr;
5678 unwrap.EmitCheck (ec);
5679 ec.Emit (OpCodes.Brfalse, nullLabel);
5680 value.EmitAssign (ec, switch_expr, false, false);
5681 } else if (switch_expr != value) {
5682 value.EmitAssign (ec, switch_expr, false, false);
5687 // Next statement is compiler generated we don't need extra
5688 // nop when we can use the statement for sequence point
5690 ec.Mark (block.StartLocation);
5691 block.IsCompilerGenerated = true;
5693 new_expr.EmitSideEffect (ec);
5698 // Restore context state.
5699 ec.MarkLabel (ec.LoopEnd);
5702 // Restore the previous context
5704 ec.LoopEnd = old_end;
5705 ec.Switch = old_switch;
5708 protected override void CloneTo (CloneContext clonectx, Statement t)
5710 Switch target = (Switch) t;
5712 target.Expr = Expr.Clone (clonectx);
5713 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5716 public override object Accept (StructuralVisitor visitor)
5718 return visitor.Visit (this);
5721 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5723 if (case_default == null && !(new_expr is Constant))
5726 if (end_reachable_das == null)
5727 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5729 end_reachable_das.Add (fc.DefiniteAssignment);
5732 public override void SetEndReachable ()
5734 end_reachable = true;
5738 // A place where execution can restart in a state machine
5739 public abstract class ResumableStatement : Statement
5742 protected Label resume_point;
5744 public Label PrepareForEmit (EmitContext ec)
5748 resume_point = ec.DefineLabel ();
5750 return resume_point;
5753 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5758 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5763 public abstract class TryFinallyBlock : ExceptionStatement
5765 protected Statement stmt;
5766 Label dispose_try_block;
5767 bool prepared_for_dispose, emitted_dispose;
5768 Method finally_host;
5770 protected TryFinallyBlock (Statement stmt, Location loc)
5778 public Statement Statement {
5786 protected abstract void EmitTryBody (EmitContext ec);
5787 public abstract void EmitFinallyBody (EmitContext ec);
5789 public override Label PrepareForDispose (EmitContext ec, Label end)
5791 if (!prepared_for_dispose) {
5792 prepared_for_dispose = true;
5793 dispose_try_block = ec.DefineLabel ();
5795 return dispose_try_block;
5798 protected sealed override void DoEmit (EmitContext ec)
5800 EmitTryBodyPrepare (ec);
5803 bool beginFinally = EmitBeginFinallyBlock (ec);
5805 Label start_finally = ec.DefineLabel ();
5806 if (resume_points != null && beginFinally) {
5807 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5809 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5810 ec.Emit (OpCodes.Brfalse_S, start_finally);
5811 ec.Emit (OpCodes.Endfinally);
5814 ec.MarkLabel (start_finally);
5816 if (finally_host != null) {
5817 finally_host.Define ();
5818 finally_host.PrepareEmit ();
5819 finally_host.Emit ();
5821 // Now it's safe to add, to close it properly and emit sequence points
5822 finally_host.Parent.AddMember (finally_host);
5824 var ce = new CallEmitter ();
5825 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5826 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5828 EmitFinallyBody (ec);
5832 ec.EndExceptionBlock ();
5835 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5837 if (emitted_dispose)
5840 emitted_dispose = true;
5842 Label end_of_try = ec.DefineLabel ();
5844 // Ensure that the only way we can get into this code is through a dispatcher
5845 if (have_dispatcher)
5846 ec.Emit (OpCodes.Br, end);
5848 ec.BeginExceptionBlock ();
5850 ec.MarkLabel (dispose_try_block);
5852 Label[] labels = null;
5853 for (int i = 0; i < resume_points.Count; ++i) {
5854 ResumableStatement s = resume_points[i];
5855 Label ret = s.PrepareForDispose (ec, end_of_try);
5856 if (ret.Equals (end_of_try) && labels == null)
5858 if (labels == null) {
5859 labels = new Label[resume_points.Count];
5860 for (int j = 0; j < i; ++j)
5861 labels[j] = end_of_try;
5866 if (labels != null) {
5868 for (j = 1; j < labels.Length; ++j)
5869 if (!labels[0].Equals (labels[j]))
5871 bool emit_dispatcher = j < labels.Length;
5873 if (emit_dispatcher) {
5874 ec.Emit (OpCodes.Ldloc, pc);
5875 ec.EmitInt (first_resume_pc);
5876 ec.Emit (OpCodes.Sub);
5877 ec.Emit (OpCodes.Switch, labels);
5880 foreach (ResumableStatement s in resume_points)
5881 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5884 ec.MarkLabel (end_of_try);
5886 ec.BeginFinallyBlock ();
5888 if (finally_host != null) {
5889 var ce = new CallEmitter ();
5890 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5891 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5893 EmitFinallyBody (ec);
5896 ec.EndExceptionBlock ();
5899 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5901 var res = stmt.FlowAnalysis (fc);
5902 parent_try_block = null;
5906 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5908 ec.BeginFinallyBlock ();
5912 public override Reachability MarkReachable (Reachability rc)
5914 base.MarkReachable (rc);
5915 return Statement.MarkReachable (rc);
5918 public override bool Resolve (BlockContext bc)
5922 parent_try_block = bc.CurrentTryBlock;
5923 bc.CurrentTryBlock = this;
5925 if (stmt is TryCatch) {
5926 ok = stmt.Resolve (bc);
5928 using (bc.Set (ResolveContext.Options.TryScope)) {
5929 ok = stmt.Resolve (bc);
5933 bc.CurrentTryBlock = parent_try_block;
5936 // Finally block inside iterator is called from MoveNext and
5937 // Dispose methods that means we need to lift the block into
5938 // newly created host method to emit the body only once. The
5939 // original block then simply calls the newly generated method.
5941 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5942 var b = stmt as Block;
5943 if (b != null && b.Explicit.HasYield) {
5944 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5948 return base.Resolve (bc) && ok;
5953 // Base class for blocks using exception handling
5955 public abstract class ExceptionStatement : ResumableStatement
5957 protected List<ResumableStatement> resume_points;
5958 protected int first_resume_pc;
5959 protected ExceptionStatement parent_try_block;
5960 protected int first_catch_resume_pc = -1;
5962 protected ExceptionStatement (Location loc)
5967 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5969 StateMachineInitializer state_machine = null;
5970 if (resume_points != null) {
5971 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5973 ec.EmitInt ((int) IteratorStorey.State.Running);
5974 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5978 // The resume points in catch section when this is try-catch-finally
5980 if (IsRewrittenTryCatchFinally ()) {
5981 ec.BeginExceptionBlock ();
5983 if (first_catch_resume_pc >= 0) {
5985 ec.MarkLabel (resume_point);
5987 // For normal control flow, we want to fall-through the Switch
5988 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5989 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5990 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
5991 ec.Emit (OpCodes.Sub);
5993 var labels = new Label [resume_points.Count - first_catch_resume_pc];
5994 for (int i = 0; i < labels.Length; ++i)
5995 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
5996 ec.Emit (OpCodes.Switch, labels);
6000 ec.BeginExceptionBlock ();
6003 // The resume points for try section
6005 if (resume_points != null && first_catch_resume_pc != 0) {
6006 if (first_catch_resume_pc < 0)
6007 ec.MarkLabel (resume_point);
6009 // For normal control flow, we want to fall-through the Switch
6010 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6011 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6012 ec.EmitInt (first_resume_pc);
6013 ec.Emit (OpCodes.Sub);
6015 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6016 for (int i = 0; i < labels.Length; ++i)
6017 labels[i] = resume_points[i].PrepareForEmit (ec);
6018 ec.Emit (OpCodes.Switch, labels);
6022 bool IsRewrittenTryCatchFinally ()
6024 var tf = this as TryFinally;
6028 var tc = tf.Statement as TryCatch;
6032 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6035 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6037 if (parent_try_block != null) {
6038 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6040 pc = stateMachine.AddResumePoint (this);
6043 if (resume_points == null) {
6044 resume_points = new List<ResumableStatement> ();
6045 first_resume_pc = pc;
6048 if (pc != first_resume_pc + resume_points.Count)
6049 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6051 var tf = this as TryFinally;
6052 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6053 first_catch_resume_pc = resume_points.Count;
6056 resume_points.Add (stmt);
6061 public class Lock : TryFinallyBlock
6064 TemporaryVariableReference expr_copy;
6065 TemporaryVariableReference lock_taken;
6067 public Lock (Expression expr, Statement stmt, Location loc)
6073 public Expression Expr {
6079 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6081 expr.FlowAnalysis (fc);
6082 return base.DoFlowAnalysis (fc);
6085 public override bool Resolve (BlockContext ec)
6087 expr = expr.Resolve (ec);
6091 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6092 ec.Report.Error (185, loc,
6093 "`{0}' is not a reference type as required by the lock statement",
6094 expr.Type.GetSignatureForError ());
6097 if (expr.Type.IsGenericParameter) {
6098 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6101 VariableReference lv = expr as VariableReference;
6104 locked = lv.IsLockedByStatement;
6105 lv.IsLockedByStatement = true;
6112 // Have to keep original lock value around to unlock same location
6113 // in the case of original value has changed or is null
6115 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6116 expr_copy.Resolve (ec);
6119 // Ensure Monitor methods are available
6121 if (ResolvePredefinedMethods (ec) > 1) {
6122 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6123 lock_taken.Resolve (ec);
6126 using (ec.Set (ResolveContext.Options.LockScope)) {
6131 lv.IsLockedByStatement = locked;
6137 protected override void EmitTryBodyPrepare (EmitContext ec)
6139 expr_copy.EmitAssign (ec, expr);
6141 if (lock_taken != null) {
6143 // Initialize ref variable
6145 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6148 // Monitor.Enter (expr_copy)
6150 expr_copy.Emit (ec);
6151 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6154 base.EmitTryBodyPrepare (ec);
6157 protected override void EmitTryBody (EmitContext ec)
6160 // Monitor.Enter (expr_copy, ref lock_taken)
6162 if (lock_taken != null) {
6163 expr_copy.Emit (ec);
6164 lock_taken.LocalInfo.CreateBuilder (ec);
6165 lock_taken.AddressOf (ec, AddressOp.Load);
6166 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6169 Statement.Emit (ec);
6172 public override void EmitFinallyBody (EmitContext ec)
6175 // if (lock_taken) Monitor.Exit (expr_copy)
6177 Label skip = ec.DefineLabel ();
6179 if (lock_taken != null) {
6180 lock_taken.Emit (ec);
6181 ec.Emit (OpCodes.Brfalse_S, skip);
6184 expr_copy.Emit (ec);
6185 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6187 ec.Emit (OpCodes.Call, m);
6189 ec.MarkLabel (skip);
6192 int ResolvePredefinedMethods (ResolveContext rc)
6194 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6195 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6199 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6203 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6207 protected override void CloneTo (CloneContext clonectx, Statement t)
6209 Lock target = (Lock) t;
6211 target.expr = expr.Clone (clonectx);
6212 target.stmt = Statement.Clone (clonectx);
6215 public override object Accept (StructuralVisitor visitor)
6217 return visitor.Visit (this);
6222 public class Unchecked : Statement {
6225 public Unchecked (Block b, Location loc)
6232 public override bool Resolve (BlockContext ec)
6234 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6235 return Block.Resolve (ec);
6238 protected override void DoEmit (EmitContext ec)
6240 using (ec.With (EmitContext.Options.CheckedScope, false))
6244 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6246 return Block.FlowAnalysis (fc);
6249 public override Reachability MarkReachable (Reachability rc)
6251 base.MarkReachable (rc);
6252 return Block.MarkReachable (rc);
6255 protected override void CloneTo (CloneContext clonectx, Statement t)
6257 Unchecked target = (Unchecked) t;
6259 target.Block = clonectx.LookupBlock (Block);
6262 public override object Accept (StructuralVisitor visitor)
6264 return visitor.Visit (this);
6268 public class Checked : Statement {
6271 public Checked (Block b, Location loc)
6274 b.Unchecked = false;
6278 public override bool Resolve (BlockContext ec)
6280 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6281 return Block.Resolve (ec);
6284 protected override void DoEmit (EmitContext ec)
6286 using (ec.With (EmitContext.Options.CheckedScope, true))
6290 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6292 return Block.FlowAnalysis (fc);
6295 public override Reachability MarkReachable (Reachability rc)
6297 base.MarkReachable (rc);
6298 return Block.MarkReachable (rc);
6301 protected override void CloneTo (CloneContext clonectx, Statement t)
6303 Checked target = (Checked) t;
6305 target.Block = clonectx.LookupBlock (Block);
6308 public override object Accept (StructuralVisitor visitor)
6310 return visitor.Visit (this);
6314 public class Unsafe : Statement {
6317 public Unsafe (Block b, Location loc)
6320 Block.Unsafe = true;
6324 public override bool Resolve (BlockContext ec)
6326 if (ec.CurrentIterator != null)
6327 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6329 using (ec.Set (ResolveContext.Options.UnsafeScope))
6330 return Block.Resolve (ec);
6333 protected override void DoEmit (EmitContext ec)
6338 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6340 return Block.FlowAnalysis (fc);
6343 public override Reachability MarkReachable (Reachability rc)
6345 base.MarkReachable (rc);
6346 return Block.MarkReachable (rc);
6349 protected override void CloneTo (CloneContext clonectx, Statement t)
6351 Unsafe target = (Unsafe) t;
6353 target.Block = clonectx.LookupBlock (Block);
6356 public override object Accept (StructuralVisitor visitor)
6358 return visitor.Visit (this);
6365 public class Fixed : Statement
6367 abstract class Emitter : ShimExpression
6369 protected LocalVariable vi;
6371 protected Emitter (Expression expr, LocalVariable li)
6377 public abstract void EmitExit (EmitContext ec);
6379 public override void FlowAnalysis (FlowAnalysisContext fc)
6381 expr.FlowAnalysis (fc);
6385 sealed class ExpressionEmitter : Emitter {
6386 public ExpressionEmitter (Expression converted, LocalVariable li)
6387 : base (converted, li)
6391 protected override Expression DoResolve (ResolveContext rc)
6393 throw new NotImplementedException ();
6396 public override void Emit (EmitContext ec) {
6398 // Store pointer in pinned location
6404 public override void EmitExit (EmitContext ec)
6407 ec.Emit (OpCodes.Conv_U);
6412 class StringEmitter : Emitter
6414 LocalVariable pinned_string;
6416 public StringEmitter (Expression expr, LocalVariable li)
6421 protected override Expression DoResolve (ResolveContext rc)
6423 pinned_string = new LocalVariable (vi.Block, "$pinned",
6424 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6426 pinned_string.Type = rc.BuiltinTypes.String;
6429 eclass = ExprClass.Variable;
6430 type = rc.BuiltinTypes.Int;
6434 public override void Emit (EmitContext ec)
6436 pinned_string.CreateBuilder (ec);
6439 pinned_string.EmitAssign (ec);
6441 // TODO: Should use Binary::Add
6442 pinned_string.Emit (ec);
6443 ec.Emit (OpCodes.Conv_I);
6445 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6449 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6450 //pe.InstanceExpression = pinned_string;
6451 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6453 ec.Emit (OpCodes.Add);
6457 public override void EmitExit (EmitContext ec)
6460 pinned_string.EmitAssign (ec);
6464 public class VariableDeclaration : BlockVariable
6466 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6471 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6473 if (!Variable.Type.IsPointer && li == Variable) {
6474 bc.Report.Error (209, TypeExpression.Location,
6475 "The type of locals declared in a fixed statement must be a pointer type");
6479 var res = initializer.Resolve (bc);
6486 var ac = res.Type as ArrayContainer;
6488 TypeSpec array_type = ac.Element;
6491 // Provided that array_type is unmanaged,
6493 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6496 Expression res_init;
6497 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6500 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6501 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6502 res = expr_variable.CreateReferenceExpression (bc, loc);
6506 // and T* is implicitly convertible to the
6507 // pointer type given in the fixed statement.
6509 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6511 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6512 if (converted == null)
6516 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6518 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6519 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6520 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6521 new NullLiteral (loc),
6524 converted = converted.Resolve (bc);
6526 return new ExpressionEmitter (converted, li);
6532 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6533 return new StringEmitter (res, li).Resolve (bc);
6536 // Case 3: fixed buffer
6537 if (res is FixedBufferPtr) {
6538 return new ExpressionEmitter (res, li);
6541 bool already_fixed = true;
6544 // Case 4: & object.
6546 Unary u = res as Unary;
6548 if (u.Oper == Unary.Operator.AddressOf) {
6549 IVariableReference vr = u.Expr as IVariableReference;
6550 if (vr == null || !vr.IsFixed) {
6551 already_fixed = false;
6554 } else if (initializer is Cast) {
6555 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6559 if (already_fixed) {
6560 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6563 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6564 return new ExpressionEmitter (res, li);
6569 VariableDeclaration decl;
6570 Statement statement;
6573 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6582 public Statement Statement {
6588 public BlockVariable Variables {
6596 public override bool Resolve (BlockContext bc)
6598 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6599 if (!decl.Resolve (bc))
6603 return statement.Resolve (bc);
6606 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6608 decl.FlowAnalysis (fc);
6609 return statement.FlowAnalysis (fc);
6612 protected override void DoEmit (EmitContext ec)
6614 decl.Variable.CreateBuilder (ec);
6615 decl.Initializer.Emit (ec);
6616 if (decl.Declarators != null) {
6617 foreach (var d in decl.Declarators) {
6618 d.Variable.CreateBuilder (ec);
6619 d.Initializer.Emit (ec);
6623 statement.Emit (ec);
6629 // Clear the pinned variable
6631 ((Emitter) decl.Initializer).EmitExit (ec);
6632 if (decl.Declarators != null) {
6633 foreach (var d in decl.Declarators) {
6634 ((Emitter)d.Initializer).EmitExit (ec);
6639 public override Reachability MarkReachable (Reachability rc)
6641 base.MarkReachable (rc);
6643 decl.MarkReachable (rc);
6645 rc = statement.MarkReachable (rc);
6647 // TODO: What if there is local exit?
6648 has_ret = rc.IsUnreachable;
6652 protected override void CloneTo (CloneContext clonectx, Statement t)
6654 Fixed target = (Fixed) t;
6656 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6657 target.statement = statement.Clone (clonectx);
6660 public override object Accept (StructuralVisitor visitor)
6662 return visitor.Visit (this);
6666 public class Catch : Statement
6668 class CatchVariableStore : Statement
6670 readonly Catch ctch;
6672 public CatchVariableStore (Catch ctch)
6677 protected override void CloneTo (CloneContext clonectx, Statement target)
6681 protected override void DoEmit (EmitContext ec)
6683 // Emits catch variable debug information inside correct block
6684 ctch.EmitCatchVariableStore (ec);
6687 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6693 class FilterStatement : Statement
6695 readonly Catch ctch;
6697 public FilterStatement (Catch ctch)
6702 protected override void CloneTo (CloneContext clonectx, Statement target)
6706 protected override void DoEmit (EmitContext ec)
6708 if (ctch.li != null) {
6709 if (ctch.hoisted_temp != null)
6710 ctch.hoisted_temp.Emit (ec);
6714 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6715 ec.Emit (OpCodes.Box, ctch.type);
6718 var expr_start = ec.DefineLabel ();
6719 var end = ec.DefineLabel ();
6721 ec.Emit (OpCodes.Brtrue_S, expr_start);
6723 ec.Emit (OpCodes.Br, end);
6724 ec.MarkLabel (expr_start);
6726 ctch.Filter.Emit (ec);
6729 ec.Emit (OpCodes.Endfilter);
6730 ec.BeginFilterHandler ();
6731 ec.Emit (OpCodes.Pop);
6734 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6736 ctch.Filter.FlowAnalysis (fc);
6740 public override bool Resolve (BlockContext bc)
6742 ctch.Filter = ctch.Filter.Resolve (bc);
6744 if (ctch.Filter != null) {
6745 if (ctch.Filter.ContainsEmitWithAwait ()) {
6746 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6749 var c = ctch.Filter as Constant;
6750 if (c != null && !c.IsDefaultValue) {
6751 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6759 ExplicitBlock block;
6761 FullNamedExpression type_expr;
6762 CompilerAssign assign;
6764 LocalTemporary hoisted_temp;
6766 public Catch (ExplicitBlock block, Location loc)
6774 public ExplicitBlock Block {
6780 public TypeSpec CatchType {
6786 public Expression Filter {
6790 public bool IsGeneral {
6792 return type_expr == null;
6796 public FullNamedExpression TypeExpression {
6805 public LocalVariable Variable {
6816 protected override void DoEmit (EmitContext ec)
6818 if (Filter != null) {
6819 ec.BeginExceptionFilterBlock ();
6820 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6822 if (Block.HasAwait) {
6823 Block.EmitScopeInitialization (ec);
6832 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6834 ec.BeginCatchBlock (CatchType);
6837 ec.Emit (OpCodes.Pop);
6839 if (Block.HasAwait) {
6841 EmitCatchVariableStore (ec);
6847 void EmitCatchVariableStore (EmitContext ec)
6849 li.CreateBuilder (ec);
6852 // For hoisted catch variable we have to use a temporary local variable
6853 // for captured variable initialization during storey setup because variable
6854 // needs to be on the stack after storey instance for stfld operation
6856 if (li.HoistedVariant != null) {
6857 hoisted_temp = new LocalTemporary (li.Type);
6858 hoisted_temp.Store (ec);
6860 // switch to assignment from temporary variable and not from top of the stack
6861 assign.UpdateSource (hoisted_temp);
6865 public override bool Resolve (BlockContext bc)
6867 using (bc.Set (ResolveContext.Options.CatchScope)) {
6868 if (type_expr == null) {
6869 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6870 if (!block.HasAwait || Filter != null)
6871 block.AddScopeStatement (new CatchVariableStore (this));
6873 Expression source = new EmptyExpression (li.Type);
6874 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6875 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6878 type = type_expr.ResolveAsType (bc);
6883 CreateExceptionVariable (type);
6885 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6886 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6887 } else if (li != null) {
6889 li.PrepareAssignmentAnalysis (bc);
6891 // source variable is at the top of the stack
6892 Expression source = new EmptyExpression (li.Type);
6893 if (li.Type.IsGenericParameter)
6894 source = new UnboxCast (source, li.Type);
6896 if (!block.HasAwait || Filter != null)
6897 block.AddScopeStatement (new CatchVariableStore (this));
6900 // Uses Location.Null to hide from symbol file
6902 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6903 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6907 if (Filter != null) {
6908 Block.AddScopeStatement (new FilterStatement (this));
6911 Block.SetCatchBlock ();
6912 return Block.Resolve (bc);
6916 bool CreateExceptionVariable (TypeSpec type)
6918 if (!Block.HasAwait)
6921 // TODO: Scan the block for rethrow expression
6922 //if (!Block.HasRethrow)
6925 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6929 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6931 if (li != null && !li.IsCompilerGenerated) {
6932 fc.SetVariableAssigned (li.VariableInfo, true);
6935 return block.FlowAnalysis (fc);
6938 public override Reachability MarkReachable (Reachability rc)
6940 base.MarkReachable (rc);
6942 var c = Filter as Constant;
6943 if (c != null && c.IsDefaultValue)
6944 return Reachability.CreateUnreachable ();
6946 return block.MarkReachable (rc);
6949 protected override void CloneTo (CloneContext clonectx, Statement t)
6951 Catch target = (Catch) t;
6953 if (type_expr != null)
6954 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6957 target.Filter = Filter.Clone (clonectx);
6959 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6963 public class TryFinally : TryFinallyBlock
6966 List<DefiniteAssignmentBitSet> try_exit_dat;
6967 List<Label> redirected_jumps;
6968 Label? start_fin_label;
6970 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6976 public ExplicitBlock FinallyBlock {
6982 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6984 if (try_exit_dat == null)
6985 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6987 try_exit_dat.Add (vector);
6990 public override bool Resolve (BlockContext bc)
6992 bool ok = base.Resolve (bc);
6994 fini.SetFinallyBlock ();
6995 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6996 ok &= fini.Resolve (bc);
7002 protected override void EmitTryBody (EmitContext ec)
7004 if (fini.HasAwait) {
7005 if (ec.TryFinallyUnwind == null)
7006 ec.TryFinallyUnwind = new List<TryFinally> ();
7008 ec.TryFinallyUnwind.Add (this);
7011 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7012 ec.EndExceptionBlock ();
7014 ec.TryFinallyUnwind.Remove (this);
7016 if (start_fin_label != null)
7017 ec.MarkLabel (start_fin_label.Value);
7025 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7030 return base.EmitBeginFinallyBlock (ec);
7033 public override void EmitFinallyBody (EmitContext ec)
7035 if (!fini.HasAwait) {
7041 // Emits catch block like
7043 // catch (object temp) {
7044 // this.exception_field = temp;
7047 var type = ec.BuiltinTypes.Object;
7048 ec.BeginCatchBlock (type);
7050 var temp = ec.GetTemporaryLocal (type);
7051 ec.Emit (OpCodes.Stloc, temp);
7053 var exception_field = ec.GetTemporaryField (type);
7054 exception_field.AutomaticallyReuse = false;
7056 ec.Emit (OpCodes.Ldloc, temp);
7057 exception_field.EmitAssignFromStack (ec);
7059 ec.EndExceptionBlock ();
7061 ec.FreeTemporaryLocal (temp, type);
7066 // Emits exception rethrow
7068 // if (this.exception_field != null)
7069 // throw this.exception_field;
7071 exception_field.Emit (ec);
7072 var skip_throw = ec.DefineLabel ();
7073 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7074 exception_field.Emit (ec);
7075 ec.Emit (OpCodes.Throw);
7076 ec.MarkLabel (skip_throw);
7078 exception_field.PrepareCleanup (ec);
7080 EmitUnwindFinallyTable (ec);
7083 bool IsParentBlock (Block block)
7085 for (Block b = fini; b != null; b = b.Parent) {
7093 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7096 if (labelBlock != null) {
7097 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7098 var fin = ec.TryFinallyUnwind [idx - 1];
7099 if (!fin.IsParentBlock (labelBlock))
7106 bool set_return_state = true;
7108 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7109 var fin = ec.TryFinallyUnwind [idx];
7110 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7113 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7114 set_return_state = false;
7116 if (fin.start_fin_label == null) {
7117 fin.start_fin_label = ec.DefineLabel ();
7120 label = fin.start_fin_label.Value;
7126 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7128 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7131 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7133 if (redirected_jumps == null) {
7134 redirected_jumps = new List<Label> ();
7136 // Add fallthrough label
7137 redirected_jumps.Add (ec.DefineLabel ());
7140 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7143 int index = redirected_jumps.IndexOf (label);
7145 redirected_jumps.Add (label);
7146 index = redirected_jumps.Count - 1;
7150 // Indicates we have captured exit jump
7152 if (setReturnState) {
7153 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7154 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7159 // Emits state table of jumps outside of try block and reload of return
7160 // value when try block returns value
7162 void EmitUnwindFinallyTable (EmitContext ec)
7164 if (redirected_jumps == null)
7167 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7168 initializer.HoistedReturnState.EmitLoad (ec);
7169 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7171 // Mark fallthrough label
7172 ec.MarkLabel (redirected_jumps [0]);
7175 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7177 var da = fc.BranchDefiniteAssignment ();
7179 var tf = fc.TryFinally;
7180 fc.TryFinally = this;
7182 var res_stmt = Statement.FlowAnalysis (fc);
7186 var try_da = fc.DefiniteAssignment;
7187 fc.DefiniteAssignment = da;
7189 var res_fin = fini.FlowAnalysis (fc);
7191 if (try_exit_dat != null) {
7193 // try block has global exit but we need to run definite assignment check
7194 // for parameter block out parameter after finally block because it's always
7195 // executed before exit
7197 foreach (var try_da_part in try_exit_dat)
7198 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7200 try_exit_dat = null;
7203 fc.DefiniteAssignment |= try_da;
7204 return res_stmt | res_fin;
7207 public override Reachability MarkReachable (Reachability rc)
7210 // Mark finally block first for any exit statement in try block
7211 // to know whether the code which follows finally is reachable
7213 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7216 protected override void CloneTo (CloneContext clonectx, Statement t)
7218 TryFinally target = (TryFinally) t;
7220 target.stmt = stmt.Clone (clonectx);
7222 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7225 public override object Accept (StructuralVisitor visitor)
7227 return visitor.Visit (this);
7231 public class TryCatch : ExceptionStatement
7234 List<Catch> clauses;
7235 readonly bool inside_try_finally;
7236 List<Catch> catch_sm;
7238 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7242 this.clauses = catch_clauses;
7243 this.inside_try_finally = inside_try_finally;
7246 public List<Catch> Clauses {
7252 public bool HasClauseWithAwait {
7254 return catch_sm != null;
7258 public bool IsTryCatchFinally {
7260 return inside_try_finally;
7264 public override bool Resolve (BlockContext bc)
7268 using (bc.Set (ResolveContext.Options.TryScope)) {
7270 parent_try_block = bc.CurrentTryBlock;
7272 if (IsTryCatchFinally) {
7273 ok = Block.Resolve (bc);
7275 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7276 bc.CurrentTryBlock = this;
7277 ok = Block.Resolve (bc);
7278 bc.CurrentTryBlock = parent_try_block;
7283 var prev_catch = bc.CurrentTryCatch;
7284 bc.CurrentTryCatch = this;
7286 for (int i = 0; i < clauses.Count; ++i) {
7289 ok &= c.Resolve (bc);
7291 if (c.Block.HasAwait) {
7292 if (catch_sm == null)
7293 catch_sm = new List<Catch> ();
7298 if (c.Filter != null)
7301 TypeSpec resolved_type = c.CatchType;
7302 if (resolved_type == null)
7305 for (int ii = 0; ii < clauses.Count; ++ii) {
7309 if (clauses[ii].Filter != null)
7312 if (clauses[ii].IsGeneral) {
7313 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7316 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7319 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7322 bc.Report.Warning (1058, 1, c.loc,
7323 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7331 var ct = clauses[ii].CatchType;
7335 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7336 bc.Report.Error (160, c.loc,
7337 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7338 ct.GetSignatureForError ());
7344 bc.CurrentTryCatch = prev_catch;
7346 return base.Resolve (bc) && ok;
7349 protected sealed override void DoEmit (EmitContext ec)
7351 if (!inside_try_finally)
7352 EmitTryBodyPrepare (ec);
7356 LocalBuilder state_variable = null;
7357 foreach (Catch c in clauses) {
7360 if (catch_sm != null) {
7361 if (state_variable == null) {
7363 // Cannot reuse temp variable because non-catch path assumes the value is 0
7364 // which may not be true for reused local variable
7366 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7369 var index = catch_sm.IndexOf (c);
7373 ec.EmitInt (index + 1);
7374 ec.Emit (OpCodes.Stloc, state_variable);
7378 if (state_variable == null) {
7379 if (!inside_try_finally)
7380 ec.EndExceptionBlock ();
7382 ec.EndExceptionBlock ();
7384 ec.Emit (OpCodes.Ldloc, state_variable);
7386 var labels = new Label [catch_sm.Count + 1];
7387 for (int i = 0; i < labels.Length; ++i) {
7388 labels [i] = ec.DefineLabel ();
7391 var end = ec.DefineLabel ();
7392 ec.Emit (OpCodes.Switch, labels);
7394 // 0 value is default label
7395 ec.MarkLabel (labels [0]);
7396 ec.Emit (OpCodes.Br, end);
7398 var atv = ec.AsyncThrowVariable;
7400 for (int i = 0; i < catch_sm.Count; ++i) {
7401 if (c != null && c.Block.HasReachableClosingBrace)
7402 ec.Emit (OpCodes.Br, end);
7404 ec.MarkLabel (labels [i + 1]);
7406 ec.AsyncThrowVariable = c.Variable;
7409 ec.AsyncThrowVariable = atv;
7415 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7417 var start_fc = fc.BranchDefiniteAssignment ();
7418 var res = Block.FlowAnalysis (fc);
7420 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7422 foreach (var c in clauses) {
7423 fc.BranchDefiniteAssignment (start_fc);
7424 if (!c.FlowAnalysis (fc)) {
7426 try_fc = fc.DefiniteAssignment;
7428 try_fc &= fc.DefiniteAssignment;
7434 fc.DefiniteAssignment = try_fc ?? start_fc;
7435 parent_try_block = null;
7439 public override Reachability MarkReachable (Reachability rc)
7441 if (rc.IsUnreachable)
7444 base.MarkReachable (rc);
7446 var tc_rc = Block.MarkReachable (rc);
7448 foreach (var c in clauses)
7449 tc_rc &= c.MarkReachable (rc);
7454 protected override void CloneTo (CloneContext clonectx, Statement t)
7456 TryCatch target = (TryCatch) t;
7458 target.Block = clonectx.LookupBlock (Block);
7459 if (clauses != null){
7460 target.clauses = new List<Catch> ();
7461 foreach (Catch c in clauses)
7462 target.clauses.Add ((Catch) c.Clone (clonectx));
7466 public override object Accept (StructuralVisitor visitor)
7468 return visitor.Visit (this);
7472 public class Using : TryFinallyBlock
7474 public class VariableDeclaration : BlockVariable
7476 Statement dispose_call;
7478 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7483 public VariableDeclaration (LocalVariable li, Location loc)
7490 public VariableDeclaration (Expression expr)
7493 loc = expr.Location;
7499 public bool IsNested { get; private set; }
7503 public void EmitDispose (EmitContext ec)
7505 dispose_call.Emit (ec);
7508 public override bool Resolve (BlockContext bc)
7513 return base.Resolve (bc, false);
7516 public Expression ResolveExpression (BlockContext bc)
7518 var e = Initializer.Resolve (bc);
7522 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7523 Initializer = ResolveInitializer (bc, Variable, e);
7527 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7529 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7530 initializer = initializer.Resolve (bc);
7531 if (initializer == null)
7534 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7535 Arguments args = new Arguments (1);
7536 args.Add (new Argument (initializer));
7537 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7538 if (initializer == null)
7541 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7542 dispose_call = CreateDisposeCall (bc, var);
7543 dispose_call.Resolve (bc);
7545 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7548 if (li == Variable) {
7549 CheckIDiposableConversion (bc, li, initializer);
7550 dispose_call = CreateDisposeCall (bc, li);
7551 dispose_call.Resolve (bc);
7554 return base.ResolveInitializer (bc, li, initializer);
7557 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7561 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7562 if (type.IsNullableType) {
7563 // it's handled in CreateDisposeCall
7567 if (type != InternalType.ErrorType) {
7568 bc.Report.SymbolRelatedToPreviousError (type);
7569 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7570 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7571 type.GetSignatureForError ());
7578 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7580 var target = bc.BuiltinTypes.IDisposable;
7581 var tp = type as TypeParameterSpec;
7583 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7585 return type.ImplementsInterface (target, false);
7588 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7590 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7592 var loc = lv.Location;
7594 var idt = bc.BuiltinTypes.IDisposable;
7595 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7597 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7598 dispose_mg.InstanceExpression = type.IsNullableType ?
7599 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7603 // Hide it from symbol file via null location
7605 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7607 // Add conditional call when disposing possible null variable
7608 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7609 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7614 public void ResolveDeclaratorInitializer (BlockContext bc)
7616 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7619 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7621 for (int i = declarators.Count - 1; i >= 0; --i) {
7622 var d = declarators [i];
7623 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7624 vd.Initializer = d.Initializer;
7626 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7627 vd.dispose_call.Resolve (bc);
7629 stmt = new Using (vd, stmt, d.Variable.Location);
7636 public override object Accept (StructuralVisitor visitor)
7638 return visitor.Visit (this);
7642 VariableDeclaration decl;
7644 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7650 public Using (Expression expr, Statement stmt, Location loc)
7653 this.decl = new VariableDeclaration (expr);
7658 public Expression Expr {
7660 return decl.Variable == null ? decl.Initializer : null;
7664 public BlockVariable Variables {
7672 public override void Emit (EmitContext ec)
7675 // Don't emit sequence point it will be set on variable declaration
7680 protected override void EmitTryBodyPrepare (EmitContext ec)
7683 base.EmitTryBodyPrepare (ec);
7686 protected override void EmitTryBody (EmitContext ec)
7691 public override void EmitFinallyBody (EmitContext ec)
7693 decl.EmitDispose (ec);
7696 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7698 decl.FlowAnalysis (fc);
7699 return stmt.FlowAnalysis (fc);
7702 public override Reachability MarkReachable (Reachability rc)
7704 decl.MarkReachable (rc);
7705 return base.MarkReachable (rc);
7708 public override bool Resolve (BlockContext ec)
7710 VariableReference vr;
7711 bool vr_locked = false;
7713 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7714 if (decl.Variable == null) {
7715 vr = decl.ResolveExpression (ec) as VariableReference;
7717 vr_locked = vr.IsLockedByStatement;
7718 vr.IsLockedByStatement = true;
7721 if (decl.IsNested) {
7722 decl.ResolveDeclaratorInitializer (ec);
7724 if (!decl.Resolve (ec))
7727 if (decl.Declarators != null) {
7728 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7736 var ok = base.Resolve (ec);
7739 vr.IsLockedByStatement = vr_locked;
7744 protected override void CloneTo (CloneContext clonectx, Statement t)
7746 Using target = (Using) t;
7748 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7749 target.stmt = stmt.Clone (clonectx);
7752 public override object Accept (StructuralVisitor visitor)
7754 return visitor.Visit (this);
7759 /// Implementation of the foreach C# statement
7761 public class Foreach : LoopStatement
7763 abstract class IteratorStatement : Statement
7765 protected readonly Foreach for_each;
7767 protected IteratorStatement (Foreach @foreach)
7769 this.for_each = @foreach;
7770 this.loc = @foreach.expr.Location;
7773 protected override void CloneTo (CloneContext clonectx, Statement target)
7775 throw new NotImplementedException ();
7778 public override void Emit (EmitContext ec)
7780 if (ec.EmitAccurateDebugInfo) {
7781 ec.Emit (OpCodes.Nop);
7787 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7789 throw new NotImplementedException ();
7793 sealed class ArrayForeach : IteratorStatement
7795 TemporaryVariableReference[] lengths;
7796 Expression [] length_exprs;
7797 StatementExpression[] counter;
7798 TemporaryVariableReference[] variables;
7800 TemporaryVariableReference copy;
7802 public ArrayForeach (Foreach @foreach, int rank)
7805 counter = new StatementExpression[rank];
7806 variables = new TemporaryVariableReference[rank];
7807 length_exprs = new Expression [rank];
7810 // Only use temporary length variables when dealing with
7811 // multi-dimensional arrays
7814 lengths = new TemporaryVariableReference [rank];
7817 public override bool Resolve (BlockContext ec)
7819 Block variables_block = for_each.variable.Block;
7820 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7823 int rank = length_exprs.Length;
7824 Arguments list = new Arguments (rank);
7825 for (int i = 0; i < rank; i++) {
7826 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7828 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7829 counter[i].Resolve (ec);
7832 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7834 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7835 lengths[i].Resolve (ec);
7837 Arguments args = new Arguments (1);
7838 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7839 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7842 list.Add (new Argument (v));
7845 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7850 if (for_each.type is VarExpr) {
7851 // Infer implicitly typed local variable from foreach array type
7852 var_type = access.Type;
7854 var_type = for_each.type.ResolveAsType (ec);
7856 if (var_type == null)
7859 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7864 for_each.variable.Type = var_type;
7866 var prev_block = ec.CurrentBlock;
7867 ec.CurrentBlock = variables_block;
7868 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7869 ec.CurrentBlock = prev_block;
7871 if (variable_ref == null)
7874 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7876 return for_each.body.Resolve (ec);
7879 protected override void DoEmit (EmitContext ec)
7881 copy.EmitAssign (ec, for_each.expr);
7883 int rank = length_exprs.Length;
7884 Label[] test = new Label [rank];
7885 Label[] loop = new Label [rank];
7887 for (int i = 0; i < rank; i++) {
7888 test [i] = ec.DefineLabel ();
7889 loop [i] = ec.DefineLabel ();
7891 if (lengths != null)
7892 lengths [i].EmitAssign (ec, length_exprs [i]);
7895 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7896 for (int i = 0; i < rank; i++) {
7897 variables [i].EmitAssign (ec, zero);
7899 ec.Emit (OpCodes.Br, test [i]);
7900 ec.MarkLabel (loop [i]);
7903 for_each.body.Emit (ec);
7905 ec.MarkLabel (ec.LoopBegin);
7906 ec.Mark (for_each.expr.Location);
7908 for (int i = rank - 1; i >= 0; i--){
7909 counter [i].Emit (ec);
7911 ec.MarkLabel (test [i]);
7912 variables [i].Emit (ec);
7914 if (lengths != null)
7915 lengths [i].Emit (ec);
7917 length_exprs [i].Emit (ec);
7919 ec.Emit (OpCodes.Blt, loop [i]);
7922 ec.MarkLabel (ec.LoopEnd);
7926 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7928 class RuntimeDispose : Using.VariableDeclaration
7930 public RuntimeDispose (LocalVariable lv, Location loc)
7936 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7938 // Defered to runtime check
7941 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7943 var idt = bc.BuiltinTypes.IDisposable;
7946 // Fabricates code like
7948 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7951 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7953 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7954 dispose_variable.CreateReferenceExpression (bc, loc),
7955 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7956 loc), new NullLiteral (loc));
7958 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7960 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7961 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7963 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7964 return new If (idisaposable_test, dispose, loc);
7968 LocalVariable variable;
7970 Statement statement;
7971 ExpressionStatement init;
7972 TemporaryVariableReference enumerator_variable;
7973 bool ambiguous_getenumerator_name;
7975 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7978 this.variable = var;
7982 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7984 rc.Report.SymbolRelatedToPreviousError (enumerator);
7985 rc.Report.Error (202, loc,
7986 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7987 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7990 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7993 // Option 1: Try to match by name GetEnumerator first
7995 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7996 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7998 var mg = mexpr as MethodGroupExpr;
8000 mg.InstanceExpression = expr;
8001 Arguments args = new Arguments (0);
8002 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8004 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8005 if (ambiguous_getenumerator_name)
8008 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8014 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8017 PredefinedMember<MethodSpec> iface_candidate = null;
8018 var ptypes = rc.Module.PredefinedTypes;
8019 var gen_ienumerable = ptypes.IEnumerableGeneric;
8020 if (!gen_ienumerable.Define ())
8021 gen_ienumerable = null;
8023 var ifaces = t.Interfaces;
8024 if (ifaces != null) {
8025 foreach (var iface in ifaces) {
8026 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8027 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8028 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8029 rc.Report.Error (1640, loc,
8030 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8031 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8036 // TODO: Cache this somehow
8037 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8038 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8043 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8044 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8049 if (iface_candidate == null) {
8050 if (expr.Type != InternalType.ErrorType) {
8051 rc.Report.Error (1579, loc,
8052 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8053 expr.Type.GetSignatureForError (), "GetEnumerator");
8059 var method = iface_candidate.Resolve (loc);
8063 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8064 mg.InstanceExpression = expr;
8068 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8070 var ms = MemberCache.FindMember (enumerator.ReturnType,
8071 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8072 BindingRestriction.InstanceOnly) as MethodSpec;
8074 if (ms == null || !ms.IsPublic) {
8075 Error_WrongEnumerator (rc, enumerator);
8079 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8082 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8084 var ps = MemberCache.FindMember (enumerator.ReturnType,
8085 MemberFilter.Property ("Current", null),
8086 BindingRestriction.InstanceOnly) as PropertySpec;
8088 if (ps == null || !ps.IsPublic) {
8089 Error_WrongEnumerator (rc, enumerator);
8096 public override bool Resolve (BlockContext ec)
8098 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8101 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8102 } else if (expr.Type.IsNullableType) {
8103 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8106 var get_enumerator_mg = ResolveGetEnumerator (ec);
8107 if (get_enumerator_mg == null) {
8111 var get_enumerator = get_enumerator_mg.BestCandidate;
8112 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8113 enumerator_variable.Resolve (ec);
8115 // Prepare bool MoveNext ()
8116 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8117 if (move_next_mg == null) {
8121 move_next_mg.InstanceExpression = enumerator_variable;
8123 // Prepare ~T~ Current { get; }
8124 var current_prop = ResolveCurrent (ec, get_enumerator);
8125 if (current_prop == null) {
8129 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8130 if (current_pe == null)
8133 VarExpr ve = for_each.type as VarExpr;
8137 // Source type is dynamic, set element type to dynamic too
8138 variable.Type = ec.BuiltinTypes.Dynamic;
8140 // Infer implicitly typed local variable from foreach enumerable type
8141 variable.Type = current_pe.Type;
8145 // Explicit cast of dynamic collection elements has to be done at runtime
8146 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8149 variable.Type = for_each.type.ResolveAsType (ec);
8151 if (variable.Type == null)
8154 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8155 if (current_pe == null)
8159 var prev_block = ec.CurrentBlock;
8160 ec.CurrentBlock = for_each.variable.Block;
8161 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8162 ec.CurrentBlock = prev_block;
8163 if (variable_ref == null)
8166 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8168 var init = new Invocation.Predefined (get_enumerator_mg, null);
8170 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8171 for_each.body, Location.Null);
8173 var enum_type = enumerator_variable.Type;
8176 // Add Dispose method call when enumerator can be IDisposable
8178 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8179 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8181 // Runtime Dispose check
8183 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8184 vd.Initializer = init;
8185 statement = new Using (vd, statement, Location.Null);
8188 // No Dispose call needed
8190 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8191 this.init.Resolve (ec);
8195 // Static Dispose check
8197 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8198 vd.Initializer = init;
8199 statement = new Using (vd, statement, Location.Null);
8202 return statement.Resolve (ec);
8205 protected override void DoEmit (EmitContext ec)
8207 enumerator_variable.LocalInfo.CreateBuilder (ec);
8210 init.EmitStatement (ec);
8212 statement.Emit (ec);
8215 #region IErrorHandler Members
8217 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8219 ec.Report.SymbolRelatedToPreviousError (best);
8220 ec.Report.Warning (278, 2, expr.Location,
8221 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8222 expr.Type.GetSignatureForError (), "enumerable",
8223 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8225 ambiguous_getenumerator_name = true;
8229 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8234 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8239 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8248 LocalVariable variable;
8252 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8256 this.variable = var;
8262 public Expression Expr {
8263 get { return expr; }
8266 public Expression TypeExpression {
8267 get { return type; }
8270 public LocalVariable Variable {
8271 get { return variable; }
8274 public override Reachability MarkReachable (Reachability rc)
8276 base.MarkReachable (rc);
8278 body.MarkReachable (rc);
8283 public override bool Resolve (BlockContext ec)
8285 expr = expr.Resolve (ec);
8290 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8294 body.AddStatement (Statement);
8296 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8297 Statement = new ArrayForeach (this, 1);
8298 } else if (expr.Type is ArrayContainer) {
8299 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8301 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8302 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8303 expr.ExprClassName);
8307 Statement = new CollectionForeach (this, variable, expr);
8310 return base.Resolve (ec);
8313 protected override void DoEmit (EmitContext ec)
8315 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8316 ec.LoopBegin = ec.DefineLabel ();
8317 ec.LoopEnd = ec.DefineLabel ();
8319 if (!(Statement is Block))
8320 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8322 variable.CreateBuilder (ec);
8324 Statement.Emit (ec);
8326 if (!(Statement is Block))
8329 ec.LoopBegin = old_begin;
8330 ec.LoopEnd = old_end;
8333 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8335 expr.FlowAnalysis (fc);
8337 var da = fc.BranchDefiniteAssignment ();
8338 body.FlowAnalysis (fc);
8339 fc.DefiniteAssignment = da;
8343 protected override void CloneTo (CloneContext clonectx, Statement t)
8345 Foreach target = (Foreach) t;
8347 target.type = type.Clone (clonectx);
8348 target.expr = expr.Clone (clonectx);
8349 target.body = (Block) body.Clone (clonectx);
8350 target.Statement = Statement.Clone (clonectx);
8353 public override object Accept (StructuralVisitor visitor)
8355 return visitor.Visit (this);
8359 class SentinelStatement: Statement
8361 protected override void CloneTo (CloneContext clonectx, Statement target)
8365 protected override void DoEmit (EmitContext ec)
8367 var l = ec.DefineLabel ();
8369 ec.Emit (OpCodes.Br_S, l);
8372 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8374 throw new NotImplementedException ();