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)
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) {
1226 ec.Report.Error (4016, loc,
1227 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1228 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1230 block_return_type = async_type.TypeArguments[0];
1234 if (block_return_type.Kind == MemberKind.Void) {
1235 ec.Report.Error (8030, loc,
1236 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1240 var l = am as AnonymousMethodBody;
1241 if (l != null && expr != null) {
1242 if (l.ReturnTypeInference != null) {
1243 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1248 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1249 // unexpected debugging experience
1251 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1252 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1261 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1262 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1265 if (am != null && block_return_type == ec.ReturnType) {
1266 ec.Report.Error (1662, loc,
1267 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
1268 am.ContainerType, am.GetSignatureForError ());
1277 protected override void DoEmit (EmitContext ec)
1281 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1282 if (async_body != null) {
1283 var storey = (AsyncTaskStorey)async_body.Storey;
1284 Label exit_label = async_body.BodyEnd;
1287 // It's null for await without async
1289 if (storey.HoistedReturnValue != null) {
1291 // Special case hoisted return value (happens in try/finally scenario)
1293 if (ec.TryFinallyUnwind != null) {
1294 if (storey.HoistedReturnValue is VariableReference) {
1295 storey.HoistedReturnValue = ec.GetTemporaryField (storey.HoistedReturnValue.Type);
1298 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1301 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1302 async_return.EmitAssign (ec, expr, false, false);
1307 if (ec.TryFinallyUnwind != null)
1308 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1311 ec.Emit (OpCodes.Leave, exit_label);
1318 if (unwind_protect || ec.EmitAccurateDebugInfo)
1319 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1322 if (unwind_protect) {
1323 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1324 } else if (ec.EmitAccurateDebugInfo) {
1325 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1327 ec.Emit (OpCodes.Ret);
1331 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1334 expr.FlowAnalysis (fc);
1336 base.DoFlowAnalysis (fc);
1340 void Error_ReturnFromIterator (ResolveContext rc)
1342 rc.Report.Error (1622, loc,
1343 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1346 public override Reachability MarkReachable (Reachability rc)
1348 base.MarkReachable (rc);
1349 return Reachability.CreateUnreachable ();
1352 protected override void CloneTo (CloneContext clonectx, Statement t)
1354 Return target = (Return) t;
1355 // It's null for simple return;
1357 target.expr = expr.Clone (clonectx);
1360 public override object Accept (StructuralVisitor visitor)
1362 return visitor.Visit (this);
1366 public class Goto : ExitStatement
1369 LabeledStatement label;
1370 TryFinally try_finally;
1372 public Goto (string label, Location l)
1378 public string Target {
1379 get { return target; }
1382 protected override bool IsLocalExit {
1388 protected override bool DoResolve (BlockContext bc)
1390 label = bc.CurrentBlock.LookupLabel (target);
1391 if (label == null) {
1392 Error_UnknownLabel (bc, target, loc);
1396 try_finally = bc.CurrentTryBlock as TryFinally;
1398 CheckExitBoundaries (bc, label.Block);
1403 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1405 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1409 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1411 if (fc.LabelStack == null) {
1412 fc.LabelStack = new List<LabeledStatement> ();
1413 } else if (fc.LabelStack.Contains (label)) {
1417 fc.LabelStack.Add (label);
1418 label.Block.ScanGotoJump (label, fc);
1419 fc.LabelStack.Remove (label);
1423 public override Reachability MarkReachable (Reachability rc)
1425 if (rc.IsUnreachable)
1428 base.MarkReachable (rc);
1430 if (try_finally != null) {
1431 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1432 label.AddGotoReference (rc, false);
1434 label.AddGotoReference (rc, true);
1437 label.AddGotoReference (rc, false);
1440 return Reachability.CreateUnreachable ();
1443 protected override void CloneTo (CloneContext clonectx, Statement target)
1448 protected override void DoEmit (EmitContext ec)
1451 throw new InternalErrorException ("goto emitted before target resolved");
1453 Label l = label.LabelTarget (ec);
1455 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1456 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1457 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1460 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1463 bool IsLeavingFinally (Block labelBlock)
1465 var b = try_finally.Statement as Block;
1467 if (b == labelBlock)
1476 public override object Accept (StructuralVisitor visitor)
1478 return visitor.Visit (this);
1482 public class LabeledStatement : Statement {
1490 public LabeledStatement (string name, Block block, Location l)
1497 public Label LabelTarget (EmitContext ec)
1502 label = ec.DefineLabel ();
1507 public Block Block {
1513 public string Name {
1514 get { return name; }
1517 protected override void CloneTo (CloneContext clonectx, Statement target)
1519 var t = (LabeledStatement) target;
1521 t.block = clonectx.RemapBlockCopy (block);
1524 public override bool Resolve (BlockContext bc)
1529 protected override void DoEmit (EmitContext ec)
1532 ec.MarkLabel (label);
1535 ec.Emit (OpCodes.Br_S, label);
1538 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1541 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1547 public override Reachability MarkReachable (Reachability rc)
1549 base.MarkReachable (rc);
1552 rc = new Reachability ();
1557 public void AddGotoReference (Reachability rc, bool finalTarget)
1566 // Label is final target when goto jumps out of try block with
1567 // finally clause. In that case we need leave with target but in C#
1568 // terms the label is unreachable. Using finalTarget we emit
1569 // explicit label not just marker
1572 this.finalTarget = true;
1576 block.ScanGotoJump (this);
1579 public override object Accept (StructuralVisitor visitor)
1581 return visitor.Visit (this);
1587 /// `goto default' statement
1589 public class GotoDefault : SwitchGoto
1591 public GotoDefault (Location l)
1596 public override bool Resolve (BlockContext bc)
1598 if (bc.Switch == null) {
1599 Error_GotoCaseRequiresSwitchBlock (bc);
1603 bc.Switch.RegisterGotoCase (null, null);
1609 protected override void DoEmit (EmitContext ec)
1611 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1614 public override Reachability MarkReachable (Reachability rc)
1616 if (!rc.IsUnreachable) {
1617 var label = switch_statement.DefaultLabel;
1618 if (label.IsUnreachable) {
1619 label.MarkReachable (rc);
1620 switch_statement.Block.ScanGotoJump (label);
1624 return base.MarkReachable (rc);
1627 public override object Accept (StructuralVisitor visitor)
1629 return visitor.Visit (this);
1634 /// `goto case' statement
1636 public class GotoCase : SwitchGoto
1640 public GotoCase (Expression e, Location l)
1646 public Expression Expr {
1652 public SwitchLabel Label { get; set; }
1654 public override bool Resolve (BlockContext ec)
1656 if (ec.Switch == null) {
1657 Error_GotoCaseRequiresSwitchBlock (ec);
1661 Constant c = expr.ResolveLabelConstant (ec);
1667 if (ec.Switch.IsNullable && c is NullLiteral) {
1670 TypeSpec type = ec.Switch.SwitchType;
1671 res = c.Reduce (ec, type);
1673 c.Error_ValueCannotBeConverted (ec, type, true);
1677 if (!Convert.ImplicitStandardConversionExists (c, type))
1678 ec.Report.Warning (469, 2, loc,
1679 "The `goto case' value is not implicitly convertible to type `{0}'",
1680 type.GetSignatureForError ());
1684 ec.Switch.RegisterGotoCase (this, res);
1691 protected override void DoEmit (EmitContext ec)
1693 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1696 protected override void CloneTo (CloneContext clonectx, Statement t)
1698 GotoCase target = (GotoCase) t;
1700 target.expr = expr.Clone (clonectx);
1703 public override Reachability MarkReachable (Reachability rc)
1705 if (!rc.IsUnreachable) {
1706 var label = switch_statement.FindLabel ((Constant) expr);
1707 if (label.IsUnreachable) {
1708 label.MarkReachable (rc);
1709 switch_statement.Block.ScanGotoJump (label);
1713 return base.MarkReachable (rc);
1716 public override object Accept (StructuralVisitor visitor)
1718 return visitor.Visit (this);
1722 public abstract class SwitchGoto : Statement
1724 protected bool unwind_protect;
1725 protected Switch switch_statement;
1727 protected SwitchGoto (Location loc)
1732 protected override void CloneTo (CloneContext clonectx, Statement target)
1737 public override bool Resolve (BlockContext bc)
1739 CheckExitBoundaries (bc, bc.Switch.Block);
1741 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1742 switch_statement = bc.Switch;
1747 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1752 public override Reachability MarkReachable (Reachability rc)
1754 base.MarkReachable (rc);
1755 return Reachability.CreateUnreachable ();
1758 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1760 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1764 public class Throw : Statement {
1767 public Throw (Expression expr, Location l)
1773 public Expression Expr {
1779 public override bool Resolve (BlockContext ec)
1782 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1783 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1784 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1785 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1786 if (b.IsFinallyBlock) {
1787 ec.Report.Error (724, loc,
1788 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1797 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1802 var et = ec.BuiltinTypes.Exception;
1803 if (Convert.ImplicitConversionExists (ec, expr, et))
1804 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1806 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1811 protected override void DoEmit (EmitContext ec)
1814 var atv = ec.AsyncThrowVariable;
1816 if (atv.HoistedVariant != null) {
1817 atv.HoistedVariant.Emit (ec);
1822 ec.Emit (OpCodes.Throw);
1824 ec.Emit (OpCodes.Rethrow);
1829 ec.Emit (OpCodes.Throw);
1833 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1836 expr.FlowAnalysis (fc);
1841 public override Reachability MarkReachable (Reachability rc)
1843 base.MarkReachable (rc);
1844 return Reachability.CreateUnreachable ();
1847 protected override void CloneTo (CloneContext clonectx, Statement t)
1849 Throw target = (Throw) t;
1852 target.expr = expr.Clone (clonectx);
1855 public override object Accept (StructuralVisitor visitor)
1857 return visitor.Visit (this);
1861 public class Break : LocalExitStatement
1863 public Break (Location l)
1868 public override object Accept (StructuralVisitor visitor)
1870 return visitor.Visit (this);
1873 protected override void DoEmit (EmitContext ec)
1877 if (ec.TryFinallyUnwind != null) {
1878 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1879 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1882 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1885 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1887 enclosing_loop.AddEndDefiniteAssignment (fc);
1891 protected override bool DoResolve (BlockContext bc)
1893 enclosing_loop = bc.EnclosingLoopOrSwitch;
1894 return base.DoResolve (bc);
1897 public override Reachability MarkReachable (Reachability rc)
1899 base.MarkReachable (rc);
1901 if (!rc.IsUnreachable)
1902 enclosing_loop.SetEndReachable ();
1904 return Reachability.CreateUnreachable ();
1908 public class Continue : LocalExitStatement
1910 public Continue (Location l)
1915 public override object Accept (StructuralVisitor visitor)
1917 return visitor.Visit (this);
1921 protected override void DoEmit (EmitContext ec)
1923 var l = ec.LoopBegin;
1925 if (ec.TryFinallyUnwind != null) {
1926 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1927 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1930 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1933 protected override bool DoResolve (BlockContext bc)
1935 enclosing_loop = bc.EnclosingLoop;
1936 return base.DoResolve (bc);
1939 public override Reachability MarkReachable (Reachability rc)
1941 base.MarkReachable (rc);
1943 if (!rc.IsUnreachable)
1944 enclosing_loop.SetIteratorReachable ();
1946 return Reachability.CreateUnreachable ();
1950 public abstract class LocalExitStatement : ExitStatement
1952 protected LoopStatement enclosing_loop;
1954 protected LocalExitStatement (Location loc)
1959 protected override bool IsLocalExit {
1965 protected override void CloneTo (CloneContext clonectx, Statement t)
1970 protected override bool DoResolve (BlockContext bc)
1972 if (enclosing_loop == null) {
1973 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1977 var block = enclosing_loop.Statement as Block;
1979 // Don't need to do extra checks for simple statements loops
1980 if (block != null) {
1981 CheckExitBoundaries (bc, block);
1988 public interface ILocalVariable
1990 void Emit (EmitContext ec);
1991 void EmitAssign (EmitContext ec);
1992 void EmitAddressOf (EmitContext ec);
1995 public interface INamedBlockVariable
1997 Block Block { get; }
1998 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1999 bool IsDeclared { get; }
2000 bool IsParameter { get; }
2001 Location Location { get; }
2004 public class BlockVariableDeclarator
2007 Expression initializer;
2009 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2011 if (li.Type != null)
2012 throw new ArgumentException ("Expected null variable type");
2015 this.initializer = initializer;
2020 public LocalVariable Variable {
2026 public Expression Initializer {
2031 initializer = value;
2037 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2039 var t = (BlockVariableDeclarator) MemberwiseClone ();
2040 if (initializer != null)
2041 t.initializer = initializer.Clone (cloneCtx);
2047 public class BlockVariable : Statement
2049 Expression initializer;
2050 protected FullNamedExpression type_expr;
2051 protected LocalVariable li;
2052 protected List<BlockVariableDeclarator> declarators;
2055 public BlockVariable (FullNamedExpression type, LocalVariable li)
2057 this.type_expr = type;
2059 this.loc = type_expr.Location;
2062 protected BlockVariable (LocalVariable li)
2069 public List<BlockVariableDeclarator> Declarators {
2075 public Expression Initializer {
2080 initializer = value;
2084 public FullNamedExpression TypeExpression {
2090 public LocalVariable Variable {
2098 public void AddDeclarator (BlockVariableDeclarator decl)
2100 if (declarators == null)
2101 declarators = new List<BlockVariableDeclarator> ();
2103 declarators.Add (decl);
2106 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2108 if (bc.Report.Errors != 0)
2111 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2113 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2114 new MemberName (li.Name, li.Location), null);
2116 container.AddField (f);
2119 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2123 public override bool Resolve (BlockContext bc)
2125 return Resolve (bc, true);
2128 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2130 if (type == null && !li.IsCompilerGenerated) {
2131 var vexpr = type_expr as VarExpr;
2134 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2135 // same name exists or as a keyword when no type was found
2137 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2138 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2139 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2142 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2146 if (li.IsConstant) {
2147 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2151 if (Initializer == null) {
2152 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2156 if (declarators != null) {
2157 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2161 Initializer = Initializer.Resolve (bc);
2162 if (Initializer != null) {
2163 ((VarExpr) type_expr).InferType (bc, Initializer);
2164 type = type_expr.Type;
2166 // Set error type to indicate the var was placed correctly but could
2169 // var a = missing ();
2171 type = InternalType.ErrorType;
2176 type = type_expr.ResolveAsType (bc);
2180 if (li.IsConstant && !type.IsConstantCompatible) {
2181 Const.Error_InvalidConstantType (type, loc, bc.Report);
2186 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2191 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2193 CreateEvaluatorVariable (bc, li);
2194 } else if (type != InternalType.ErrorType) {
2195 li.PrepareAssignmentAnalysis (bc);
2198 if (initializer != null) {
2199 initializer = ResolveInitializer (bc, li, initializer);
2200 // li.Variable.DefinitelyAssigned
2203 if (declarators != null) {
2204 foreach (var d in declarators) {
2205 d.Variable.Type = li.Type;
2207 CreateEvaluatorVariable (bc, d.Variable);
2208 } else if (type != InternalType.ErrorType) {
2209 d.Variable.PrepareAssignmentAnalysis (bc);
2212 if (d.Initializer != null && resolveDeclaratorInitializers) {
2213 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2214 // d.Variable.DefinitelyAssigned
2222 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2224 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2225 return a.ResolveStatement (bc);
2228 protected override void DoEmit (EmitContext ec)
2230 li.CreateBuilder (ec);
2232 if (Initializer != null && !IsUnreachable)
2233 ((ExpressionStatement) Initializer).EmitStatement (ec);
2235 if (declarators != null) {
2236 foreach (var d in declarators) {
2237 d.Variable.CreateBuilder (ec);
2238 if (d.Initializer != null && !IsUnreachable) {
2239 ec.Mark (d.Variable.Location);
2240 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2246 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2248 if (Initializer != null)
2249 Initializer.FlowAnalysis (fc);
2251 if (declarators != null) {
2252 foreach (var d in declarators) {
2253 if (d.Initializer != null)
2254 d.Initializer.FlowAnalysis (fc);
2261 public override Reachability MarkReachable (Reachability rc)
2263 var init = initializer as ExpressionStatement;
2265 init.MarkReachable (rc);
2267 return base.MarkReachable (rc);
2270 protected override void CloneTo (CloneContext clonectx, Statement target)
2272 BlockVariable t = (BlockVariable) target;
2274 if (type_expr != null)
2275 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2277 if (initializer != null)
2278 t.initializer = initializer.Clone (clonectx);
2280 if (declarators != null) {
2281 t.declarators = null;
2282 foreach (var d in declarators)
2283 t.AddDeclarator (d.Clone (clonectx));
2287 public override object Accept (StructuralVisitor visitor)
2289 return visitor.Visit (this);
2293 public class BlockConstant : BlockVariable
2295 public BlockConstant (FullNamedExpression type, LocalVariable li)
2300 public override void Emit (EmitContext ec)
2302 // Nothing to emit, not even sequence point
2305 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2307 initializer = initializer.Resolve (bc);
2308 if (initializer == null)
2311 var c = initializer as Constant;
2313 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2317 c = c.ConvertImplicitly (li.Type);
2319 if (TypeSpec.IsReferenceType (li.Type))
2320 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2322 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2327 li.ConstantValue = c;
2331 public override object Accept (StructuralVisitor visitor)
2333 return visitor.Visit (this);
2338 // The information about a user-perceived local variable
2340 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2347 AddressTaken = 1 << 2,
2348 CompilerGenerated = 1 << 3,
2350 ForeachVariable = 1 << 5,
2351 FixedVariable = 1 << 6,
2352 UsingVariable = 1 << 7,
2355 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2359 readonly string name;
2360 readonly Location loc;
2361 readonly Block block;
2363 Constant const_value;
2365 public VariableInfo VariableInfo;
2366 HoistedVariable hoisted_variant;
2368 LocalBuilder builder;
2370 public LocalVariable (Block block, string name, Location loc)
2377 public LocalVariable (Block block, string name, Flags flags, Location loc)
2378 : this (block, name, loc)
2384 // Used by variable declarators
2386 public LocalVariable (LocalVariable li, string name, Location loc)
2387 : this (li.block, name, li.flags, loc)
2393 public bool AddressTaken {
2395 return (flags & Flags.AddressTaken) != 0;
2399 public Block Block {
2405 public Constant ConstantValue {
2410 const_value = value;
2415 // Hoisted local variable variant
2417 public HoistedVariable HoistedVariant {
2419 return hoisted_variant;
2422 hoisted_variant = value;
2426 public bool IsDeclared {
2428 return type != null;
2432 public bool IsCompilerGenerated {
2434 return (flags & Flags.CompilerGenerated) != 0;
2438 public bool IsConstant {
2440 return (flags & Flags.Constant) != 0;
2444 public bool IsLocked {
2446 return (flags & Flags.IsLocked) != 0;
2449 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2453 public bool IsThis {
2455 return (flags & Flags.IsThis) != 0;
2459 public bool IsFixed {
2461 return (flags & Flags.FixedVariable) != 0;
2465 bool INamedBlockVariable.IsParameter {
2471 public bool IsReadonly {
2473 return (flags & Flags.ReadonlyMask) != 0;
2477 public Location Location {
2483 public string Name {
2489 public TypeSpec Type {
2500 public void CreateBuilder (EmitContext ec)
2502 if ((flags & Flags.Used) == 0) {
2503 if (VariableInfo == null) {
2504 // Missing flow analysis or wrong variable flags
2505 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2508 if (VariableInfo.IsEverAssigned)
2509 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2511 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2514 if (HoistedVariant != null)
2517 if (builder != null) {
2518 if ((flags & Flags.CompilerGenerated) != 0)
2521 // To avoid Used warning duplicates
2522 throw new InternalErrorException ("Already created variable `{0}'", name);
2526 // All fixed variabled are pinned, a slot has to be alocated
2528 builder = ec.DeclareLocal (Type, IsFixed);
2529 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2530 ec.DefineLocalVariable (name, builder);
2533 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2535 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2540 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2542 if (IsConstant && const_value != null)
2543 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2545 return new LocalVariableReference (this, loc);
2548 public void Emit (EmitContext ec)
2550 // TODO: Need something better for temporary variables
2551 if ((flags & Flags.CompilerGenerated) != 0)
2554 ec.Emit (OpCodes.Ldloc, builder);
2557 public void EmitAssign (EmitContext ec)
2559 // TODO: Need something better for temporary variables
2560 if ((flags & Flags.CompilerGenerated) != 0)
2563 ec.Emit (OpCodes.Stloc, builder);
2566 public void EmitAddressOf (EmitContext ec)
2568 // TODO: Need something better for temporary variables
2569 if ((flags & Flags.CompilerGenerated) != 0)
2572 ec.Emit (OpCodes.Ldloca, builder);
2575 public static string GetCompilerGeneratedName (Block block)
2577 // HACK: Debugger depends on the name semantics
2578 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2581 public string GetReadOnlyContext ()
2583 switch (flags & Flags.ReadonlyMask) {
2584 case Flags.FixedVariable:
2585 return "fixed variable";
2586 case Flags.ForeachVariable:
2587 return "foreach iteration variable";
2588 case Flags.UsingVariable:
2589 return "using variable";
2592 throw new InternalErrorException ("Variable is not readonly");
2595 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2597 if (VariableInfo == null)
2598 throw new Exception ();
2600 if (IsAssigned (fc))
2603 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2606 public bool IsAssigned (FlowAnalysisContext fc)
2608 return fc.IsDefinitelyAssigned (VariableInfo);
2611 public void PrepareAssignmentAnalysis (BlockContext bc)
2614 // No need to run assignment analysis for these guys
2616 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2619 VariableInfo = VariableInfo.Create (bc, this);
2623 // Mark the variables as referenced in the user code
2625 public void SetIsUsed ()
2627 flags |= Flags.Used;
2630 public void SetHasAddressTaken ()
2632 flags |= (Flags.AddressTaken | Flags.Used);
2635 public override string ToString ()
2637 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2642 /// Block represents a C# block.
2646 /// This class is used in a number of places: either to represent
2647 /// explicit blocks that the programmer places or implicit blocks.
2649 /// Implicit blocks are used as labels or to introduce variable
2652 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2653 /// they contain extra information that is not necessary on normal blocks.
2655 public class Block : Statement {
2662 HasCapturedVariable = 64,
2663 HasCapturedThis = 1 << 7,
2664 IsExpressionTree = 1 << 8,
2665 CompilerGenerated = 1 << 9,
2666 HasAsyncModifier = 1 << 10,
2668 YieldBlock = 1 << 12,
2669 AwaitBlock = 1 << 13,
2670 FinallyBlock = 1 << 14,
2671 CatchBlock = 1 << 15,
2673 NoFlowAnalysis = 1 << 21,
2674 InitializationEmitted = 1 << 22
2677 public Block Parent;
2678 public Location StartLocation;
2679 public Location EndLocation;
2681 public ExplicitBlock Explicit;
2682 public ParametersBlock ParametersBlock;
2684 protected Flags flags;
2687 // The statements in this block
2689 protected List<Statement> statements;
2691 protected List<Statement> scope_initializers;
2693 int? resolving_init_idx;
2699 public int ID = id++;
2701 static int clone_id_counter;
2705 // int assignable_slots;
2707 public Block (Block parent, Location start, Location end)
2708 : this (parent, 0, start, end)
2712 public Block (Block parent, Flags flags, Location start, Location end)
2714 if (parent != null) {
2715 // the appropriate constructors will fixup these fields
2716 ParametersBlock = parent.ParametersBlock;
2717 Explicit = parent.Explicit;
2720 this.Parent = parent;
2722 this.StartLocation = start;
2723 this.EndLocation = end;
2725 statements = new List<Statement> (4);
2727 this.original = this;
2732 public Block Original {
2741 public bool IsCompilerGenerated {
2742 get { return (flags & Flags.CompilerGenerated) != 0; }
2743 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2747 public bool IsCatchBlock {
2749 return (flags & Flags.CatchBlock) != 0;
2753 public bool IsFinallyBlock {
2755 return (flags & Flags.FinallyBlock) != 0;
2759 public bool Unchecked {
2760 get { return (flags & Flags.Unchecked) != 0; }
2761 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2764 public bool Unsafe {
2765 get { return (flags & Flags.Unsafe) != 0; }
2766 set { flags |= Flags.Unsafe; }
2769 public List<Statement> Statements {
2770 get { return statements; }
2775 public void SetEndLocation (Location loc)
2780 public void AddLabel (LabeledStatement target)
2782 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2785 public void AddLocalName (LocalVariable li)
2787 AddLocalName (li.Name, li);
2790 public void AddLocalName (string name, INamedBlockVariable li)
2792 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2795 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2797 if (reason == null) {
2798 Error_AlreadyDeclared (name, variable);
2802 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2803 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2804 "to `{0}', which is already used in a `{1}' scope to denote something else",
2808 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2810 var pi = variable as ParametersBlock.ParameterInfo;
2812 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2814 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2815 "A local variable named `{0}' is already defined in this scope", name);
2819 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2821 ParametersBlock.TopBlock.Report.Error (412, loc,
2822 "The type parameter name `{0}' is the same as local variable or parameter name",
2827 // It should be used by expressions which require to
2828 // register a statement during resolve process.
2830 public void AddScopeStatement (Statement s)
2832 if (scope_initializers == null)
2833 scope_initializers = new List<Statement> ();
2836 // Simple recursive helper, when resolve scope initializer another
2837 // new scope initializer can be added, this ensures it's initialized
2838 // before existing one. For now this can happen with expression trees
2839 // in base ctor initializer only
2841 if (resolving_init_idx.HasValue) {
2842 scope_initializers.Insert (resolving_init_idx.Value, s);
2843 ++resolving_init_idx;
2845 scope_initializers.Add (s);
2849 public void InsertStatement (int index, Statement s)
2851 statements.Insert (index, s);
2854 public void AddStatement (Statement s)
2859 public LabeledStatement LookupLabel (string name)
2861 return ParametersBlock.GetLabel (name, this);
2864 public override Reachability MarkReachable (Reachability rc)
2866 if (rc.IsUnreachable)
2869 MarkReachableScope (rc);
2871 foreach (var s in statements) {
2872 rc = s.MarkReachable (rc);
2873 if (rc.IsUnreachable) {
2874 if ((flags & Flags.ReachableEnd) != 0)
2875 return new Reachability ();
2881 flags |= Flags.ReachableEnd;
2886 public void MarkReachableScope (Reachability rc)
2888 base.MarkReachable (rc);
2890 if (scope_initializers != null) {
2891 foreach (var si in scope_initializers)
2892 si.MarkReachable (rc);
2896 public override bool Resolve (BlockContext bc)
2898 if ((flags & Flags.Resolved) != 0)
2901 Block prev_block = bc.CurrentBlock;
2902 bc.CurrentBlock = this;
2905 // Compiler generated scope statements
2907 if (scope_initializers != null) {
2908 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2909 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2912 resolving_init_idx = null;
2916 int statement_count = statements.Count;
2917 for (int ix = 0; ix < statement_count; ix++){
2918 Statement s = statements [ix];
2920 if (!s.Resolve (bc)) {
2922 if (!bc.IsInProbingMode)
2923 statements [ix] = new EmptyStatement (s.loc);
2929 bc.CurrentBlock = prev_block;
2931 flags |= Flags.Resolved;
2935 protected override void DoEmit (EmitContext ec)
2937 for (int ix = 0; ix < statements.Count; ix++){
2938 statements [ix].Emit (ec);
2942 public override void Emit (EmitContext ec)
2944 if (scope_initializers != null)
2945 EmitScopeInitializers (ec);
2950 protected void EmitScopeInitializers (EmitContext ec)
2952 foreach (Statement s in scope_initializers)
2956 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2958 if (scope_initializers != null) {
2959 foreach (var si in scope_initializers)
2960 si.FlowAnalysis (fc);
2963 return DoFlowAnalysis (fc, 0);
2966 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2968 bool end_unreachable = !reachable;
2969 for (; startIndex < statements.Count; ++startIndex) {
2970 var s = statements[startIndex];
2972 end_unreachable = s.FlowAnalysis (fc);
2973 if (s.IsUnreachable) {
2974 statements [startIndex] = RewriteUnreachableStatement (s);
2979 // Statement end reachability is needed mostly due to goto support. Consider
2988 // X label is reachable only via goto not as another statement after if. We need
2989 // this for flow-analysis only to carry variable info correctly.
2991 if (end_unreachable) {
2992 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2993 s = statements[startIndex];
2994 if (s is SwitchLabel) {
2995 s.FlowAnalysis (fc);
2999 if (s.IsUnreachable) {
3000 s.FlowAnalysis (fc);
3001 statements [startIndex] = RewriteUnreachableStatement (s);
3008 // The condition should be true unless there is forward jumping goto
3010 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3013 return !Explicit.HasReachableClosingBrace;
3016 static Statement RewriteUnreachableStatement (Statement s)
3018 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3019 // analysis. Even csc report unreachable warning for it but it's actually used hence
3020 // we try to emulate this behaviour
3028 if (s is BlockVariable)
3031 return new EmptyStatement (s.loc);
3034 public void ScanGotoJump (Statement label)
3037 for (i = 0; i < statements.Count; ++i) {
3038 if (statements[i] == label)
3042 var rc = new Reachability ();
3043 for (++i; i < statements.Count; ++i) {
3044 var s = statements[i];
3045 rc = s.MarkReachable (rc);
3046 if (rc.IsUnreachable)
3050 flags |= Flags.ReachableEnd;
3053 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3056 for (i = 0; i < statements.Count; ++i) {
3057 if (statements[i] == label)
3061 DoFlowAnalysis (fc, ++i);
3065 public override string ToString ()
3067 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3071 protected override void CloneTo (CloneContext clonectx, Statement t)
3073 Block target = (Block) t;
3075 target.clone_id = ++clone_id_counter;
3078 clonectx.AddBlockMap (this, target);
3079 if (original != this)
3080 clonectx.AddBlockMap (original, target);
3082 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3083 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3086 target.Parent = clonectx.RemapBlockCopy (Parent);
3088 target.statements = new List<Statement> (statements.Count);
3089 foreach (Statement s in statements)
3090 target.statements.Add (s.Clone (clonectx));
3093 public override object Accept (StructuralVisitor visitor)
3095 return visitor.Visit (this);
3099 public class ExplicitBlock : Block
3101 protected AnonymousMethodStorey am_storey;
3103 public ExplicitBlock (Block parent, Location start, Location end)
3104 : this (parent, (Flags) 0, start, end)
3108 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3109 : base (parent, flags, start, end)
3111 this.Explicit = this;
3116 public AnonymousMethodStorey AnonymousMethodStorey {
3122 public bool HasAwait {
3124 return (flags & Flags.AwaitBlock) != 0;
3128 public bool HasCapturedThis {
3130 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3133 return (flags & Flags.HasCapturedThis) != 0;
3138 // Used to indicate that the block has reference to parent
3139 // block and cannot be made static when defining anonymous method
3141 public bool HasCapturedVariable {
3143 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3146 return (flags & Flags.HasCapturedVariable) != 0;
3150 public bool HasReachableClosingBrace {
3152 return (flags & Flags.ReachableEnd) != 0;
3155 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3159 public bool HasYield {
3161 return (flags & Flags.YieldBlock) != 0;
3168 // Creates anonymous method storey in current block
3170 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3173 // Return same story for iterator and async blocks unless we are
3174 // in nested anonymous method
3176 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3177 return ec.CurrentAnonymousMethod.Storey;
3179 if (am_storey == null) {
3180 MemberBase mc = ec.MemberContext as MemberBase;
3183 // Creates anonymous method storey for this block
3185 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3191 public void EmitScopeInitialization (EmitContext ec)
3193 if ((flags & Flags.InitializationEmitted) != 0)
3196 if (am_storey != null) {
3197 DefineStoreyContainer (ec, am_storey);
3198 am_storey.EmitStoreyInstantiation (ec, this);
3201 if (scope_initializers != null)
3202 EmitScopeInitializers (ec);
3204 flags |= Flags.InitializationEmitted;
3207 public override void Emit (EmitContext ec)
3209 EmitScopeInitialization (ec);
3211 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3212 ec.Emit (OpCodes.Nop);
3223 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3224 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3225 ec.Emit (OpCodes.Nop);
3229 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3231 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3232 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3233 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3237 // Creates anonymous method storey
3239 storey.CreateContainer ();
3240 storey.DefineContainer ();
3242 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3245 // Only first storey in path will hold this reference. All children blocks will
3246 // reference it indirectly using $ref field
3248 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3249 if (b.Parent != null) {
3250 var s = b.Parent.Explicit.AnonymousMethodStorey;
3252 storey.HoistedThis = s.HoistedThis;
3257 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3258 if (storey.HoistedThis == null)
3259 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3261 if (storey.HoistedThis != null)
3267 // We are the first storey on path and 'this' has to be hoisted
3269 if (storey.HoistedThis == null) {
3270 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3272 // ThisReferencesFromChildrenBlock holds all reference even if they
3273 // are not on this path. It saves some memory otherwise it'd have to
3274 // be in every explicit block. We run this check to see if the reference
3275 // is valid for this storey
3277 Block block_on_path = ref_block;
3278 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3280 if (block_on_path == null)
3283 if (storey.HoistedThis == null) {
3284 storey.AddCapturedThisField (ec, null);
3287 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3289 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3291 if (b_storey != null) {
3293 // Don't add storey cross reference for `this' when the storey ends up not
3294 // beeing attached to any parent
3296 if (b.ParametersBlock.StateMachine == null) {
3297 AnonymousMethodStorey s = null;
3298 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3299 s = ab.Explicit.AnonymousMethodStorey;
3304 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3306 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3307 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3314 // Stop propagation inside same top block
3316 if (b.ParametersBlock == ParametersBlock.Original) {
3317 b_storey.AddParentStoreyReference (ec, storey);
3318 // b_storey.HoistedThis = storey.HoistedThis;
3322 b = pb = b.ParametersBlock;
3324 pb = b as ParametersBlock;
3327 if (pb != null && pb.StateMachine != null) {
3328 if (pb.StateMachine == storey)
3332 // If we are state machine with no parent. We can hook into parent without additional
3333 // reference and capture this directly
3335 ExplicitBlock parent_storey_block = pb;
3336 while (parent_storey_block.Parent != null) {
3337 parent_storey_block = parent_storey_block.Parent.Explicit;
3338 if (parent_storey_block.AnonymousMethodStorey != null) {
3343 if (parent_storey_block.AnonymousMethodStorey == null) {
3344 pb.StateMachine.AddCapturedThisField (ec, null);
3345 b.HasCapturedThis = true;
3349 pb.StateMachine.AddParentStoreyReference (ec, storey);
3353 // Add parent storey reference only when this is not captured directly
3355 if (b_storey != null) {
3356 b_storey.AddParentStoreyReference (ec, storey);
3357 b_storey.HoistedThis = storey.HoistedThis;
3364 var ref_blocks = storey.ReferencesFromChildrenBlock;
3365 if (ref_blocks != null) {
3366 foreach (ExplicitBlock ref_block in ref_blocks) {
3367 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3368 if (b.AnonymousMethodStorey != null) {
3369 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3372 // Stop propagation inside same top block
3374 if (b.ParametersBlock == ParametersBlock.Original)
3377 b = b.ParametersBlock;
3380 var pb = b as ParametersBlock;
3381 if (pb != null && pb.StateMachine != null) {
3382 if (pb.StateMachine == storey)
3385 pb.StateMachine.AddParentStoreyReference (ec, storey);
3388 b.HasCapturedVariable = true;
3394 storey.PrepareEmit ();
3395 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3398 public void RegisterAsyncAwait ()
3401 while ((block.flags & Flags.AwaitBlock) == 0) {
3402 block.flags |= Flags.AwaitBlock;
3404 if (block is ParametersBlock)
3407 block = block.Parent.Explicit;
3411 public void RegisterIteratorYield ()
3413 ParametersBlock.TopBlock.IsIterator = true;
3416 while ((block.flags & Flags.YieldBlock) == 0) {
3417 block.flags |= Flags.YieldBlock;
3419 if (block.Parent == null)
3422 block = block.Parent.Explicit;
3426 public void SetCatchBlock ()
3428 flags |= Flags.CatchBlock;
3431 public void SetFinallyBlock ()
3433 flags |= Flags.FinallyBlock;
3436 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3438 tryBlock.statements = statements;
3439 statements = new List<Statement> (1);
3440 statements.Add (tf);
3445 // ParametersBlock was introduced to support anonymous methods
3446 // and lambda expressions
3448 public class ParametersBlock : ExplicitBlock
3450 public class ParameterInfo : INamedBlockVariable
3452 readonly ParametersBlock block;
3454 public VariableInfo VariableInfo;
3457 public ParameterInfo (ParametersBlock block, int index)
3465 public ParametersBlock Block {
3471 Block INamedBlockVariable.Block {
3477 public bool IsDeclared {
3483 public bool IsParameter {
3489 public bool IsLocked {
3498 public Location Location {
3500 return Parameter.Location;
3504 public Parameter Parameter {
3506 return block.Parameters [index];
3510 public TypeSpec ParameterType {
3512 return Parameter.Type;
3518 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3520 return new ParameterReference (this, loc);
3525 // Block is converted into an expression
3527 sealed class BlockScopeExpression : Expression
3530 readonly ParametersBlock block;
3532 public BlockScopeExpression (Expression child, ParametersBlock block)
3538 public override bool ContainsEmitWithAwait ()
3540 return child.ContainsEmitWithAwait ();
3543 public override Expression CreateExpressionTree (ResolveContext ec)
3545 throw new NotSupportedException ();
3548 protected override Expression DoResolve (ResolveContext ec)
3553 child = child.Resolve (ec);
3557 eclass = child.eclass;
3562 public override void Emit (EmitContext ec)
3564 block.EmitScopeInitializers (ec);
3569 protected ParametersCompiled parameters;
3570 protected ParameterInfo[] parameter_info;
3571 protected bool resolved;
3572 protected ToplevelBlock top_block;
3573 protected StateMachine state_machine;
3574 protected Dictionary<string, object> labels;
3576 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3577 : base (parent, 0, start, start)
3579 if (parameters == null)
3580 throw new ArgumentNullException ("parameters");
3582 this.parameters = parameters;
3583 ParametersBlock = this;
3585 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3587 this.top_block = parent.ParametersBlock.top_block;
3588 ProcessParameters ();
3591 protected ParametersBlock (ParametersCompiled parameters, Location start)
3592 : base (null, 0, start, start)
3594 if (parameters == null)
3595 throw new ArgumentNullException ("parameters");
3597 this.parameters = parameters;
3598 ParametersBlock = this;
3602 // It's supposed to be used by method body implementation of anonymous methods
3604 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3605 : base (null, 0, source.StartLocation, source.EndLocation)
3607 this.parameters = parameters;
3608 this.statements = source.statements;
3609 this.scope_initializers = source.scope_initializers;
3611 this.resolved = true;
3612 this.reachable = source.reachable;
3613 this.am_storey = source.am_storey;
3614 this.state_machine = source.state_machine;
3615 this.flags = source.flags & Flags.ReachableEnd;
3617 ParametersBlock = this;
3620 // Overwrite original for comparison purposes when linking cross references
3621 // between anonymous methods
3623 Original = source.Original;
3628 public bool IsAsync {
3630 return (flags & Flags.HasAsyncModifier) != 0;
3633 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3638 // Block has been converted to expression tree
3640 public bool IsExpressionTree {
3642 return (flags & Flags.IsExpressionTree) != 0;
3647 // The parameters for the block.
3649 public ParametersCompiled Parameters {
3655 public StateMachine StateMachine {
3657 return state_machine;
3661 public ToplevelBlock TopBlock {
3670 public bool Resolved {
3672 return (flags & Flags.Resolved) != 0;
3676 public int TemporaryLocalsCount { get; set; }
3681 // Checks whether all `out' parameters have been assigned.
3683 public void CheckControlExit (FlowAnalysisContext fc)
3685 CheckControlExit (fc, fc.DefiniteAssignment);
3688 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3690 if (parameter_info == null)
3693 foreach (var p in parameter_info) {
3694 if (p.VariableInfo == null)
3697 if (p.VariableInfo.IsAssigned (dat))
3700 fc.Report.Error (177, p.Location,
3701 "The out parameter `{0}' must be assigned to before control leaves the current method",
3706 protected override void CloneTo (CloneContext clonectx, Statement t)
3708 base.CloneTo (clonectx, t);
3710 var target = (ParametersBlock) t;
3713 // Clone label statements as well as they contain block reference
3717 if (pb.labels != null) {
3718 target.labels = new Dictionary<string, object> ();
3720 foreach (var entry in pb.labels) {
3721 var list = entry.Value as List<LabeledStatement>;
3724 var list_clone = new List<LabeledStatement> ();
3725 foreach (var lentry in list) {
3726 list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block)));
3729 target.labels.Add (entry.Key, list_clone);
3731 var labeled = (LabeledStatement) entry.Value;
3732 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block)));
3739 if (pb.Parent == null)
3742 pb = pb.Parent.ParametersBlock;
3746 public override Expression CreateExpressionTree (ResolveContext ec)
3748 if (statements.Count == 1) {
3749 Expression expr = statements[0].CreateExpressionTree (ec);
3750 if (scope_initializers != null)
3751 expr = new BlockScopeExpression (expr, this);
3756 return base.CreateExpressionTree (ec);
3759 public override void Emit (EmitContext ec)
3761 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3762 DefineStoreyContainer (ec, state_machine);
3763 state_machine.EmitStoreyInstantiation (ec, this);
3769 public void EmitEmbedded (EmitContext ec)
3771 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3772 DefineStoreyContainer (ec, state_machine);
3773 state_machine.EmitStoreyInstantiation (ec, this);
3779 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3781 var res = base.DoFlowAnalysis (fc);
3783 if (HasReachableClosingBrace)
3784 CheckControlExit (fc);
3789 public LabeledStatement GetLabel (string name, Block block)
3792 // Cloned parameters blocks can have their own cloned version of top-level labels
3794 if (labels == null) {
3796 return Parent.ParametersBlock.GetLabel (name, block);
3802 if (!labels.TryGetValue (name, out value)) {
3806 var label = value as LabeledStatement;
3808 if (label != null) {
3809 if (IsLabelVisible (label, b))
3813 List<LabeledStatement> list = (List<LabeledStatement>) value;
3814 for (int i = 0; i < list.Count; ++i) {
3816 if (IsLabelVisible (label, b))
3824 static bool IsLabelVisible (LabeledStatement label, Block b)
3827 if (label.Block == b)
3830 } while (b != null);
3835 public ParameterInfo GetParameterInfo (Parameter p)
3837 for (int i = 0; i < parameters.Count; ++i) {
3838 if (parameters[i] == p)
3839 return parameter_info[i];
3842 throw new ArgumentException ("Invalid parameter");
3845 public ParameterReference GetParameterReference (int index, Location loc)
3847 return new ParameterReference (parameter_info[index], loc);
3850 public Statement PerformClone ()
3852 CloneContext clonectx = new CloneContext ();
3853 return Clone (clonectx);
3856 protected void ProcessParameters ()
3858 if (parameters.Count == 0)
3861 parameter_info = new ParameterInfo[parameters.Count];
3862 for (int i = 0; i < parameter_info.Length; ++i) {
3863 var p = parameters.FixedParameters[i];
3867 // TODO: Should use Parameter only and more block there
3868 parameter_info[i] = new ParameterInfo (this, i);
3870 AddLocalName (p.Name, parameter_info[i]);
3874 static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst)
3876 var src_stmts = src.Statements;
3877 for (int i = 0; i < src_stmts.Count; ++i) {
3878 if (src_stmts[i] == stmt)
3879 return (LabeledStatement) dst.Statements[i];
3882 throw new InternalErrorException ("Should never be reached");
3885 public override bool Resolve (BlockContext bc)
3887 // TODO: if ((flags & Flags.Resolved) != 0)
3894 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3895 flags |= Flags.IsExpressionTree;
3898 PrepareAssignmentAnalysis (bc);
3900 if (!base.Resolve (bc))
3903 } catch (Exception e) {
3904 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3907 if (bc.CurrentBlock != null) {
3908 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3910 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3915 // If an asynchronous body of F is either an expression classified as nothing, or a
3916 // statement block where no return statements have expressions, the inferred return type is Task
3919 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3920 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3921 am.ReturnTypeInference = null;
3922 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3930 void PrepareAssignmentAnalysis (BlockContext bc)
3932 for (int i = 0; i < parameters.Count; ++i) {
3933 var par = parameters.FixedParameters[i];
3935 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3938 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3942 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3944 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3945 var stateMachine = new IteratorStorey (iterator);
3947 state_machine = stateMachine;
3948 iterator.SetStateMachine (stateMachine);
3950 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3951 tlb.Original = this;
3952 tlb.state_machine = stateMachine;
3953 tlb.AddStatement (new Return (iterator, iterator.Location));
3957 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3959 for (int i = 0; i < parameters.Count; i++) {
3960 Parameter p = parameters[i];
3961 Parameter.Modifier mod = p.ModFlags;
3962 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3963 host.Compiler.Report.Error (1988, p.Location,
3964 "Async methods cannot have ref or out parameters");
3968 if (p is ArglistParameter) {
3969 host.Compiler.Report.Error (4006, p.Location,
3970 "__arglist is not allowed in parameter list of async methods");
3974 if (parameters.Types[i].IsPointer) {
3975 host.Compiler.Report.Error (4005, p.Location,
3976 "Async methods cannot have unsafe parameters");
3982 host.Compiler.Report.Warning (1998, 1, loc,
3983 "Async block lacks `await' operator and will run synchronously");
3986 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3987 var initializer = new AsyncInitializer (this, host, block_type);
3988 initializer.Type = block_type;
3989 initializer.DelegateType = delegateType;
3991 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3993 state_machine = stateMachine;
3994 initializer.SetStateMachine (stateMachine);
3996 const Flags flags = Flags.CompilerGenerated;
3998 var b = this is ToplevelBlock ?
3999 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4000 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4003 b.state_machine = stateMachine;
4004 b.AddStatement (new AsyncInitializerStatement (initializer));
4012 public class ToplevelBlock : ParametersBlock
4014 LocalVariable this_variable;
4015 CompilerContext compiler;
4016 Dictionary<string, object> names;
4018 List<ExplicitBlock> this_references;
4020 public ToplevelBlock (CompilerContext ctx, Location loc)
4021 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4025 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4026 : base (parameters, start)
4028 this.compiler = ctx;
4032 ProcessParameters ();
4036 // Recreates a top level block from parameters block. Used for
4037 // compiler generated methods where the original block comes from
4038 // explicit child block. This works for already resolved blocks
4039 // only to ensure we resolve them in the correct flow order
4041 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4042 : base (source, parameters)
4044 this.compiler = source.TopBlock.compiler;
4048 public bool IsIterator {
4050 return (flags & Flags.Iterator) != 0;
4053 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4057 public Report Report {
4059 return compiler.Report;
4064 // Used by anonymous blocks to track references of `this' variable
4066 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4068 return this_references;
4073 // Returns the "this" instance variable of this block.
4074 // See AddThisVariable() for more information.
4076 public LocalVariable ThisVariable {
4078 return this_variable;
4082 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4085 names = new Dictionary<string, object> ();
4088 if (!names.TryGetValue (name, out value)) {
4089 names.Add (name, li);
4093 INamedBlockVariable existing = value as INamedBlockVariable;
4094 List<INamedBlockVariable> existing_list;
4095 if (existing != null) {
4096 existing_list = new List<INamedBlockVariable> ();
4097 existing_list.Add (existing);
4098 names[name] = existing_list;
4100 existing_list = (List<INamedBlockVariable>) value;
4104 // A collision checking between local names
4106 var variable_block = li.Block.Explicit;
4107 for (int i = 0; i < existing_list.Count; ++i) {
4108 existing = existing_list[i];
4109 Block b = existing.Block.Explicit;
4111 // Collision at same level
4112 if (variable_block == b) {
4113 li.Block.Error_AlreadyDeclared (name, li);
4117 // Collision with parent
4118 Block parent = variable_block;
4119 while ((parent = parent.Parent) != null) {
4121 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4122 i = existing_list.Count;
4127 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4128 // Collision with children
4129 while ((b = b.Parent) != null) {
4130 if (variable_block == b) {
4131 li.Block.Error_AlreadyDeclared (name, li, "child");
4132 i = existing_list.Count;
4139 existing_list.Add (li);
4142 public void AddLabel (string name, LabeledStatement label)
4145 labels = new Dictionary<string, object> ();
4148 if (!labels.TryGetValue (name, out value)) {
4149 labels.Add (name, label);
4153 LabeledStatement existing = value as LabeledStatement;
4154 List<LabeledStatement> existing_list;
4155 if (existing != null) {
4156 existing_list = new List<LabeledStatement> ();
4157 existing_list.Add (existing);
4158 labels[name] = existing_list;
4160 existing_list = (List<LabeledStatement>) value;
4164 // A collision checking between labels
4166 for (int i = 0; i < existing_list.Count; ++i) {
4167 existing = existing_list[i];
4168 Block b = existing.Block;
4170 // Collision at same level
4171 if (label.Block == b) {
4172 Report.SymbolRelatedToPreviousError (existing.loc, name);
4173 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4177 // Collision with parent
4179 while ((b = b.Parent) != null) {
4180 if (existing.Block == b) {
4181 Report.Error (158, label.loc,
4182 "The label `{0}' shadows another label by the same name in a contained scope", name);
4183 i = existing_list.Count;
4188 // Collision with with children
4190 while ((b = b.Parent) != null) {
4191 if (label.Block == b) {
4192 Report.Error (158, label.loc,
4193 "The label `{0}' shadows another label by the same name in a contained scope", name);
4194 i = existing_list.Count;
4200 existing_list.Add (label);
4203 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4205 if (this_references == null)
4206 this_references = new List<ExplicitBlock> ();
4208 if (!this_references.Contains (block))
4209 this_references.Add (block);
4212 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4214 this_references.Remove (block);
4218 // Creates an arguments set from all parameters, useful for method proxy calls
4220 public Arguments GetAllParametersArguments ()
4222 int count = parameters.Count;
4223 Arguments args = new Arguments (count);
4224 for (int i = 0; i < count; ++i) {
4225 var pi = parameter_info[i];
4226 var arg_expr = GetParameterReference (i, pi.Location);
4228 Argument.AType atype_modifier;
4229 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4230 case Parameter.Modifier.REF:
4231 atype_modifier = Argument.AType.Ref;
4233 case Parameter.Modifier.OUT:
4234 atype_modifier = Argument.AType.Out;
4241 args.Add (new Argument (arg_expr, atype_modifier));
4248 // Lookup inside a block, the returned value can represent 3 states
4250 // true+variable: A local name was found and it's valid
4251 // false+variable: A local name was found in a child block only
4252 // false+null: No local name was found
4254 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4260 if (!names.TryGetValue (name, out value))
4263 variable = value as INamedBlockVariable;
4265 if (variable != null) {
4267 if (variable.Block == b.Original)
4271 } while (b != null);
4279 } while (b != null);
4281 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4282 for (int i = 0; i < list.Count; ++i) {
4285 if (variable.Block == b.Original)
4289 } while (b != null);
4297 } while (b != null);
4307 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4309 if (block.names != null) {
4310 foreach (var n in block.names) {
4311 var variable = n.Value as INamedBlockVariable;
4312 if (variable != null) {
4313 if (variable.Block.ParametersBlock == pb)
4314 AddLocalName (n.Key, variable, false);
4318 foreach (var v in (List<INamedBlockVariable>) n.Value)
4319 if (v.Block.ParametersBlock == pb)
4320 AddLocalName (n.Key, v, false);
4326 // This is used by non-static `struct' constructors which do not have an
4327 // initializer - in this case, the constructor must initialize all of the
4328 // struct's fields. To do this, we add a "this" variable and use the flow
4329 // analysis code to ensure that it's been fully initialized before control
4330 // leaves the constructor.
4332 public void AddThisVariable (BlockContext bc)
4334 if (this_variable != null)
4335 throw new InternalErrorException (StartLocation.ToString ());
4337 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4338 this_variable.Type = bc.CurrentType;
4339 this_variable.PrepareAssignmentAnalysis (bc);
4342 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4345 // If we're a non-static struct constructor which doesn't have an
4346 // initializer, then we must initialize all of the struct's fields.
4348 if (this_variable != null)
4349 this_variable.IsThisAssigned (fc, this);
4351 base.CheckControlExit (fc, dat);
4354 public override void Emit (EmitContext ec)
4356 if (Report.Errors > 0)
4360 if (IsCompilerGenerated) {
4361 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4369 // If `HasReturnLabel' is set, then we already emitted a
4370 // jump to the end of the method, so we must emit a `ret'
4373 // Unfortunately, System.Reflection.Emit automatically emits
4374 // a leave to the end of a finally block. This is a problem
4375 // if no code is following the try/finally block since we may
4376 // jump to a point after the end of the method.
4377 // As a workaround, we're always creating a return label in
4380 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4381 if (ec.HasReturnLabel)
4382 ec.MarkLabel (ec.ReturnLabel);
4384 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4385 ec.Mark (EndLocation);
4387 if (ec.ReturnType.Kind != MemberKind.Void)
4388 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4390 ec.Emit (OpCodes.Ret);
4393 } catch (Exception e) {
4394 throw new InternalErrorException (e, StartLocation);
4398 public bool Resolve (BlockContext bc, IMethodData md)
4403 var errors = bc.Report.Errors;
4407 if (bc.Report.Errors > errors)
4410 MarkReachable (new Reachability ());
4412 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4413 // TODO: var md = bc.CurrentMemberDefinition;
4414 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4417 if ((flags & Flags.NoFlowAnalysis) != 0)
4420 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4423 } catch (Exception e) {
4424 throw new InternalErrorException (e, StartLocation);
4431 public class SwitchLabel : Statement
4439 // if expr == null, then it is the default case.
4441 public SwitchLabel (Expression expr, Location l)
4447 public bool IsDefault {
4449 return label == null;
4453 public Expression Label {
4459 public Location Location {
4465 public Constant Converted {
4474 public bool PatternMatching { get; set; }
4476 public bool SectionStart { get; set; }
4478 public Label GetILLabel (EmitContext ec)
4480 if (il_label == null){
4481 il_label = ec.DefineLabel ();
4484 return il_label.Value;
4487 protected override void DoEmit (EmitContext ec)
4489 ec.MarkLabel (GetILLabel (ec));
4492 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4497 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
4501 public override bool Resolve (BlockContext bc)
4503 if (ResolveAndReduce (bc))
4504 bc.Switch.RegisterLabel (bc, this);
4510 // Resolves the expression, reduces it to a literal if possible
4511 // and then converts it to the requested type.
4513 bool ResolveAndReduce (BlockContext bc)
4518 var switch_statement = bc.Switch;
4520 if (PatternMatching) {
4521 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4522 return label != null;
4525 var c = label.ResolveLabelConstant (bc);
4529 if (switch_statement.IsNullable && c is NullLiteral) {
4534 if (switch_statement.IsPatternMatching) {
4535 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4539 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4540 return converted != null;
4543 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4545 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4546 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4549 protected override void CloneTo (CloneContext clonectx, Statement target)
4551 var t = (SwitchLabel) target;
4553 t.label = label.Clone (clonectx);
4556 public override object Accept (StructuralVisitor visitor)
4558 return visitor.Visit (this);
4561 public string GetSignatureForError ()
4564 if (converted == null)
4567 label = converted.GetValueAsLiteral ();
4569 return string.Format ("case {0}:", label);
4573 public class Switch : LoopStatement
4575 // structure used to hold blocks of keys while calculating table switch
4576 sealed class LabelsRange : IComparable<LabelsRange>
4578 public readonly long min;
4580 public readonly List<long> label_values;
4582 public LabelsRange (long value)
4585 label_values = new List<long> ();
4586 label_values.Add (value);
4589 public LabelsRange (long min, long max, ICollection<long> values)
4593 this.label_values = new List<long> (values);
4598 return max - min + 1;
4602 public bool AddValue (long value)
4604 var gap = value - min + 1;
4605 // Ensure the range has > 50% occupancy
4606 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4610 label_values.Add (value);
4614 public int CompareTo (LabelsRange other)
4616 int nLength = label_values.Count;
4617 int nLengthOther = other.label_values.Count;
4618 if (nLengthOther == nLength)
4619 return (int) (other.min - min);
4621 return nLength - nLengthOther;
4625 sealed class DispatchStatement : Statement
4627 readonly Switch body;
4629 public DispatchStatement (Switch body)
4634 protected override void CloneTo (CloneContext clonectx, Statement target)
4636 throw new NotImplementedException ();
4639 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4644 protected override void DoEmit (EmitContext ec)
4646 body.EmitDispatch (ec);
4650 class MissingBreak : Statement
4652 readonly SwitchLabel label;
4654 public MissingBreak (SwitchLabel sl)
4660 public bool FallOut { get; set; }
4662 protected override void DoEmit (EmitContext ec)
4666 protected override void CloneTo (CloneContext clonectx, Statement target)
4670 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4673 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4674 label.GetSignatureForError ());
4676 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4677 label.GetSignatureForError ());
4683 public Expression Expr;
4686 // Mapping of all labels to their SwitchLabels
4688 Dictionary<long, SwitchLabel> labels;
4689 Dictionary<string, SwitchLabel> string_labels;
4690 List<SwitchLabel> case_labels;
4692 List<Tuple<GotoCase, Constant>> goto_cases;
4693 List<DefiniteAssignmentBitSet> end_reachable_das;
4696 /// The governing switch type
4698 public TypeSpec SwitchType;
4700 Expression new_expr;
4702 SwitchLabel case_null;
4703 SwitchLabel case_default;
4705 Label defaultLabel, nullLabel;
4706 VariableReference value;
4707 ExpressionStatement string_dictionary;
4708 FieldExpr switch_cache_field;
4709 ExplicitBlock block;
4713 // Nullable Types support
4715 Nullable.Unwrap unwrap;
4717 public Switch (Expression e, ExplicitBlock block, Location l)
4725 public SwitchLabel ActiveLabel { get; set; }
4727 public ExplicitBlock Block {
4733 public SwitchLabel DefaultLabel {
4735 return case_default;
4739 public bool IsNullable {
4741 return unwrap != null;
4745 public bool IsPatternMatching {
4747 return new_expr == null && SwitchType != null;
4751 public List<SwitchLabel> RegisteredLabels {
4757 public VariableReference ExpressionValue {
4764 // Determines the governing type for a switch. The returned
4765 // expression might be the expression from the switch, or an
4766 // expression that includes any potential conversions to
4768 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4770 switch (expr.Type.BuiltinType) {
4771 case BuiltinTypeSpec.Type.Byte:
4772 case BuiltinTypeSpec.Type.SByte:
4773 case BuiltinTypeSpec.Type.UShort:
4774 case BuiltinTypeSpec.Type.Short:
4775 case BuiltinTypeSpec.Type.UInt:
4776 case BuiltinTypeSpec.Type.Int:
4777 case BuiltinTypeSpec.Type.ULong:
4778 case BuiltinTypeSpec.Type.Long:
4779 case BuiltinTypeSpec.Type.Char:
4780 case BuiltinTypeSpec.Type.String:
4781 case BuiltinTypeSpec.Type.Bool:
4785 if (expr.Type.IsEnum)
4789 // Try to find a *user* defined implicit conversion.
4791 // If there is no implicit conversion, or if there are multiple
4792 // conversions, we have to report an error
4794 Expression converted = null;
4795 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4797 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4800 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4801 Convert.UserConversionRestriction.ProbingOnly;
4804 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4806 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4811 // Ignore over-worked ImplicitUserConversions that do
4812 // an implicit conversion in addition to the user conversion.
4814 var uc = e as UserCast;
4818 if (converted != null){
4819 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4828 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4830 var types = module.Compiler.BuiltinTypes;
4832 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4833 TypeSpec[] stypes = new[] {
4846 if (nullable != null) {
4848 Array.Resize (ref stypes, stypes.Length + 9);
4850 for (int i = 0; i < 9; ++i) {
4851 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4858 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4860 case_labels.Add (sl);
4863 if (case_default != null) {
4864 sl.Error_AlreadyOccurs (rc, case_default);
4872 if (sl.Converted == null)
4876 if (string_labels != null) {
4877 string string_value = sl.Converted.GetValue () as string;
4878 if (string_value == null)
4881 string_labels.Add (string_value, sl);
4883 if (sl.Converted.IsNull) {
4886 labels.Add (sl.Converted.GetValueAsLong (), sl);
4889 } catch (ArgumentException) {
4890 if (string_labels != null)
4891 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4893 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4898 // This method emits code for a lookup-based switch statement (non-string)
4899 // Basically it groups the cases into blocks that are at least half full,
4900 // and then spits out individual lookup opcodes for each block.
4901 // It emits the longest blocks first, and short blocks are just
4902 // handled with direct compares.
4904 void EmitTableSwitch (EmitContext ec, Expression val)
4906 if (labels != null && labels.Count > 0) {
4907 List<LabelsRange> ranges;
4908 if (string_labels != null) {
4909 // We have done all hard work for string already
4910 // setup single range only
4911 ranges = new List<LabelsRange> (1);
4912 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4914 var element_keys = new long[labels.Count];
4915 labels.Keys.CopyTo (element_keys, 0);
4916 Array.Sort (element_keys);
4919 // Build possible ranges of switch labes to reduce number
4922 ranges = new List<LabelsRange> (element_keys.Length);
4923 var range = new LabelsRange (element_keys[0]);
4925 for (int i = 1; i < element_keys.Length; ++i) {
4926 var l = element_keys[i];
4927 if (range.AddValue (l))
4930 range = new LabelsRange (l);
4934 // sort the blocks so we can tackle the largest ones first
4938 Label lbl_default = defaultLabel;
4939 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4941 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4942 LabelsRange kb = ranges[range_index];
4943 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4945 // Optimize small ranges using simple equality check
4946 if (kb.Range <= 2) {
4947 foreach (var key in kb.label_values) {
4948 SwitchLabel sl = labels[key];
4949 if (sl == case_default || sl == case_null)
4952 if (sl.Converted.IsZeroInteger) {
4953 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4956 sl.Converted.Emit (ec);
4957 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4961 // TODO: if all the keys in the block are the same and there are
4962 // no gaps/defaults then just use a range-check.
4963 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4964 // TODO: optimize constant/I4 cases
4966 // check block range (could be > 2^31)
4968 ec.EmitLong (kb.min);
4969 ec.Emit (OpCodes.Blt, lbl_default);
4972 ec.EmitLong (kb.max);
4973 ec.Emit (OpCodes.Bgt, lbl_default);
4978 ec.EmitLong (kb.min);
4979 ec.Emit (OpCodes.Sub);
4982 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4986 int first = (int) kb.min;
4989 ec.Emit (OpCodes.Sub);
4990 } else if (first < 0) {
4991 ec.EmitInt (-first);
4992 ec.Emit (OpCodes.Add);
4996 // first, build the list of labels for the switch
4998 long cJumps = kb.Range;
4999 Label[] switch_labels = new Label[cJumps];
5000 for (int iJump = 0; iJump < cJumps; iJump++) {
5001 var key = kb.label_values[iKey];
5002 if (key == kb.min + iJump) {
5003 switch_labels[iJump] = labels[key].GetILLabel (ec);
5006 switch_labels[iJump] = lbl_default;
5010 // emit the switch opcode
5011 ec.Emit (OpCodes.Switch, switch_labels);
5014 // mark the default for this block
5015 if (range_index != 0)
5016 ec.MarkLabel (lbl_default);
5019 // the last default just goes to the end
5020 if (ranges.Count > 0)
5021 ec.Emit (OpCodes.Br, lbl_default);
5025 public SwitchLabel FindLabel (Constant value)
5027 SwitchLabel sl = null;
5029 if (string_labels != null) {
5030 string s = value.GetValue () as string;
5032 if (case_null != null)
5034 else if (case_default != null)
5037 string_labels.TryGetValue (s, out sl);
5040 if (value is NullLiteral) {
5043 labels.TryGetValue (value.GetValueAsLong (), out sl);
5047 if (sl == null || sl.SectionStart)
5051 // Always return section start, it simplifies handling of switch labels
5053 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5054 var cs = case_labels [idx];
5055 if (cs.SectionStart)
5060 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5062 Expr.FlowAnalysis (fc);
5064 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5065 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5066 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5068 block.FlowAnalysis (fc);
5070 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5072 if (end_reachable_das != null) {
5073 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5074 InitialDefinitiveAssignment |= sections_das;
5075 end_reachable_das = null;
5078 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5080 return case_default != null && !end_reachable;
5083 public override bool Resolve (BlockContext ec)
5085 Expr = Expr.Resolve (ec);
5090 // LAMESPEC: User conversion from non-nullable governing type has a priority
5092 new_expr = SwitchGoverningType (ec, Expr, false);
5094 if (new_expr == null) {
5095 if (Expr.Type.IsNullableType) {
5096 unwrap = Nullable.Unwrap.Create (Expr, false);
5101 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5102 // involving nullable Expr and nullable governing type is
5104 new_expr = SwitchGoverningType (ec, unwrap, true);
5108 Expression switch_expr;
5109 if (new_expr == null) {
5110 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5111 if (Expr.Type != InternalType.ErrorType) {
5112 ec.Report.Error (151, loc,
5113 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5114 Expr.Type.GetSignatureForError ());
5121 SwitchType = Expr.Type;
5123 switch_expr = new_expr;
5124 SwitchType = new_expr.Type;
5125 if (SwitchType.IsNullableType) {
5126 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5127 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5130 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5131 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5135 if (block.Statements.Count == 0)
5138 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5139 string_labels = new Dictionary<string, SwitchLabel> ();
5141 labels = new Dictionary<long, SwitchLabel> ();
5145 var constant = switch_expr as Constant;
5148 // Don't need extra variable for constant switch or switch with
5149 // only default case
5151 if (constant == null) {
5153 // Store switch expression for comparison purposes
5155 value = switch_expr as VariableReference;
5156 if (value == null && !HasOnlyDefaultSection ()) {
5157 var current_block = ec.CurrentBlock;
5158 ec.CurrentBlock = Block;
5159 // Create temporary variable inside switch scope
5160 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5162 ec.CurrentBlock = current_block;
5166 case_labels = new List<SwitchLabel> ();
5168 Switch old_switch = ec.Switch;
5170 var parent_los = ec.EnclosingLoopOrSwitch;
5171 ec.EnclosingLoopOrSwitch = this;
5173 var ok = Statement.Resolve (ec);
5175 ec.EnclosingLoopOrSwitch = parent_los;
5176 ec.Switch = old_switch;
5179 // Check if all goto cases are valid. Needs to be done after switch
5180 // is resolved because goto can jump forward in the scope.
5182 if (goto_cases != null) {
5183 foreach (var gc in goto_cases) {
5184 if (gc.Item1 == null) {
5185 if (DefaultLabel == null) {
5186 Goto.Error_UnknownLabel (ec, "default", loc);
5192 var sl = FindLabel (gc.Item2);
5194 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5196 gc.Item1.Label = sl;
5204 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5205 ResolveStringSwitchMap (ec);
5209 // Anonymous storey initialization has to happen before
5210 // any generated switch dispatch
5212 block.InsertStatement (0, new DispatchStatement (this));
5217 bool HasOnlyDefaultSection ()
5219 for (int i = 0; i < block.Statements.Count; ++i) {
5220 var s = block.Statements[i] as SwitchLabel;
5222 if (s == null || s.IsDefault)
5231 public override Reachability MarkReachable (Reachability rc)
5233 if (rc.IsUnreachable)
5236 base.MarkReachable (rc);
5238 block.MarkReachableScope (rc);
5240 if (block.Statements.Count == 0)
5243 SwitchLabel constant_label = null;
5244 var constant = new_expr as Constant;
5246 if (constant != null) {
5247 constant_label = FindLabel (constant) ?? case_default;
5248 if (constant_label == null) {
5249 block.Statements.RemoveAt (0);
5254 var section_rc = new Reachability ();
5255 SwitchLabel prev_label = null;
5257 for (int i = 0; i < block.Statements.Count; ++i) {
5258 var s = block.Statements[i];
5259 var sl = s as SwitchLabel;
5261 if (sl != null && sl.SectionStart) {
5263 // Section is marked already via goto case
5265 if (!sl.IsUnreachable) {
5266 section_rc = new Reachability ();
5270 if (constant_label != null && constant_label != sl)
5271 section_rc = Reachability.CreateUnreachable ();
5272 else if (section_rc.IsUnreachable) {
5273 section_rc = new Reachability ();
5275 if (prev_label != null) {
5276 sl.SectionStart = false;
5277 s = new MissingBreak (prev_label);
5278 s.MarkReachable (rc);
5279 block.Statements.Insert (i - 1, s);
5287 section_rc = s.MarkReachable (section_rc);
5290 if (!section_rc.IsUnreachable && prev_label != null) {
5291 prev_label.SectionStart = false;
5292 var s = new MissingBreak (prev_label) {
5296 s.MarkReachable (rc);
5297 block.Statements.Add (s);
5301 // Reachability can affect parent only when all possible paths are handled but
5302 // we still need to run reachability check on switch body to check for fall-through
5304 if (case_default == null && constant_label == null)
5308 // We have at least one local exit from the switch
5313 return Reachability.CreateUnreachable ();
5316 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5318 if (goto_cases == null)
5319 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5321 goto_cases.Add (Tuple.Create (gotoCase, value));
5325 // Converts string switch into string hashtable
5327 void ResolveStringSwitchMap (ResolveContext ec)
5329 FullNamedExpression string_dictionary_type;
5330 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5331 string_dictionary_type = new TypeExpression (
5332 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5333 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5335 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5336 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5338 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5342 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5343 Field field = new Field (ctype, string_dictionary_type,
5344 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5345 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5346 if (!field.Define ())
5348 ctype.AddField (field);
5350 var init = new List<Expression> ();
5352 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5353 string value = null;
5355 foreach (SwitchLabel sl in case_labels) {
5357 if (sl.SectionStart)
5358 labels.Add (++counter, sl);
5360 if (sl == case_default || sl == case_null)
5363 value = (string) sl.Converted.GetValue ();
5364 var init_args = new List<Expression> (2);
5365 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5367 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5368 init_args.Add (sl.Converted);
5370 init.Add (new CollectionElementInitializer (init_args, loc));
5373 Arguments args = new Arguments (1);
5374 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5375 Expression initializer = new NewInitialize (string_dictionary_type, args,
5376 new CollectionOrObjectInitializers (init, loc), loc);
5378 switch_cache_field = new FieldExpr (field, loc);
5379 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5382 void DoEmitStringSwitch (EmitContext ec)
5384 Label l_initialized = ec.DefineLabel ();
5387 // Skip initialization when value is null
5389 value.EmitBranchable (ec, nullLabel, false);
5392 // Check if string dictionary is initialized and initialize
5394 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5395 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5396 string_dictionary.EmitStatement (ec);
5398 ec.MarkLabel (l_initialized);
5400 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5402 ResolveContext rc = new ResolveContext (ec.MemberContext);
5404 if (switch_cache_field.Type.IsGeneric) {
5405 Arguments get_value_args = new Arguments (2);
5406 get_value_args.Add (new Argument (value));
5407 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5408 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5409 if (get_item == null)
5413 // A value was not found, go to default case
5415 get_item.EmitBranchable (ec, defaultLabel, false);
5417 Arguments get_value_args = new Arguments (1);
5418 get_value_args.Add (new Argument (value));
5420 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5421 if (get_item == null)
5424 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5425 get_item_object.EmitAssign (ec, get_item, true, false);
5426 ec.Emit (OpCodes.Brfalse, defaultLabel);
5428 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5429 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5431 get_item_int.EmitStatement (ec);
5432 get_item_object.Release (ec);
5435 EmitTableSwitch (ec, string_switch_variable);
5436 string_switch_variable.Release (ec);
5440 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5442 void EmitShortSwitch (EmitContext ec)
5444 MethodSpec equal_method = null;
5445 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5446 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5449 if (equal_method != null) {
5450 value.EmitBranchable (ec, nullLabel, false);
5453 for (int i = 0; i < case_labels.Count; ++i) {
5454 var label = case_labels [i];
5455 if (label == case_default || label == case_null)
5458 var constant = label.Converted;
5460 if (constant == null) {
5461 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5465 if (equal_method != null) {
5469 var call = new CallEmitter ();
5470 call.EmitPredefined (ec, equal_method, new Arguments (0));
5471 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5475 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5476 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5482 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5485 ec.Emit (OpCodes.Br, defaultLabel);
5488 void EmitDispatch (EmitContext ec)
5490 if (IsPatternMatching) {
5491 EmitShortSwitch (ec);
5495 if (value == null) {
5497 // Constant switch, we've already done the work if there is only 1 label
5501 foreach (var sl in case_labels) {
5502 if (sl.IsUnreachable)
5505 if (reachable++ > 0) {
5506 var constant = (Constant) new_expr;
5507 var constant_label = FindLabel (constant) ?? case_default;
5509 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5517 if (string_dictionary != null) {
5518 DoEmitStringSwitch (ec);
5519 } else if (case_labels.Count < 4 || string_labels != null) {
5520 EmitShortSwitch (ec);
5522 EmitTableSwitch (ec, value);
5526 protected override void DoEmit (EmitContext ec)
5529 // Setup the codegen context
5531 Label old_end = ec.LoopEnd;
5532 Switch old_switch = ec.Switch;
5534 ec.LoopEnd = ec.DefineLabel ();
5537 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5538 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5540 if (value != null) {
5543 var switch_expr = new_expr ?? Expr;
5545 unwrap.EmitCheck (ec);
5546 ec.Emit (OpCodes.Brfalse, nullLabel);
5547 value.EmitAssign (ec, switch_expr, false, false);
5548 } else if (switch_expr != value) {
5549 value.EmitAssign (ec, switch_expr, false, false);
5554 // Next statement is compiler generated we don't need extra
5555 // nop when we can use the statement for sequence point
5557 ec.Mark (block.StartLocation);
5558 block.IsCompilerGenerated = true;
5560 new_expr.EmitSideEffect (ec);
5565 // Restore context state.
5566 ec.MarkLabel (ec.LoopEnd);
5569 // Restore the previous context
5571 ec.LoopEnd = old_end;
5572 ec.Switch = old_switch;
5575 protected override void CloneTo (CloneContext clonectx, Statement t)
5577 Switch target = (Switch) t;
5579 target.Expr = Expr.Clone (clonectx);
5580 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5583 public override object Accept (StructuralVisitor visitor)
5585 return visitor.Visit (this);
5588 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5590 if (case_default == null && !(new_expr is Constant))
5593 if (end_reachable_das == null)
5594 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5596 end_reachable_das.Add (fc.DefiniteAssignment);
5599 public override void SetEndReachable ()
5601 end_reachable = true;
5605 // A place where execution can restart in a state machine
5606 public abstract class ResumableStatement : Statement
5609 protected Label resume_point;
5611 public Label PrepareForEmit (EmitContext ec)
5615 resume_point = ec.DefineLabel ();
5617 return resume_point;
5620 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5625 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5630 public abstract class TryFinallyBlock : ExceptionStatement
5632 protected Statement stmt;
5633 Label dispose_try_block;
5634 bool prepared_for_dispose, emitted_dispose;
5635 Method finally_host;
5637 protected TryFinallyBlock (Statement stmt, Location loc)
5645 public Statement Statement {
5653 protected abstract void EmitTryBody (EmitContext ec);
5654 public abstract void EmitFinallyBody (EmitContext ec);
5656 public override Label PrepareForDispose (EmitContext ec, Label end)
5658 if (!prepared_for_dispose) {
5659 prepared_for_dispose = true;
5660 dispose_try_block = ec.DefineLabel ();
5662 return dispose_try_block;
5665 protected sealed override void DoEmit (EmitContext ec)
5667 EmitTryBodyPrepare (ec);
5670 bool beginFinally = EmitBeginFinallyBlock (ec);
5672 Label start_finally = ec.DefineLabel ();
5673 if (resume_points != null && beginFinally) {
5674 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5676 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5677 ec.Emit (OpCodes.Brfalse_S, start_finally);
5678 ec.Emit (OpCodes.Endfinally);
5681 ec.MarkLabel (start_finally);
5683 if (finally_host != null) {
5684 finally_host.Define ();
5685 finally_host.PrepareEmit ();
5686 finally_host.Emit ();
5688 // Now it's safe to add, to close it properly and emit sequence points
5689 finally_host.Parent.AddMember (finally_host);
5691 var ce = new CallEmitter ();
5692 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5693 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5695 EmitFinallyBody (ec);
5699 ec.EndExceptionBlock ();
5702 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5704 if (emitted_dispose)
5707 emitted_dispose = true;
5709 Label end_of_try = ec.DefineLabel ();
5711 // Ensure that the only way we can get into this code is through a dispatcher
5712 if (have_dispatcher)
5713 ec.Emit (OpCodes.Br, end);
5715 ec.BeginExceptionBlock ();
5717 ec.MarkLabel (dispose_try_block);
5719 Label[] labels = null;
5720 for (int i = 0; i < resume_points.Count; ++i) {
5721 ResumableStatement s = resume_points[i];
5722 Label ret = s.PrepareForDispose (ec, end_of_try);
5723 if (ret.Equals (end_of_try) && labels == null)
5725 if (labels == null) {
5726 labels = new Label[resume_points.Count];
5727 for (int j = 0; j < i; ++j)
5728 labels[j] = end_of_try;
5733 if (labels != null) {
5735 for (j = 1; j < labels.Length; ++j)
5736 if (!labels[0].Equals (labels[j]))
5738 bool emit_dispatcher = j < labels.Length;
5740 if (emit_dispatcher) {
5741 ec.Emit (OpCodes.Ldloc, pc);
5742 ec.EmitInt (first_resume_pc);
5743 ec.Emit (OpCodes.Sub);
5744 ec.Emit (OpCodes.Switch, labels);
5747 foreach (ResumableStatement s in resume_points)
5748 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5751 ec.MarkLabel (end_of_try);
5753 ec.BeginFinallyBlock ();
5755 if (finally_host != null) {
5756 var ce = new CallEmitter ();
5757 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5758 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5760 EmitFinallyBody (ec);
5763 ec.EndExceptionBlock ();
5766 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5768 var res = stmt.FlowAnalysis (fc);
5773 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5775 ec.BeginFinallyBlock ();
5779 public override Reachability MarkReachable (Reachability rc)
5781 base.MarkReachable (rc);
5782 return Statement.MarkReachable (rc);
5785 public override bool Resolve (BlockContext bc)
5789 parent = bc.CurrentTryBlock;
5790 bc.CurrentTryBlock = this;
5792 using (bc.Set (ResolveContext.Options.TryScope)) {
5793 ok = stmt.Resolve (bc);
5796 bc.CurrentTryBlock = parent;
5799 // Finally block inside iterator is called from MoveNext and
5800 // Dispose methods that means we need to lift the block into
5801 // newly created host method to emit the body only once. The
5802 // original block then simply calls the newly generated method.
5804 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5805 var b = stmt as Block;
5806 if (b != null && b.Explicit.HasYield) {
5807 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5811 return base.Resolve (bc) && ok;
5816 // Base class for blocks using exception handling
5818 public abstract class ExceptionStatement : ResumableStatement
5820 protected List<ResumableStatement> resume_points;
5821 protected int first_resume_pc;
5822 protected ExceptionStatement parent;
5824 protected ExceptionStatement (Location loc)
5829 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5831 StateMachineInitializer state_machine = null;
5832 if (resume_points != null) {
5833 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5835 ec.EmitInt ((int) IteratorStorey.State.Running);
5836 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5839 ec.BeginExceptionBlock ();
5841 if (resume_points != null) {
5842 ec.MarkLabel (resume_point);
5844 // For normal control flow, we want to fall-through the Switch
5845 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5846 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5847 ec.EmitInt (first_resume_pc);
5848 ec.Emit (OpCodes.Sub);
5850 Label[] labels = new Label[resume_points.Count];
5851 for (int i = 0; i < resume_points.Count; ++i)
5852 labels[i] = resume_points[i].PrepareForEmit (ec);
5853 ec.Emit (OpCodes.Switch, labels);
5857 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5859 if (parent != null) {
5860 // TODO: MOVE to virtual TryCatch
5861 var tc = this as TryCatch;
5862 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5864 pc = parent.AddResumePoint (s, pc, stateMachine);
5866 pc = stateMachine.AddResumePoint (this);
5869 if (resume_points == null) {
5870 resume_points = new List<ResumableStatement> ();
5871 first_resume_pc = pc;
5874 if (pc != first_resume_pc + resume_points.Count)
5875 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5877 resume_points.Add (stmt);
5882 public class Lock : TryFinallyBlock
5885 TemporaryVariableReference expr_copy;
5886 TemporaryVariableReference lock_taken;
5888 public Lock (Expression expr, Statement stmt, Location loc)
5894 public Expression Expr {
5900 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5902 expr.FlowAnalysis (fc);
5903 return base.DoFlowAnalysis (fc);
5906 public override bool Resolve (BlockContext ec)
5908 expr = expr.Resolve (ec);
5912 if (!TypeSpec.IsReferenceType (expr.Type)) {
5913 ec.Report.Error (185, loc,
5914 "`{0}' is not a reference type as required by the lock statement",
5915 expr.Type.GetSignatureForError ());
5918 if (expr.Type.IsGenericParameter) {
5919 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5922 VariableReference lv = expr as VariableReference;
5925 locked = lv.IsLockedByStatement;
5926 lv.IsLockedByStatement = true;
5933 // Have to keep original lock value around to unlock same location
5934 // in the case of original value has changed or is null
5936 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5937 expr_copy.Resolve (ec);
5940 // Ensure Monitor methods are available
5942 if (ResolvePredefinedMethods (ec) > 1) {
5943 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5944 lock_taken.Resolve (ec);
5947 using (ec.Set (ResolveContext.Options.LockScope)) {
5952 lv.IsLockedByStatement = locked;
5958 protected override void EmitTryBodyPrepare (EmitContext ec)
5960 expr_copy.EmitAssign (ec, expr);
5962 if (lock_taken != null) {
5964 // Initialize ref variable
5966 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5969 // Monitor.Enter (expr_copy)
5971 expr_copy.Emit (ec);
5972 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5975 base.EmitTryBodyPrepare (ec);
5978 protected override void EmitTryBody (EmitContext ec)
5981 // Monitor.Enter (expr_copy, ref lock_taken)
5983 if (lock_taken != null) {
5984 expr_copy.Emit (ec);
5985 lock_taken.LocalInfo.CreateBuilder (ec);
5986 lock_taken.AddressOf (ec, AddressOp.Load);
5987 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
5990 Statement.Emit (ec);
5993 public override void EmitFinallyBody (EmitContext ec)
5996 // if (lock_taken) Monitor.Exit (expr_copy)
5998 Label skip = ec.DefineLabel ();
6000 if (lock_taken != null) {
6001 lock_taken.Emit (ec);
6002 ec.Emit (OpCodes.Brfalse_S, skip);
6005 expr_copy.Emit (ec);
6006 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6008 ec.Emit (OpCodes.Call, m);
6010 ec.MarkLabel (skip);
6013 int ResolvePredefinedMethods (ResolveContext rc)
6015 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6016 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6020 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6024 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6028 protected override void CloneTo (CloneContext clonectx, Statement t)
6030 Lock target = (Lock) t;
6032 target.expr = expr.Clone (clonectx);
6033 target.stmt = Statement.Clone (clonectx);
6036 public override object Accept (StructuralVisitor visitor)
6038 return visitor.Visit (this);
6043 public class Unchecked : Statement {
6046 public Unchecked (Block b, Location loc)
6053 public override bool Resolve (BlockContext ec)
6055 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6056 return Block.Resolve (ec);
6059 protected override void DoEmit (EmitContext ec)
6061 using (ec.With (EmitContext.Options.CheckedScope, false))
6065 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6067 return Block.FlowAnalysis (fc);
6070 public override Reachability MarkReachable (Reachability rc)
6072 base.MarkReachable (rc);
6073 return Block.MarkReachable (rc);
6076 protected override void CloneTo (CloneContext clonectx, Statement t)
6078 Unchecked target = (Unchecked) t;
6080 target.Block = clonectx.LookupBlock (Block);
6083 public override object Accept (StructuralVisitor visitor)
6085 return visitor.Visit (this);
6089 public class Checked : Statement {
6092 public Checked (Block b, Location loc)
6095 b.Unchecked = false;
6099 public override bool Resolve (BlockContext ec)
6101 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6102 return Block.Resolve (ec);
6105 protected override void DoEmit (EmitContext ec)
6107 using (ec.With (EmitContext.Options.CheckedScope, true))
6111 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6113 return Block.FlowAnalysis (fc);
6116 public override Reachability MarkReachable (Reachability rc)
6118 base.MarkReachable (rc);
6119 return Block.MarkReachable (rc);
6122 protected override void CloneTo (CloneContext clonectx, Statement t)
6124 Checked target = (Checked) t;
6126 target.Block = clonectx.LookupBlock (Block);
6129 public override object Accept (StructuralVisitor visitor)
6131 return visitor.Visit (this);
6135 public class Unsafe : Statement {
6138 public Unsafe (Block b, Location loc)
6141 Block.Unsafe = true;
6145 public override bool Resolve (BlockContext ec)
6147 if (ec.CurrentIterator != null)
6148 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6150 using (ec.Set (ResolveContext.Options.UnsafeScope))
6151 return Block.Resolve (ec);
6154 protected override void DoEmit (EmitContext ec)
6159 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6161 return Block.FlowAnalysis (fc);
6164 public override Reachability MarkReachable (Reachability rc)
6166 base.MarkReachable (rc);
6167 return Block.MarkReachable (rc);
6170 protected override void CloneTo (CloneContext clonectx, Statement t)
6172 Unsafe target = (Unsafe) t;
6174 target.Block = clonectx.LookupBlock (Block);
6177 public override object Accept (StructuralVisitor visitor)
6179 return visitor.Visit (this);
6186 public class Fixed : Statement
6188 abstract class Emitter : ShimExpression
6190 protected LocalVariable vi;
6192 protected Emitter (Expression expr, LocalVariable li)
6198 public abstract void EmitExit (EmitContext ec);
6200 public override void FlowAnalysis (FlowAnalysisContext fc)
6202 expr.FlowAnalysis (fc);
6206 class ExpressionEmitter : Emitter {
6207 public ExpressionEmitter (Expression converted, LocalVariable li) :
6208 base (converted, li)
6212 protected override Expression DoResolve (ResolveContext rc)
6214 throw new NotImplementedException ();
6217 public override void Emit (EmitContext ec) {
6219 // Store pointer in pinned location
6225 public override void EmitExit (EmitContext ec)
6228 ec.Emit (OpCodes.Conv_U);
6233 class StringEmitter : Emitter
6235 LocalVariable pinned_string;
6237 public StringEmitter (Expression expr, LocalVariable li)
6242 protected override Expression DoResolve (ResolveContext rc)
6244 pinned_string = new LocalVariable (vi.Block, "$pinned",
6245 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6247 pinned_string.Type = rc.BuiltinTypes.String;
6249 eclass = ExprClass.Variable;
6250 type = rc.BuiltinTypes.Int;
6254 public override void Emit (EmitContext ec)
6256 pinned_string.CreateBuilder (ec);
6259 pinned_string.EmitAssign (ec);
6261 // TODO: Should use Binary::Add
6262 pinned_string.Emit (ec);
6263 ec.Emit (OpCodes.Conv_I);
6265 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6269 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6270 //pe.InstanceExpression = pinned_string;
6271 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6273 ec.Emit (OpCodes.Add);
6277 public override void EmitExit (EmitContext ec)
6280 pinned_string.EmitAssign (ec);
6284 public class VariableDeclaration : BlockVariable
6286 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6291 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6293 if (!Variable.Type.IsPointer && li == Variable) {
6294 bc.Report.Error (209, TypeExpression.Location,
6295 "The type of locals declared in a fixed statement must be a pointer type");
6300 // The rules for the possible declarators are pretty wise,
6301 // but the production on the grammar is more concise.
6303 // So we have to enforce these rules here.
6305 // We do not resolve before doing the case 1 test,
6306 // because the grammar is explicit in that the token &
6307 // is present, so we need to test for this particular case.
6310 if (initializer is Cast) {
6311 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6315 initializer = initializer.Resolve (bc);
6317 if (initializer == null)
6323 if (initializer.Type.IsArray) {
6324 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
6327 // Provided that array_type is unmanaged,
6329 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6333 // and T* is implicitly convertible to the
6334 // pointer type given in the fixed statement.
6336 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
6338 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6339 if (converted == null)
6343 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6345 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6346 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
6347 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6348 new NullLiteral (loc),
6351 converted = converted.Resolve (bc);
6353 return new ExpressionEmitter (converted, li);
6359 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6360 return new StringEmitter (initializer, li).Resolve (bc);
6363 // Case 3: fixed buffer
6364 if (initializer is FixedBufferPtr) {
6365 return new ExpressionEmitter (initializer, li);
6369 // Case 4: & object.
6371 bool already_fixed = true;
6372 Unary u = initializer as Unary;
6373 if (u != null && u.Oper == Unary.Operator.AddressOf) {
6374 IVariableReference vr = u.Expr as IVariableReference;
6375 if (vr == null || !vr.IsFixed) {
6376 already_fixed = false;
6380 if (already_fixed) {
6381 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6384 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
6385 return new ExpressionEmitter (initializer, li);
6390 VariableDeclaration decl;
6391 Statement statement;
6394 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6403 public Statement Statement {
6409 public BlockVariable Variables {
6417 public override bool Resolve (BlockContext bc)
6419 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6420 if (!decl.Resolve (bc))
6424 return statement.Resolve (bc);
6427 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6429 decl.FlowAnalysis (fc);
6430 return statement.FlowAnalysis (fc);
6433 protected override void DoEmit (EmitContext ec)
6435 decl.Variable.CreateBuilder (ec);
6436 decl.Initializer.Emit (ec);
6437 if (decl.Declarators != null) {
6438 foreach (var d in decl.Declarators) {
6439 d.Variable.CreateBuilder (ec);
6440 d.Initializer.Emit (ec);
6444 statement.Emit (ec);
6450 // Clear the pinned variable
6452 ((Emitter) decl.Initializer).EmitExit (ec);
6453 if (decl.Declarators != null) {
6454 foreach (var d in decl.Declarators) {
6455 ((Emitter)d.Initializer).EmitExit (ec);
6460 public override Reachability MarkReachable (Reachability rc)
6462 base.MarkReachable (rc);
6464 decl.MarkReachable (rc);
6466 rc = statement.MarkReachable (rc);
6468 // TODO: What if there is local exit?
6469 has_ret = rc.IsUnreachable;
6473 protected override void CloneTo (CloneContext clonectx, Statement t)
6475 Fixed target = (Fixed) t;
6477 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6478 target.statement = statement.Clone (clonectx);
6481 public override object Accept (StructuralVisitor visitor)
6483 return visitor.Visit (this);
6487 public class Catch : Statement
6489 class FilterStatement : Statement
6491 readonly Catch ctch;
6493 public FilterStatement (Catch ctch)
6498 protected override void CloneTo (CloneContext clonectx, Statement target)
6502 protected override void DoEmit (EmitContext ec)
6504 if (ctch.li != null) {
6505 if (ctch.hoisted_temp != null)
6506 ctch.hoisted_temp.Emit (ec);
6510 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6511 ec.Emit (OpCodes.Box, ctch.type);
6514 var expr_start = ec.DefineLabel ();
6515 var end = ec.DefineLabel ();
6517 ec.Emit (OpCodes.Brtrue_S, expr_start);
6519 ec.Emit (OpCodes.Br, end);
6520 ec.MarkLabel (expr_start);
6522 ctch.Filter.Emit (ec);
6525 ec.Emit (OpCodes.Endfilter);
6526 ec.BeginFilterHandler ();
6527 ec.Emit (OpCodes.Pop);
6530 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6532 ctch.Filter.FlowAnalysis (fc);
6536 public override bool Resolve (BlockContext bc)
6538 ctch.Filter = ctch.Filter.Resolve (bc);
6540 if (ctch.Filter != null) {
6541 if (ctch.Filter.ContainsEmitWithAwait ()) {
6542 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6545 var c = ctch.Filter as Constant;
6546 if (c != null && !c.IsDefaultValue) {
6547 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6555 ExplicitBlock block;
6557 FullNamedExpression type_expr;
6558 CompilerAssign assign;
6560 LocalTemporary hoisted_temp;
6562 public Catch (ExplicitBlock block, Location loc)
6570 public ExplicitBlock Block {
6576 public TypeSpec CatchType {
6582 public Expression Filter {
6586 public bool IsGeneral {
6588 return type_expr == null;
6592 public FullNamedExpression TypeExpression {
6601 public LocalVariable Variable {
6612 protected override void DoEmit (EmitContext ec)
6614 if (Filter != null) {
6615 ec.BeginExceptionFilterBlock ();
6616 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6619 EmitCatchVariableStore (ec);
6621 if (Block.HasAwait) {
6622 Block.EmitScopeInitialization (ec);
6631 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6633 ec.BeginCatchBlock (CatchType);
6636 EmitCatchVariableStore (ec);
6638 ec.Emit (OpCodes.Pop);
6641 if (!Block.HasAwait)
6645 void EmitCatchVariableStore (EmitContext ec)
6647 li.CreateBuilder (ec);
6650 // Special case hoisted catch variable, we have to use a temporary variable
6651 // to pass via anonymous storey initialization with the value still on top
6654 if (li.HoistedVariant != null) {
6655 hoisted_temp = new LocalTemporary (li.Type);
6656 hoisted_temp.Store (ec);
6658 // switch to assignment from temporary variable and not from top of the stack
6659 assign.UpdateSource (hoisted_temp);
6663 public override bool Resolve (BlockContext bc)
6665 using (bc.Set (ResolveContext.Options.CatchScope)) {
6666 if (type_expr == null) {
6667 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6668 Expression source = new EmptyExpression (li.Type);
6669 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6670 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6673 type = type_expr.ResolveAsType (bc);
6678 CreateExceptionVariable (type);
6680 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6681 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6682 } else if (li != null) {
6684 li.PrepareAssignmentAnalysis (bc);
6686 // source variable is at the top of the stack
6687 Expression source = new EmptyExpression (li.Type);
6688 if (li.Type.IsGenericParameter)
6689 source = new UnboxCast (source, li.Type);
6692 // Uses Location.Null to hide from symbol file
6694 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6695 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6699 if (Filter != null) {
6700 Block.AddScopeStatement (new FilterStatement (this));
6703 Block.SetCatchBlock ();
6704 return Block.Resolve (bc);
6708 bool CreateExceptionVariable (TypeSpec type)
6710 if (!Block.HasAwait)
6713 // TODO: Scan the block for rethrow expression
6714 //if (!Block.HasRethrow)
6717 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6721 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6723 if (li != null && !li.IsCompilerGenerated) {
6724 fc.SetVariableAssigned (li.VariableInfo, true);
6727 return block.FlowAnalysis (fc);
6730 public override Reachability MarkReachable (Reachability rc)
6732 base.MarkReachable (rc);
6734 var c = Filter as Constant;
6735 if (c != null && c.IsDefaultValue)
6736 return Reachability.CreateUnreachable ();
6738 return block.MarkReachable (rc);
6741 protected override void CloneTo (CloneContext clonectx, Statement t)
6743 Catch target = (Catch) t;
6745 if (type_expr != null)
6746 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6749 target.Filter = Filter.Clone (clonectx);
6751 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6755 public class TryFinally : TryFinallyBlock
6758 List<DefiniteAssignmentBitSet> try_exit_dat;
6759 List<Label> redirected_jumps;
6760 Label? start_fin_label;
6762 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6768 public ExplicitBlock FinallyBlock {
6774 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6776 if (try_exit_dat == null)
6777 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6779 try_exit_dat.Add (vector);
6782 public override bool Resolve (BlockContext bc)
6784 bool ok = base.Resolve (bc);
6786 fini.SetFinallyBlock ();
6787 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6788 ok &= fini.Resolve (bc);
6794 protected override void EmitTryBody (EmitContext ec)
6796 if (fini.HasAwait) {
6797 if (ec.TryFinallyUnwind == null)
6798 ec.TryFinallyUnwind = new List<TryFinally> ();
6800 ec.TryFinallyUnwind.Add (this);
6802 ec.TryFinallyUnwind.Remove (this);
6804 if (start_fin_label != null)
6805 ec.MarkLabel (start_fin_label.Value);
6813 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6818 return base.EmitBeginFinallyBlock (ec);
6821 public override void EmitFinallyBody (EmitContext ec)
6823 if (!fini.HasAwait) {
6829 // Emits catch block like
6831 // catch (object temp) {
6832 // this.exception_field = temp;
6835 var type = ec.BuiltinTypes.Object;
6836 ec.BeginCatchBlock (type);
6838 var temp = ec.GetTemporaryLocal (type);
6839 ec.Emit (OpCodes.Stloc, temp);
6841 var exception_field = ec.GetTemporaryField (type);
6843 ec.Emit (OpCodes.Ldloc, temp);
6844 exception_field.EmitAssignFromStack (ec);
6846 ec.EndExceptionBlock ();
6848 ec.FreeTemporaryLocal (temp, type);
6853 // Emits exception rethrow
6855 // if (this.exception_field != null)
6856 // throw this.exception_field;
6858 exception_field.Emit (ec);
6859 var skip_throw = ec.DefineLabel ();
6860 ec.Emit (OpCodes.Brfalse_S, skip_throw);
6861 exception_field.Emit (ec);
6862 ec.Emit (OpCodes.Throw);
6863 ec.MarkLabel (skip_throw);
6865 exception_field.IsAvailableForReuse = true;
6867 EmitUnwindFinallyTable (ec);
6870 bool IsParentBlock (Block block)
6872 for (Block b = fini; b != null; b = b.Parent) {
6880 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
6883 if (labelBlock != null) {
6884 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
6885 var fin = ec.TryFinallyUnwind [idx - 1];
6886 if (!fin.IsParentBlock (labelBlock))
6893 bool set_return_state = true;
6895 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
6896 var fin = ec.TryFinallyUnwind [idx];
6897 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
6900 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
6901 set_return_state = false;
6903 if (fin.start_fin_label == null) {
6904 fin.start_fin_label = ec.DefineLabel ();
6907 label = fin.start_fin_label.Value;
6913 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
6915 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
6918 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
6920 if (redirected_jumps == null) {
6921 redirected_jumps = new List<Label> ();
6923 // Add fallthrough label
6924 redirected_jumps.Add (ec.DefineLabel ());
6927 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
6930 int index = redirected_jumps.IndexOf (label);
6932 redirected_jumps.Add (label);
6933 index = redirected_jumps.Count - 1;
6937 // Indicates we have captured exit jump
6939 if (setReturnState) {
6940 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
6941 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
6946 // Emits state table of jumps outside of try block and reload of return
6947 // value when try block returns value
6949 void EmitUnwindFinallyTable (EmitContext ec)
6951 if (redirected_jumps == null)
6954 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
6955 initializer.HoistedReturnState.EmitLoad (ec);
6956 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
6958 // Mark fallthrough label
6959 ec.MarkLabel (redirected_jumps [0]);
6962 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6964 var da = fc.BranchDefiniteAssignment ();
6966 var tf = fc.TryFinally;
6967 fc.TryFinally = this;
6969 var res_stmt = Statement.FlowAnalysis (fc);
6973 var try_da = fc.DefiniteAssignment;
6974 fc.DefiniteAssignment = da;
6976 var res_fin = fini.FlowAnalysis (fc);
6978 if (try_exit_dat != null) {
6980 // try block has global exit but we need to run definite assignment check
6981 // for parameter block out parameter after finally block because it's always
6982 // executed before exit
6984 foreach (var try_da_part in try_exit_dat)
6985 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
6987 try_exit_dat = null;
6990 fc.DefiniteAssignment |= try_da;
6991 return res_stmt | res_fin;
6994 public override Reachability MarkReachable (Reachability rc)
6997 // Mark finally block first for any exit statement in try block
6998 // to know whether the code which follows finally is reachable
7000 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7003 protected override void CloneTo (CloneContext clonectx, Statement t)
7005 TryFinally target = (TryFinally) t;
7007 target.stmt = stmt.Clone (clonectx);
7009 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7012 public override object Accept (StructuralVisitor visitor)
7014 return visitor.Visit (this);
7018 public class TryCatch : ExceptionStatement
7021 List<Catch> clauses;
7022 readonly bool inside_try_finally;
7023 List<Catch> catch_sm;
7025 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7029 this.clauses = catch_clauses;
7030 this.inside_try_finally = inside_try_finally;
7033 public List<Catch> Clauses {
7039 public bool IsTryCatchFinally {
7041 return inside_try_finally;
7045 public override bool Resolve (BlockContext bc)
7049 using (bc.Set (ResolveContext.Options.TryScope)) {
7050 parent = bc.CurrentTryBlock;
7052 if (IsTryCatchFinally) {
7053 ok = Block.Resolve (bc);
7055 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7056 bc.CurrentTryBlock = this;
7057 ok = Block.Resolve (bc);
7058 bc.CurrentTryBlock = parent;
7063 for (int i = 0; i < clauses.Count; ++i) {
7066 ok &= c.Resolve (bc);
7068 if (c.Block.HasAwait) {
7069 if (catch_sm == null)
7070 catch_sm = new List<Catch> ();
7075 if (c.Filter != null)
7078 TypeSpec resolved_type = c.CatchType;
7079 if (resolved_type == null)
7082 for (int ii = 0; ii < clauses.Count; ++ii) {
7086 if (clauses[ii].Filter != null)
7089 if (clauses[ii].IsGeneral) {
7090 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7093 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7096 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7099 bc.Report.Warning (1058, 1, c.loc,
7100 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7108 var ct = clauses[ii].CatchType;
7112 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7113 bc.Report.Error (160, c.loc,
7114 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7115 ct.GetSignatureForError ());
7121 return base.Resolve (bc) && ok;
7124 protected sealed override void DoEmit (EmitContext ec)
7126 if (!inside_try_finally)
7127 EmitTryBodyPrepare (ec);
7131 LocalBuilder state_variable = null;
7132 foreach (Catch c in clauses) {
7135 if (catch_sm != null) {
7136 if (state_variable == null) {
7138 // Cannot reuse temp variable because non-catch path assumes the value is 0
7139 // which may not be true for reused local variable
7141 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7144 var index = catch_sm.IndexOf (c);
7148 ec.EmitInt (index + 1);
7149 ec.Emit (OpCodes.Stloc, state_variable);
7153 if (!inside_try_finally)
7154 ec.EndExceptionBlock ();
7156 if (state_variable != null) {
7157 ec.Emit (OpCodes.Ldloc, state_variable);
7159 var labels = new Label [catch_sm.Count + 1];
7160 for (int i = 0; i < labels.Length; ++i) {
7161 labels [i] = ec.DefineLabel ();
7164 var end = ec.DefineLabel ();
7165 ec.Emit (OpCodes.Switch, labels);
7167 // 0 value is default label
7168 ec.MarkLabel (labels [0]);
7169 ec.Emit (OpCodes.Br, end);
7171 var atv = ec.AsyncThrowVariable;
7173 for (int i = 0; i < catch_sm.Count; ++i) {
7174 if (c != null && c.Block.HasReachableClosingBrace)
7175 ec.Emit (OpCodes.Br, end);
7177 ec.MarkLabel (labels [i + 1]);
7179 ec.AsyncThrowVariable = c.Variable;
7182 ec.AsyncThrowVariable = atv;
7188 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7190 var start_fc = fc.BranchDefiniteAssignment ();
7191 var res = Block.FlowAnalysis (fc);
7193 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7195 foreach (var c in clauses) {
7196 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
7197 if (!c.FlowAnalysis (fc)) {
7199 try_fc = fc.DefiniteAssignment;
7201 try_fc &= fc.DefiniteAssignment;
7207 fc.DefiniteAssignment = try_fc ?? start_fc;
7212 public override Reachability MarkReachable (Reachability rc)
7214 if (rc.IsUnreachable)
7217 base.MarkReachable (rc);
7219 var tc_rc = Block.MarkReachable (rc);
7221 foreach (var c in clauses)
7222 tc_rc &= c.MarkReachable (rc);
7227 protected override void CloneTo (CloneContext clonectx, Statement t)
7229 TryCatch target = (TryCatch) t;
7231 target.Block = clonectx.LookupBlock (Block);
7232 if (clauses != null){
7233 target.clauses = new List<Catch> ();
7234 foreach (Catch c in clauses)
7235 target.clauses.Add ((Catch) c.Clone (clonectx));
7239 public override object Accept (StructuralVisitor visitor)
7241 return visitor.Visit (this);
7245 public class Using : TryFinallyBlock
7247 public class VariableDeclaration : BlockVariable
7249 Statement dispose_call;
7251 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7256 public VariableDeclaration (LocalVariable li, Location loc)
7263 public VariableDeclaration (Expression expr)
7266 loc = expr.Location;
7272 public bool IsNested { get; private set; }
7276 public void EmitDispose (EmitContext ec)
7278 dispose_call.Emit (ec);
7281 public override bool Resolve (BlockContext bc)
7286 return base.Resolve (bc, false);
7289 public Expression ResolveExpression (BlockContext bc)
7291 var e = Initializer.Resolve (bc);
7295 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7296 Initializer = ResolveInitializer (bc, Variable, e);
7300 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7302 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7303 initializer = initializer.Resolve (bc);
7304 if (initializer == null)
7307 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7308 Arguments args = new Arguments (1);
7309 args.Add (new Argument (initializer));
7310 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7311 if (initializer == null)
7314 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7315 dispose_call = CreateDisposeCall (bc, var);
7316 dispose_call.Resolve (bc);
7318 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7321 if (li == Variable) {
7322 CheckIDiposableConversion (bc, li, initializer);
7323 dispose_call = CreateDisposeCall (bc, li);
7324 dispose_call.Resolve (bc);
7327 return base.ResolveInitializer (bc, li, initializer);
7330 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7334 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7335 if (type.IsNullableType) {
7336 // it's handled in CreateDisposeCall
7340 if (type != InternalType.ErrorType) {
7341 bc.Report.SymbolRelatedToPreviousError (type);
7342 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7343 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7344 type.GetSignatureForError ());
7351 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7353 var target = bc.BuiltinTypes.IDisposable;
7354 var tp = type as TypeParameterSpec;
7356 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7358 return type.ImplementsInterface (target, false);
7361 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7363 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7365 var loc = lv.Location;
7367 var idt = bc.BuiltinTypes.IDisposable;
7368 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7370 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7371 dispose_mg.InstanceExpression = type.IsNullableType ?
7372 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7376 // Hide it from symbol file via null location
7378 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7380 // Add conditional call when disposing possible null variable
7381 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7382 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7387 public void ResolveDeclaratorInitializer (BlockContext bc)
7389 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7392 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7394 for (int i = declarators.Count - 1; i >= 0; --i) {
7395 var d = declarators [i];
7396 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7397 vd.Initializer = d.Initializer;
7399 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7400 vd.dispose_call.Resolve (bc);
7402 stmt = new Using (vd, stmt, d.Variable.Location);
7409 public override object Accept (StructuralVisitor visitor)
7411 return visitor.Visit (this);
7415 VariableDeclaration decl;
7417 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7423 public Using (Expression expr, Statement stmt, Location loc)
7426 this.decl = new VariableDeclaration (expr);
7431 public Expression Expr {
7433 return decl.Variable == null ? decl.Initializer : null;
7437 public BlockVariable Variables {
7445 public override void Emit (EmitContext ec)
7448 // Don't emit sequence point it will be set on variable declaration
7453 protected override void EmitTryBodyPrepare (EmitContext ec)
7456 base.EmitTryBodyPrepare (ec);
7459 protected override void EmitTryBody (EmitContext ec)
7464 public override void EmitFinallyBody (EmitContext ec)
7466 decl.EmitDispose (ec);
7469 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7471 decl.FlowAnalysis (fc);
7472 return stmt.FlowAnalysis (fc);
7475 public override Reachability MarkReachable (Reachability rc)
7477 decl.MarkReachable (rc);
7478 return base.MarkReachable (rc);
7481 public override bool Resolve (BlockContext ec)
7483 VariableReference vr;
7484 bool vr_locked = false;
7486 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7487 if (decl.Variable == null) {
7488 vr = decl.ResolveExpression (ec) as VariableReference;
7490 vr_locked = vr.IsLockedByStatement;
7491 vr.IsLockedByStatement = true;
7494 if (decl.IsNested) {
7495 decl.ResolveDeclaratorInitializer (ec);
7497 if (!decl.Resolve (ec))
7500 if (decl.Declarators != null) {
7501 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7509 var ok = base.Resolve (ec);
7512 vr.IsLockedByStatement = vr_locked;
7517 protected override void CloneTo (CloneContext clonectx, Statement t)
7519 Using target = (Using) t;
7521 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7522 target.stmt = stmt.Clone (clonectx);
7525 public override object Accept (StructuralVisitor visitor)
7527 return visitor.Visit (this);
7532 /// Implementation of the foreach C# statement
7534 public class Foreach : LoopStatement
7536 abstract class IteratorStatement : Statement
7538 protected readonly Foreach for_each;
7540 protected IteratorStatement (Foreach @foreach)
7542 this.for_each = @foreach;
7543 this.loc = @foreach.expr.Location;
7546 protected override void CloneTo (CloneContext clonectx, Statement target)
7548 throw new NotImplementedException ();
7551 public override void Emit (EmitContext ec)
7553 if (ec.EmitAccurateDebugInfo) {
7554 ec.Emit (OpCodes.Nop);
7560 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7562 throw new NotImplementedException ();
7566 sealed class ArrayForeach : IteratorStatement
7568 TemporaryVariableReference[] lengths;
7569 Expression [] length_exprs;
7570 StatementExpression[] counter;
7571 TemporaryVariableReference[] variables;
7573 TemporaryVariableReference copy;
7575 public ArrayForeach (Foreach @foreach, int rank)
7578 counter = new StatementExpression[rank];
7579 variables = new TemporaryVariableReference[rank];
7580 length_exprs = new Expression [rank];
7583 // Only use temporary length variables when dealing with
7584 // multi-dimensional arrays
7587 lengths = new TemporaryVariableReference [rank];
7590 public override bool Resolve (BlockContext ec)
7592 Block variables_block = for_each.variable.Block;
7593 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7596 int rank = length_exprs.Length;
7597 Arguments list = new Arguments (rank);
7598 for (int i = 0; i < rank; i++) {
7599 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7601 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7602 counter[i].Resolve (ec);
7605 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7607 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7608 lengths[i].Resolve (ec);
7610 Arguments args = new Arguments (1);
7611 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7612 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7615 list.Add (new Argument (v));
7618 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7623 if (for_each.type is VarExpr) {
7624 // Infer implicitly typed local variable from foreach array type
7625 var_type = access.Type;
7627 var_type = for_each.type.ResolveAsType (ec);
7629 if (var_type == null)
7632 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7637 for_each.variable.Type = var_type;
7639 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7640 if (variable_ref == null)
7643 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7645 return for_each.body.Resolve (ec);
7648 protected override void DoEmit (EmitContext ec)
7650 copy.EmitAssign (ec, for_each.expr);
7652 int rank = length_exprs.Length;
7653 Label[] test = new Label [rank];
7654 Label[] loop = new Label [rank];
7656 for (int i = 0; i < rank; i++) {
7657 test [i] = ec.DefineLabel ();
7658 loop [i] = ec.DefineLabel ();
7660 if (lengths != null)
7661 lengths [i].EmitAssign (ec, length_exprs [i]);
7664 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7665 for (int i = 0; i < rank; i++) {
7666 variables [i].EmitAssign (ec, zero);
7668 ec.Emit (OpCodes.Br, test [i]);
7669 ec.MarkLabel (loop [i]);
7672 for_each.body.Emit (ec);
7674 ec.MarkLabel (ec.LoopBegin);
7675 ec.Mark (for_each.expr.Location);
7677 for (int i = rank - 1; i >= 0; i--){
7678 counter [i].Emit (ec);
7680 ec.MarkLabel (test [i]);
7681 variables [i].Emit (ec);
7683 if (lengths != null)
7684 lengths [i].Emit (ec);
7686 length_exprs [i].Emit (ec);
7688 ec.Emit (OpCodes.Blt, loop [i]);
7691 ec.MarkLabel (ec.LoopEnd);
7695 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7697 class RuntimeDispose : Using.VariableDeclaration
7699 public RuntimeDispose (LocalVariable lv, Location loc)
7705 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7707 // Defered to runtime check
7710 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7712 var idt = bc.BuiltinTypes.IDisposable;
7715 // Fabricates code like
7717 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7720 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7722 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7723 dispose_variable.CreateReferenceExpression (bc, loc),
7724 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7725 loc), new NullLiteral (loc));
7727 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7729 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7730 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7732 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7733 return new If (idisaposable_test, dispose, loc);
7737 LocalVariable variable;
7739 Statement statement;
7740 ExpressionStatement init;
7741 TemporaryVariableReference enumerator_variable;
7742 bool ambiguous_getenumerator_name;
7744 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7747 this.variable = var;
7751 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7753 rc.Report.SymbolRelatedToPreviousError (enumerator);
7754 rc.Report.Error (202, loc,
7755 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7756 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7759 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7762 // Option 1: Try to match by name GetEnumerator first
7764 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7765 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7767 var mg = mexpr as MethodGroupExpr;
7769 mg.InstanceExpression = expr;
7770 Arguments args = new Arguments (0);
7771 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7773 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7774 if (ambiguous_getenumerator_name)
7777 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7783 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7786 PredefinedMember<MethodSpec> iface_candidate = null;
7787 var ptypes = rc.Module.PredefinedTypes;
7788 var gen_ienumerable = ptypes.IEnumerableGeneric;
7789 if (!gen_ienumerable.Define ())
7790 gen_ienumerable = null;
7792 var ifaces = t.Interfaces;
7793 if (ifaces != null) {
7794 foreach (var iface in ifaces) {
7795 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7796 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7797 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7798 rc.Report.Error (1640, loc,
7799 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7800 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7805 // TODO: Cache this somehow
7806 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7807 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7812 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7813 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7818 if (iface_candidate == null) {
7819 if (expr.Type != InternalType.ErrorType) {
7820 rc.Report.Error (1579, loc,
7821 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7822 expr.Type.GetSignatureForError (), "GetEnumerator");
7828 var method = iface_candidate.Resolve (loc);
7832 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7833 mg.InstanceExpression = expr;
7837 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7839 var ms = MemberCache.FindMember (enumerator.ReturnType,
7840 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7841 BindingRestriction.InstanceOnly) as MethodSpec;
7843 if (ms == null || !ms.IsPublic) {
7844 Error_WrongEnumerator (rc, enumerator);
7848 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7851 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7853 var ps = MemberCache.FindMember (enumerator.ReturnType,
7854 MemberFilter.Property ("Current", null),
7855 BindingRestriction.InstanceOnly) as PropertySpec;
7857 if (ps == null || !ps.IsPublic) {
7858 Error_WrongEnumerator (rc, enumerator);
7865 public override bool Resolve (BlockContext ec)
7867 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7870 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7871 } else if (expr.Type.IsNullableType) {
7872 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7875 var get_enumerator_mg = ResolveGetEnumerator (ec);
7876 if (get_enumerator_mg == null) {
7880 var get_enumerator = get_enumerator_mg.BestCandidate;
7881 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7882 enumerator_variable.Resolve (ec);
7884 // Prepare bool MoveNext ()
7885 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7886 if (move_next_mg == null) {
7890 move_next_mg.InstanceExpression = enumerator_variable;
7892 // Prepare ~T~ Current { get; }
7893 var current_prop = ResolveCurrent (ec, get_enumerator);
7894 if (current_prop == null) {
7898 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7899 if (current_pe == null)
7902 VarExpr ve = for_each.type as VarExpr;
7906 // Source type is dynamic, set element type to dynamic too
7907 variable.Type = ec.BuiltinTypes.Dynamic;
7909 // Infer implicitly typed local variable from foreach enumerable type
7910 variable.Type = current_pe.Type;
7914 // Explicit cast of dynamic collection elements has to be done at runtime
7915 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7918 variable.Type = for_each.type.ResolveAsType (ec);
7920 if (variable.Type == null)
7923 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7924 if (current_pe == null)
7928 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7929 if (variable_ref == null)
7932 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7934 var init = new Invocation.Predefined (get_enumerator_mg, null);
7936 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7937 for_each.body, Location.Null);
7939 var enum_type = enumerator_variable.Type;
7942 // Add Dispose method call when enumerator can be IDisposable
7944 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7945 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7947 // Runtime Dispose check
7949 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7950 vd.Initializer = init;
7951 statement = new Using (vd, statement, Location.Null);
7954 // No Dispose call needed
7956 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7957 this.init.Resolve (ec);
7961 // Static Dispose check
7963 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
7964 vd.Initializer = init;
7965 statement = new Using (vd, statement, Location.Null);
7968 return statement.Resolve (ec);
7971 protected override void DoEmit (EmitContext ec)
7973 enumerator_variable.LocalInfo.CreateBuilder (ec);
7976 init.EmitStatement (ec);
7978 statement.Emit (ec);
7981 #region IErrorHandler Members
7983 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
7985 ec.Report.SymbolRelatedToPreviousError (best);
7986 ec.Report.Warning (278, 2, expr.Location,
7987 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
7988 expr.Type.GetSignatureForError (), "enumerable",
7989 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
7991 ambiguous_getenumerator_name = true;
7995 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8000 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8005 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8014 LocalVariable variable;
8018 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8022 this.variable = var;
8028 public Expression Expr {
8029 get { return expr; }
8032 public Expression TypeExpression {
8033 get { return type; }
8036 public LocalVariable Variable {
8037 get { return variable; }
8040 public override Reachability MarkReachable (Reachability rc)
8042 base.MarkReachable (rc);
8044 body.MarkReachable (rc);
8049 public override bool Resolve (BlockContext ec)
8051 expr = expr.Resolve (ec);
8056 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8060 body.AddStatement (Statement);
8062 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8063 Statement = new ArrayForeach (this, 1);
8064 } else if (expr.Type is ArrayContainer) {
8065 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8067 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8068 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8069 expr.ExprClassName);
8073 Statement = new CollectionForeach (this, variable, expr);
8076 return base.Resolve (ec);
8079 protected override void DoEmit (EmitContext ec)
8081 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8082 ec.LoopBegin = ec.DefineLabel ();
8083 ec.LoopEnd = ec.DefineLabel ();
8085 if (!(Statement is Block))
8086 ec.BeginCompilerScope ();
8088 variable.CreateBuilder (ec);
8090 Statement.Emit (ec);
8092 if (!(Statement is Block))
8095 ec.LoopBegin = old_begin;
8096 ec.LoopEnd = old_end;
8099 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8101 expr.FlowAnalysis (fc);
8103 var da = fc.BranchDefiniteAssignment ();
8104 body.FlowAnalysis (fc);
8105 fc.DefiniteAssignment = da;
8109 protected override void CloneTo (CloneContext clonectx, Statement t)
8111 Foreach target = (Foreach) t;
8113 target.type = type.Clone (clonectx);
8114 target.expr = expr.Clone (clonectx);
8115 target.body = (Block) body.Clone (clonectx);
8116 target.Statement = Statement.Clone (clonectx);
8119 public override object Accept (StructuralVisitor visitor)
8121 return visitor.Visit (this);