2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
27 protected bool reachable;
29 public bool IsUnreachable {
36 /// Resolves the statement, true means that all sub-statements
39 public virtual bool Resolve (BlockContext bc)
45 /// Return value indicates whether all code paths emitted return.
47 protected abstract void DoEmit (EmitContext ec);
49 public virtual void Emit (EmitContext ec)
54 if (ec.StatementEpilogue != null) {
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
63 protected abstract void CloneTo (CloneContext clonectx, Statement target);
65 public Statement Clone (CloneContext clonectx)
67 Statement s = (Statement) this.MemberwiseClone ();
68 CloneTo (clonectx, s);
72 public virtual Expression CreateExpressionTree (ResolveContext ec)
74 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
78 public virtual object Accept (StructuralVisitor visitor)
80 return visitor.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
88 public bool FlowAnalysis (FlowAnalysisContext fc)
91 fc.UnreachableReported = false;
92 var res = DoFlowAnalysis (fc);
97 // Special handling cases
100 return DoFlowAnalysis (fc);
103 if (this is EmptyStatement || loc.IsNull)
106 if (fc.UnreachableReported)
109 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
110 fc.UnreachableReported = true;
114 public virtual Reachability MarkReachable (Reachability rc)
116 if (!rc.IsUnreachable)
122 protected void CheckExitBoundaries (BlockContext bc, Block scope)
124 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
125 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
129 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
130 if (b.IsFinallyBlock) {
131 Error_FinallyClauseExit (bc);
137 protected void Error_FinallyClauseExit (BlockContext bc)
139 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
143 public sealed class EmptyStatement : Statement
145 public EmptyStatement (Location loc)
150 public override bool Resolve (BlockContext ec)
155 public override void Emit (EmitContext ec)
159 protected override void DoEmit (EmitContext ec)
161 throw new NotSupportedException ();
164 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
169 protected override void CloneTo (CloneContext clonectx, Statement target)
174 public override object Accept (StructuralVisitor visitor)
176 return visitor.Visit (this);
180 public class If : Statement {
182 public Statement TrueStatement;
183 public Statement FalseStatement;
185 bool true_returns, false_returns;
187 public If (Expression bool_expr, Statement true_statement, Location l)
188 : this (bool_expr, true_statement, null, l)
192 public If (Expression bool_expr,
193 Statement true_statement,
194 Statement false_statement,
197 this.expr = bool_expr;
198 TrueStatement = true_statement;
199 FalseStatement = false_statement;
203 public Expression Expr {
209 public override bool Resolve (BlockContext ec)
211 expr = expr.Resolve (ec);
213 var ok = TrueStatement.Resolve (ec);
215 if (FalseStatement != null) {
216 ok &= FalseStatement.Resolve (ec);
222 protected override void DoEmit (EmitContext ec)
224 Label false_target = ec.DefineLabel ();
228 // If we're a boolean constant, Resolve() already
229 // eliminated dead code for us.
231 Constant c = expr as Constant;
233 c.EmitSideEffect (ec);
235 if (!c.IsDefaultValue)
236 TrueStatement.Emit (ec);
237 else if (FalseStatement != null)
238 FalseStatement.Emit (ec);
243 expr.EmitBranchable (ec, false_target, false);
245 TrueStatement.Emit (ec);
247 if (FalseStatement != null){
248 bool branch_emitted = false;
250 end = ec.DefineLabel ();
252 ec.Emit (OpCodes.Br, end);
253 branch_emitted = true;
256 ec.MarkLabel (false_target);
257 FalseStatement.Emit (ec);
262 ec.MarkLabel (false_target);
266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
268 expr.FlowAnalysisConditional (fc);
270 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
272 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
274 var res = TrueStatement.FlowAnalysis (fc);
276 if (FalseStatement == null) {
277 var c = expr as Constant;
278 if (c != null && !c.IsDefaultValue)
282 fc.DefiniteAssignment = da_false;
284 fc.DefiniteAssignment &= da_false;
290 fc.DefiniteAssignment = da_false;
291 return FalseStatement.FlowAnalysis (fc);
294 var da_true = fc.DefiniteAssignment;
296 fc.DefiniteAssignment = da_false;
297 res &= FalseStatement.FlowAnalysis (fc);
299 if (!TrueStatement.IsUnreachable) {
300 if (false_returns || FalseStatement.IsUnreachable)
301 fc.DefiniteAssignment = da_true;
303 fc.DefiniteAssignment &= da_true;
309 public override Reachability MarkReachable (Reachability rc)
311 if (rc.IsUnreachable)
314 base.MarkReachable (rc);
316 var c = expr as Constant;
318 bool take = !c.IsDefaultValue;
320 rc = TrueStatement.MarkReachable (rc);
322 if (FalseStatement != null)
323 rc = FalseStatement.MarkReachable (rc);
329 var true_rc = TrueStatement.MarkReachable (rc);
330 true_returns = true_rc.IsUnreachable;
332 if (FalseStatement == null)
335 var false_rc = FalseStatement.MarkReachable (rc);
336 false_returns = false_rc.IsUnreachable;
338 return true_rc & false_rc;
341 protected override void CloneTo (CloneContext clonectx, Statement t)
345 target.expr = expr.Clone (clonectx);
346 target.TrueStatement = TrueStatement.Clone (clonectx);
347 if (FalseStatement != null)
348 target.FalseStatement = FalseStatement.Clone (clonectx);
351 public override object Accept (StructuralVisitor visitor)
353 return visitor.Visit (this);
357 public class Do : LoopStatement
359 public Expression expr;
360 bool iterator_reachable, end_reachable;
362 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
367 WhileLocation = whileLocation;
370 public Location WhileLocation {
374 public override bool Resolve (BlockContext bc)
376 var ok = base.Resolve (bc);
378 expr = expr.Resolve (bc);
383 protected override void DoEmit (EmitContext ec)
385 Label loop = ec.DefineLabel ();
386 Label old_begin = ec.LoopBegin;
387 Label old_end = ec.LoopEnd;
389 ec.LoopBegin = ec.DefineLabel ();
390 ec.LoopEnd = ec.DefineLabel ();
394 ec.MarkLabel (ec.LoopBegin);
396 // Mark start of while condition
397 ec.Mark (WhileLocation);
400 // Dead code elimination
402 if (expr is Constant) {
403 bool res = !((Constant) expr).IsDefaultValue;
405 expr.EmitSideEffect (ec);
407 ec.Emit (OpCodes.Br, loop);
409 expr.EmitBranchable (ec, loop, true);
412 ec.MarkLabel (ec.LoopEnd);
414 ec.LoopBegin = old_begin;
415 ec.LoopEnd = old_end;
418 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
420 var res = Statement.FlowAnalysis (fc);
422 expr.FlowAnalysisConditional (fc);
424 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
426 if (res && !iterator_reachable)
427 return !end_reachable;
429 if (!end_reachable) {
430 var c = expr as Constant;
431 if (c != null && !c.IsDefaultValue)
438 public override Reachability MarkReachable (Reachability rc)
440 base.MarkReachable (rc);
442 var body_rc = Statement.MarkReachable (rc);
444 if (body_rc.IsUnreachable && !iterator_reachable) {
445 expr = new UnreachableExpression (expr);
446 return end_reachable ? rc : Reachability.CreateUnreachable ();
449 if (!end_reachable) {
450 var c = expr as Constant;
451 if (c != null && !c.IsDefaultValue)
452 return Reachability.CreateUnreachable ();
458 protected override void CloneTo (CloneContext clonectx, Statement t)
462 target.Statement = Statement.Clone (clonectx);
463 target.expr = expr.Clone (clonectx);
466 public override object Accept (StructuralVisitor visitor)
468 return visitor.Visit (this);
471 public override void SetEndReachable ()
473 end_reachable = true;
476 public override void SetIteratorReachable ()
478 iterator_reachable = true;
482 public class While : LoopStatement
484 public Expression expr;
485 bool empty, infinite, end_reachable;
486 List<DefiniteAssignmentBitSet> end_reachable_das;
488 public While (BooleanExpression bool_expr, Statement statement, Location l)
491 this.expr = bool_expr;
495 public override bool Resolve (BlockContext bc)
499 expr = expr.Resolve (bc);
503 var c = expr as Constant;
505 empty = c.IsDefaultValue;
509 ok &= base.Resolve (bc);
513 protected override void DoEmit (EmitContext ec)
516 expr.EmitSideEffect (ec);
520 Label old_begin = ec.LoopBegin;
521 Label old_end = ec.LoopEnd;
523 ec.LoopBegin = ec.DefineLabel ();
524 ec.LoopEnd = ec.DefineLabel ();
527 // Inform whether we are infinite or not
529 if (expr is Constant) {
530 // expr is 'true', since the 'empty' case above handles the 'false' case
531 ec.MarkLabel (ec.LoopBegin);
533 if (ec.EmitAccurateDebugInfo)
534 ec.Emit (OpCodes.Nop);
536 expr.EmitSideEffect (ec);
538 ec.Emit (OpCodes.Br, ec.LoopBegin);
541 // Inform that we are infinite (ie, `we return'), only
542 // if we do not `break' inside the code.
544 ec.MarkLabel (ec.LoopEnd);
546 Label while_loop = ec.DefineLabel ();
548 ec.Emit (OpCodes.Br, ec.LoopBegin);
549 ec.MarkLabel (while_loop);
553 ec.MarkLabel (ec.LoopBegin);
556 expr.EmitBranchable (ec, while_loop, true);
558 ec.MarkLabel (ec.LoopEnd);
561 ec.LoopBegin = old_begin;
562 ec.LoopEnd = old_end;
565 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
567 expr.FlowAnalysisConditional (fc);
569 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
570 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
572 Statement.FlowAnalysis (fc);
575 // Special case infinite while with breaks
577 if (end_reachable_das != null) {
578 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
579 end_reachable_das = null;
582 fc.DefiniteAssignment = da_false;
584 if (infinite && !end_reachable)
590 public override Reachability MarkReachable (Reachability rc)
592 if (rc.IsUnreachable)
595 base.MarkReachable (rc);
598 // Special case unreachable while body
601 Statement.MarkReachable (Reachability.CreateUnreachable ());
605 Statement.MarkReachable (rc);
608 // When infinite while end is unreachable via break anything what follows is unreachable too
610 if (infinite && !end_reachable)
611 return Reachability.CreateUnreachable ();
616 protected override void CloneTo (CloneContext clonectx, Statement t)
618 While target = (While) t;
620 target.expr = expr.Clone (clonectx);
621 target.Statement = Statement.Clone (clonectx);
624 public override object Accept (StructuralVisitor visitor)
626 return visitor.Visit (this);
629 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
634 if (end_reachable_das == null)
635 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
637 end_reachable_das.Add (fc.DefiniteAssignment);
640 public override void SetEndReachable ()
642 end_reachable = true;
646 public class For : LoopStatement
648 bool infinite, empty, iterator_reachable, end_reachable;
649 List<DefiniteAssignmentBitSet> end_reachable_das;
651 public For (Location l)
657 public Statement Initializer {
661 public Expression Condition {
665 public Statement Iterator {
669 public override bool Resolve (BlockContext bc)
671 Initializer.Resolve (bc);
673 if (Condition != null) {
674 Condition = Condition.Resolve (bc);
675 var condition_constant = Condition as Constant;
676 if (condition_constant != null) {
677 if (condition_constant.IsDefaultValue) {
687 return base.Resolve (bc) && Iterator.Resolve (bc);
690 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
692 Initializer.FlowAnalysis (fc);
694 DefiniteAssignmentBitSet da_false;
695 if (Condition != null) {
696 Condition.FlowAnalysisConditional (fc);
697 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
698 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
700 da_false = fc.BranchDefiniteAssignment ();
703 Statement.FlowAnalysis (fc);
705 Iterator.FlowAnalysis (fc);
708 // Special case infinite for with breaks
710 if (end_reachable_das != null) {
711 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
712 end_reachable_das = null;
715 fc.DefiniteAssignment = da_false;
717 if (infinite && !end_reachable)
723 public override Reachability MarkReachable (Reachability rc)
725 base.MarkReachable (rc);
727 Initializer.MarkReachable (rc);
729 var body_rc = Statement.MarkReachable (rc);
730 if (!body_rc.IsUnreachable || iterator_reachable) {
731 Iterator.MarkReachable (rc);
735 // When infinite for end is unreachable via break anything what follows is unreachable too
737 if (infinite && !end_reachable) {
738 return Reachability.CreateUnreachable ();
744 protected override void DoEmit (EmitContext ec)
746 if (Initializer != null)
747 Initializer.Emit (ec);
750 Condition.EmitSideEffect (ec);
754 Label old_begin = ec.LoopBegin;
755 Label old_end = ec.LoopEnd;
756 Label loop = ec.DefineLabel ();
757 Label test = ec.DefineLabel ();
759 ec.LoopBegin = ec.DefineLabel ();
760 ec.LoopEnd = ec.DefineLabel ();
762 ec.Emit (OpCodes.Br, test);
766 ec.MarkLabel (ec.LoopBegin);
771 // If test is null, there is no test, and we are just
774 if (Condition != null) {
775 ec.Mark (Condition.Location);
778 // The Resolve code already catches the case for
779 // Test == Constant (false) so we know that
782 if (Condition is Constant) {
783 Condition.EmitSideEffect (ec);
784 ec.Emit (OpCodes.Br, loop);
786 Condition.EmitBranchable (ec, loop, true);
790 ec.Emit (OpCodes.Br, loop);
791 ec.MarkLabel (ec.LoopEnd);
793 ec.LoopBegin = old_begin;
794 ec.LoopEnd = old_end;
797 protected override void CloneTo (CloneContext clonectx, Statement t)
799 For target = (For) t;
801 if (Initializer != null)
802 target.Initializer = Initializer.Clone (clonectx);
803 if (Condition != null)
804 target.Condition = Condition.Clone (clonectx);
805 if (Iterator != null)
806 target.Iterator = Iterator.Clone (clonectx);
807 target.Statement = Statement.Clone (clonectx);
810 public override object Accept (StructuralVisitor visitor)
812 return visitor.Visit (this);
815 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
820 if (end_reachable_das == null)
821 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
823 end_reachable_das.Add (fc.DefiniteAssignment);
826 public override void SetEndReachable ()
828 end_reachable = true;
831 public override void SetIteratorReachable ()
833 iterator_reachable = true;
837 public abstract class LoopStatement : Statement
839 protected LoopStatement (Statement statement)
841 Statement = statement;
844 public Statement Statement { get; set; }
846 public override bool Resolve (BlockContext bc)
848 var prev_loop = bc.EnclosingLoop;
849 var prev_los = bc.EnclosingLoopOrSwitch;
850 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
851 var ok = Statement.Resolve (bc);
852 bc.EnclosingLoopOrSwitch = prev_los;
853 bc.EnclosingLoop = prev_loop;
859 // Needed by possibly infinite loops statements (for, while) and switch statment
861 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
865 public virtual void SetEndReachable ()
869 public virtual void SetIteratorReachable ()
874 public class StatementExpression : Statement
876 ExpressionStatement expr;
878 public StatementExpression (ExpressionStatement expr)
881 loc = expr.StartLocation;
884 public StatementExpression (ExpressionStatement expr, Location loc)
890 public ExpressionStatement Expr {
896 protected override void CloneTo (CloneContext clonectx, Statement t)
898 StatementExpression target = (StatementExpression) t;
899 target.expr = (ExpressionStatement) expr.Clone (clonectx);
902 protected override void DoEmit (EmitContext ec)
904 expr.EmitStatement (ec);
907 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
909 expr.FlowAnalysis (fc);
913 public override Reachability MarkReachable (Reachability rc)
915 base.MarkReachable (rc);
916 expr.MarkReachable (rc);
920 public override bool Resolve (BlockContext ec)
922 expr = expr.ResolveStatement (ec);
926 public override object Accept (StructuralVisitor visitor)
928 return visitor.Visit (this);
932 public class StatementErrorExpression : Statement
936 public StatementErrorExpression (Expression expr)
939 this.loc = expr.StartLocation;
942 public Expression Expr {
948 public override bool Resolve (BlockContext bc)
950 expr.Error_InvalidExpressionStatement (bc);
954 protected override void DoEmit (EmitContext ec)
956 throw new NotSupportedException ();
959 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
964 protected override void CloneTo (CloneContext clonectx, Statement target)
966 var t = (StatementErrorExpression) target;
968 t.expr = expr.Clone (clonectx);
971 public override object Accept (StructuralVisitor visitor)
973 return visitor.Visit (this);
978 // Simple version of statement list not requiring a block
980 public class StatementList : Statement
982 List<Statement> statements;
984 public StatementList (Statement first, Statement second)
986 statements = new List<Statement> { first, second };
990 public IList<Statement> Statements {
997 public void Add (Statement statement)
999 statements.Add (statement);
1002 public override bool Resolve (BlockContext ec)
1004 foreach (var s in statements)
1010 protected override void DoEmit (EmitContext ec)
1012 foreach (var s in statements)
1016 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1018 foreach (var s in statements)
1019 s.FlowAnalysis (fc);
1024 public override Reachability MarkReachable (Reachability rc)
1026 base.MarkReachable (rc);
1028 Reachability res = rc;
1029 foreach (var s in statements)
1030 res = s.MarkReachable (rc);
1035 protected override void CloneTo (CloneContext clonectx, Statement target)
1037 StatementList t = (StatementList) target;
1039 t.statements = new List<Statement> (statements.Count);
1040 foreach (Statement s in statements)
1041 t.statements.Add (s.Clone (clonectx));
1044 public override object Accept (StructuralVisitor visitor)
1046 return visitor.Visit (this);
1051 // For statements which require special handling when inside try or catch block
1053 public abstract class ExitStatement : Statement
1055 protected bool unwind_protect;
1057 protected abstract bool DoResolve (BlockContext bc);
1058 protected abstract bool IsLocalExit { get; }
1060 public override bool Resolve (BlockContext bc)
1062 var res = DoResolve (bc);
1066 // We are inside finally scope but is it the scope we are exiting
1068 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1070 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1071 if (b.IsFinallyBlock) {
1072 Error_FinallyClauseExit (bc);
1076 if (b is ParametersBlock)
1082 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1086 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1091 if (fc.TryFinally != null) {
1092 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1094 fc.ParametersBlock.CheckControlExit (fc);
1102 /// Implements the return statement
1104 public class Return : ExitStatement
1108 public Return (Expression expr, Location l)
1116 public Expression Expr {
1125 protected override bool IsLocalExit {
1133 protected override bool DoResolve (BlockContext ec)
1135 var block_return_type = ec.ReturnType;
1138 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1142 // Return must not be followed by an expression when
1143 // the method return type is Task
1145 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1146 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1147 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1149 // Extra trick not to emit ret/leave inside awaiter body
1151 expr = EmptyExpression.Null;
1155 if (storey.ReturnType.IsGenericTask)
1156 block_return_type = storey.ReturnType.TypeArguments[0];
1159 if (ec.CurrentIterator != null) {
1160 Error_ReturnFromIterator (ec);
1161 } else if (block_return_type != InternalType.ErrorType) {
1162 ec.Report.Error (126, loc,
1163 "An object of a type convertible to `{0}' is required for the return statement",
1164 block_return_type.GetSignatureForError ());
1170 expr = expr.Resolve (ec);
1172 AnonymousExpression am = ec.CurrentAnonymousMethod;
1174 if (block_return_type.Kind == MemberKind.Void) {
1175 ec.Report.Error (127, loc,
1176 "`{0}': A return keyword must not be followed by any expression when method returns void",
1177 ec.GetSignatureForError ());
1182 if (am.IsIterator) {
1183 Error_ReturnFromIterator (ec);
1187 var async_block = am as AsyncInitializer;
1188 if (async_block != null) {
1190 var storey = (AsyncTaskStorey) am.Storey;
1191 var async_type = storey.ReturnType;
1193 if (async_type == null && async_block.ReturnTypeInference != null) {
1194 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1195 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1197 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1201 if (async_type.Kind == MemberKind.Void) {
1202 ec.Report.Error (8030, loc,
1203 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1207 if (!async_type.IsGenericTask) {
1208 if (this is ContextualReturn)
1211 if (async_block.DelegateType != null) {
1212 ec.Report.Error (8031, loc,
1213 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1215 ec.Report.Error (1997, loc,
1216 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1217 ec.GetSignatureForError ());
1223 // The return type is actually Task<T> type argument
1225 if (expr.Type == async_type) {
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.AddReachedLabel (label))
1414 label.Block.ScanGotoJump (label, fc);
1418 public override Reachability MarkReachable (Reachability rc)
1420 if (rc.IsUnreachable)
1423 base.MarkReachable (rc);
1425 if (try_finally != null) {
1426 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1427 label.AddGotoReference (rc, false);
1429 label.AddGotoReference (rc, true);
1432 label.AddGotoReference (rc, false);
1435 return Reachability.CreateUnreachable ();
1438 protected override void CloneTo (CloneContext clonectx, Statement target)
1443 protected override void DoEmit (EmitContext ec)
1446 throw new InternalErrorException ("goto emitted before target resolved");
1448 Label l = label.LabelTarget (ec);
1450 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1451 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1452 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1455 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1458 bool IsLeavingFinally (Block labelBlock)
1460 var b = try_finally.Statement as Block;
1462 if (b == labelBlock)
1471 public override object Accept (StructuralVisitor visitor)
1473 return visitor.Visit (this);
1477 public class LabeledStatement : Statement {
1485 public LabeledStatement (string name, Block block, Location l)
1492 public Label LabelTarget (EmitContext ec)
1497 label = ec.DefineLabel ();
1502 public Block Block {
1508 public string Name {
1509 get { return name; }
1512 protected override void CloneTo (CloneContext clonectx, Statement target)
1514 var t = (LabeledStatement) target;
1516 t.block = clonectx.RemapBlockCopy (block);
1519 public override bool Resolve (BlockContext bc)
1524 protected override void DoEmit (EmitContext ec)
1527 ec.MarkLabel (label);
1530 ec.Emit (OpCodes.Br_S, label);
1533 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1536 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1542 public override Reachability MarkReachable (Reachability rc)
1544 base.MarkReachable (rc);
1547 rc = new Reachability ();
1552 public void AddGotoReference (Reachability rc, bool finalTarget)
1561 // Label is final target when goto jumps out of try block with
1562 // finally clause. In that case we need leave with target but in C#
1563 // terms the label is unreachable. Using finalTarget we emit
1564 // explicit label not just marker
1567 this.finalTarget = true;
1571 block.ScanGotoJump (this);
1574 public override object Accept (StructuralVisitor visitor)
1576 return visitor.Visit (this);
1582 /// `goto default' statement
1584 public class GotoDefault : SwitchGoto
1586 public GotoDefault (Location l)
1591 public override bool Resolve (BlockContext bc)
1593 if (bc.Switch == null) {
1594 Error_GotoCaseRequiresSwitchBlock (bc);
1598 bc.Switch.RegisterGotoCase (null, null);
1604 protected override void DoEmit (EmitContext ec)
1606 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1609 public override Reachability MarkReachable (Reachability rc)
1611 if (!rc.IsUnreachable) {
1612 var label = switch_statement.DefaultLabel;
1613 if (label.IsUnreachable) {
1614 label.MarkReachable (rc);
1615 switch_statement.Block.ScanGotoJump (label);
1619 return base.MarkReachable (rc);
1622 public override object Accept (StructuralVisitor visitor)
1624 return visitor.Visit (this);
1629 /// `goto case' statement
1631 public class GotoCase : SwitchGoto
1635 public GotoCase (Expression e, Location l)
1641 public Expression Expr {
1647 public SwitchLabel Label { get; set; }
1649 public override bool Resolve (BlockContext ec)
1651 if (ec.Switch == null) {
1652 Error_GotoCaseRequiresSwitchBlock (ec);
1656 Constant c = expr.ResolveLabelConstant (ec);
1662 if (ec.Switch.IsNullable && c is NullLiteral) {
1665 TypeSpec type = ec.Switch.SwitchType;
1666 res = c.Reduce (ec, type);
1668 c.Error_ValueCannotBeConverted (ec, type, true);
1672 if (!Convert.ImplicitStandardConversionExists (c, type))
1673 ec.Report.Warning (469, 2, loc,
1674 "The `goto case' value is not implicitly convertible to type `{0}'",
1675 type.GetSignatureForError ());
1679 ec.Switch.RegisterGotoCase (this, res);
1686 protected override void DoEmit (EmitContext ec)
1688 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1691 protected override void CloneTo (CloneContext clonectx, Statement t)
1693 GotoCase target = (GotoCase) t;
1695 target.expr = expr.Clone (clonectx);
1698 public override Reachability MarkReachable (Reachability rc)
1700 if (!rc.IsUnreachable) {
1701 var label = switch_statement.FindLabel ((Constant) expr);
1702 if (label.IsUnreachable) {
1703 label.MarkReachable (rc);
1704 switch_statement.Block.ScanGotoJump (label);
1708 return base.MarkReachable (rc);
1711 public override object Accept (StructuralVisitor visitor)
1713 return visitor.Visit (this);
1717 public abstract class SwitchGoto : Statement
1719 protected bool unwind_protect;
1720 protected Switch switch_statement;
1722 protected SwitchGoto (Location loc)
1727 protected override void CloneTo (CloneContext clonectx, Statement target)
1732 public override bool Resolve (BlockContext bc)
1734 CheckExitBoundaries (bc, bc.Switch.Block);
1736 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1737 switch_statement = bc.Switch;
1742 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1747 public override Reachability MarkReachable (Reachability rc)
1749 base.MarkReachable (rc);
1750 return Reachability.CreateUnreachable ();
1753 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1755 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1759 public class Throw : Statement {
1762 public Throw (Expression expr, Location l)
1768 public Expression Expr {
1774 public override bool Resolve (BlockContext ec)
1777 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1778 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1779 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1780 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1781 if (b.IsFinallyBlock) {
1782 ec.Report.Error (724, loc,
1783 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1792 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1797 var et = ec.BuiltinTypes.Exception;
1798 if (Convert.ImplicitConversionExists (ec, expr, et))
1799 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1801 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1806 protected override void DoEmit (EmitContext ec)
1809 var atv = ec.AsyncThrowVariable;
1811 if (atv.HoistedVariant != null) {
1812 atv.HoistedVariant.Emit (ec);
1817 ec.Emit (OpCodes.Throw);
1819 ec.Emit (OpCodes.Rethrow);
1824 ec.Emit (OpCodes.Throw);
1828 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1831 expr.FlowAnalysis (fc);
1836 public override Reachability MarkReachable (Reachability rc)
1838 base.MarkReachable (rc);
1839 return Reachability.CreateUnreachable ();
1842 protected override void CloneTo (CloneContext clonectx, Statement t)
1844 Throw target = (Throw) t;
1847 target.expr = expr.Clone (clonectx);
1850 public override object Accept (StructuralVisitor visitor)
1852 return visitor.Visit (this);
1856 public class Break : LocalExitStatement
1858 public Break (Location l)
1863 public override object Accept (StructuralVisitor visitor)
1865 return visitor.Visit (this);
1868 protected override void DoEmit (EmitContext ec)
1872 if (ec.TryFinallyUnwind != null) {
1873 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1874 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1877 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1880 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1882 enclosing_loop.AddEndDefiniteAssignment (fc);
1886 protected override bool DoResolve (BlockContext bc)
1888 enclosing_loop = bc.EnclosingLoopOrSwitch;
1889 return base.DoResolve (bc);
1892 public override Reachability MarkReachable (Reachability rc)
1894 base.MarkReachable (rc);
1896 if (!rc.IsUnreachable)
1897 enclosing_loop.SetEndReachable ();
1899 return Reachability.CreateUnreachable ();
1903 public class Continue : LocalExitStatement
1905 public Continue (Location l)
1910 public override object Accept (StructuralVisitor visitor)
1912 return visitor.Visit (this);
1916 protected override void DoEmit (EmitContext ec)
1918 var l = ec.LoopBegin;
1920 if (ec.TryFinallyUnwind != null) {
1921 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1922 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1925 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1928 protected override bool DoResolve (BlockContext bc)
1930 enclosing_loop = bc.EnclosingLoop;
1931 return base.DoResolve (bc);
1934 public override Reachability MarkReachable (Reachability rc)
1936 base.MarkReachable (rc);
1938 if (!rc.IsUnreachable)
1939 enclosing_loop.SetIteratorReachable ();
1941 return Reachability.CreateUnreachable ();
1945 public abstract class LocalExitStatement : ExitStatement
1947 protected LoopStatement enclosing_loop;
1949 protected LocalExitStatement (Location loc)
1954 protected override bool IsLocalExit {
1960 protected override void CloneTo (CloneContext clonectx, Statement t)
1965 protected override bool DoResolve (BlockContext bc)
1967 if (enclosing_loop == null) {
1968 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1972 var block = enclosing_loop.Statement as Block;
1974 // Don't need to do extra checks for simple statements loops
1975 if (block != null) {
1976 CheckExitBoundaries (bc, block);
1983 public interface ILocalVariable
1985 void Emit (EmitContext ec);
1986 void EmitAssign (EmitContext ec);
1987 void EmitAddressOf (EmitContext ec);
1990 public interface INamedBlockVariable
1992 Block Block { get; }
1993 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1994 bool IsDeclared { get; }
1995 bool IsParameter { get; }
1996 Location Location { get; }
1999 public class BlockVariableDeclarator
2002 Expression initializer;
2004 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2006 if (li.Type != null)
2007 throw new ArgumentException ("Expected null variable type");
2010 this.initializer = initializer;
2015 public LocalVariable Variable {
2021 public Expression Initializer {
2026 initializer = value;
2032 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2034 var t = (BlockVariableDeclarator) MemberwiseClone ();
2035 if (initializer != null)
2036 t.initializer = initializer.Clone (cloneCtx);
2042 public class BlockVariable : Statement
2044 Expression initializer;
2045 protected FullNamedExpression type_expr;
2046 protected LocalVariable li;
2047 protected List<BlockVariableDeclarator> declarators;
2050 public BlockVariable (FullNamedExpression type, LocalVariable li)
2052 this.type_expr = type;
2054 this.loc = type_expr.Location;
2057 protected BlockVariable (LocalVariable li)
2064 public List<BlockVariableDeclarator> Declarators {
2070 public Expression Initializer {
2075 initializer = value;
2079 public FullNamedExpression TypeExpression {
2085 public LocalVariable Variable {
2093 public void AddDeclarator (BlockVariableDeclarator decl)
2095 if (declarators == null)
2096 declarators = new List<BlockVariableDeclarator> ();
2098 declarators.Add (decl);
2101 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2103 if (bc.Report.Errors != 0)
2106 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2108 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2109 new MemberName (li.Name, li.Location), null);
2111 container.AddField (f);
2114 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2118 public override bool Resolve (BlockContext bc)
2120 return Resolve (bc, true);
2123 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2125 if (type == null && !li.IsCompilerGenerated) {
2126 var vexpr = type_expr as VarExpr;
2129 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2130 // same name exists or as a keyword when no type was found
2132 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2133 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2134 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2137 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2141 if (li.IsConstant) {
2142 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2146 if (Initializer == null) {
2147 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2151 if (declarators != null) {
2152 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2156 Initializer = Initializer.Resolve (bc);
2157 if (Initializer != null) {
2158 ((VarExpr) type_expr).InferType (bc, Initializer);
2159 type = type_expr.Type;
2161 // Set error type to indicate the var was placed correctly but could
2164 // var a = missing ();
2166 type = InternalType.ErrorType;
2171 type = type_expr.ResolveAsType (bc);
2175 if (li.IsConstant && !type.IsConstantCompatible) {
2176 Const.Error_InvalidConstantType (type, loc, bc.Report);
2181 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2186 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2188 CreateEvaluatorVariable (bc, li);
2189 } else if (type != InternalType.ErrorType) {
2190 li.PrepareAssignmentAnalysis (bc);
2193 if (initializer != null) {
2194 initializer = ResolveInitializer (bc, li, initializer);
2195 // li.Variable.DefinitelyAssigned
2198 if (declarators != null) {
2199 foreach (var d in declarators) {
2200 d.Variable.Type = li.Type;
2202 CreateEvaluatorVariable (bc, d.Variable);
2203 } else if (type != InternalType.ErrorType) {
2204 d.Variable.PrepareAssignmentAnalysis (bc);
2207 if (d.Initializer != null && resolveDeclaratorInitializers) {
2208 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2209 // d.Variable.DefinitelyAssigned
2217 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2219 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2220 return a.ResolveStatement (bc);
2223 protected override void DoEmit (EmitContext ec)
2225 li.CreateBuilder (ec);
2227 if (Initializer != null && !IsUnreachable)
2228 ((ExpressionStatement) Initializer).EmitStatement (ec);
2230 if (declarators != null) {
2231 foreach (var d in declarators) {
2232 d.Variable.CreateBuilder (ec);
2233 if (d.Initializer != null && !IsUnreachable) {
2234 ec.Mark (d.Variable.Location);
2235 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2241 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2243 if (Initializer != null)
2244 Initializer.FlowAnalysis (fc);
2246 if (declarators != null) {
2247 foreach (var d in declarators) {
2248 if (d.Initializer != null)
2249 d.Initializer.FlowAnalysis (fc);
2256 public override Reachability MarkReachable (Reachability rc)
2258 var init = initializer as ExpressionStatement;
2260 init.MarkReachable (rc);
2262 return base.MarkReachable (rc);
2265 protected override void CloneTo (CloneContext clonectx, Statement target)
2267 BlockVariable t = (BlockVariable) target;
2269 if (type_expr != null)
2270 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2272 if (initializer != null)
2273 t.initializer = initializer.Clone (clonectx);
2275 if (declarators != null) {
2276 t.declarators = null;
2277 foreach (var d in declarators)
2278 t.AddDeclarator (d.Clone (clonectx));
2282 public override object Accept (StructuralVisitor visitor)
2284 return visitor.Visit (this);
2288 public class BlockConstant : BlockVariable
2290 public BlockConstant (FullNamedExpression type, LocalVariable li)
2295 public override void Emit (EmitContext ec)
2297 // Nothing to emit, not even sequence point
2300 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2302 initializer = initializer.Resolve (bc);
2303 if (initializer == null)
2306 var c = initializer as Constant;
2308 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2312 c = c.ConvertImplicitly (li.Type);
2314 if (TypeSpec.IsReferenceType (li.Type))
2315 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2317 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2322 li.ConstantValue = c;
2326 public override object Accept (StructuralVisitor visitor)
2328 return visitor.Visit (this);
2333 // The information about a user-perceived local variable
2335 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2342 AddressTaken = 1 << 2,
2343 CompilerGenerated = 1 << 3,
2345 ForeachVariable = 1 << 5,
2346 FixedVariable = 1 << 6,
2347 UsingVariable = 1 << 7,
2350 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2354 readonly string name;
2355 readonly Location loc;
2356 readonly Block block;
2358 Constant const_value;
2360 public VariableInfo VariableInfo;
2361 HoistedVariable hoisted_variant;
2363 LocalBuilder builder;
2365 public LocalVariable (Block block, string name, Location loc)
2372 public LocalVariable (Block block, string name, Flags flags, Location loc)
2373 : this (block, name, loc)
2379 // Used by variable declarators
2381 public LocalVariable (LocalVariable li, string name, Location loc)
2382 : this (li.block, name, li.flags, loc)
2388 public bool AddressTaken {
2390 return (flags & Flags.AddressTaken) != 0;
2394 public Block Block {
2400 public Constant ConstantValue {
2405 const_value = value;
2410 // Hoisted local variable variant
2412 public HoistedVariable HoistedVariant {
2414 return hoisted_variant;
2417 hoisted_variant = value;
2421 public bool IsDeclared {
2423 return type != null;
2427 public bool IsCompilerGenerated {
2429 return (flags & Flags.CompilerGenerated) != 0;
2433 public bool IsConstant {
2435 return (flags & Flags.Constant) != 0;
2439 public bool IsLocked {
2441 return (flags & Flags.IsLocked) != 0;
2444 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2448 public bool IsThis {
2450 return (flags & Flags.IsThis) != 0;
2454 public bool IsFixed {
2456 return (flags & Flags.FixedVariable) != 0;
2460 bool INamedBlockVariable.IsParameter {
2466 public bool IsReadonly {
2468 return (flags & Flags.ReadonlyMask) != 0;
2472 public Location Location {
2478 public string Name {
2484 public TypeSpec Type {
2495 public void CreateBuilder (EmitContext ec)
2497 if ((flags & Flags.Used) == 0) {
2498 if (VariableInfo == null) {
2499 // Missing flow analysis or wrong variable flags
2500 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2503 if (VariableInfo.IsEverAssigned)
2504 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2506 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2509 if (HoistedVariant != null)
2512 if (builder != null) {
2513 if ((flags & Flags.CompilerGenerated) != 0)
2516 // To avoid Used warning duplicates
2517 throw new InternalErrorException ("Already created variable `{0}'", name);
2521 // All fixed variabled are pinned, a slot has to be alocated
2523 builder = ec.DeclareLocal (Type, IsFixed);
2524 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2525 ec.DefineLocalVariable (name, builder);
2528 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2530 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2535 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2537 if (IsConstant && const_value != null)
2538 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2540 return new LocalVariableReference (this, loc);
2543 public void Emit (EmitContext ec)
2545 // TODO: Need something better for temporary variables
2546 if ((flags & Flags.CompilerGenerated) != 0)
2549 ec.Emit (OpCodes.Ldloc, builder);
2552 public void EmitAssign (EmitContext ec)
2554 // TODO: Need something better for temporary variables
2555 if ((flags & Flags.CompilerGenerated) != 0)
2558 ec.Emit (OpCodes.Stloc, builder);
2561 public void EmitAddressOf (EmitContext ec)
2563 // TODO: Need something better for temporary variables
2564 if ((flags & Flags.CompilerGenerated) != 0)
2567 ec.Emit (OpCodes.Ldloca, builder);
2570 public static string GetCompilerGeneratedName (Block block)
2572 // HACK: Debugger depends on the name semantics
2573 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2576 public string GetReadOnlyContext ()
2578 switch (flags & Flags.ReadonlyMask) {
2579 case Flags.FixedVariable:
2580 return "fixed variable";
2581 case Flags.ForeachVariable:
2582 return "foreach iteration variable";
2583 case Flags.UsingVariable:
2584 return "using variable";
2587 throw new InternalErrorException ("Variable is not readonly");
2590 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2592 if (VariableInfo == null)
2593 throw new Exception ();
2595 if (IsAssigned (fc))
2598 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2601 public bool IsAssigned (FlowAnalysisContext fc)
2603 return fc.IsDefinitelyAssigned (VariableInfo);
2606 public void PrepareAssignmentAnalysis (BlockContext bc)
2609 // No need to run assignment analysis for these guys
2611 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2614 VariableInfo = VariableInfo.Create (bc, this);
2618 // Mark the variables as referenced in the user code
2620 public void SetIsUsed ()
2622 flags |= Flags.Used;
2625 public void SetHasAddressTaken ()
2627 flags |= (Flags.AddressTaken | Flags.Used);
2630 public override string ToString ()
2632 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2637 /// Block represents a C# block.
2641 /// This class is used in a number of places: either to represent
2642 /// explicit blocks that the programmer places or implicit blocks.
2644 /// Implicit blocks are used as labels or to introduce variable
2647 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2648 /// they contain extra information that is not necessary on normal blocks.
2650 public class Block : Statement {
2657 HasCapturedVariable = 64,
2658 HasCapturedThis = 1 << 7,
2659 IsExpressionTree = 1 << 8,
2660 CompilerGenerated = 1 << 9,
2661 HasAsyncModifier = 1 << 10,
2663 YieldBlock = 1 << 12,
2664 AwaitBlock = 1 << 13,
2665 FinallyBlock = 1 << 14,
2666 CatchBlock = 1 << 15,
2668 NoFlowAnalysis = 1 << 21,
2669 InitializationEmitted = 1 << 22
2672 public Block Parent;
2673 public Location StartLocation;
2674 public Location EndLocation;
2676 public ExplicitBlock Explicit;
2677 public ParametersBlock ParametersBlock;
2679 protected Flags flags;
2682 // The statements in this block
2684 protected List<Statement> statements;
2686 protected List<Statement> scope_initializers;
2688 int? resolving_init_idx;
2694 public int ID = id++;
2696 static int clone_id_counter;
2700 // int assignable_slots;
2702 public Block (Block parent, Location start, Location end)
2703 : this (parent, 0, start, end)
2707 public Block (Block parent, Flags flags, Location start, Location end)
2709 if (parent != null) {
2710 // the appropriate constructors will fixup these fields
2711 ParametersBlock = parent.ParametersBlock;
2712 Explicit = parent.Explicit;
2715 this.Parent = parent;
2717 this.StartLocation = start;
2718 this.EndLocation = end;
2720 statements = new List<Statement> (4);
2722 this.original = this;
2727 public Block Original {
2736 public bool IsCompilerGenerated {
2737 get { return (flags & Flags.CompilerGenerated) != 0; }
2738 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2742 public bool IsCatchBlock {
2744 return (flags & Flags.CatchBlock) != 0;
2748 public bool IsFinallyBlock {
2750 return (flags & Flags.FinallyBlock) != 0;
2754 public bool Unchecked {
2755 get { return (flags & Flags.Unchecked) != 0; }
2756 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2759 public bool Unsafe {
2760 get { return (flags & Flags.Unsafe) != 0; }
2761 set { flags |= Flags.Unsafe; }
2764 public List<Statement> Statements {
2765 get { return statements; }
2770 public void SetEndLocation (Location loc)
2775 public void AddLabel (LabeledStatement target)
2777 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2780 public void AddLocalName (LocalVariable li)
2782 AddLocalName (li.Name, li);
2785 public void AddLocalName (string name, INamedBlockVariable li)
2787 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2790 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2792 if (reason == null) {
2793 Error_AlreadyDeclared (name, variable);
2797 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2798 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2799 "to `{0}', which is already used in a `{1}' scope to denote something else",
2803 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2805 var pi = variable as ParametersBlock.ParameterInfo;
2807 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2809 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2810 "A local variable named `{0}' is already defined in this scope", name);
2814 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2816 ParametersBlock.TopBlock.Report.Error (412, loc,
2817 "The type parameter name `{0}' is the same as local variable or parameter name",
2822 // It should be used by expressions which require to
2823 // register a statement during resolve process.
2825 public void AddScopeStatement (Statement s)
2827 if (scope_initializers == null)
2828 scope_initializers = new List<Statement> ();
2831 // Simple recursive helper, when resolve scope initializer another
2832 // new scope initializer can be added, this ensures it's initialized
2833 // before existing one. For now this can happen with expression trees
2834 // in base ctor initializer only
2836 if (resolving_init_idx.HasValue) {
2837 scope_initializers.Insert (resolving_init_idx.Value, s);
2838 ++resolving_init_idx;
2840 scope_initializers.Add (s);
2844 public void InsertStatement (int index, Statement s)
2846 statements.Insert (index, s);
2849 public void AddStatement (Statement s)
2854 public LabeledStatement LookupLabel (string name)
2856 return ParametersBlock.GetLabel (name, this);
2859 public override Reachability MarkReachable (Reachability rc)
2861 if (rc.IsUnreachable)
2864 MarkReachableScope (rc);
2866 foreach (var s in statements) {
2867 rc = s.MarkReachable (rc);
2868 if (rc.IsUnreachable) {
2869 if ((flags & Flags.ReachableEnd) != 0)
2870 return new Reachability ();
2876 flags |= Flags.ReachableEnd;
2881 public void MarkReachableScope (Reachability rc)
2883 base.MarkReachable (rc);
2885 if (scope_initializers != null) {
2886 foreach (var si in scope_initializers)
2887 si.MarkReachable (rc);
2891 public override bool Resolve (BlockContext bc)
2893 if ((flags & Flags.Resolved) != 0)
2896 Block prev_block = bc.CurrentBlock;
2897 bc.CurrentBlock = this;
2900 // Compiler generated scope statements
2902 if (scope_initializers != null) {
2903 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2904 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2907 resolving_init_idx = null;
2911 int statement_count = statements.Count;
2912 for (int ix = 0; ix < statement_count; ix++){
2913 Statement s = statements [ix];
2915 if (!s.Resolve (bc)) {
2917 statements [ix] = new EmptyStatement (s.loc);
2922 bc.CurrentBlock = prev_block;
2924 flags |= Flags.Resolved;
2928 protected override void DoEmit (EmitContext ec)
2930 for (int ix = 0; ix < statements.Count; ix++){
2931 statements [ix].Emit (ec);
2935 public override void Emit (EmitContext ec)
2937 if (scope_initializers != null)
2938 EmitScopeInitializers (ec);
2943 protected void EmitScopeInitializers (EmitContext ec)
2945 foreach (Statement s in scope_initializers)
2949 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2951 if (scope_initializers != null) {
2952 foreach (var si in scope_initializers)
2953 si.FlowAnalysis (fc);
2956 return DoFlowAnalysis (fc, 0);
2959 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2961 bool end_unreachable = !reachable;
2962 bool goto_flow_analysis = startIndex != 0;
2963 for (; startIndex < statements.Count; ++startIndex) {
2964 var s = statements[startIndex];
2966 end_unreachable = s.FlowAnalysis (fc);
2967 if (s.IsUnreachable) {
2968 statements [startIndex] = RewriteUnreachableStatement (s);
2973 // Statement end reachability is needed mostly due to goto support. Consider
2982 // X label is reachable only via goto not as another statement after if. We need
2983 // this for flow-analysis only to carry variable info correctly.
2985 if (end_unreachable) {
2986 bool after_goto_case = goto_flow_analysis && s is GotoCase;
2988 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2989 s = statements[startIndex];
2990 if (s is SwitchLabel) {
2991 if (!after_goto_case)
2992 s.FlowAnalysis (fc);
2997 if (s.IsUnreachable) {
2998 s.FlowAnalysis (fc);
2999 statements [startIndex] = RewriteUnreachableStatement (s);
3004 // Idea is to stop after goto case because goto case will always have at least same
3005 // variable assigned as switch case label. This saves a lot for complex goto case tests
3007 if (after_goto_case)
3013 var lb = s as LabeledStatement;
3014 if (lb != null && fc.AddReachedLabel (lb))
3019 // The condition should be true unless there is forward jumping goto
3021 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3024 return !Explicit.HasReachableClosingBrace;
3027 static Statement RewriteUnreachableStatement (Statement s)
3029 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3030 // analysis. Even csc report unreachable warning for it but it's actually used hence
3031 // we try to emulate this behaviour
3039 if (s is BlockVariable || s is EmptyStatement)
3042 return new EmptyStatement (s.loc);
3045 public void ScanGotoJump (Statement label)
3048 for (i = 0; i < statements.Count; ++i) {
3049 if (statements[i] == label)
3053 var rc = new Reachability ();
3054 for (++i; i < statements.Count; ++i) {
3055 var s = statements[i];
3056 rc = s.MarkReachable (rc);
3057 if (rc.IsUnreachable)
3061 flags |= Flags.ReachableEnd;
3064 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3067 for (i = 0; i < statements.Count; ++i) {
3068 if (statements[i] == label)
3072 DoFlowAnalysis (fc, ++i);
3076 public override string ToString ()
3078 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3082 protected override void CloneTo (CloneContext clonectx, Statement t)
3084 Block target = (Block) t;
3086 target.clone_id = ++clone_id_counter;
3089 clonectx.AddBlockMap (this, target);
3090 if (original != this)
3091 clonectx.AddBlockMap (original, target);
3093 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3094 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3097 target.Parent = clonectx.RemapBlockCopy (Parent);
3099 target.statements = new List<Statement> (statements.Count);
3100 foreach (Statement s in statements)
3101 target.statements.Add (s.Clone (clonectx));
3104 public override object Accept (StructuralVisitor visitor)
3106 return visitor.Visit (this);
3110 public class ExplicitBlock : Block
3112 protected AnonymousMethodStorey am_storey;
3114 public ExplicitBlock (Block parent, Location start, Location end)
3115 : this (parent, (Flags) 0, start, end)
3119 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3120 : base (parent, flags, start, end)
3122 this.Explicit = this;
3127 public AnonymousMethodStorey AnonymousMethodStorey {
3133 public bool HasAwait {
3135 return (flags & Flags.AwaitBlock) != 0;
3139 public bool HasCapturedThis {
3141 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3144 return (flags & Flags.HasCapturedThis) != 0;
3149 // Used to indicate that the block has reference to parent
3150 // block and cannot be made static when defining anonymous method
3152 public bool HasCapturedVariable {
3154 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3157 return (flags & Flags.HasCapturedVariable) != 0;
3161 public bool HasReachableClosingBrace {
3163 return (flags & Flags.ReachableEnd) != 0;
3166 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3170 public bool HasYield {
3172 return (flags & Flags.YieldBlock) != 0;
3179 // Creates anonymous method storey in current block
3181 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3184 // Return same story for iterator and async blocks unless we are
3185 // in nested anonymous method
3187 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3188 return ec.CurrentAnonymousMethod.Storey;
3190 if (am_storey == null) {
3191 MemberBase mc = ec.MemberContext as MemberBase;
3194 // Creates anonymous method storey for this block
3196 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3202 public void EmitScopeInitialization (EmitContext ec)
3204 if ((flags & Flags.InitializationEmitted) != 0)
3207 if (am_storey != null) {
3208 DefineStoreyContainer (ec, am_storey);
3209 am_storey.EmitStoreyInstantiation (ec, this);
3212 if (scope_initializers != null)
3213 EmitScopeInitializers (ec);
3215 flags |= Flags.InitializationEmitted;
3218 public override void Emit (EmitContext ec)
3223 EmitScopeInitialization (ec);
3225 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3226 ec.Emit (OpCodes.Nop);
3234 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3235 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3236 ec.Emit (OpCodes.Nop);
3240 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3242 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3243 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3244 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3248 // Creates anonymous method storey
3250 storey.CreateContainer ();
3251 storey.DefineContainer ();
3253 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3256 // Only first storey in path will hold this reference. All children blocks will
3257 // reference it indirectly using $ref field
3259 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3260 if (b.Parent != null) {
3261 var s = b.Parent.Explicit.AnonymousMethodStorey;
3263 storey.HoistedThis = s.HoistedThis;
3268 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3269 if (storey.HoistedThis == null)
3270 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3272 if (storey.HoistedThis != null)
3278 // We are the first storey on path and 'this' has to be hoisted
3280 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3281 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3283 // ThisReferencesFromChildrenBlock holds all reference even if they
3284 // are not on this path. It saves some memory otherwise it'd have to
3285 // be in every explicit block. We run this check to see if the reference
3286 // is valid for this storey
3288 Block block_on_path = ref_block;
3289 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3291 if (block_on_path == null)
3294 if (storey.HoistedThis == null) {
3295 storey.AddCapturedThisField (ec, null);
3298 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3300 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3302 if (b_storey != null) {
3304 // Don't add storey cross reference for `this' when the storey ends up not
3305 // beeing attached to any parent
3307 if (b.ParametersBlock.StateMachine == null) {
3308 AnonymousMethodStorey s = null;
3309 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3310 s = ab.Explicit.AnonymousMethodStorey;
3315 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3317 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3318 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3325 // Stop propagation inside same top block
3327 if (b.ParametersBlock == ParametersBlock.Original) {
3328 b_storey.AddParentStoreyReference (ec, storey);
3329 // b_storey.HoistedThis = storey.HoistedThis;
3333 b = pb = b.ParametersBlock;
3335 pb = b as ParametersBlock;
3338 if (pb != null && pb.StateMachine != null) {
3339 if (pb.StateMachine == storey)
3343 // If we are state machine with no parent. We can hook into parent without additional
3344 // reference and capture this directly
3346 ExplicitBlock parent_storey_block = pb;
3347 while (parent_storey_block.Parent != null) {
3348 parent_storey_block = parent_storey_block.Parent.Explicit;
3349 if (parent_storey_block.AnonymousMethodStorey != null) {
3354 if (parent_storey_block.AnonymousMethodStorey == null) {
3355 pb.StateMachine.AddCapturedThisField (ec, null);
3356 b.HasCapturedThis = true;
3360 pb.StateMachine.AddParentStoreyReference (ec, storey);
3364 // Add parent storey reference only when this is not captured directly
3366 if (b_storey != null) {
3367 b_storey.AddParentStoreyReference (ec, storey);
3368 b_storey.HoistedThis = storey.HoistedThis;
3375 var ref_blocks = storey.ReferencesFromChildrenBlock;
3376 if (ref_blocks != null) {
3377 foreach (ExplicitBlock ref_block in ref_blocks) {
3378 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3379 if (b.AnonymousMethodStorey != null) {
3380 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3383 // Stop propagation inside same top block
3385 if (b.ParametersBlock == ParametersBlock.Original)
3388 b = b.ParametersBlock;
3391 var pb = b as ParametersBlock;
3392 if (pb != null && pb.StateMachine != null) {
3393 if (pb.StateMachine == storey)
3396 pb.StateMachine.AddParentStoreyReference (ec, storey);
3399 b.HasCapturedVariable = true;
3405 storey.PrepareEmit ();
3406 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3409 public void RegisterAsyncAwait ()
3412 while ((block.flags & Flags.AwaitBlock) == 0) {
3413 block.flags |= Flags.AwaitBlock;
3415 if (block is ParametersBlock)
3418 block = block.Parent.Explicit;
3422 public void RegisterIteratorYield ()
3424 ParametersBlock.TopBlock.IsIterator = true;
3427 while ((block.flags & Flags.YieldBlock) == 0) {
3428 block.flags |= Flags.YieldBlock;
3430 if (block.Parent == null)
3433 block = block.Parent.Explicit;
3437 public void SetCatchBlock ()
3439 flags |= Flags.CatchBlock;
3442 public void SetFinallyBlock ()
3444 flags |= Flags.FinallyBlock;
3447 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3449 tryBlock.statements = statements;
3450 statements = new List<Statement> (1);
3451 statements.Add (tf);
3456 // ParametersBlock was introduced to support anonymous methods
3457 // and lambda expressions
3459 public class ParametersBlock : ExplicitBlock
3461 public class ParameterInfo : INamedBlockVariable
3463 readonly ParametersBlock block;
3465 public VariableInfo VariableInfo;
3468 public ParameterInfo (ParametersBlock block, int index)
3476 public ParametersBlock Block {
3482 Block INamedBlockVariable.Block {
3488 public bool IsDeclared {
3494 public bool IsParameter {
3500 public bool IsLocked {
3509 public Location Location {
3511 return Parameter.Location;
3515 public Parameter Parameter {
3517 return block.Parameters [index];
3521 public TypeSpec ParameterType {
3523 return Parameter.Type;
3529 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3531 return new ParameterReference (this, loc);
3536 // Block is converted into an expression
3538 sealed class BlockScopeExpression : Expression
3541 readonly ParametersBlock block;
3543 public BlockScopeExpression (Expression child, ParametersBlock block)
3549 public override bool ContainsEmitWithAwait ()
3551 return child.ContainsEmitWithAwait ();
3554 public override Expression CreateExpressionTree (ResolveContext ec)
3556 throw new NotSupportedException ();
3559 protected override Expression DoResolve (ResolveContext ec)
3564 child = child.Resolve (ec);
3568 eclass = child.eclass;
3573 public override void Emit (EmitContext ec)
3575 block.EmitScopeInitializers (ec);
3580 protected ParametersCompiled parameters;
3581 protected ParameterInfo[] parameter_info;
3582 protected bool resolved;
3583 protected ToplevelBlock top_block;
3584 protected StateMachine state_machine;
3585 protected Dictionary<string, object> labels;
3587 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3588 : base (parent, 0, start, start)
3590 if (parameters == null)
3591 throw new ArgumentNullException ("parameters");
3593 this.parameters = parameters;
3594 ParametersBlock = this;
3596 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3598 this.top_block = parent.ParametersBlock.top_block;
3599 ProcessParameters ();
3602 protected ParametersBlock (ParametersCompiled parameters, Location start)
3603 : base (null, 0, start, start)
3605 if (parameters == null)
3606 throw new ArgumentNullException ("parameters");
3608 this.parameters = parameters;
3609 ParametersBlock = this;
3613 // It's supposed to be used by method body implementation of anonymous methods
3615 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3616 : base (null, 0, source.StartLocation, source.EndLocation)
3618 this.parameters = parameters;
3619 this.statements = source.statements;
3620 this.scope_initializers = source.scope_initializers;
3622 this.resolved = true;
3623 this.reachable = source.reachable;
3624 this.am_storey = source.am_storey;
3625 this.state_machine = source.state_machine;
3626 this.flags = source.flags & Flags.ReachableEnd;
3628 ParametersBlock = this;
3631 // Overwrite original for comparison purposes when linking cross references
3632 // between anonymous methods
3634 Original = source.Original;
3639 public bool IsAsync {
3641 return (flags & Flags.HasAsyncModifier) != 0;
3644 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3649 // Block has been converted to expression tree
3651 public bool IsExpressionTree {
3653 return (flags & Flags.IsExpressionTree) != 0;
3658 // The parameters for the block.
3660 public ParametersCompiled Parameters {
3666 public StateMachine StateMachine {
3668 return state_machine;
3672 public ToplevelBlock TopBlock {
3681 public bool Resolved {
3683 return (flags & Flags.Resolved) != 0;
3687 public int TemporaryLocalsCount { get; set; }
3692 // Checks whether all `out' parameters have been assigned.
3694 public void CheckControlExit (FlowAnalysisContext fc)
3696 CheckControlExit (fc, fc.DefiniteAssignment);
3699 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3701 if (parameter_info == null)
3704 foreach (var p in parameter_info) {
3705 if (p.VariableInfo == null)
3708 if (p.VariableInfo.IsAssigned (dat))
3711 fc.Report.Error (177, p.Location,
3712 "The out parameter `{0}' must be assigned to before control leaves the current method",
3717 protected override void CloneTo (CloneContext clonectx, Statement t)
3719 base.CloneTo (clonectx, t);
3721 var target = (ParametersBlock) t;
3724 // Clone label statements as well as they contain block reference
3728 if (pb.labels != null) {
3729 target.labels = new Dictionary<string, object> ();
3731 foreach (var entry in pb.labels) {
3732 var list = entry.Value as List<LabeledStatement>;
3735 var list_clone = new List<LabeledStatement> ();
3736 foreach (var lentry in list) {
3737 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3740 target.labels.Add (entry.Key, list_clone);
3742 var labeled = (LabeledStatement) entry.Value;
3743 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3750 if (pb.Parent == null)
3753 pb = pb.Parent.ParametersBlock;
3757 public override Expression CreateExpressionTree (ResolveContext ec)
3759 if (statements.Count == 1) {
3760 Expression expr = statements[0].CreateExpressionTree (ec);
3761 if (scope_initializers != null)
3762 expr = new BlockScopeExpression (expr, this);
3767 return base.CreateExpressionTree (ec);
3770 public override void Emit (EmitContext ec)
3772 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3773 DefineStoreyContainer (ec, state_machine);
3774 state_machine.EmitStoreyInstantiation (ec, this);
3780 public void EmitEmbedded (EmitContext ec)
3782 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3783 DefineStoreyContainer (ec, state_machine);
3784 state_machine.EmitStoreyInstantiation (ec, this);
3790 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3792 var res = base.DoFlowAnalysis (fc);
3794 if (HasReachableClosingBrace)
3795 CheckControlExit (fc);
3800 public LabeledStatement GetLabel (string name, Block block)
3803 // Cloned parameters blocks can have their own cloned version of top-level labels
3805 if (labels == null) {
3807 return Parent.ParametersBlock.GetLabel (name, block);
3813 if (!labels.TryGetValue (name, out value)) {
3817 var label = value as LabeledStatement;
3819 if (label != null) {
3820 if (IsLabelVisible (label, b))
3824 List<LabeledStatement> list = (List<LabeledStatement>) value;
3825 for (int i = 0; i < list.Count; ++i) {
3827 if (IsLabelVisible (label, b))
3835 static bool IsLabelVisible (LabeledStatement label, Block b)
3838 if (label.Block == b)
3841 } while (b != null);
3846 public ParameterInfo GetParameterInfo (Parameter p)
3848 for (int i = 0; i < parameters.Count; ++i) {
3849 if (parameters[i] == p)
3850 return parameter_info[i];
3853 throw new ArgumentException ("Invalid parameter");
3856 public ParameterReference GetParameterReference (int index, Location loc)
3858 return new ParameterReference (parameter_info[index], loc);
3861 public Statement PerformClone ()
3863 CloneContext clonectx = new CloneContext ();
3864 return Clone (clonectx);
3867 protected void ProcessParameters ()
3869 if (parameters.Count == 0)
3872 parameter_info = new ParameterInfo[parameters.Count];
3873 for (int i = 0; i < parameter_info.Length; ++i) {
3874 var p = parameters.FixedParameters[i];
3878 // TODO: Should use Parameter only and more block there
3879 parameter_info[i] = new ParameterInfo (this, i);
3881 AddLocalName (p.Name, parameter_info[i]);
3885 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3887 var src = stmt.Block;
3890 // Cannot remap label block if the label was not yet cloned which
3891 // can happen in case of anonymous method inside anoynymous method
3892 // with a label. But in this case we don't care because goto cannot
3893 // jump of out anonymous method
3895 if (src.ParametersBlock != this)
3898 var src_stmts = src.Statements;
3899 for (int i = 0; i < src_stmts.Count; ++i) {
3900 if (src_stmts[i] == stmt)
3901 return (LabeledStatement) dst.Statements[i];
3904 throw new InternalErrorException ("Should never be reached");
3907 public override bool Resolve (BlockContext bc)
3909 // TODO: if ((flags & Flags.Resolved) != 0)
3916 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3917 flags |= Flags.IsExpressionTree;
3920 PrepareAssignmentAnalysis (bc);
3922 if (!base.Resolve (bc))
3925 } catch (Exception e) {
3926 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3929 if (bc.CurrentBlock != null) {
3930 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3932 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3937 // If an asynchronous body of F is either an expression classified as nothing, or a
3938 // statement block where no return statements have expressions, the inferred return type is Task
3941 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3942 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3943 am.ReturnTypeInference = null;
3944 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3952 void PrepareAssignmentAnalysis (BlockContext bc)
3954 for (int i = 0; i < parameters.Count; ++i) {
3955 var par = parameters.FixedParameters[i];
3957 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3960 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3964 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3966 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3967 var stateMachine = new IteratorStorey (iterator);
3969 state_machine = stateMachine;
3970 iterator.SetStateMachine (stateMachine);
3972 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3973 tlb.Original = this;
3974 tlb.state_machine = stateMachine;
3975 tlb.AddStatement (new Return (iterator, iterator.Location));
3979 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3981 for (int i = 0; i < parameters.Count; i++) {
3982 Parameter p = parameters[i];
3983 Parameter.Modifier mod = p.ModFlags;
3984 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3985 host.Compiler.Report.Error (1988, p.Location,
3986 "Async methods cannot have ref or out parameters");
3990 if (p is ArglistParameter) {
3991 host.Compiler.Report.Error (4006, p.Location,
3992 "__arglist is not allowed in parameter list of async methods");
3996 if (parameters.Types[i].IsPointer) {
3997 host.Compiler.Report.Error (4005, p.Location,
3998 "Async methods cannot have unsafe parameters");
4004 host.Compiler.Report.Warning (1998, 1, loc,
4005 "Async block lacks `await' operator and will run synchronously");
4008 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4009 var initializer = new AsyncInitializer (this, host, block_type);
4010 initializer.Type = block_type;
4011 initializer.DelegateType = delegateType;
4013 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4015 state_machine = stateMachine;
4016 initializer.SetStateMachine (stateMachine);
4018 const Flags flags = Flags.CompilerGenerated;
4020 var b = this is ToplevelBlock ?
4021 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4022 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4025 b.state_machine = stateMachine;
4026 b.AddStatement (new AsyncInitializerStatement (initializer));
4034 public class ToplevelBlock : ParametersBlock
4036 LocalVariable this_variable;
4037 CompilerContext compiler;
4038 Dictionary<string, object> names;
4040 List<ExplicitBlock> this_references;
4042 public ToplevelBlock (CompilerContext ctx, Location loc)
4043 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4047 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4048 : base (parameters, start)
4050 this.compiler = ctx;
4054 ProcessParameters ();
4058 // Recreates a top level block from parameters block. Used for
4059 // compiler generated methods where the original block comes from
4060 // explicit child block. This works for already resolved blocks
4061 // only to ensure we resolve them in the correct flow order
4063 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4064 : base (source, parameters)
4066 this.compiler = source.TopBlock.compiler;
4070 public bool IsIterator {
4072 return (flags & Flags.Iterator) != 0;
4075 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4079 public Report Report {
4081 return compiler.Report;
4086 // Used by anonymous blocks to track references of `this' variable
4088 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4090 return this_references;
4095 // Returns the "this" instance variable of this block.
4096 // See AddThisVariable() for more information.
4098 public LocalVariable ThisVariable {
4100 return this_variable;
4104 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4107 names = new Dictionary<string, object> ();
4110 if (!names.TryGetValue (name, out value)) {
4111 names.Add (name, li);
4115 INamedBlockVariable existing = value as INamedBlockVariable;
4116 List<INamedBlockVariable> existing_list;
4117 if (existing != null) {
4118 existing_list = new List<INamedBlockVariable> ();
4119 existing_list.Add (existing);
4120 names[name] = existing_list;
4122 existing_list = (List<INamedBlockVariable>) value;
4126 // A collision checking between local names
4128 var variable_block = li.Block.Explicit;
4129 for (int i = 0; i < existing_list.Count; ++i) {
4130 existing = existing_list[i];
4131 Block b = existing.Block.Explicit;
4133 // Collision at same level
4134 if (variable_block == b) {
4135 li.Block.Error_AlreadyDeclared (name, li);
4139 // Collision with parent
4140 Block parent = variable_block;
4141 while ((parent = parent.Parent) != null) {
4143 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4144 i = existing_list.Count;
4149 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4150 // Collision with children
4151 while ((b = b.Parent) != null) {
4152 if (variable_block == b) {
4153 li.Block.Error_AlreadyDeclared (name, li, "child");
4154 i = existing_list.Count;
4161 existing_list.Add (li);
4164 public void AddLabel (string name, LabeledStatement label)
4167 labels = new Dictionary<string, object> ();
4170 if (!labels.TryGetValue (name, out value)) {
4171 labels.Add (name, label);
4175 LabeledStatement existing = value as LabeledStatement;
4176 List<LabeledStatement> existing_list;
4177 if (existing != null) {
4178 existing_list = new List<LabeledStatement> ();
4179 existing_list.Add (existing);
4180 labels[name] = existing_list;
4182 existing_list = (List<LabeledStatement>) value;
4186 // A collision checking between labels
4188 for (int i = 0; i < existing_list.Count; ++i) {
4189 existing = existing_list[i];
4190 Block b = existing.Block;
4192 // Collision at same level
4193 if (label.Block == b) {
4194 Report.SymbolRelatedToPreviousError (existing.loc, name);
4195 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4199 // Collision with parent
4201 while ((b = b.Parent) != null) {
4202 if (existing.Block == b) {
4203 Report.Error (158, label.loc,
4204 "The label `{0}' shadows another label by the same name in a contained scope", name);
4205 i = existing_list.Count;
4210 // Collision with with children
4212 while ((b = b.Parent) != null) {
4213 if (label.Block == b) {
4214 Report.Error (158, label.loc,
4215 "The label `{0}' shadows another label by the same name in a contained scope", name);
4216 i = existing_list.Count;
4222 existing_list.Add (label);
4225 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4227 if (this_references == null)
4228 this_references = new List<ExplicitBlock> ();
4230 if (!this_references.Contains (block))
4231 this_references.Add (block);
4234 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4236 this_references.Remove (block);
4240 // Creates an arguments set from all parameters, useful for method proxy calls
4242 public Arguments GetAllParametersArguments ()
4244 int count = parameters.Count;
4245 Arguments args = new Arguments (count);
4246 for (int i = 0; i < count; ++i) {
4247 var pi = parameter_info[i];
4248 var arg_expr = GetParameterReference (i, pi.Location);
4250 Argument.AType atype_modifier;
4251 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4252 case Parameter.Modifier.REF:
4253 atype_modifier = Argument.AType.Ref;
4255 case Parameter.Modifier.OUT:
4256 atype_modifier = Argument.AType.Out;
4263 args.Add (new Argument (arg_expr, atype_modifier));
4270 // Lookup inside a block, the returned value can represent 3 states
4272 // true+variable: A local name was found and it's valid
4273 // false+variable: A local name was found in a child block only
4274 // false+null: No local name was found
4276 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4282 if (!names.TryGetValue (name, out value))
4285 variable = value as INamedBlockVariable;
4287 if (variable != null) {
4289 if (variable.Block == b.Original)
4293 } while (b != null);
4301 } while (b != null);
4303 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4304 for (int i = 0; i < list.Count; ++i) {
4307 if (variable.Block == b.Original)
4311 } while (b != null);
4319 } while (b != null);
4329 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4331 if (block.names != null) {
4332 foreach (var n in block.names) {
4333 var variable = n.Value as INamedBlockVariable;
4334 if (variable != null) {
4335 if (variable.Block.ParametersBlock == pb)
4336 AddLocalName (n.Key, variable, false);
4340 foreach (var v in (List<INamedBlockVariable>) n.Value)
4341 if (v.Block.ParametersBlock == pb)
4342 AddLocalName (n.Key, v, false);
4348 // This is used by non-static `struct' constructors which do not have an
4349 // initializer - in this case, the constructor must initialize all of the
4350 // struct's fields. To do this, we add a "this" variable and use the flow
4351 // analysis code to ensure that it's been fully initialized before control
4352 // leaves the constructor.
4354 public void AddThisVariable (BlockContext bc)
4356 if (this_variable != null)
4357 throw new InternalErrorException (StartLocation.ToString ());
4359 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4360 this_variable.Type = bc.CurrentType;
4361 this_variable.PrepareAssignmentAnalysis (bc);
4364 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4367 // If we're a non-static struct constructor which doesn't have an
4368 // initializer, then we must initialize all of the struct's fields.
4370 if (this_variable != null)
4371 this_variable.IsThisAssigned (fc, this);
4373 base.CheckControlExit (fc, dat);
4376 public override void Emit (EmitContext ec)
4378 if (Report.Errors > 0)
4382 if (IsCompilerGenerated) {
4383 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4391 // If `HasReturnLabel' is set, then we already emitted a
4392 // jump to the end of the method, so we must emit a `ret'
4395 // Unfortunately, System.Reflection.Emit automatically emits
4396 // a leave to the end of a finally block. This is a problem
4397 // if no code is following the try/finally block since we may
4398 // jump to a point after the end of the method.
4399 // As a workaround, we're always creating a return label in
4402 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4403 if (ec.HasReturnLabel)
4404 ec.MarkLabel (ec.ReturnLabel);
4406 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4407 ec.Mark (EndLocation);
4409 if (ec.ReturnType.Kind != MemberKind.Void)
4410 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4412 ec.Emit (OpCodes.Ret);
4415 } catch (Exception e) {
4416 throw new InternalErrorException (e, StartLocation);
4420 public bool Resolve (BlockContext bc, IMethodData md)
4425 var errors = bc.Report.Errors;
4429 if (bc.Report.Errors > errors)
4432 MarkReachable (new Reachability ());
4434 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4435 // TODO: var md = bc.CurrentMemberDefinition;
4436 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4439 if ((flags & Flags.NoFlowAnalysis) != 0)
4442 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4445 } catch (Exception e) {
4446 throw new InternalErrorException (e, StartLocation);
4453 public class SwitchLabel : Statement
4461 // if expr == null, then it is the default case.
4463 public SwitchLabel (Expression expr, Location l)
4469 public bool IsDefault {
4471 return label == null;
4475 public Expression Label {
4481 public Location Location {
4487 public Constant Converted {
4496 public bool PatternMatching { get; set; }
4498 public bool SectionStart { get; set; }
4500 public Label GetILLabel (EmitContext ec)
4502 if (il_label == null){
4503 il_label = ec.DefineLabel ();
4506 return il_label.Value;
4509 protected override void DoEmit (EmitContext ec)
4511 ec.MarkLabel (GetILLabel (ec));
4514 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4519 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4523 public override bool Resolve (BlockContext bc)
4525 if (ResolveAndReduce (bc))
4526 bc.Switch.RegisterLabel (bc, this);
4532 // Resolves the expression, reduces it to a literal if possible
4533 // and then converts it to the requested type.
4535 bool ResolveAndReduce (BlockContext bc)
4540 var switch_statement = bc.Switch;
4542 if (PatternMatching) {
4543 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4544 return label != null;
4547 var c = label.ResolveLabelConstant (bc);
4551 if (switch_statement.IsNullable && c is NullLiteral) {
4556 if (switch_statement.IsPatternMatching) {
4557 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4561 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4562 return converted != null;
4565 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4567 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4568 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4571 protected override void CloneTo (CloneContext clonectx, Statement target)
4573 var t = (SwitchLabel) target;
4575 t.label = label.Clone (clonectx);
4578 public override object Accept (StructuralVisitor visitor)
4580 return visitor.Visit (this);
4583 public string GetSignatureForError ()
4586 if (converted == null)
4589 label = converted.GetValueAsLiteral ();
4591 return string.Format ("case {0}:", label);
4595 public class Switch : LoopStatement
4597 // structure used to hold blocks of keys while calculating table switch
4598 sealed class LabelsRange : IComparable<LabelsRange>
4600 public readonly long min;
4602 public readonly List<long> label_values;
4604 public LabelsRange (long value)
4607 label_values = new List<long> ();
4608 label_values.Add (value);
4611 public LabelsRange (long min, long max, ICollection<long> values)
4615 this.label_values = new List<long> (values);
4620 return max - min + 1;
4624 public bool AddValue (long value)
4626 var gap = value - min + 1;
4627 // Ensure the range has > 50% occupancy
4628 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4632 label_values.Add (value);
4636 public int CompareTo (LabelsRange other)
4638 int nLength = label_values.Count;
4639 int nLengthOther = other.label_values.Count;
4640 if (nLengthOther == nLength)
4641 return (int) (other.min - min);
4643 return nLength - nLengthOther;
4647 sealed class DispatchStatement : Statement
4649 readonly Switch body;
4651 public DispatchStatement (Switch body)
4656 protected override void CloneTo (CloneContext clonectx, Statement target)
4658 throw new NotImplementedException ();
4661 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4666 protected override void DoEmit (EmitContext ec)
4668 body.EmitDispatch (ec);
4672 class MissingBreak : Statement
4674 readonly SwitchLabel label;
4676 public MissingBreak (SwitchLabel sl)
4682 public bool FallOut { get; set; }
4684 protected override void DoEmit (EmitContext ec)
4688 protected override void CloneTo (CloneContext clonectx, Statement target)
4692 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4695 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4696 label.GetSignatureForError ());
4698 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4699 label.GetSignatureForError ());
4705 public Expression Expr;
4708 // Mapping of all labels to their SwitchLabels
4710 Dictionary<long, SwitchLabel> labels;
4711 Dictionary<string, SwitchLabel> string_labels;
4712 List<SwitchLabel> case_labels;
4714 List<Tuple<GotoCase, Constant>> goto_cases;
4715 List<DefiniteAssignmentBitSet> end_reachable_das;
4718 /// The governing switch type
4720 public TypeSpec SwitchType;
4722 Expression new_expr;
4724 SwitchLabel case_null;
4725 SwitchLabel case_default;
4727 Label defaultLabel, nullLabel;
4728 VariableReference value;
4729 ExpressionStatement string_dictionary;
4730 FieldExpr switch_cache_field;
4731 ExplicitBlock block;
4735 // Nullable Types support
4737 Nullable.Unwrap unwrap;
4739 public Switch (Expression e, ExplicitBlock block, Location l)
4747 public SwitchLabel ActiveLabel { get; set; }
4749 public ExplicitBlock Block {
4755 public SwitchLabel DefaultLabel {
4757 return case_default;
4761 public bool IsNullable {
4763 return unwrap != null;
4767 public bool IsPatternMatching {
4769 return new_expr == null && SwitchType != null;
4773 public List<SwitchLabel> RegisteredLabels {
4779 public VariableReference ExpressionValue {
4786 // Determines the governing type for a switch. The returned
4787 // expression might be the expression from the switch, or an
4788 // expression that includes any potential conversions to
4790 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4792 switch (expr.Type.BuiltinType) {
4793 case BuiltinTypeSpec.Type.Byte:
4794 case BuiltinTypeSpec.Type.SByte:
4795 case BuiltinTypeSpec.Type.UShort:
4796 case BuiltinTypeSpec.Type.Short:
4797 case BuiltinTypeSpec.Type.UInt:
4798 case BuiltinTypeSpec.Type.Int:
4799 case BuiltinTypeSpec.Type.ULong:
4800 case BuiltinTypeSpec.Type.Long:
4801 case BuiltinTypeSpec.Type.Char:
4802 case BuiltinTypeSpec.Type.String:
4803 case BuiltinTypeSpec.Type.Bool:
4807 if (expr.Type.IsEnum)
4811 // Try to find a *user* defined implicit conversion.
4813 // If there is no implicit conversion, or if there are multiple
4814 // conversions, we have to report an error
4816 Expression converted = null;
4817 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4819 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4822 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4823 Convert.UserConversionRestriction.ProbingOnly;
4826 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4828 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4833 // Ignore over-worked ImplicitUserConversions that do
4834 // an implicit conversion in addition to the user conversion.
4836 var uc = e as UserCast;
4840 if (converted != null){
4841 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4850 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4852 var types = module.Compiler.BuiltinTypes;
4854 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4855 TypeSpec[] stypes = new[] {
4868 if (nullable != null) {
4870 Array.Resize (ref stypes, stypes.Length + 9);
4872 for (int i = 0; i < 9; ++i) {
4873 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
4880 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4882 case_labels.Add (sl);
4885 if (case_default != null) {
4886 sl.Error_AlreadyOccurs (rc, case_default);
4894 if (sl.Converted == null)
4898 if (string_labels != null) {
4899 string string_value = sl.Converted.GetValue () as string;
4900 if (string_value == null)
4903 string_labels.Add (string_value, sl);
4905 if (sl.Converted.IsNull) {
4908 labels.Add (sl.Converted.GetValueAsLong (), sl);
4911 } catch (ArgumentException) {
4912 if (string_labels != null)
4913 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4915 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4920 // This method emits code for a lookup-based switch statement (non-string)
4921 // Basically it groups the cases into blocks that are at least half full,
4922 // and then spits out individual lookup opcodes for each block.
4923 // It emits the longest blocks first, and short blocks are just
4924 // handled with direct compares.
4926 void EmitTableSwitch (EmitContext ec, Expression val)
4928 if (labels != null && labels.Count > 0) {
4929 List<LabelsRange> ranges;
4930 if (string_labels != null) {
4931 // We have done all hard work for string already
4932 // setup single range only
4933 ranges = new List<LabelsRange> (1);
4934 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4936 var element_keys = new long[labels.Count];
4937 labels.Keys.CopyTo (element_keys, 0);
4938 Array.Sort (element_keys);
4941 // Build possible ranges of switch labes to reduce number
4944 ranges = new List<LabelsRange> (element_keys.Length);
4945 var range = new LabelsRange (element_keys[0]);
4947 for (int i = 1; i < element_keys.Length; ++i) {
4948 var l = element_keys[i];
4949 if (range.AddValue (l))
4952 range = new LabelsRange (l);
4956 // sort the blocks so we can tackle the largest ones first
4960 Label lbl_default = defaultLabel;
4961 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4963 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4964 LabelsRange kb = ranges[range_index];
4965 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4967 // Optimize small ranges using simple equality check
4968 if (kb.Range <= 2) {
4969 foreach (var key in kb.label_values) {
4970 SwitchLabel sl = labels[key];
4971 if (sl == case_default || sl == case_null)
4974 if (sl.Converted.IsZeroInteger) {
4975 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4978 sl.Converted.Emit (ec);
4979 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4983 // TODO: if all the keys in the block are the same and there are
4984 // no gaps/defaults then just use a range-check.
4985 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4986 // TODO: optimize constant/I4 cases
4988 // check block range (could be > 2^31)
4990 ec.EmitLong (kb.min);
4991 ec.Emit (OpCodes.Blt, lbl_default);
4994 ec.EmitLong (kb.max);
4995 ec.Emit (OpCodes.Bgt, lbl_default);
5000 ec.EmitLong (kb.min);
5001 ec.Emit (OpCodes.Sub);
5004 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5008 int first = (int) kb.min;
5011 ec.Emit (OpCodes.Sub);
5012 } else if (first < 0) {
5013 ec.EmitInt (-first);
5014 ec.Emit (OpCodes.Add);
5018 // first, build the list of labels for the switch
5020 long cJumps = kb.Range;
5021 Label[] switch_labels = new Label[cJumps];
5022 for (int iJump = 0; iJump < cJumps; iJump++) {
5023 var key = kb.label_values[iKey];
5024 if (key == kb.min + iJump) {
5025 switch_labels[iJump] = labels[key].GetILLabel (ec);
5028 switch_labels[iJump] = lbl_default;
5032 // emit the switch opcode
5033 ec.Emit (OpCodes.Switch, switch_labels);
5036 // mark the default for this block
5037 if (range_index != 0)
5038 ec.MarkLabel (lbl_default);
5041 // the last default just goes to the end
5042 if (ranges.Count > 0)
5043 ec.Emit (OpCodes.Br, lbl_default);
5047 public SwitchLabel FindLabel (Constant value)
5049 SwitchLabel sl = null;
5051 if (string_labels != null) {
5052 string s = value.GetValue () as string;
5054 if (case_null != null)
5056 else if (case_default != null)
5059 string_labels.TryGetValue (s, out sl);
5062 if (value is NullLiteral) {
5065 labels.TryGetValue (value.GetValueAsLong (), out sl);
5069 if (sl == null || sl.SectionStart)
5073 // Always return section start, it simplifies handling of switch labels
5075 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5076 var cs = case_labels [idx];
5077 if (cs.SectionStart)
5082 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5084 Expr.FlowAnalysis (fc);
5086 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5087 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5088 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5090 block.FlowAnalysis (fc);
5092 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5094 if (end_reachable_das != null) {
5095 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5096 InitialDefinitiveAssignment |= sections_das;
5097 end_reachable_das = null;
5100 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5102 return case_default != null && !end_reachable;
5105 public override bool Resolve (BlockContext ec)
5107 Expr = Expr.Resolve (ec);
5112 // LAMESPEC: User conversion from non-nullable governing type has a priority
5114 new_expr = SwitchGoverningType (ec, Expr, false);
5116 if (new_expr == null) {
5117 if (Expr.Type.IsNullableType) {
5118 unwrap = Nullable.Unwrap.Create (Expr, false);
5123 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5124 // involving nullable Expr and nullable governing type is
5126 new_expr = SwitchGoverningType (ec, unwrap, true);
5130 Expression switch_expr;
5131 if (new_expr == null) {
5132 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5133 if (Expr.Type != InternalType.ErrorType) {
5134 ec.Report.Error (151, loc,
5135 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5136 Expr.Type.GetSignatureForError ());
5143 SwitchType = Expr.Type;
5145 switch_expr = new_expr;
5146 SwitchType = new_expr.Type;
5147 if (SwitchType.IsNullableType) {
5148 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5149 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5152 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5153 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5157 if (block.Statements.Count == 0)
5160 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5161 string_labels = new Dictionary<string, SwitchLabel> ();
5163 labels = new Dictionary<long, SwitchLabel> ();
5167 var constant = switch_expr as Constant;
5170 // Don't need extra variable for constant switch or switch with
5171 // only default case
5173 if (constant == null) {
5175 // Store switch expression for comparison purposes
5177 value = switch_expr as VariableReference;
5178 if (value == null && !HasOnlyDefaultSection ()) {
5179 var current_block = ec.CurrentBlock;
5180 ec.CurrentBlock = Block;
5181 // Create temporary variable inside switch scope
5182 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5184 ec.CurrentBlock = current_block;
5188 case_labels = new List<SwitchLabel> ();
5190 Switch old_switch = ec.Switch;
5192 var parent_los = ec.EnclosingLoopOrSwitch;
5193 ec.EnclosingLoopOrSwitch = this;
5195 var ok = Statement.Resolve (ec);
5197 ec.EnclosingLoopOrSwitch = parent_los;
5198 ec.Switch = old_switch;
5201 // Check if all goto cases are valid. Needs to be done after switch
5202 // is resolved because goto can jump forward in the scope.
5204 if (goto_cases != null) {
5205 foreach (var gc in goto_cases) {
5206 if (gc.Item1 == null) {
5207 if (DefaultLabel == null) {
5208 Goto.Error_UnknownLabel (ec, "default", loc);
5214 var sl = FindLabel (gc.Item2);
5216 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5218 gc.Item1.Label = sl;
5226 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5227 ResolveStringSwitchMap (ec);
5231 // Anonymous storey initialization has to happen before
5232 // any generated switch dispatch
5234 block.InsertStatement (0, new DispatchStatement (this));
5239 bool HasOnlyDefaultSection ()
5241 for (int i = 0; i < block.Statements.Count; ++i) {
5242 var s = block.Statements[i] as SwitchLabel;
5244 if (s == null || s.IsDefault)
5253 public override Reachability MarkReachable (Reachability rc)
5255 if (rc.IsUnreachable)
5258 base.MarkReachable (rc);
5260 block.MarkReachableScope (rc);
5262 if (block.Statements.Count == 0)
5265 SwitchLabel constant_label = null;
5266 var constant = new_expr as Constant;
5268 if (constant != null) {
5269 constant_label = FindLabel (constant) ?? case_default;
5270 if (constant_label == null) {
5271 block.Statements.RemoveAt (0);
5276 var section_rc = new Reachability ();
5277 SwitchLabel prev_label = null;
5279 for (int i = 0; i < block.Statements.Count; ++i) {
5280 var s = block.Statements[i];
5281 var sl = s as SwitchLabel;
5283 if (sl != null && sl.SectionStart) {
5285 // Section is marked already via goto case
5287 if (!sl.IsUnreachable) {
5288 section_rc = new Reachability ();
5292 if (constant_label != null && constant_label != sl)
5293 section_rc = Reachability.CreateUnreachable ();
5294 else if (section_rc.IsUnreachable) {
5295 section_rc = new Reachability ();
5297 if (prev_label != null) {
5298 sl.SectionStart = false;
5299 s = new MissingBreak (prev_label);
5300 s.MarkReachable (rc);
5301 block.Statements.Insert (i - 1, s);
5309 section_rc = s.MarkReachable (section_rc);
5312 if (!section_rc.IsUnreachable && prev_label != null) {
5313 prev_label.SectionStart = false;
5314 var s = new MissingBreak (prev_label) {
5318 s.MarkReachable (rc);
5319 block.Statements.Add (s);
5323 // Reachability can affect parent only when all possible paths are handled but
5324 // we still need to run reachability check on switch body to check for fall-through
5326 if (case_default == null && constant_label == null)
5330 // We have at least one local exit from the switch
5335 return Reachability.CreateUnreachable ();
5338 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5340 if (goto_cases == null)
5341 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5343 goto_cases.Add (Tuple.Create (gotoCase, value));
5347 // Converts string switch into string hashtable
5349 void ResolveStringSwitchMap (ResolveContext ec)
5351 FullNamedExpression string_dictionary_type;
5352 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5353 string_dictionary_type = new TypeExpression (
5354 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5355 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5357 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5358 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5360 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5364 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5365 Field field = new Field (ctype, string_dictionary_type,
5366 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5367 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5368 if (!field.Define ())
5370 ctype.AddField (field);
5372 var init = new List<Expression> ();
5374 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5375 string value = null;
5377 foreach (SwitchLabel sl in case_labels) {
5379 if (sl.SectionStart)
5380 labels.Add (++counter, sl);
5382 if (sl == case_default || sl == case_null)
5385 value = (string) sl.Converted.GetValue ();
5386 var init_args = new List<Expression> (2);
5387 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5389 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5390 init_args.Add (sl.Converted);
5392 init.Add (new CollectionElementInitializer (init_args, loc));
5395 Arguments args = new Arguments (1);
5396 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5397 Expression initializer = new NewInitialize (string_dictionary_type, args,
5398 new CollectionOrObjectInitializers (init, loc), loc);
5400 switch_cache_field = new FieldExpr (field, loc);
5401 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5404 void DoEmitStringSwitch (EmitContext ec)
5406 Label l_initialized = ec.DefineLabel ();
5409 // Skip initialization when value is null
5411 value.EmitBranchable (ec, nullLabel, false);
5414 // Check if string dictionary is initialized and initialize
5416 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5417 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5418 string_dictionary.EmitStatement (ec);
5420 ec.MarkLabel (l_initialized);
5422 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5424 ResolveContext rc = new ResolveContext (ec.MemberContext);
5426 if (switch_cache_field.Type.IsGeneric) {
5427 Arguments get_value_args = new Arguments (2);
5428 get_value_args.Add (new Argument (value));
5429 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5430 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5431 if (get_item == null)
5435 // A value was not found, go to default case
5437 get_item.EmitBranchable (ec, defaultLabel, false);
5439 Arguments get_value_args = new Arguments (1);
5440 get_value_args.Add (new Argument (value));
5442 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5443 if (get_item == null)
5446 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5447 get_item_object.EmitAssign (ec, get_item, true, false);
5448 ec.Emit (OpCodes.Brfalse, defaultLabel);
5450 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5451 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5453 get_item_int.EmitStatement (ec);
5454 get_item_object.Release (ec);
5457 EmitTableSwitch (ec, string_switch_variable);
5458 string_switch_variable.Release (ec);
5462 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5464 void EmitShortSwitch (EmitContext ec)
5466 MethodSpec equal_method = null;
5467 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5468 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5471 if (equal_method != null) {
5472 value.EmitBranchable (ec, nullLabel, false);
5475 for (int i = 0; i < case_labels.Count; ++i) {
5476 var label = case_labels [i];
5477 if (label == case_default || label == case_null)
5480 var constant = label.Converted;
5482 if (constant == null) {
5483 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5487 if (equal_method != null) {
5491 var call = new CallEmitter ();
5492 call.EmitPredefined (ec, equal_method, new Arguments (0));
5493 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5497 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5498 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5504 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5507 ec.Emit (OpCodes.Br, defaultLabel);
5510 void EmitDispatch (EmitContext ec)
5512 if (IsPatternMatching) {
5513 EmitShortSwitch (ec);
5517 if (value == null) {
5519 // Constant switch, we've already done the work if there is only 1 label
5523 foreach (var sl in case_labels) {
5524 if (sl.IsUnreachable)
5527 if (reachable++ > 0) {
5528 var constant = (Constant) new_expr;
5529 var constant_label = FindLabel (constant) ?? case_default;
5531 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5539 if (string_dictionary != null) {
5540 DoEmitStringSwitch (ec);
5541 } else if (case_labels.Count < 4 || string_labels != null) {
5542 EmitShortSwitch (ec);
5544 EmitTableSwitch (ec, value);
5548 protected override void DoEmit (EmitContext ec)
5551 // Setup the codegen context
5553 Label old_end = ec.LoopEnd;
5554 Switch old_switch = ec.Switch;
5556 ec.LoopEnd = ec.DefineLabel ();
5559 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5560 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5562 if (value != null) {
5565 var switch_expr = new_expr ?? Expr;
5567 unwrap.EmitCheck (ec);
5568 ec.Emit (OpCodes.Brfalse, nullLabel);
5569 value.EmitAssign (ec, switch_expr, false, false);
5570 } else if (switch_expr != value) {
5571 value.EmitAssign (ec, switch_expr, false, false);
5576 // Next statement is compiler generated we don't need extra
5577 // nop when we can use the statement for sequence point
5579 ec.Mark (block.StartLocation);
5580 block.IsCompilerGenerated = true;
5582 new_expr.EmitSideEffect (ec);
5587 // Restore context state.
5588 ec.MarkLabel (ec.LoopEnd);
5591 // Restore the previous context
5593 ec.LoopEnd = old_end;
5594 ec.Switch = old_switch;
5597 protected override void CloneTo (CloneContext clonectx, Statement t)
5599 Switch target = (Switch) t;
5601 target.Expr = Expr.Clone (clonectx);
5602 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5605 public override object Accept (StructuralVisitor visitor)
5607 return visitor.Visit (this);
5610 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5612 if (case_default == null && !(new_expr is Constant))
5615 if (end_reachable_das == null)
5616 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5618 end_reachable_das.Add (fc.DefiniteAssignment);
5621 public override void SetEndReachable ()
5623 end_reachable = true;
5627 // A place where execution can restart in a state machine
5628 public abstract class ResumableStatement : Statement
5631 protected Label resume_point;
5633 public Label PrepareForEmit (EmitContext ec)
5637 resume_point = ec.DefineLabel ();
5639 return resume_point;
5642 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5647 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5652 public abstract class TryFinallyBlock : ExceptionStatement
5654 protected Statement stmt;
5655 Label dispose_try_block;
5656 bool prepared_for_dispose, emitted_dispose;
5657 Method finally_host;
5659 protected TryFinallyBlock (Statement stmt, Location loc)
5667 public Statement Statement {
5675 protected abstract void EmitTryBody (EmitContext ec);
5676 public abstract void EmitFinallyBody (EmitContext ec);
5678 public override Label PrepareForDispose (EmitContext ec, Label end)
5680 if (!prepared_for_dispose) {
5681 prepared_for_dispose = true;
5682 dispose_try_block = ec.DefineLabel ();
5684 return dispose_try_block;
5687 protected sealed override void DoEmit (EmitContext ec)
5689 EmitTryBodyPrepare (ec);
5692 bool beginFinally = EmitBeginFinallyBlock (ec);
5694 Label start_finally = ec.DefineLabel ();
5695 if (resume_points != null && beginFinally) {
5696 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5698 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5699 ec.Emit (OpCodes.Brfalse_S, start_finally);
5700 ec.Emit (OpCodes.Endfinally);
5703 ec.MarkLabel (start_finally);
5705 if (finally_host != null) {
5706 finally_host.Define ();
5707 finally_host.PrepareEmit ();
5708 finally_host.Emit ();
5710 // Now it's safe to add, to close it properly and emit sequence points
5711 finally_host.Parent.AddMember (finally_host);
5713 var ce = new CallEmitter ();
5714 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5715 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5717 EmitFinallyBody (ec);
5721 ec.EndExceptionBlock ();
5724 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5726 if (emitted_dispose)
5729 emitted_dispose = true;
5731 Label end_of_try = ec.DefineLabel ();
5733 // Ensure that the only way we can get into this code is through a dispatcher
5734 if (have_dispatcher)
5735 ec.Emit (OpCodes.Br, end);
5737 ec.BeginExceptionBlock ();
5739 ec.MarkLabel (dispose_try_block);
5741 Label[] labels = null;
5742 for (int i = 0; i < resume_points.Count; ++i) {
5743 ResumableStatement s = resume_points[i];
5744 Label ret = s.PrepareForDispose (ec, end_of_try);
5745 if (ret.Equals (end_of_try) && labels == null)
5747 if (labels == null) {
5748 labels = new Label[resume_points.Count];
5749 for (int j = 0; j < i; ++j)
5750 labels[j] = end_of_try;
5755 if (labels != null) {
5757 for (j = 1; j < labels.Length; ++j)
5758 if (!labels[0].Equals (labels[j]))
5760 bool emit_dispatcher = j < labels.Length;
5762 if (emit_dispatcher) {
5763 ec.Emit (OpCodes.Ldloc, pc);
5764 ec.EmitInt (first_resume_pc);
5765 ec.Emit (OpCodes.Sub);
5766 ec.Emit (OpCodes.Switch, labels);
5769 foreach (ResumableStatement s in resume_points)
5770 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5773 ec.MarkLabel (end_of_try);
5775 ec.BeginFinallyBlock ();
5777 if (finally_host != null) {
5778 var ce = new CallEmitter ();
5779 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5780 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5782 EmitFinallyBody (ec);
5785 ec.EndExceptionBlock ();
5788 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5790 var res = stmt.FlowAnalysis (fc);
5795 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5797 ec.BeginFinallyBlock ();
5801 public override Reachability MarkReachable (Reachability rc)
5803 base.MarkReachable (rc);
5804 return Statement.MarkReachable (rc);
5807 public override bool Resolve (BlockContext bc)
5811 parent = bc.CurrentTryBlock;
5812 bc.CurrentTryBlock = this;
5814 using (bc.Set (ResolveContext.Options.TryScope)) {
5815 ok = stmt.Resolve (bc);
5818 bc.CurrentTryBlock = parent;
5821 // Finally block inside iterator is called from MoveNext and
5822 // Dispose methods that means we need to lift the block into
5823 // newly created host method to emit the body only once. The
5824 // original block then simply calls the newly generated method.
5826 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5827 var b = stmt as Block;
5828 if (b != null && b.Explicit.HasYield) {
5829 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5833 return base.Resolve (bc) && ok;
5838 // Base class for blocks using exception handling
5840 public abstract class ExceptionStatement : ResumableStatement
5842 protected List<ResumableStatement> resume_points;
5843 protected int first_resume_pc;
5844 protected ExceptionStatement parent;
5846 protected ExceptionStatement (Location loc)
5851 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5853 StateMachineInitializer state_machine = null;
5854 if (resume_points != null) {
5855 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5857 ec.EmitInt ((int) IteratorStorey.State.Running);
5858 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5861 ec.BeginExceptionBlock ();
5863 if (resume_points != null) {
5864 ec.MarkLabel (resume_point);
5866 // For normal control flow, we want to fall-through the Switch
5867 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5868 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5869 ec.EmitInt (first_resume_pc);
5870 ec.Emit (OpCodes.Sub);
5872 Label[] labels = new Label[resume_points.Count];
5873 for (int i = 0; i < resume_points.Count; ++i)
5874 labels[i] = resume_points[i].PrepareForEmit (ec);
5875 ec.Emit (OpCodes.Switch, labels);
5879 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5881 if (parent != null) {
5882 // TODO: MOVE to virtual TryCatch
5883 var tc = this as TryCatch;
5884 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5886 pc = parent.AddResumePoint (s, pc, stateMachine);
5888 pc = stateMachine.AddResumePoint (this);
5891 if (resume_points == null) {
5892 resume_points = new List<ResumableStatement> ();
5893 first_resume_pc = pc;
5896 if (pc != first_resume_pc + resume_points.Count)
5897 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5899 resume_points.Add (stmt);
5904 public class Lock : TryFinallyBlock
5907 TemporaryVariableReference expr_copy;
5908 TemporaryVariableReference lock_taken;
5910 public Lock (Expression expr, Statement stmt, Location loc)
5916 public Expression Expr {
5922 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5924 expr.FlowAnalysis (fc);
5925 return base.DoFlowAnalysis (fc);
5928 public override bool Resolve (BlockContext ec)
5930 expr = expr.Resolve (ec);
5934 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
5935 ec.Report.Error (185, loc,
5936 "`{0}' is not a reference type as required by the lock statement",
5937 expr.Type.GetSignatureForError ());
5940 if (expr.Type.IsGenericParameter) {
5941 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5944 VariableReference lv = expr as VariableReference;
5947 locked = lv.IsLockedByStatement;
5948 lv.IsLockedByStatement = true;
5955 // Have to keep original lock value around to unlock same location
5956 // in the case of original value has changed or is null
5958 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5959 expr_copy.Resolve (ec);
5962 // Ensure Monitor methods are available
5964 if (ResolvePredefinedMethods (ec) > 1) {
5965 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5966 lock_taken.Resolve (ec);
5969 using (ec.Set (ResolveContext.Options.LockScope)) {
5974 lv.IsLockedByStatement = locked;
5980 protected override void EmitTryBodyPrepare (EmitContext ec)
5982 expr_copy.EmitAssign (ec, expr);
5984 if (lock_taken != null) {
5986 // Initialize ref variable
5988 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5991 // Monitor.Enter (expr_copy)
5993 expr_copy.Emit (ec);
5994 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5997 base.EmitTryBodyPrepare (ec);
6000 protected override void EmitTryBody (EmitContext ec)
6003 // Monitor.Enter (expr_copy, ref lock_taken)
6005 if (lock_taken != null) {
6006 expr_copy.Emit (ec);
6007 lock_taken.LocalInfo.CreateBuilder (ec);
6008 lock_taken.AddressOf (ec, AddressOp.Load);
6009 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6012 Statement.Emit (ec);
6015 public override void EmitFinallyBody (EmitContext ec)
6018 // if (lock_taken) Monitor.Exit (expr_copy)
6020 Label skip = ec.DefineLabel ();
6022 if (lock_taken != null) {
6023 lock_taken.Emit (ec);
6024 ec.Emit (OpCodes.Brfalse_S, skip);
6027 expr_copy.Emit (ec);
6028 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6030 ec.Emit (OpCodes.Call, m);
6032 ec.MarkLabel (skip);
6035 int ResolvePredefinedMethods (ResolveContext rc)
6037 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6038 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6042 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6046 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6050 protected override void CloneTo (CloneContext clonectx, Statement t)
6052 Lock target = (Lock) t;
6054 target.expr = expr.Clone (clonectx);
6055 target.stmt = Statement.Clone (clonectx);
6058 public override object Accept (StructuralVisitor visitor)
6060 return visitor.Visit (this);
6065 public class Unchecked : Statement {
6068 public Unchecked (Block b, Location loc)
6075 public override bool Resolve (BlockContext ec)
6077 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6078 return Block.Resolve (ec);
6081 protected override void DoEmit (EmitContext ec)
6083 using (ec.With (EmitContext.Options.CheckedScope, false))
6087 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6089 return Block.FlowAnalysis (fc);
6092 public override Reachability MarkReachable (Reachability rc)
6094 base.MarkReachable (rc);
6095 return Block.MarkReachable (rc);
6098 protected override void CloneTo (CloneContext clonectx, Statement t)
6100 Unchecked target = (Unchecked) t;
6102 target.Block = clonectx.LookupBlock (Block);
6105 public override object Accept (StructuralVisitor visitor)
6107 return visitor.Visit (this);
6111 public class Checked : Statement {
6114 public Checked (Block b, Location loc)
6117 b.Unchecked = false;
6121 public override bool Resolve (BlockContext ec)
6123 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6124 return Block.Resolve (ec);
6127 protected override void DoEmit (EmitContext ec)
6129 using (ec.With (EmitContext.Options.CheckedScope, true))
6133 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6135 return Block.FlowAnalysis (fc);
6138 public override Reachability MarkReachable (Reachability rc)
6140 base.MarkReachable (rc);
6141 return Block.MarkReachable (rc);
6144 protected override void CloneTo (CloneContext clonectx, Statement t)
6146 Checked target = (Checked) t;
6148 target.Block = clonectx.LookupBlock (Block);
6151 public override object Accept (StructuralVisitor visitor)
6153 return visitor.Visit (this);
6157 public class Unsafe : Statement {
6160 public Unsafe (Block b, Location loc)
6163 Block.Unsafe = true;
6167 public override bool Resolve (BlockContext ec)
6169 if (ec.CurrentIterator != null)
6170 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6172 using (ec.Set (ResolveContext.Options.UnsafeScope))
6173 return Block.Resolve (ec);
6176 protected override void DoEmit (EmitContext ec)
6181 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6183 return Block.FlowAnalysis (fc);
6186 public override Reachability MarkReachable (Reachability rc)
6188 base.MarkReachable (rc);
6189 return Block.MarkReachable (rc);
6192 protected override void CloneTo (CloneContext clonectx, Statement t)
6194 Unsafe target = (Unsafe) t;
6196 target.Block = clonectx.LookupBlock (Block);
6199 public override object Accept (StructuralVisitor visitor)
6201 return visitor.Visit (this);
6208 public class Fixed : Statement
6210 abstract class Emitter : ShimExpression
6212 protected LocalVariable vi;
6214 protected Emitter (Expression expr, LocalVariable li)
6220 public abstract void EmitExit (EmitContext ec);
6222 public override void FlowAnalysis (FlowAnalysisContext fc)
6224 expr.FlowAnalysis (fc);
6228 class ExpressionEmitter : Emitter {
6229 public ExpressionEmitter (Expression converted, LocalVariable li) :
6230 base (converted, li)
6234 protected override Expression DoResolve (ResolveContext rc)
6236 throw new NotImplementedException ();
6239 public override void Emit (EmitContext ec) {
6241 // Store pointer in pinned location
6247 public override void EmitExit (EmitContext ec)
6250 ec.Emit (OpCodes.Conv_U);
6255 class StringEmitter : Emitter
6257 LocalVariable pinned_string;
6259 public StringEmitter (Expression expr, LocalVariable li)
6264 protected override Expression DoResolve (ResolveContext rc)
6266 pinned_string = new LocalVariable (vi.Block, "$pinned",
6267 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6269 pinned_string.Type = rc.BuiltinTypes.String;
6271 eclass = ExprClass.Variable;
6272 type = rc.BuiltinTypes.Int;
6276 public override void Emit (EmitContext ec)
6278 pinned_string.CreateBuilder (ec);
6281 pinned_string.EmitAssign (ec);
6283 // TODO: Should use Binary::Add
6284 pinned_string.Emit (ec);
6285 ec.Emit (OpCodes.Conv_I);
6287 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6291 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6292 //pe.InstanceExpression = pinned_string;
6293 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6295 ec.Emit (OpCodes.Add);
6299 public override void EmitExit (EmitContext ec)
6302 pinned_string.EmitAssign (ec);
6306 public class VariableDeclaration : BlockVariable
6308 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6313 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6315 if (!Variable.Type.IsPointer && li == Variable) {
6316 bc.Report.Error (209, TypeExpression.Location,
6317 "The type of locals declared in a fixed statement must be a pointer type");
6321 var res = initializer.Resolve (bc);
6328 if (res.Type.IsArray) {
6329 TypeSpec array_type = TypeManager.GetElementType (res.Type);
6332 // Provided that array_type is unmanaged,
6334 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6338 // and T* is implicitly convertible to the
6339 // pointer type given in the fixed statement.
6341 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6343 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6344 if (converted == null)
6348 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6350 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6351 new Binary (Binary.Operator.Equality, res, new NullLiteral (loc)),
6352 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6353 new NullLiteral (loc),
6356 converted = converted.Resolve (bc);
6358 return new ExpressionEmitter (converted, li);
6364 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6365 return new StringEmitter (res, li).Resolve (bc);
6368 // Case 3: fixed buffer
6369 if (res is FixedBufferPtr) {
6370 return new ExpressionEmitter (res, li);
6373 bool already_fixed = true;
6376 // Case 4: & object.
6378 Unary u = res as Unary;
6380 if (u.Oper == Unary.Operator.AddressOf) {
6381 IVariableReference vr = u.Expr as IVariableReference;
6382 if (vr == null || !vr.IsFixed) {
6383 already_fixed = false;
6386 } else if (initializer is Cast) {
6387 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6391 if (already_fixed) {
6392 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6395 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6396 return new ExpressionEmitter (res, li);
6401 VariableDeclaration decl;
6402 Statement statement;
6405 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6414 public Statement Statement {
6420 public BlockVariable Variables {
6428 public override bool Resolve (BlockContext bc)
6430 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6431 if (!decl.Resolve (bc))
6435 return statement.Resolve (bc);
6438 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6440 decl.FlowAnalysis (fc);
6441 return statement.FlowAnalysis (fc);
6444 protected override void DoEmit (EmitContext ec)
6446 decl.Variable.CreateBuilder (ec);
6447 decl.Initializer.Emit (ec);
6448 if (decl.Declarators != null) {
6449 foreach (var d in decl.Declarators) {
6450 d.Variable.CreateBuilder (ec);
6451 d.Initializer.Emit (ec);
6455 statement.Emit (ec);
6461 // Clear the pinned variable
6463 ((Emitter) decl.Initializer).EmitExit (ec);
6464 if (decl.Declarators != null) {
6465 foreach (var d in decl.Declarators) {
6466 ((Emitter)d.Initializer).EmitExit (ec);
6471 public override Reachability MarkReachable (Reachability rc)
6473 base.MarkReachable (rc);
6475 decl.MarkReachable (rc);
6477 rc = statement.MarkReachable (rc);
6479 // TODO: What if there is local exit?
6480 has_ret = rc.IsUnreachable;
6484 protected override void CloneTo (CloneContext clonectx, Statement t)
6486 Fixed target = (Fixed) t;
6488 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6489 target.statement = statement.Clone (clonectx);
6492 public override object Accept (StructuralVisitor visitor)
6494 return visitor.Visit (this);
6498 public class Catch : Statement
6500 class CatchVariableStore : Statement
6502 readonly Catch ctch;
6504 public CatchVariableStore (Catch ctch)
6509 protected override void CloneTo (CloneContext clonectx, Statement target)
6513 protected override void DoEmit (EmitContext ec)
6515 // Emits catch variable debug information inside correct block
6516 ctch.EmitCatchVariableStore (ec);
6519 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6525 class FilterStatement : Statement
6527 readonly Catch ctch;
6529 public FilterStatement (Catch ctch)
6534 protected override void CloneTo (CloneContext clonectx, Statement target)
6538 protected override void DoEmit (EmitContext ec)
6540 if (ctch.li != null) {
6541 if (ctch.hoisted_temp != null)
6542 ctch.hoisted_temp.Emit (ec);
6546 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6547 ec.Emit (OpCodes.Box, ctch.type);
6550 var expr_start = ec.DefineLabel ();
6551 var end = ec.DefineLabel ();
6553 ec.Emit (OpCodes.Brtrue_S, expr_start);
6555 ec.Emit (OpCodes.Br, end);
6556 ec.MarkLabel (expr_start);
6558 ctch.Filter.Emit (ec);
6561 ec.Emit (OpCodes.Endfilter);
6562 ec.BeginFilterHandler ();
6563 ec.Emit (OpCodes.Pop);
6566 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6568 ctch.Filter.FlowAnalysis (fc);
6572 public override bool Resolve (BlockContext bc)
6574 ctch.Filter = ctch.Filter.Resolve (bc);
6576 if (ctch.Filter != null) {
6577 if (ctch.Filter.ContainsEmitWithAwait ()) {
6578 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6581 var c = ctch.Filter as Constant;
6582 if (c != null && !c.IsDefaultValue) {
6583 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6591 ExplicitBlock block;
6593 FullNamedExpression type_expr;
6594 CompilerAssign assign;
6596 LocalTemporary hoisted_temp;
6598 public Catch (ExplicitBlock block, Location loc)
6606 public ExplicitBlock Block {
6612 public TypeSpec CatchType {
6618 public Expression Filter {
6622 public bool IsGeneral {
6624 return type_expr == null;
6628 public FullNamedExpression TypeExpression {
6637 public LocalVariable Variable {
6648 protected override void DoEmit (EmitContext ec)
6650 if (Filter != null) {
6651 ec.BeginExceptionFilterBlock ();
6652 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6654 if (Block.HasAwait) {
6655 Block.EmitScopeInitialization (ec);
6664 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6666 ec.BeginCatchBlock (CatchType);
6669 ec.Emit (OpCodes.Pop);
6671 if (Block.HasAwait) {
6673 EmitCatchVariableStore (ec);
6679 void EmitCatchVariableStore (EmitContext ec)
6681 li.CreateBuilder (ec);
6684 // For hoisted catch variable we have to use a temporary local variable
6685 // for captured variable initialization during storey setup because variable
6686 // needs to be on the stack after storey instance for stfld operation
6688 if (li.HoistedVariant != null) {
6689 hoisted_temp = new LocalTemporary (li.Type);
6690 hoisted_temp.Store (ec);
6692 // switch to assignment from temporary variable and not from top of the stack
6693 assign.UpdateSource (hoisted_temp);
6697 public override bool Resolve (BlockContext bc)
6699 using (bc.Set (ResolveContext.Options.CatchScope)) {
6700 if (type_expr == null) {
6701 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6702 if (!block.HasAwait || Filter != null)
6703 block.AddScopeStatement (new CatchVariableStore (this));
6705 Expression source = new EmptyExpression (li.Type);
6706 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6707 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6710 type = type_expr.ResolveAsType (bc);
6715 CreateExceptionVariable (type);
6717 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6718 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6719 } else if (li != null) {
6721 li.PrepareAssignmentAnalysis (bc);
6723 // source variable is at the top of the stack
6724 Expression source = new EmptyExpression (li.Type);
6725 if (li.Type.IsGenericParameter)
6726 source = new UnboxCast (source, li.Type);
6728 if (!block.HasAwait || Filter != null)
6729 block.AddScopeStatement (new CatchVariableStore (this));
6732 // Uses Location.Null to hide from symbol file
6734 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6735 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6739 if (Filter != null) {
6740 Block.AddScopeStatement (new FilterStatement (this));
6743 Block.SetCatchBlock ();
6744 return Block.Resolve (bc);
6748 bool CreateExceptionVariable (TypeSpec type)
6750 if (!Block.HasAwait)
6753 // TODO: Scan the block for rethrow expression
6754 //if (!Block.HasRethrow)
6757 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6761 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6763 if (li != null && !li.IsCompilerGenerated) {
6764 fc.SetVariableAssigned (li.VariableInfo, true);
6767 return block.FlowAnalysis (fc);
6770 public override Reachability MarkReachable (Reachability rc)
6772 base.MarkReachable (rc);
6774 var c = Filter as Constant;
6775 if (c != null && c.IsDefaultValue)
6776 return Reachability.CreateUnreachable ();
6778 return block.MarkReachable (rc);
6781 protected override void CloneTo (CloneContext clonectx, Statement t)
6783 Catch target = (Catch) t;
6785 if (type_expr != null)
6786 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6789 target.Filter = Filter.Clone (clonectx);
6791 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6795 public class TryFinally : TryFinallyBlock
6798 List<DefiniteAssignmentBitSet> try_exit_dat;
6799 List<Label> redirected_jumps;
6800 Label? start_fin_label;
6802 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6808 public ExplicitBlock FinallyBlock {
6814 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6816 if (try_exit_dat == null)
6817 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6819 try_exit_dat.Add (vector);
6822 public override bool Resolve (BlockContext bc)
6824 bool ok = base.Resolve (bc);
6826 fini.SetFinallyBlock ();
6827 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6828 ok &= fini.Resolve (bc);
6834 protected override void EmitTryBody (EmitContext ec)
6836 if (fini.HasAwait) {
6837 if (ec.TryFinallyUnwind == null)
6838 ec.TryFinallyUnwind = new List<TryFinally> ();
6840 ec.TryFinallyUnwind.Add (this);
6842 ec.TryFinallyUnwind.Remove (this);
6844 if (start_fin_label != null)
6845 ec.MarkLabel (start_fin_label.Value);
6853 protected override bool EmitBeginFinallyBlock (EmitContext ec)
6858 return base.EmitBeginFinallyBlock (ec);
6861 public override void EmitFinallyBody (EmitContext ec)
6863 if (!fini.HasAwait) {
6869 // Emits catch block like
6871 // catch (object temp) {
6872 // this.exception_field = temp;
6875 var type = ec.BuiltinTypes.Object;
6876 ec.BeginCatchBlock (type);
6878 var temp = ec.GetTemporaryLocal (type);
6879 ec.Emit (OpCodes.Stloc, temp);
6881 var exception_field = ec.GetTemporaryField (type);
6883 ec.Emit (OpCodes.Ldloc, temp);
6884 exception_field.EmitAssignFromStack (ec);
6886 ec.EndExceptionBlock ();
6888 ec.FreeTemporaryLocal (temp, type);
6893 // Emits exception rethrow
6895 // if (this.exception_field != null)
6896 // throw this.exception_field;
6898 exception_field.Emit (ec);
6899 var skip_throw = ec.DefineLabel ();
6900 ec.Emit (OpCodes.Brfalse_S, skip_throw);
6901 exception_field.Emit (ec);
6902 ec.Emit (OpCodes.Throw);
6903 ec.MarkLabel (skip_throw);
6905 exception_field.IsAvailableForReuse = true;
6907 EmitUnwindFinallyTable (ec);
6910 bool IsParentBlock (Block block)
6912 for (Block b = fini; b != null; b = b.Parent) {
6920 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
6923 if (labelBlock != null) {
6924 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
6925 var fin = ec.TryFinallyUnwind [idx - 1];
6926 if (!fin.IsParentBlock (labelBlock))
6933 bool set_return_state = true;
6935 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
6936 var fin = ec.TryFinallyUnwind [idx];
6937 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
6940 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
6941 set_return_state = false;
6943 if (fin.start_fin_label == null) {
6944 fin.start_fin_label = ec.DefineLabel ();
6947 label = fin.start_fin_label.Value;
6953 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
6955 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
6958 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
6960 if (redirected_jumps == null) {
6961 redirected_jumps = new List<Label> ();
6963 // Add fallthrough label
6964 redirected_jumps.Add (ec.DefineLabel ());
6967 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
6970 int index = redirected_jumps.IndexOf (label);
6972 redirected_jumps.Add (label);
6973 index = redirected_jumps.Count - 1;
6977 // Indicates we have captured exit jump
6979 if (setReturnState) {
6980 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
6981 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
6986 // Emits state table of jumps outside of try block and reload of return
6987 // value when try block returns value
6989 void EmitUnwindFinallyTable (EmitContext ec)
6991 if (redirected_jumps == null)
6994 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
6995 initializer.HoistedReturnState.EmitLoad (ec);
6996 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
6998 // Mark fallthrough label
6999 ec.MarkLabel (redirected_jumps [0]);
7002 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7004 var da = fc.BranchDefiniteAssignment ();
7006 var tf = fc.TryFinally;
7007 fc.TryFinally = this;
7009 var res_stmt = Statement.FlowAnalysis (fc);
7013 var try_da = fc.DefiniteAssignment;
7014 fc.DefiniteAssignment = da;
7016 var res_fin = fini.FlowAnalysis (fc);
7018 if (try_exit_dat != null) {
7020 // try block has global exit but we need to run definite assignment check
7021 // for parameter block out parameter after finally block because it's always
7022 // executed before exit
7024 foreach (var try_da_part in try_exit_dat)
7025 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7027 try_exit_dat = null;
7030 fc.DefiniteAssignment |= try_da;
7031 return res_stmt | res_fin;
7034 public override Reachability MarkReachable (Reachability rc)
7037 // Mark finally block first for any exit statement in try block
7038 // to know whether the code which follows finally is reachable
7040 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7043 protected override void CloneTo (CloneContext clonectx, Statement t)
7045 TryFinally target = (TryFinally) t;
7047 target.stmt = stmt.Clone (clonectx);
7049 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7052 public override object Accept (StructuralVisitor visitor)
7054 return visitor.Visit (this);
7058 public class TryCatch : ExceptionStatement
7061 List<Catch> clauses;
7062 readonly bool inside_try_finally;
7063 List<Catch> catch_sm;
7065 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7069 this.clauses = catch_clauses;
7070 this.inside_try_finally = inside_try_finally;
7073 public List<Catch> Clauses {
7079 public bool IsTryCatchFinally {
7081 return inside_try_finally;
7085 public override bool Resolve (BlockContext bc)
7089 using (bc.Set (ResolveContext.Options.TryScope)) {
7090 parent = bc.CurrentTryBlock;
7092 if (IsTryCatchFinally) {
7093 ok = Block.Resolve (bc);
7095 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7096 bc.CurrentTryBlock = this;
7097 ok = Block.Resolve (bc);
7098 bc.CurrentTryBlock = parent;
7103 for (int i = 0; i < clauses.Count; ++i) {
7106 ok &= c.Resolve (bc);
7108 if (c.Block.HasAwait) {
7109 if (catch_sm == null)
7110 catch_sm = new List<Catch> ();
7115 if (c.Filter != null)
7118 TypeSpec resolved_type = c.CatchType;
7119 if (resolved_type == null)
7122 for (int ii = 0; ii < clauses.Count; ++ii) {
7126 if (clauses[ii].Filter != null)
7129 if (clauses[ii].IsGeneral) {
7130 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7133 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7136 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7139 bc.Report.Warning (1058, 1, c.loc,
7140 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7148 var ct = clauses[ii].CatchType;
7152 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7153 bc.Report.Error (160, c.loc,
7154 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7155 ct.GetSignatureForError ());
7161 return base.Resolve (bc) && ok;
7164 protected sealed override void DoEmit (EmitContext ec)
7166 if (!inside_try_finally)
7167 EmitTryBodyPrepare (ec);
7171 LocalBuilder state_variable = null;
7172 foreach (Catch c in clauses) {
7175 if (catch_sm != null) {
7176 if (state_variable == null) {
7178 // Cannot reuse temp variable because non-catch path assumes the value is 0
7179 // which may not be true for reused local variable
7181 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7184 var index = catch_sm.IndexOf (c);
7188 ec.EmitInt (index + 1);
7189 ec.Emit (OpCodes.Stloc, state_variable);
7193 if (!inside_try_finally)
7194 ec.EndExceptionBlock ();
7196 if (state_variable != null) {
7197 ec.Emit (OpCodes.Ldloc, state_variable);
7199 var labels = new Label [catch_sm.Count + 1];
7200 for (int i = 0; i < labels.Length; ++i) {
7201 labels [i] = ec.DefineLabel ();
7204 var end = ec.DefineLabel ();
7205 ec.Emit (OpCodes.Switch, labels);
7207 // 0 value is default label
7208 ec.MarkLabel (labels [0]);
7209 ec.Emit (OpCodes.Br, end);
7211 var atv = ec.AsyncThrowVariable;
7213 for (int i = 0; i < catch_sm.Count; ++i) {
7214 if (c != null && c.Block.HasReachableClosingBrace)
7215 ec.Emit (OpCodes.Br, end);
7217 ec.MarkLabel (labels [i + 1]);
7219 ec.AsyncThrowVariable = c.Variable;
7222 ec.AsyncThrowVariable = atv;
7228 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7230 var start_fc = fc.BranchDefiniteAssignment ();
7231 var res = Block.FlowAnalysis (fc);
7233 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7235 foreach (var c in clauses) {
7236 fc.BranchDefiniteAssignment (start_fc);
7237 if (!c.FlowAnalysis (fc)) {
7239 try_fc = fc.DefiniteAssignment;
7241 try_fc &= fc.DefiniteAssignment;
7247 fc.DefiniteAssignment = try_fc ?? start_fc;
7252 public override Reachability MarkReachable (Reachability rc)
7254 if (rc.IsUnreachable)
7257 base.MarkReachable (rc);
7259 var tc_rc = Block.MarkReachable (rc);
7261 foreach (var c in clauses)
7262 tc_rc &= c.MarkReachable (rc);
7267 protected override void CloneTo (CloneContext clonectx, Statement t)
7269 TryCatch target = (TryCatch) t;
7271 target.Block = clonectx.LookupBlock (Block);
7272 if (clauses != null){
7273 target.clauses = new List<Catch> ();
7274 foreach (Catch c in clauses)
7275 target.clauses.Add ((Catch) c.Clone (clonectx));
7279 public override object Accept (StructuralVisitor visitor)
7281 return visitor.Visit (this);
7285 public class Using : TryFinallyBlock
7287 public class VariableDeclaration : BlockVariable
7289 Statement dispose_call;
7291 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7296 public VariableDeclaration (LocalVariable li, Location loc)
7303 public VariableDeclaration (Expression expr)
7306 loc = expr.Location;
7312 public bool IsNested { get; private set; }
7316 public void EmitDispose (EmitContext ec)
7318 dispose_call.Emit (ec);
7321 public override bool Resolve (BlockContext bc)
7326 return base.Resolve (bc, false);
7329 public Expression ResolveExpression (BlockContext bc)
7331 var e = Initializer.Resolve (bc);
7335 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7336 Initializer = ResolveInitializer (bc, Variable, e);
7340 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7342 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7343 initializer = initializer.Resolve (bc);
7344 if (initializer == null)
7347 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7348 Arguments args = new Arguments (1);
7349 args.Add (new Argument (initializer));
7350 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7351 if (initializer == null)
7354 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7355 dispose_call = CreateDisposeCall (bc, var);
7356 dispose_call.Resolve (bc);
7358 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7361 if (li == Variable) {
7362 CheckIDiposableConversion (bc, li, initializer);
7363 dispose_call = CreateDisposeCall (bc, li);
7364 dispose_call.Resolve (bc);
7367 return base.ResolveInitializer (bc, li, initializer);
7370 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7374 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7375 if (type.IsNullableType) {
7376 // it's handled in CreateDisposeCall
7380 if (type != InternalType.ErrorType) {
7381 bc.Report.SymbolRelatedToPreviousError (type);
7382 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7383 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7384 type.GetSignatureForError ());
7391 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7393 var target = bc.BuiltinTypes.IDisposable;
7394 var tp = type as TypeParameterSpec;
7396 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7398 return type.ImplementsInterface (target, false);
7401 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7403 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7405 var loc = lv.Location;
7407 var idt = bc.BuiltinTypes.IDisposable;
7408 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7410 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7411 dispose_mg.InstanceExpression = type.IsNullableType ?
7412 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7416 // Hide it from symbol file via null location
7418 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7420 // Add conditional call when disposing possible null variable
7421 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7422 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7427 public void ResolveDeclaratorInitializer (BlockContext bc)
7429 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7432 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7434 for (int i = declarators.Count - 1; i >= 0; --i) {
7435 var d = declarators [i];
7436 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7437 vd.Initializer = d.Initializer;
7439 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7440 vd.dispose_call.Resolve (bc);
7442 stmt = new Using (vd, stmt, d.Variable.Location);
7449 public override object Accept (StructuralVisitor visitor)
7451 return visitor.Visit (this);
7455 VariableDeclaration decl;
7457 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7463 public Using (Expression expr, Statement stmt, Location loc)
7466 this.decl = new VariableDeclaration (expr);
7471 public Expression Expr {
7473 return decl.Variable == null ? decl.Initializer : null;
7477 public BlockVariable Variables {
7485 public override void Emit (EmitContext ec)
7488 // Don't emit sequence point it will be set on variable declaration
7493 protected override void EmitTryBodyPrepare (EmitContext ec)
7496 base.EmitTryBodyPrepare (ec);
7499 protected override void EmitTryBody (EmitContext ec)
7504 public override void EmitFinallyBody (EmitContext ec)
7506 decl.EmitDispose (ec);
7509 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7511 decl.FlowAnalysis (fc);
7512 return stmt.FlowAnalysis (fc);
7515 public override Reachability MarkReachable (Reachability rc)
7517 decl.MarkReachable (rc);
7518 return base.MarkReachable (rc);
7521 public override bool Resolve (BlockContext ec)
7523 VariableReference vr;
7524 bool vr_locked = false;
7526 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7527 if (decl.Variable == null) {
7528 vr = decl.ResolveExpression (ec) as VariableReference;
7530 vr_locked = vr.IsLockedByStatement;
7531 vr.IsLockedByStatement = true;
7534 if (decl.IsNested) {
7535 decl.ResolveDeclaratorInitializer (ec);
7537 if (!decl.Resolve (ec))
7540 if (decl.Declarators != null) {
7541 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7549 var ok = base.Resolve (ec);
7552 vr.IsLockedByStatement = vr_locked;
7557 protected override void CloneTo (CloneContext clonectx, Statement t)
7559 Using target = (Using) t;
7561 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7562 target.stmt = stmt.Clone (clonectx);
7565 public override object Accept (StructuralVisitor visitor)
7567 return visitor.Visit (this);
7572 /// Implementation of the foreach C# statement
7574 public class Foreach : LoopStatement
7576 abstract class IteratorStatement : Statement
7578 protected readonly Foreach for_each;
7580 protected IteratorStatement (Foreach @foreach)
7582 this.for_each = @foreach;
7583 this.loc = @foreach.expr.Location;
7586 protected override void CloneTo (CloneContext clonectx, Statement target)
7588 throw new NotImplementedException ();
7591 public override void Emit (EmitContext ec)
7593 if (ec.EmitAccurateDebugInfo) {
7594 ec.Emit (OpCodes.Nop);
7600 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7602 throw new NotImplementedException ();
7606 sealed class ArrayForeach : IteratorStatement
7608 TemporaryVariableReference[] lengths;
7609 Expression [] length_exprs;
7610 StatementExpression[] counter;
7611 TemporaryVariableReference[] variables;
7613 TemporaryVariableReference copy;
7615 public ArrayForeach (Foreach @foreach, int rank)
7618 counter = new StatementExpression[rank];
7619 variables = new TemporaryVariableReference[rank];
7620 length_exprs = new Expression [rank];
7623 // Only use temporary length variables when dealing with
7624 // multi-dimensional arrays
7627 lengths = new TemporaryVariableReference [rank];
7630 public override bool Resolve (BlockContext ec)
7632 Block variables_block = for_each.variable.Block;
7633 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7636 int rank = length_exprs.Length;
7637 Arguments list = new Arguments (rank);
7638 for (int i = 0; i < rank; i++) {
7639 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7641 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7642 counter[i].Resolve (ec);
7645 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7647 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7648 lengths[i].Resolve (ec);
7650 Arguments args = new Arguments (1);
7651 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7652 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7655 list.Add (new Argument (v));
7658 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7663 if (for_each.type is VarExpr) {
7664 // Infer implicitly typed local variable from foreach array type
7665 var_type = access.Type;
7667 var_type = for_each.type.ResolveAsType (ec);
7669 if (var_type == null)
7672 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7677 for_each.variable.Type = var_type;
7679 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7680 if (variable_ref == null)
7683 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7685 return for_each.body.Resolve (ec);
7688 protected override void DoEmit (EmitContext ec)
7690 copy.EmitAssign (ec, for_each.expr);
7692 int rank = length_exprs.Length;
7693 Label[] test = new Label [rank];
7694 Label[] loop = new Label [rank];
7696 for (int i = 0; i < rank; i++) {
7697 test [i] = ec.DefineLabel ();
7698 loop [i] = ec.DefineLabel ();
7700 if (lengths != null)
7701 lengths [i].EmitAssign (ec, length_exprs [i]);
7704 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7705 for (int i = 0; i < rank; i++) {
7706 variables [i].EmitAssign (ec, zero);
7708 ec.Emit (OpCodes.Br, test [i]);
7709 ec.MarkLabel (loop [i]);
7712 for_each.body.Emit (ec);
7714 ec.MarkLabel (ec.LoopBegin);
7715 ec.Mark (for_each.expr.Location);
7717 for (int i = rank - 1; i >= 0; i--){
7718 counter [i].Emit (ec);
7720 ec.MarkLabel (test [i]);
7721 variables [i].Emit (ec);
7723 if (lengths != null)
7724 lengths [i].Emit (ec);
7726 length_exprs [i].Emit (ec);
7728 ec.Emit (OpCodes.Blt, loop [i]);
7731 ec.MarkLabel (ec.LoopEnd);
7735 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7737 class RuntimeDispose : Using.VariableDeclaration
7739 public RuntimeDispose (LocalVariable lv, Location loc)
7745 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7747 // Defered to runtime check
7750 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7752 var idt = bc.BuiltinTypes.IDisposable;
7755 // Fabricates code like
7757 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7760 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7762 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7763 dispose_variable.CreateReferenceExpression (bc, loc),
7764 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7765 loc), new NullLiteral (loc));
7767 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7769 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7770 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7772 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7773 return new If (idisaposable_test, dispose, loc);
7777 LocalVariable variable;
7779 Statement statement;
7780 ExpressionStatement init;
7781 TemporaryVariableReference enumerator_variable;
7782 bool ambiguous_getenumerator_name;
7784 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7787 this.variable = var;
7791 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7793 rc.Report.SymbolRelatedToPreviousError (enumerator);
7794 rc.Report.Error (202, loc,
7795 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7796 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7799 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7802 // Option 1: Try to match by name GetEnumerator first
7804 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7805 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7807 var mg = mexpr as MethodGroupExpr;
7809 mg.InstanceExpression = expr;
7810 Arguments args = new Arguments (0);
7811 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
7813 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7814 if (ambiguous_getenumerator_name)
7817 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7823 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7826 PredefinedMember<MethodSpec> iface_candidate = null;
7827 var ptypes = rc.Module.PredefinedTypes;
7828 var gen_ienumerable = ptypes.IEnumerableGeneric;
7829 if (!gen_ienumerable.Define ())
7830 gen_ienumerable = null;
7832 var ifaces = t.Interfaces;
7833 if (ifaces != null) {
7834 foreach (var iface in ifaces) {
7835 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7836 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7837 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7838 rc.Report.Error (1640, loc,
7839 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7840 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7845 // TODO: Cache this somehow
7846 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7847 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7852 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7853 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7858 if (iface_candidate == null) {
7859 if (expr.Type != InternalType.ErrorType) {
7860 rc.Report.Error (1579, loc,
7861 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7862 expr.Type.GetSignatureForError (), "GetEnumerator");
7868 var method = iface_candidate.Resolve (loc);
7872 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7873 mg.InstanceExpression = expr;
7877 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7879 var ms = MemberCache.FindMember (enumerator.ReturnType,
7880 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7881 BindingRestriction.InstanceOnly) as MethodSpec;
7883 if (ms == null || !ms.IsPublic) {
7884 Error_WrongEnumerator (rc, enumerator);
7888 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7891 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7893 var ps = MemberCache.FindMember (enumerator.ReturnType,
7894 MemberFilter.Property ("Current", null),
7895 BindingRestriction.InstanceOnly) as PropertySpec;
7897 if (ps == null || !ps.IsPublic) {
7898 Error_WrongEnumerator (rc, enumerator);
7905 public override bool Resolve (BlockContext ec)
7907 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7910 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7911 } else if (expr.Type.IsNullableType) {
7912 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7915 var get_enumerator_mg = ResolveGetEnumerator (ec);
7916 if (get_enumerator_mg == null) {
7920 var get_enumerator = get_enumerator_mg.BestCandidate;
7921 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7922 enumerator_variable.Resolve (ec);
7924 // Prepare bool MoveNext ()
7925 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7926 if (move_next_mg == null) {
7930 move_next_mg.InstanceExpression = enumerator_variable;
7932 // Prepare ~T~ Current { get; }
7933 var current_prop = ResolveCurrent (ec, get_enumerator);
7934 if (current_prop == null) {
7938 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7939 if (current_pe == null)
7942 VarExpr ve = for_each.type as VarExpr;
7946 // Source type is dynamic, set element type to dynamic too
7947 variable.Type = ec.BuiltinTypes.Dynamic;
7949 // Infer implicitly typed local variable from foreach enumerable type
7950 variable.Type = current_pe.Type;
7954 // Explicit cast of dynamic collection elements has to be done at runtime
7955 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7958 variable.Type = for_each.type.ResolveAsType (ec);
7960 if (variable.Type == null)
7963 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7964 if (current_pe == null)
7968 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7969 if (variable_ref == null)
7972 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7974 var init = new Invocation.Predefined (get_enumerator_mg, null);
7976 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7977 for_each.body, Location.Null);
7979 var enum_type = enumerator_variable.Type;
7982 // Add Dispose method call when enumerator can be IDisposable
7984 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7985 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7987 // Runtime Dispose check
7989 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7990 vd.Initializer = init;
7991 statement = new Using (vd, statement, Location.Null);
7994 // No Dispose call needed
7996 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7997 this.init.Resolve (ec);
8001 // Static Dispose check
8003 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8004 vd.Initializer = init;
8005 statement = new Using (vd, statement, Location.Null);
8008 return statement.Resolve (ec);
8011 protected override void DoEmit (EmitContext ec)
8013 enumerator_variable.LocalInfo.CreateBuilder (ec);
8016 init.EmitStatement (ec);
8018 statement.Emit (ec);
8021 #region IErrorHandler Members
8023 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8025 ec.Report.SymbolRelatedToPreviousError (best);
8026 ec.Report.Warning (278, 2, expr.Location,
8027 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8028 expr.Type.GetSignatureForError (), "enumerable",
8029 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8031 ambiguous_getenumerator_name = true;
8035 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8040 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8045 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8054 LocalVariable variable;
8058 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8062 this.variable = var;
8068 public Expression Expr {
8069 get { return expr; }
8072 public Expression TypeExpression {
8073 get { return type; }
8076 public LocalVariable Variable {
8077 get { return variable; }
8080 public override Reachability MarkReachable (Reachability rc)
8082 base.MarkReachable (rc);
8084 body.MarkReachable (rc);
8089 public override bool Resolve (BlockContext ec)
8091 expr = expr.Resolve (ec);
8096 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8100 body.AddStatement (Statement);
8102 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8103 Statement = new ArrayForeach (this, 1);
8104 } else if (expr.Type is ArrayContainer) {
8105 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8107 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8108 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8109 expr.ExprClassName);
8113 Statement = new CollectionForeach (this, variable, expr);
8116 return base.Resolve (ec);
8119 protected override void DoEmit (EmitContext ec)
8121 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8122 ec.LoopBegin = ec.DefineLabel ();
8123 ec.LoopEnd = ec.DefineLabel ();
8125 if (!(Statement is Block))
8126 ec.BeginCompilerScope ();
8128 variable.CreateBuilder (ec);
8130 Statement.Emit (ec);
8132 if (!(Statement is Block))
8135 ec.LoopBegin = old_begin;
8136 ec.LoopEnd = old_end;
8139 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8141 expr.FlowAnalysis (fc);
8143 var da = fc.BranchDefiniteAssignment ();
8144 body.FlowAnalysis (fc);
8145 fc.DefiniteAssignment = da;
8149 protected override void CloneTo (CloneContext clonectx, Statement t)
8151 Foreach target = (Foreach) t;
8153 target.type = type.Clone (clonectx);
8154 target.expr = expr.Clone (clonectx);
8155 target.body = (Block) body.Clone (clonectx);
8156 target.Statement = Statement.Clone (clonectx);
8159 public override object Accept (StructuralVisitor visitor)
8161 return visitor.Visit (this);