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;
273 var labels = fc.CopyLabelStack ();
275 var res = TrueStatement.FlowAnalysis (fc);
277 fc.SetLabelStack (labels);
279 if (FalseStatement == null) {
281 var c = expr as Constant;
282 if (c != null && !c.IsDefaultValue)
286 fc.DefiniteAssignment = da_false;
288 fc.DefiniteAssignment &= da_false;
294 fc.DefiniteAssignment = da_false;
296 res = FalseStatement.FlowAnalysis (fc);
297 fc.SetLabelStack (labels);
301 var da_true = fc.DefiniteAssignment;
303 fc.DefiniteAssignment = da_false;
305 res &= FalseStatement.FlowAnalysis (fc);
307 fc.SetLabelStack (labels);
309 if (!TrueStatement.IsUnreachable) {
310 if (false_returns || FalseStatement.IsUnreachable)
311 fc.DefiniteAssignment = da_true;
313 fc.DefiniteAssignment &= da_true;
319 public override Reachability MarkReachable (Reachability rc)
321 if (rc.IsUnreachable)
324 base.MarkReachable (rc);
326 var c = expr as Constant;
328 bool take = !c.IsDefaultValue;
330 rc = TrueStatement.MarkReachable (rc);
332 if (FalseStatement != null)
333 rc = FalseStatement.MarkReachable (rc);
339 var true_rc = TrueStatement.MarkReachable (rc);
340 true_returns = true_rc.IsUnreachable;
342 if (FalseStatement == null)
345 var false_rc = FalseStatement.MarkReachable (rc);
346 false_returns = false_rc.IsUnreachable;
348 return true_rc & false_rc;
351 protected override void CloneTo (CloneContext clonectx, Statement t)
355 target.expr = expr.Clone (clonectx);
356 target.TrueStatement = TrueStatement.Clone (clonectx);
357 if (FalseStatement != null)
358 target.FalseStatement = FalseStatement.Clone (clonectx);
361 public override object Accept (StructuralVisitor visitor)
363 return visitor.Visit (this);
367 public class Do : LoopStatement
369 public Expression expr;
370 bool iterator_reachable, end_reachable;
372 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
377 WhileLocation = whileLocation;
380 public Location WhileLocation {
384 public override bool Resolve (BlockContext bc)
386 var ok = base.Resolve (bc);
388 expr = expr.Resolve (bc);
393 protected override void DoEmit (EmitContext ec)
395 Label loop = ec.DefineLabel ();
396 Label old_begin = ec.LoopBegin;
397 Label old_end = ec.LoopEnd;
399 ec.LoopBegin = ec.DefineLabel ();
400 ec.LoopEnd = ec.DefineLabel ();
404 ec.MarkLabel (ec.LoopBegin);
406 // Mark start of while condition
407 ec.Mark (WhileLocation);
410 // Dead code elimination
412 if (expr is Constant) {
413 bool res = !((Constant) expr).IsDefaultValue;
415 expr.EmitSideEffect (ec);
417 ec.Emit (OpCodes.Br, loop);
419 expr.EmitBranchable (ec, loop, true);
422 ec.MarkLabel (ec.LoopEnd);
424 ec.LoopBegin = old_begin;
425 ec.LoopEnd = old_end;
428 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
430 var res = Statement.FlowAnalysis (fc);
432 expr.FlowAnalysisConditional (fc);
434 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
436 if (res && !iterator_reachable)
437 return !end_reachable;
439 if (!end_reachable) {
440 var c = expr as Constant;
441 if (c != null && !c.IsDefaultValue)
448 public override Reachability MarkReachable (Reachability rc)
450 base.MarkReachable (rc);
452 var body_rc = Statement.MarkReachable (rc);
454 if (body_rc.IsUnreachable && !iterator_reachable) {
455 expr = new UnreachableExpression (expr);
456 return end_reachable ? rc : Reachability.CreateUnreachable ();
459 if (!end_reachable) {
460 var c = expr as Constant;
461 if (c != null && !c.IsDefaultValue)
462 return Reachability.CreateUnreachable ();
468 protected override void CloneTo (CloneContext clonectx, Statement t)
472 target.Statement = Statement.Clone (clonectx);
473 target.expr = expr.Clone (clonectx);
476 public override object Accept (StructuralVisitor visitor)
478 return visitor.Visit (this);
481 public override void SetEndReachable ()
483 end_reachable = true;
486 public override void SetIteratorReachable ()
488 iterator_reachable = true;
492 public class While : LoopStatement
494 public Expression expr;
495 bool empty, infinite, end_reachable;
496 List<DefiniteAssignmentBitSet> end_reachable_das;
498 public While (BooleanExpression bool_expr, Statement statement, Location l)
501 this.expr = bool_expr;
505 public override bool Resolve (BlockContext bc)
509 expr = expr.Resolve (bc);
513 var c = expr as Constant;
515 empty = c.IsDefaultValue;
519 ok &= base.Resolve (bc);
523 protected override void DoEmit (EmitContext ec)
526 expr.EmitSideEffect (ec);
530 Label old_begin = ec.LoopBegin;
531 Label old_end = ec.LoopEnd;
533 ec.LoopBegin = ec.DefineLabel ();
534 ec.LoopEnd = ec.DefineLabel ();
537 // Inform whether we are infinite or not
539 if (expr is Constant) {
540 // expr is 'true', since the 'empty' case above handles the 'false' case
541 ec.MarkLabel (ec.LoopBegin);
543 if (ec.EmitAccurateDebugInfo)
544 ec.Emit (OpCodes.Nop);
546 expr.EmitSideEffect (ec);
548 ec.Emit (OpCodes.Br, ec.LoopBegin);
551 // Inform that we are infinite (ie, `we return'), only
552 // if we do not `break' inside the code.
554 ec.MarkLabel (ec.LoopEnd);
556 Label while_loop = ec.DefineLabel ();
558 ec.Emit (OpCodes.Br, ec.LoopBegin);
559 ec.MarkLabel (while_loop);
563 ec.MarkLabel (ec.LoopBegin);
566 expr.EmitBranchable (ec, while_loop, true);
568 ec.MarkLabel (ec.LoopEnd);
571 ec.LoopBegin = old_begin;
572 ec.LoopEnd = old_end;
575 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
577 expr.FlowAnalysisConditional (fc);
579 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
580 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
582 Statement.FlowAnalysis (fc);
585 // Special case infinite while with breaks
587 if (end_reachable_das != null) {
588 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
589 end_reachable_das = null;
592 fc.DefiniteAssignment = da_false;
594 if (infinite && !end_reachable)
600 public override Reachability MarkReachable (Reachability rc)
602 if (rc.IsUnreachable)
605 base.MarkReachable (rc);
608 // Special case unreachable while body
611 Statement.MarkReachable (Reachability.CreateUnreachable ());
615 Statement.MarkReachable (rc);
618 // When infinite while end is unreachable via break anything what follows is unreachable too
620 if (infinite && !end_reachable)
621 return Reachability.CreateUnreachable ();
626 protected override void CloneTo (CloneContext clonectx, Statement t)
628 While target = (While) t;
630 target.expr = expr.Clone (clonectx);
631 target.Statement = Statement.Clone (clonectx);
634 public override object Accept (StructuralVisitor visitor)
636 return visitor.Visit (this);
639 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
644 if (end_reachable_das == null)
645 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
647 end_reachable_das.Add (fc.DefiniteAssignment);
650 public override void SetEndReachable ()
652 end_reachable = true;
656 public class For : LoopStatement
658 bool infinite, empty, iterator_reachable, end_reachable;
659 List<DefiniteAssignmentBitSet> end_reachable_das;
661 public For (Location l)
667 public Statement Initializer {
671 public Expression Condition {
675 public Statement Iterator {
679 public override bool Resolve (BlockContext bc)
681 Initializer.Resolve (bc);
683 if (Condition != null) {
684 Condition = Condition.Resolve (bc);
685 var condition_constant = Condition as Constant;
686 if (condition_constant != null) {
687 if (condition_constant.IsDefaultValue) {
697 return base.Resolve (bc) && Iterator.Resolve (bc);
700 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
702 Initializer.FlowAnalysis (fc);
704 DefiniteAssignmentBitSet da_false;
705 if (Condition != null) {
706 Condition.FlowAnalysisConditional (fc);
707 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
708 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
710 da_false = fc.BranchDefiniteAssignment ();
713 Statement.FlowAnalysis (fc);
715 Iterator.FlowAnalysis (fc);
718 // Special case infinite for with breaks
720 if (end_reachable_das != null) {
721 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
722 end_reachable_das = null;
725 fc.DefiniteAssignment = da_false;
727 if (infinite && !end_reachable)
733 public override Reachability MarkReachable (Reachability rc)
735 base.MarkReachable (rc);
737 Initializer.MarkReachable (rc);
739 var body_rc = Statement.MarkReachable (rc);
740 if (!body_rc.IsUnreachable || iterator_reachable) {
741 Iterator.MarkReachable (rc);
745 // When infinite for end is unreachable via break anything what follows is unreachable too
747 if (infinite && !end_reachable) {
748 return Reachability.CreateUnreachable ();
754 protected override void DoEmit (EmitContext ec)
756 if (Initializer != null)
757 Initializer.Emit (ec);
760 Condition.EmitSideEffect (ec);
764 Label old_begin = ec.LoopBegin;
765 Label old_end = ec.LoopEnd;
766 Label loop = ec.DefineLabel ();
767 Label test = ec.DefineLabel ();
769 ec.LoopBegin = ec.DefineLabel ();
770 ec.LoopEnd = ec.DefineLabel ();
772 ec.Emit (OpCodes.Br, test);
776 ec.MarkLabel (ec.LoopBegin);
781 // If test is null, there is no test, and we are just
784 if (Condition != null) {
785 ec.Mark (Condition.Location);
788 // The Resolve code already catches the case for
789 // Test == Constant (false) so we know that
792 if (Condition is Constant) {
793 Condition.EmitSideEffect (ec);
794 ec.Emit (OpCodes.Br, loop);
796 Condition.EmitBranchable (ec, loop, true);
800 ec.Emit (OpCodes.Br, loop);
801 ec.MarkLabel (ec.LoopEnd);
803 ec.LoopBegin = old_begin;
804 ec.LoopEnd = old_end;
807 protected override void CloneTo (CloneContext clonectx, Statement t)
809 For target = (For) t;
811 if (Initializer != null)
812 target.Initializer = Initializer.Clone (clonectx);
813 if (Condition != null)
814 target.Condition = Condition.Clone (clonectx);
815 if (Iterator != null)
816 target.Iterator = Iterator.Clone (clonectx);
817 target.Statement = Statement.Clone (clonectx);
820 public override object Accept (StructuralVisitor visitor)
822 return visitor.Visit (this);
825 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
830 if (end_reachable_das == null)
831 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
833 end_reachable_das.Add (fc.DefiniteAssignment);
836 public override void SetEndReachable ()
838 end_reachable = true;
841 public override void SetIteratorReachable ()
843 iterator_reachable = true;
847 public abstract class LoopStatement : Statement
849 protected LoopStatement (Statement statement)
851 Statement = statement;
854 public Statement Statement { get; set; }
856 public override bool Resolve (BlockContext bc)
858 var prev_loop = bc.EnclosingLoop;
859 var prev_los = bc.EnclosingLoopOrSwitch;
860 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
861 var ok = Statement.Resolve (bc);
862 bc.EnclosingLoopOrSwitch = prev_los;
863 bc.EnclosingLoop = prev_loop;
869 // Needed by possibly infinite loops statements (for, while) and switch statment
871 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
875 public virtual void SetEndReachable ()
879 public virtual void SetIteratorReachable ()
884 public class StatementExpression : Statement
886 ExpressionStatement expr;
888 public StatementExpression (ExpressionStatement expr)
891 loc = expr.StartLocation;
894 public StatementExpression (ExpressionStatement expr, Location loc)
900 public ExpressionStatement Expr {
906 protected override void CloneTo (CloneContext clonectx, Statement t)
908 StatementExpression target = (StatementExpression) t;
909 target.expr = (ExpressionStatement) expr.Clone (clonectx);
912 protected override void DoEmit (EmitContext ec)
914 expr.EmitStatement (ec);
917 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
919 expr.FlowAnalysis (fc);
923 public override Reachability MarkReachable (Reachability rc)
925 base.MarkReachable (rc);
926 expr.MarkReachable (rc);
930 public override bool Resolve (BlockContext ec)
932 expr = expr.ResolveStatement (ec);
936 public override object Accept (StructuralVisitor visitor)
938 return visitor.Visit (this);
942 public class StatementErrorExpression : Statement
946 public StatementErrorExpression (Expression expr)
949 this.loc = expr.StartLocation;
952 public Expression Expr {
958 public override bool Resolve (BlockContext bc)
960 expr.Error_InvalidExpressionStatement (bc);
964 protected override void DoEmit (EmitContext ec)
966 throw new NotSupportedException ();
969 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
974 protected override void CloneTo (CloneContext clonectx, Statement target)
976 var t = (StatementErrorExpression) target;
978 t.expr = expr.Clone (clonectx);
981 public override object Accept (StructuralVisitor visitor)
983 return visitor.Visit (this);
988 // Simple version of statement list not requiring a block
990 public class StatementList : Statement
992 List<Statement> statements;
994 public StatementList (Statement first, Statement second)
996 statements = new List<Statement> { first, second };
1000 public IList<Statement> Statements {
1007 public void Add (Statement statement)
1009 statements.Add (statement);
1012 public override bool Resolve (BlockContext ec)
1014 foreach (var s in statements)
1020 protected override void DoEmit (EmitContext ec)
1022 foreach (var s in statements)
1026 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1028 foreach (var s in statements)
1029 s.FlowAnalysis (fc);
1034 public override Reachability MarkReachable (Reachability rc)
1036 base.MarkReachable (rc);
1038 Reachability res = rc;
1039 foreach (var s in statements)
1040 res = s.MarkReachable (rc);
1045 protected override void CloneTo (CloneContext clonectx, Statement target)
1047 StatementList t = (StatementList) target;
1049 t.statements = new List<Statement> (statements.Count);
1050 foreach (Statement s in statements)
1051 t.statements.Add (s.Clone (clonectx));
1054 public override object Accept (StructuralVisitor visitor)
1056 return visitor.Visit (this);
1061 // For statements which require special handling when inside try or catch block
1063 public abstract class ExitStatement : Statement
1065 protected bool unwind_protect;
1067 protected abstract bool DoResolve (BlockContext bc);
1068 protected abstract bool IsLocalExit { get; }
1070 public override bool Resolve (BlockContext bc)
1072 var res = DoResolve (bc);
1076 // We are inside finally scope but is it the scope we are exiting
1078 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1080 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1081 if (b.IsFinallyBlock) {
1082 Error_FinallyClauseExit (bc);
1086 if (b is ParametersBlock)
1092 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1096 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1101 if (fc.TryFinally != null) {
1102 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1104 fc.ParametersBlock.CheckControlExit (fc);
1112 /// Implements the return statement
1114 public class Return : ExitStatement
1118 public Return (Expression expr, Location l)
1126 public Expression Expr {
1135 protected override bool IsLocalExit {
1143 protected override bool DoResolve (BlockContext ec)
1145 var block_return_type = ec.ReturnType;
1148 if (block_return_type.Kind == MemberKind.Void || block_return_type == InternalType.ErrorType)
1152 // Return must not be followed by an expression when
1153 // the method return type is Task
1155 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1156 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1157 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1159 // Extra trick not to emit ret/leave inside awaiter body
1161 expr = EmptyExpression.Null;
1165 if (storey.ReturnType.IsGenericTask)
1166 block_return_type = storey.ReturnType.TypeArguments[0];
1169 if (ec.CurrentIterator != null) {
1170 Error_ReturnFromIterator (ec);
1171 } else if (block_return_type != InternalType.ErrorType) {
1172 ec.Report.Error (126, loc,
1173 "An object of a type convertible to `{0}' is required for the return statement",
1174 block_return_type.GetSignatureForError ());
1180 expr = expr.Resolve (ec);
1182 AnonymousExpression am = ec.CurrentAnonymousMethod;
1184 if (block_return_type.Kind == MemberKind.Void) {
1185 ec.Report.Error (127, loc,
1186 "`{0}': A return keyword must not be followed by any expression when method returns void",
1187 ec.GetSignatureForError ());
1192 if (am.IsIterator) {
1193 Error_ReturnFromIterator (ec);
1197 var async_block = am as AsyncInitializer;
1198 if (async_block != null) {
1200 var storey = (AsyncTaskStorey) am.Storey;
1201 var async_type = storey.ReturnType;
1203 if (async_type == null && async_block.ReturnTypeInference != null) {
1204 if (expr.Type.Kind == MemberKind.Void && !(this is ContextualReturn))
1205 ec.Report.Error (4029, loc, "Cannot return an expression of type `void'");
1207 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1211 if (async_type.Kind == MemberKind.Void) {
1212 ec.Report.Error (8030, loc,
1213 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1217 if (!async_type.IsGenericTask) {
1218 if (this is ContextualReturn)
1221 if (async_block.DelegateType != null) {
1222 ec.Report.Error (8031, loc,
1223 "Async lambda expression or anonymous method converted to a `Task' cannot return a value. Consider returning `Task<T>'");
1225 ec.Report.Error (1997, loc,
1226 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1227 ec.GetSignatureForError ());
1233 // The return type is actually Task<T> type argument
1235 if (expr.Type == async_type && async_type.TypeArguments [0] != ec.Module.PredefinedTypes.Task.TypeSpec) {
1236 ec.Report.Error (4016, loc,
1237 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1238 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1240 block_return_type = async_type.TypeArguments[0];
1244 if (block_return_type.Kind == MemberKind.Void) {
1245 ec.Report.Error (8030, loc,
1246 "Anonymous function or lambda expression converted to a void returning delegate cannot return a value");
1250 var l = am as AnonymousMethodBody;
1251 if (l != null && expr != null) {
1252 if (l.ReturnTypeInference != null) {
1253 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1258 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1259 // unexpected debugging experience
1261 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1262 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1271 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1272 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1275 if (am != null && block_return_type == ec.ReturnType) {
1276 ec.Report.Error (1662, loc,
1277 "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",
1278 am.ContainerType, am.GetSignatureForError ());
1287 protected override void DoEmit (EmitContext ec)
1291 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1292 if (async_body != null) {
1293 var storey = (AsyncTaskStorey)async_body.Storey;
1294 Label exit_label = async_body.BodyEnd;
1297 // It's null for await without async
1299 if (storey.HoistedReturnValue != null) {
1301 // Special case hoisted return value (happens in try/finally scenario)
1303 if (ec.TryFinallyUnwind != null) {
1304 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1307 var async_return = (IAssignMethod)storey.HoistedReturnValue;
1308 async_return.EmitAssign (ec, expr, false, false);
1313 if (ec.TryFinallyUnwind != null)
1314 exit_label = TryFinally.EmitRedirectedReturn (ec, async_body);
1317 ec.Emit (OpCodes.Leave, exit_label);
1324 if (unwind_protect || ec.EmitAccurateDebugInfo)
1325 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1328 if (unwind_protect) {
1329 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1330 } else if (ec.EmitAccurateDebugInfo) {
1331 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1333 ec.Emit (OpCodes.Ret);
1337 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1340 expr.FlowAnalysis (fc);
1342 base.DoFlowAnalysis (fc);
1346 void Error_ReturnFromIterator (ResolveContext rc)
1348 rc.Report.Error (1622, loc,
1349 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1352 public override Reachability MarkReachable (Reachability rc)
1354 base.MarkReachable (rc);
1355 return Reachability.CreateUnreachable ();
1358 protected override void CloneTo (CloneContext clonectx, Statement t)
1360 Return target = (Return) t;
1361 // It's null for simple return;
1363 target.expr = expr.Clone (clonectx);
1366 public override object Accept (StructuralVisitor visitor)
1368 return visitor.Visit (this);
1372 public class Goto : ExitStatement
1375 LabeledStatement label;
1376 TryFinally try_finally;
1378 public Goto (string label, Location l)
1384 public string Target {
1385 get { return target; }
1388 protected override bool IsLocalExit {
1394 protected override bool DoResolve (BlockContext bc)
1396 label = bc.CurrentBlock.LookupLabel (target);
1397 if (label == null) {
1398 Error_UnknownLabel (bc, target, loc);
1402 try_finally = bc.CurrentTryBlock as TryFinally;
1404 CheckExitBoundaries (bc, label.Block);
1409 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1411 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1415 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1417 // Goto to unreachable label
1421 if (fc.AddReachedLabel (label))
1424 label.Block.ScanGotoJump (label, fc);
1428 public override Reachability MarkReachable (Reachability rc)
1430 if (rc.IsUnreachable)
1433 base.MarkReachable (rc);
1435 if (try_finally != null) {
1436 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1437 label.AddGotoReference (rc);
1442 label.AddGotoReference (rc);
1445 return Reachability.CreateUnreachable ();
1448 protected override void CloneTo (CloneContext clonectx, Statement target)
1453 protected override void DoEmit (EmitContext ec)
1455 // This should only happen for goto from try block to unrechable label
1459 Label l = label.LabelTarget (ec);
1461 if (ec.TryFinallyUnwind != null && IsLeavingFinally (label.Block)) {
1462 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1463 l = TryFinally.EmitRedirectedJump (ec, async_body, l, label.Block);
1466 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1469 bool IsLeavingFinally (Block labelBlock)
1471 var b = try_finally.Statement as Block;
1473 if (b == labelBlock)
1482 public override object Accept (StructuralVisitor visitor)
1484 return visitor.Visit (this);
1488 public class LabeledStatement : Statement {
1495 public LabeledStatement (string name, Block block, Location l)
1502 public Label LabelTarget (EmitContext ec)
1507 label = ec.DefineLabel ();
1512 public Block Block {
1518 public string Name {
1519 get { return name; }
1522 protected override void CloneTo (CloneContext clonectx, Statement target)
1524 var t = (LabeledStatement) target;
1526 t.block = clonectx.RemapBlockCopy (block);
1529 public override bool Resolve (BlockContext bc)
1534 protected override void DoEmit (EmitContext ec)
1537 ec.MarkLabel (label);
1540 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1543 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1549 public override Reachability MarkReachable (Reachability rc)
1551 base.MarkReachable (rc);
1554 rc = new Reachability ();
1559 public void AddGotoReference (Reachability rc)
1567 block.ScanGotoJump (this);
1570 public override object Accept (StructuralVisitor visitor)
1572 return visitor.Visit (this);
1578 /// `goto default' statement
1580 public class GotoDefault : SwitchGoto
1582 public GotoDefault (Location l)
1587 public override bool Resolve (BlockContext bc)
1589 if (bc.Switch == null) {
1590 Error_GotoCaseRequiresSwitchBlock (bc);
1594 bc.Switch.RegisterGotoCase (null, null);
1600 protected override void DoEmit (EmitContext ec)
1602 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1605 public override Reachability MarkReachable (Reachability rc)
1607 if (!rc.IsUnreachable) {
1608 var label = switch_statement.DefaultLabel;
1609 if (label.IsUnreachable) {
1610 label.MarkReachable (rc);
1611 switch_statement.Block.ScanGotoJump (label);
1615 return base.MarkReachable (rc);
1618 public override object Accept (StructuralVisitor visitor)
1620 return visitor.Visit (this);
1625 /// `goto case' statement
1627 public class GotoCase : SwitchGoto
1631 public GotoCase (Expression e, Location l)
1637 public Expression Expr {
1643 public SwitchLabel Label { get; set; }
1645 public override bool Resolve (BlockContext ec)
1647 if (ec.Switch == null) {
1648 Error_GotoCaseRequiresSwitchBlock (ec);
1652 Constant c = expr.ResolveLabelConstant (ec);
1658 if (ec.Switch.IsNullable && c is NullLiteral) {
1661 TypeSpec type = ec.Switch.SwitchType;
1662 res = c.Reduce (ec, type);
1664 c.Error_ValueCannotBeConverted (ec, type, true);
1668 if (!Convert.ImplicitStandardConversionExists (c, type))
1669 ec.Report.Warning (469, 2, loc,
1670 "The `goto case' value is not implicitly convertible to type `{0}'",
1671 type.GetSignatureForError ());
1675 ec.Switch.RegisterGotoCase (this, res);
1682 protected override void DoEmit (EmitContext ec)
1684 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1687 protected override void CloneTo (CloneContext clonectx, Statement t)
1689 GotoCase target = (GotoCase) t;
1691 target.expr = expr.Clone (clonectx);
1694 public override Reachability MarkReachable (Reachability rc)
1696 if (!rc.IsUnreachable) {
1697 var label = switch_statement.FindLabel ((Constant) expr);
1698 if (label.IsUnreachable) {
1699 label.MarkReachable (rc);
1700 switch_statement.Block.ScanGotoJump (label);
1704 return base.MarkReachable (rc);
1707 public override object Accept (StructuralVisitor visitor)
1709 return visitor.Visit (this);
1713 public abstract class SwitchGoto : Statement
1715 protected bool unwind_protect;
1716 protected Switch switch_statement;
1718 protected SwitchGoto (Location loc)
1723 protected override void CloneTo (CloneContext clonectx, Statement target)
1728 public override bool Resolve (BlockContext bc)
1730 CheckExitBoundaries (bc, bc.Switch.Block);
1732 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1733 switch_statement = bc.Switch;
1738 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1743 public override Reachability MarkReachable (Reachability rc)
1745 base.MarkReachable (rc);
1746 return Reachability.CreateUnreachable ();
1749 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1751 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1755 public class Throw : Statement {
1758 public Throw (Expression expr, Location l)
1764 public Expression Expr {
1770 public override bool Resolve (BlockContext ec)
1773 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1774 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1775 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1776 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1777 if (b.IsFinallyBlock) {
1778 ec.Report.Error (724, loc,
1779 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1788 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1793 var et = ec.BuiltinTypes.Exception;
1794 if (Convert.ImplicitConversionExists (ec, expr, et))
1795 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1797 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1802 protected override void DoEmit (EmitContext ec)
1805 var atv = ec.AsyncThrowVariable;
1807 if (atv.HoistedVariant != null) {
1808 atv.HoistedVariant.Emit (ec);
1813 ec.Emit (OpCodes.Throw);
1815 ec.Emit (OpCodes.Rethrow);
1820 ec.Emit (OpCodes.Throw);
1824 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1827 expr.FlowAnalysis (fc);
1832 public override Reachability MarkReachable (Reachability rc)
1834 base.MarkReachable (rc);
1835 return Reachability.CreateUnreachable ();
1838 protected override void CloneTo (CloneContext clonectx, Statement t)
1840 Throw target = (Throw) t;
1843 target.expr = expr.Clone (clonectx);
1846 public override object Accept (StructuralVisitor visitor)
1848 return visitor.Visit (this);
1852 public class Break : LocalExitStatement
1854 public Break (Location l)
1859 public override object Accept (StructuralVisitor visitor)
1861 return visitor.Visit (this);
1864 protected override void DoEmit (EmitContext ec)
1868 if (ec.TryFinallyUnwind != null) {
1869 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1870 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1873 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1876 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1878 enclosing_loop.AddEndDefiniteAssignment (fc);
1882 protected override bool DoResolve (BlockContext bc)
1884 enclosing_loop = bc.EnclosingLoopOrSwitch;
1885 return base.DoResolve (bc);
1888 public override Reachability MarkReachable (Reachability rc)
1890 base.MarkReachable (rc);
1892 if (!rc.IsUnreachable)
1893 enclosing_loop.SetEndReachable ();
1895 return Reachability.CreateUnreachable ();
1899 public class Continue : LocalExitStatement
1901 public Continue (Location l)
1906 public override object Accept (StructuralVisitor visitor)
1908 return visitor.Visit (this);
1912 protected override void DoEmit (EmitContext ec)
1914 var l = ec.LoopBegin;
1916 if (ec.TryFinallyUnwind != null) {
1917 var async_body = (AsyncInitializer) ec.CurrentAnonymousMethod;
1918 l = TryFinally.EmitRedirectedJump (ec, async_body, l, enclosing_loop.Statement as Block);
1921 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1924 protected override bool DoResolve (BlockContext bc)
1926 enclosing_loop = bc.EnclosingLoop;
1927 return base.DoResolve (bc);
1930 public override Reachability MarkReachable (Reachability rc)
1932 base.MarkReachable (rc);
1934 if (!rc.IsUnreachable)
1935 enclosing_loop.SetIteratorReachable ();
1937 return Reachability.CreateUnreachable ();
1941 public abstract class LocalExitStatement : ExitStatement
1943 protected LoopStatement enclosing_loop;
1945 protected LocalExitStatement (Location loc)
1950 protected override bool IsLocalExit {
1956 protected override void CloneTo (CloneContext clonectx, Statement t)
1961 protected override bool DoResolve (BlockContext bc)
1963 if (enclosing_loop == null) {
1964 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1968 var block = enclosing_loop.Statement as Block;
1970 // Don't need to do extra checks for simple statements loops
1971 if (block != null) {
1972 CheckExitBoundaries (bc, block);
1979 public interface ILocalVariable
1981 void Emit (EmitContext ec);
1982 void EmitAssign (EmitContext ec);
1983 void EmitAddressOf (EmitContext ec);
1986 public interface INamedBlockVariable
1988 Block Block { get; }
1989 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1990 bool IsDeclared { get; }
1991 bool IsParameter { get; }
1992 Location Location { get; }
1995 public class BlockVariableDeclarator
1998 Expression initializer;
2000 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
2002 if (li.Type != null)
2003 throw new ArgumentException ("Expected null variable type");
2006 this.initializer = initializer;
2011 public LocalVariable Variable {
2017 public Expression Initializer {
2022 initializer = value;
2028 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
2030 var t = (BlockVariableDeclarator) MemberwiseClone ();
2031 if (initializer != null)
2032 t.initializer = initializer.Clone (cloneCtx);
2038 public class BlockVariable : Statement
2040 Expression initializer;
2041 protected FullNamedExpression type_expr;
2042 protected LocalVariable li;
2043 protected List<BlockVariableDeclarator> declarators;
2046 public BlockVariable (FullNamedExpression type, LocalVariable li)
2048 this.type_expr = type;
2050 this.loc = type_expr.Location;
2053 protected BlockVariable (LocalVariable li)
2060 public List<BlockVariableDeclarator> Declarators {
2066 public Expression Initializer {
2071 initializer = value;
2075 public FullNamedExpression TypeExpression {
2081 public LocalVariable Variable {
2089 public void AddDeclarator (BlockVariableDeclarator decl)
2091 if (declarators == null)
2092 declarators = new List<BlockVariableDeclarator> ();
2094 declarators.Add (decl);
2097 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2099 if (bc.Report.Errors != 0)
2102 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2104 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2105 new MemberName (li.Name, li.Location), null);
2107 container.AddField (f);
2110 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2114 public override bool Resolve (BlockContext bc)
2116 return Resolve (bc, true);
2119 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2121 if (type == null && !li.IsCompilerGenerated) {
2122 var vexpr = type_expr as VarExpr;
2125 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2126 // same name exists or as a keyword when no type was found
2128 if (vexpr != null && !vexpr.IsPossibleType (bc)) {
2129 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2130 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2133 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2137 if (li.IsConstant) {
2138 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2142 if (Initializer == null) {
2143 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2147 if (declarators != null) {
2148 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2152 Initializer = Initializer.Resolve (bc);
2153 if (Initializer != null) {
2154 ((VarExpr) type_expr).InferType (bc, Initializer);
2155 type = type_expr.Type;
2157 // Set error type to indicate the var was placed correctly but could
2160 // var a = missing ();
2162 type = InternalType.ErrorType;
2167 type = type_expr.ResolveAsType (bc);
2171 if (li.IsConstant && !type.IsConstantCompatible) {
2172 Const.Error_InvalidConstantType (type, loc, bc.Report);
2177 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2182 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2184 CreateEvaluatorVariable (bc, li);
2185 } else if (type != InternalType.ErrorType) {
2186 li.PrepareAssignmentAnalysis (bc);
2189 if (initializer != null) {
2190 initializer = ResolveInitializer (bc, li, initializer);
2191 // li.Variable.DefinitelyAssigned
2194 if (declarators != null) {
2195 foreach (var d in declarators) {
2196 d.Variable.Type = li.Type;
2198 CreateEvaluatorVariable (bc, d.Variable);
2199 } else if (type != InternalType.ErrorType) {
2200 d.Variable.PrepareAssignmentAnalysis (bc);
2203 if (d.Initializer != null && resolveDeclaratorInitializers) {
2204 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2205 // d.Variable.DefinitelyAssigned
2213 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2215 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2216 return a.ResolveStatement (bc);
2219 protected override void DoEmit (EmitContext ec)
2221 li.CreateBuilder (ec);
2223 if (Initializer != null && !IsUnreachable)
2224 ((ExpressionStatement) Initializer).EmitStatement (ec);
2226 if (declarators != null) {
2227 foreach (var d in declarators) {
2228 d.Variable.CreateBuilder (ec);
2229 if (d.Initializer != null && !IsUnreachable) {
2230 ec.Mark (d.Variable.Location);
2231 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2237 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2239 if (Initializer != null)
2240 Initializer.FlowAnalysis (fc);
2242 if (declarators != null) {
2243 foreach (var d in declarators) {
2244 if (d.Initializer != null)
2245 d.Initializer.FlowAnalysis (fc);
2252 public override Reachability MarkReachable (Reachability rc)
2254 var init = initializer as ExpressionStatement;
2256 init.MarkReachable (rc);
2258 return base.MarkReachable (rc);
2261 protected override void CloneTo (CloneContext clonectx, Statement target)
2263 BlockVariable t = (BlockVariable) target;
2265 if (type_expr != null)
2266 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2268 if (initializer != null)
2269 t.initializer = initializer.Clone (clonectx);
2271 if (declarators != null) {
2272 t.declarators = null;
2273 foreach (var d in declarators)
2274 t.AddDeclarator (d.Clone (clonectx));
2278 public override object Accept (StructuralVisitor visitor)
2280 return visitor.Visit (this);
2284 public class BlockConstant : BlockVariable
2286 public BlockConstant (FullNamedExpression type, LocalVariable li)
2291 public override void Emit (EmitContext ec)
2293 if (!Variable.IsUsed)
2294 ec.Report.Warning (219, 3, loc, "The constant `{0}' is never used", Variable.Name);
2296 // Nothing to emit, not even sequence point
2299 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2301 initializer = initializer.Resolve (bc);
2302 if (initializer == null)
2305 var c = initializer as Constant;
2307 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2311 c = c.ConvertImplicitly (li.Type);
2313 if (TypeSpec.IsReferenceType (li.Type))
2314 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2316 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2321 li.ConstantValue = c;
2325 public override object Accept (StructuralVisitor visitor)
2327 return visitor.Visit (this);
2332 // The information about a user-perceived local variable
2334 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2341 AddressTaken = 1 << 2,
2342 CompilerGenerated = 1 << 3,
2344 ForeachVariable = 1 << 5,
2345 FixedVariable = 1 << 6,
2346 UsingVariable = 1 << 7,
2348 SymbolFileHidden = 1 << 9,
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 IsUsed {
2456 return (flags & Flags.Used) != 0;
2460 public bool IsFixed {
2462 return (flags & Flags.FixedVariable) != 0;
2465 flags = value ? flags | Flags.FixedVariable : flags & ~Flags.FixedVariable;
2469 bool INamedBlockVariable.IsParameter {
2475 public bool IsReadonly {
2477 return (flags & Flags.ReadonlyMask) != 0;
2481 public Location Location {
2487 public string Name {
2493 public TypeSpec Type {
2504 public void CreateBuilder (EmitContext ec)
2506 if ((flags & Flags.Used) == 0) {
2507 if (VariableInfo == null) {
2508 // Missing flow analysis or wrong variable flags
2509 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2512 if (VariableInfo.IsEverAssigned)
2513 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2515 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2518 if (HoistedVariant != null)
2521 if (builder != null) {
2522 if ((flags & Flags.CompilerGenerated) != 0)
2525 // To avoid Used warning duplicates
2526 throw new InternalErrorException ("Already created variable `{0}'", name);
2530 // All fixed variabled are pinned, a slot has to be alocated
2532 builder = ec.DeclareLocal (Type, IsFixed);
2533 if ((flags & Flags.SymbolFileHidden) == 0)
2534 ec.DefineLocalVariable (name, builder);
2537 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc, bool writeToSymbolFile = false)
2539 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2540 if (!writeToSymbolFile)
2541 li.flags |= Flags.SymbolFileHidden;
2547 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2549 if (IsConstant && const_value != null) {
2551 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2554 return new LocalVariableReference (this, loc);
2557 public void Emit (EmitContext ec)
2559 // TODO: Need something better for temporary variables
2560 if ((flags & Flags.CompilerGenerated) != 0)
2563 ec.Emit (OpCodes.Ldloc, builder);
2566 public void EmitAssign (EmitContext ec)
2568 // TODO: Need something better for temporary variables
2569 if ((flags & Flags.CompilerGenerated) != 0)
2572 ec.Emit (OpCodes.Stloc, builder);
2575 public void EmitAddressOf (EmitContext ec)
2577 // TODO: Need something better for temporary variables
2578 if ((flags & Flags.CompilerGenerated) != 0)
2581 ec.Emit (OpCodes.Ldloca, builder);
2584 public static string GetCompilerGeneratedName (Block block)
2586 // HACK: Debugger depends on the name semantics
2587 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2590 public string GetReadOnlyContext ()
2592 switch (flags & Flags.ReadonlyMask) {
2593 case Flags.FixedVariable:
2594 return "fixed variable";
2595 case Flags.ForeachVariable:
2596 return "foreach iteration variable";
2597 case Flags.UsingVariable:
2598 return "using variable";
2601 throw new InternalErrorException ("Variable is not readonly");
2604 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2606 if (VariableInfo == null)
2607 throw new Exception ();
2609 if (IsAssigned (fc))
2612 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2615 public bool IsAssigned (FlowAnalysisContext fc)
2617 return fc.IsDefinitelyAssigned (VariableInfo);
2620 public void PrepareAssignmentAnalysis (BlockContext bc)
2623 // No need to run assignment analysis for these guys
2625 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2628 VariableInfo = VariableInfo.Create (bc, this);
2632 // Mark the variables as referenced in the user code
2634 public void SetIsUsed ()
2636 flags |= Flags.Used;
2639 public void SetHasAddressTaken ()
2641 flags |= (Flags.AddressTaken | Flags.Used);
2644 public override string ToString ()
2646 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2651 /// Block represents a C# block.
2655 /// This class is used in a number of places: either to represent
2656 /// explicit blocks that the programmer places or implicit blocks.
2658 /// Implicit blocks are used as labels or to introduce variable
2661 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2662 /// they contain extra information that is not necessary on normal blocks.
2664 public class Block : Statement {
2671 HasCapturedVariable = 64,
2672 HasCapturedThis = 1 << 7,
2673 IsExpressionTree = 1 << 8,
2674 CompilerGenerated = 1 << 9,
2675 HasAsyncModifier = 1 << 10,
2677 YieldBlock = 1 << 12,
2678 AwaitBlock = 1 << 13,
2679 FinallyBlock = 1 << 14,
2680 CatchBlock = 1 << 15,
2681 HasReferenceToStoreyForInstanceLambdas = 1 << 16,
2683 NoFlowAnalysis = 1 << 21,
2684 InitializationEmitted = 1 << 22
2687 public Block Parent;
2688 public Location StartLocation;
2689 public Location EndLocation;
2691 public ExplicitBlock Explicit;
2692 public ParametersBlock ParametersBlock;
2694 protected Flags flags;
2697 // The statements in this block
2699 protected List<Statement> statements;
2701 protected List<Statement> scope_initializers;
2703 int? resolving_init_idx;
2709 public int ID = id++;
2711 static int clone_id_counter;
2715 // int assignable_slots;
2717 public Block (Block parent, Location start, Location end)
2718 : this (parent, 0, start, end)
2722 public Block (Block parent, Flags flags, Location start, Location end)
2724 if (parent != null) {
2725 // the appropriate constructors will fixup these fields
2726 ParametersBlock = parent.ParametersBlock;
2727 Explicit = parent.Explicit;
2730 this.Parent = parent;
2732 this.StartLocation = start;
2733 this.EndLocation = end;
2735 statements = new List<Statement> (4);
2737 this.original = this;
2742 public Block Original {
2751 public bool IsCompilerGenerated {
2752 get { return (flags & Flags.CompilerGenerated) != 0; }
2753 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2757 public bool IsCatchBlock {
2759 return (flags & Flags.CatchBlock) != 0;
2763 public bool IsFinallyBlock {
2765 return (flags & Flags.FinallyBlock) != 0;
2769 public bool Unchecked {
2770 get { return (flags & Flags.Unchecked) != 0; }
2771 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2774 public bool Unsafe {
2775 get { return (flags & Flags.Unsafe) != 0; }
2776 set { flags |= Flags.Unsafe; }
2779 public List<Statement> Statements {
2780 get { return statements; }
2785 public void SetEndLocation (Location loc)
2790 public void AddLabel (LabeledStatement target)
2792 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2795 public void AddLocalName (LocalVariable li)
2797 AddLocalName (li.Name, li);
2800 public void AddLocalName (string name, INamedBlockVariable li)
2802 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2805 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2807 if (reason == null) {
2808 Error_AlreadyDeclared (name, variable);
2812 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2813 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2814 "to `{0}', which is already used in a `{1}' scope to denote something else",
2818 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2820 var pi = variable as ParametersBlock.ParameterInfo;
2822 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2824 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2825 "A local variable named `{0}' is already defined in this scope", name);
2829 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2831 ParametersBlock.TopBlock.Report.Error (412, loc,
2832 "The type parameter name `{0}' is the same as local variable or parameter name",
2837 // It should be used by expressions which require to
2838 // register a statement during resolve process.
2840 public void AddScopeStatement (Statement s)
2842 if (scope_initializers == null)
2843 scope_initializers = new List<Statement> ();
2846 // Simple recursive helper, when resolve scope initializer another
2847 // new scope initializer can be added, this ensures it's initialized
2848 // before existing one. For now this can happen with expression trees
2849 // in base ctor initializer only
2851 if (resolving_init_idx.HasValue) {
2852 scope_initializers.Insert (resolving_init_idx.Value, s);
2853 ++resolving_init_idx;
2855 scope_initializers.Add (s);
2859 public void InsertStatement (int index, Statement s)
2861 statements.Insert (index, s);
2864 public void AddStatement (Statement s)
2869 public LabeledStatement LookupLabel (string name)
2871 return ParametersBlock.GetLabel (name, this);
2874 public override Reachability MarkReachable (Reachability rc)
2876 if (rc.IsUnreachable)
2879 MarkReachableScope (rc);
2881 foreach (var s in statements) {
2882 rc = s.MarkReachable (rc);
2883 if (rc.IsUnreachable) {
2884 if ((flags & Flags.ReachableEnd) != 0)
2885 return new Reachability ();
2891 flags |= Flags.ReachableEnd;
2896 public void MarkReachableScope (Reachability rc)
2898 base.MarkReachable (rc);
2900 if (scope_initializers != null) {
2901 foreach (var si in scope_initializers)
2902 si.MarkReachable (rc);
2906 public override bool Resolve (BlockContext bc)
2908 if ((flags & Flags.Resolved) != 0)
2911 Block prev_block = bc.CurrentBlock;
2912 bc.CurrentBlock = this;
2915 // Compiler generated scope statements
2917 if (scope_initializers != null) {
2918 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2919 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2922 resolving_init_idx = null;
2926 int statement_count = statements.Count;
2927 for (int ix = 0; ix < statement_count; ix++){
2928 Statement s = statements [ix];
2930 if (!s.Resolve (bc)) {
2932 statements [ix] = new EmptyStatement (s.loc);
2937 bc.CurrentBlock = prev_block;
2939 flags |= Flags.Resolved;
2943 protected override void DoEmit (EmitContext ec)
2945 for (int ix = 0; ix < statements.Count; ix++){
2946 statements [ix].Emit (ec);
2950 public override void Emit (EmitContext ec)
2952 if (scope_initializers != null)
2953 EmitScopeInitializers (ec);
2958 protected void EmitScopeInitializers (EmitContext ec)
2960 foreach (Statement s in scope_initializers)
2964 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2966 if (scope_initializers != null) {
2967 foreach (var si in scope_initializers)
2968 si.FlowAnalysis (fc);
2971 return DoFlowAnalysis (fc, 0);
2974 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2976 bool end_unreachable = !reachable;
2977 bool goto_flow_analysis = startIndex != 0;
2978 for (; startIndex < statements.Count; ++startIndex) {
2979 var s = statements[startIndex];
2981 end_unreachable = s.FlowAnalysis (fc);
2982 if (s.IsUnreachable) {
2983 statements [startIndex] = RewriteUnreachableStatement (s);
2988 // Statement end reachability is needed mostly due to goto support. Consider
2997 // X label is reachable only via goto not as another statement after if. We need
2998 // this for flow-analysis only to carry variable info correctly.
3000 if (end_unreachable) {
3001 bool after_goto_case = goto_flow_analysis && s is GotoCase;
3003 var f = s as TryFinally;
3004 if (f != null && !f.FinallyBlock.HasReachableClosingBrace) {
3006 // Special case for try-finally with unreachable code after
3007 // finally block. Try block has to include leave opcode but there is
3008 // no label to leave to after unreachable finally block closing
3009 // brace. This sentinel ensures there is always IL instruction to
3010 // leave to even if we know it'll never be reached.
3012 statements.Insert (startIndex + 1, new SentinelStatement ());
3014 for (++startIndex; startIndex < statements.Count; ++startIndex) {
3015 s = statements [startIndex];
3016 if (s is SwitchLabel) {
3017 if (!after_goto_case)
3018 s.FlowAnalysis (fc);
3023 if (s.IsUnreachable) {
3024 s.FlowAnalysis (fc);
3025 statements [startIndex] = RewriteUnreachableStatement (s);
3031 // Idea is to stop after goto case because goto case will always have at least same
3032 // variable assigned as switch case label. This saves a lot for complex goto case tests
3034 if (after_goto_case)
3040 var lb = s as LabeledStatement;
3041 if (lb != null && fc.AddReachedLabel (lb))
3046 // The condition should be true unless there is forward jumping goto
3048 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
3051 return !Explicit.HasReachableClosingBrace;
3054 static Statement RewriteUnreachableStatement (Statement s)
3056 // LAMESPEC: It's not clear whether declararion statement should be part of reachability
3057 // analysis. Even csc report unreachable warning for it but it's actually used hence
3058 // we try to emulate this behaviour
3066 if (s is BlockVariable || s is EmptyStatement || s is SentinelStatement)
3069 return new EmptyStatement (s.loc);
3072 public void ScanGotoJump (Statement label)
3075 for (i = 0; i < statements.Count; ++i) {
3076 if (statements[i] == label)
3080 var rc = new Reachability ();
3081 for (++i; i < statements.Count; ++i) {
3082 var s = statements[i];
3083 rc = s.MarkReachable (rc);
3084 if (rc.IsUnreachable)
3088 flags |= Flags.ReachableEnd;
3091 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
3094 for (i = 0; i < statements.Count; ++i) {
3095 if (statements[i] == label)
3099 DoFlowAnalysis (fc, ++i);
3103 public override string ToString ()
3105 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3109 protected override void CloneTo (CloneContext clonectx, Statement t)
3111 Block target = (Block) t;
3113 target.clone_id = ++clone_id_counter;
3116 clonectx.AddBlockMap (this, target);
3117 if (original != this)
3118 clonectx.AddBlockMap (original, target);
3120 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3121 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3124 target.Parent = clonectx.RemapBlockCopy (Parent);
3126 target.statements = new List<Statement> (statements.Count);
3127 foreach (Statement s in statements)
3128 target.statements.Add (s.Clone (clonectx));
3131 public override object Accept (StructuralVisitor visitor)
3133 return visitor.Visit (this);
3137 public class ExplicitBlock : Block
3139 protected AnonymousMethodStorey am_storey;
3140 int debug_scope_index;
3142 public ExplicitBlock (Block parent, Location start, Location end)
3143 : this (parent, (Flags) 0, start, end)
3147 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3148 : base (parent, flags, start, end)
3150 this.Explicit = this;
3155 public AnonymousMethodStorey AnonymousMethodStorey {
3161 public bool HasAwait {
3163 return (flags & Flags.AwaitBlock) != 0;
3167 public bool HasCapturedThis {
3169 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3172 return (flags & Flags.HasCapturedThis) != 0;
3177 // Used to indicate that the block has reference to parent
3178 // block and cannot be made static when defining anonymous method
3180 public bool HasCapturedVariable {
3182 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3185 return (flags & Flags.HasCapturedVariable) != 0;
3189 public bool HasReachableClosingBrace {
3191 return (flags & Flags.ReachableEnd) != 0;
3194 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3198 public bool HasYield {
3200 return (flags & Flags.YieldBlock) != 0;
3207 // Creates anonymous method storey in current block
3209 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3212 // Return same story for iterator and async blocks unless we are
3213 // in nested anonymous method
3215 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3216 return ec.CurrentAnonymousMethod.Storey;
3218 if (am_storey == null) {
3219 MemberBase mc = ec.MemberContext as MemberBase;
3222 // Creates anonymous method storey for this block
3224 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3230 public void EmitScopeInitialization (EmitContext ec)
3232 if ((flags & Flags.InitializationEmitted) != 0)
3235 if (am_storey != null) {
3236 DefineStoreyContainer (ec, am_storey);
3237 am_storey.EmitStoreyInstantiation (ec, this);
3240 if (scope_initializers != null)
3241 EmitScopeInitializers (ec);
3243 flags |= Flags.InitializationEmitted;
3246 public override void Emit (EmitContext ec)
3248 // TODO: It's needed only when scope has variable (normal or lifted)
3249 var scopeIndex = GetDebugSymbolScopeIndex ();
3250 if (scopeIndex > 0) {
3251 ec.BeginScope (scopeIndex);
3254 EmitScopeInitialization (ec);
3256 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3257 ec.Emit (OpCodes.Nop);
3265 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3266 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3267 ec.Emit (OpCodes.Nop);
3271 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3273 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3274 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3275 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3279 // Creates anonymous method storey
3281 storey.CreateContainer ();
3282 storey.DefineContainer ();
3283 storey.ExpandBaseInterfaces ();
3285 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3288 // Only first storey in path will hold this reference. All children blocks will
3289 // reference it indirectly using $ref field
3291 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3292 if (b.Parent != null) {
3293 var s = b.Parent.Explicit.AnonymousMethodStorey;
3295 storey.HoistedThis = s.HoistedThis;
3300 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3301 if (storey.HoistedThis == null)
3302 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3304 if (storey.HoistedThis != null)
3310 // We are the first storey on path and 'this' has to be hoisted
3312 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3313 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3315 // ThisReferencesFromChildrenBlock holds all reference even if they
3316 // are not on this path. It saves some memory otherwise it'd have to
3317 // be in every explicit block. We run this check to see if the reference
3318 // is valid for this storey
3320 Block block_on_path = ref_block;
3321 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3323 if (block_on_path == null)
3326 if (storey.HoistedThis == null) {
3327 storey.AddCapturedThisField (ec, null);
3330 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3332 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3334 if (b_storey != null) {
3336 // Don't add storey cross reference for `this' when the storey ends up not
3337 // beeing attached to any parent
3339 if (b.ParametersBlock.StateMachine == null) {
3340 AnonymousMethodStorey s = null;
3341 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3342 s = ab.Explicit.AnonymousMethodStorey;
3347 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3349 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3350 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3357 // Stop propagation inside same top block
3359 if (b.ParametersBlock == ParametersBlock.Original) {
3360 b_storey.AddParentStoreyReference (ec, storey);
3361 // b_storey.HoistedThis = storey.HoistedThis;
3365 b = pb = b.ParametersBlock;
3367 pb = b as ParametersBlock;
3370 if (pb != null && pb.StateMachine != null) {
3371 if (pb.StateMachine == storey)
3375 // If we are state machine with no parent. We can hook into parent without additional
3376 // reference and capture this directly
3378 ExplicitBlock parent_storey_block = pb;
3379 while (parent_storey_block.Parent != null) {
3380 parent_storey_block = parent_storey_block.Parent.Explicit;
3381 if (parent_storey_block.AnonymousMethodStorey != null) {
3386 if (parent_storey_block.AnonymousMethodStorey == null) {
3387 if (pb.StateMachine.HoistedThis == null) {
3388 pb.StateMachine.AddCapturedThisField (ec, null);
3389 b.HasCapturedThis = true;
3395 var parent_this_block = pb;
3396 while (parent_this_block.Parent != null) {
3397 parent_this_block = parent_this_block.Parent.ParametersBlock;
3398 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3404 // Add reference to closest storey which holds captured this
3406 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3410 // Add parent storey reference only when this is not captured directly
3412 if (b_storey != null) {
3413 b_storey.AddParentStoreyReference (ec, storey);
3414 b_storey.HoistedThis = storey.HoistedThis;
3421 var ref_blocks = storey.ReferencesFromChildrenBlock;
3422 if (ref_blocks != null) {
3423 foreach (ExplicitBlock ref_block in ref_blocks) {
3424 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3425 if (b.AnonymousMethodStorey != null) {
3426 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3429 // Stop propagation inside same top block
3431 if (b.ParametersBlock == ParametersBlock.Original)
3434 b = b.ParametersBlock;
3437 var pb = b as ParametersBlock;
3438 if (pb != null && pb.StateMachine != null) {
3439 if (pb.StateMachine == storey)
3442 pb.StateMachine.AddParentStoreyReference (ec, storey);
3445 b.HasCapturedVariable = true;
3451 storey.PrepareEmit ();
3452 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3455 public void DisableDebugScopeIndex ()
3457 debug_scope_index = -1;
3460 public virtual int GetDebugSymbolScopeIndex ()
3462 if (debug_scope_index == 0)
3463 debug_scope_index = ++ParametersBlock.debug_scope_index;
3465 return debug_scope_index;
3468 public void RegisterAsyncAwait ()
3471 while ((block.flags & Flags.AwaitBlock) == 0) {
3472 block.flags |= Flags.AwaitBlock;
3474 if (block is ParametersBlock)
3477 block = block.Parent.Explicit;
3481 public void RegisterIteratorYield ()
3483 ParametersBlock.TopBlock.IsIterator = true;
3486 while ((block.flags & Flags.YieldBlock) == 0) {
3487 block.flags |= Flags.YieldBlock;
3489 if (block.Parent == null)
3492 block = block.Parent.Explicit;
3496 public void SetCatchBlock ()
3498 flags |= Flags.CatchBlock;
3501 public void SetFinallyBlock ()
3503 flags |= Flags.FinallyBlock;
3506 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3508 tryBlock.statements = statements;
3509 statements = new List<Statement> (1);
3510 statements.Add (tf);
3515 // ParametersBlock was introduced to support anonymous methods
3516 // and lambda expressions
3518 public class ParametersBlock : ExplicitBlock
3520 public class ParameterInfo : INamedBlockVariable
3522 readonly ParametersBlock block;
3524 public VariableInfo VariableInfo;
3527 public ParameterInfo (ParametersBlock block, int index)
3535 public ParametersBlock Block {
3541 Block INamedBlockVariable.Block {
3547 public bool IsDeclared {
3553 public bool IsParameter {
3559 public bool IsLocked {
3568 public Location Location {
3570 return Parameter.Location;
3574 public Parameter Parameter {
3576 return block.Parameters [index];
3580 public TypeSpec ParameterType {
3582 return Parameter.Type;
3588 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3590 return new ParameterReference (this, loc);
3595 // Block is converted into an expression
3597 sealed class BlockScopeExpression : Expression
3600 readonly ParametersBlock block;
3602 public BlockScopeExpression (Expression child, ParametersBlock block)
3608 public override bool ContainsEmitWithAwait ()
3610 return child.ContainsEmitWithAwait ();
3613 public override Expression CreateExpressionTree (ResolveContext ec)
3615 throw new NotSupportedException ();
3618 protected override Expression DoResolve (ResolveContext ec)
3623 child = child.Resolve (ec);
3627 eclass = child.eclass;
3632 public override void Emit (EmitContext ec)
3634 block.EmitScopeInitializers (ec);
3639 protected ParametersCompiled parameters;
3640 protected ParameterInfo[] parameter_info;
3641 protected bool resolved;
3642 protected ToplevelBlock top_block;
3643 protected StateMachine state_machine;
3644 protected Dictionary<string, object> labels;
3646 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3647 : base (parent, 0, start, start)
3649 if (parameters == null)
3650 throw new ArgumentNullException ("parameters");
3652 this.parameters = parameters;
3653 ParametersBlock = this;
3655 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3657 this.top_block = parent.ParametersBlock.top_block;
3658 ProcessParameters ();
3661 protected ParametersBlock (ParametersCompiled parameters, Location start)
3662 : base (null, 0, start, start)
3664 if (parameters == null)
3665 throw new ArgumentNullException ("parameters");
3667 this.parameters = parameters;
3668 ParametersBlock = this;
3672 // It's supposed to be used by method body implementation of anonymous methods
3674 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3675 : base (null, 0, source.StartLocation, source.EndLocation)
3677 this.parameters = parameters;
3678 this.statements = source.statements;
3679 this.scope_initializers = source.scope_initializers;
3681 this.resolved = true;
3682 this.reachable = source.reachable;
3683 this.am_storey = source.am_storey;
3684 this.state_machine = source.state_machine;
3685 this.flags = source.flags & Flags.ReachableEnd;
3687 ParametersBlock = this;
3690 // Overwrite original for comparison purposes when linking cross references
3691 // between anonymous methods
3693 Original = source.Original;
3698 public bool HasReferenceToStoreyForInstanceLambdas {
3700 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3703 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3707 public bool IsAsync {
3709 return (flags & Flags.HasAsyncModifier) != 0;
3712 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3717 // Block has been converted to expression tree
3719 public bool IsExpressionTree {
3721 return (flags & Flags.IsExpressionTree) != 0;
3726 // The parameters for the block.
3728 public ParametersCompiled Parameters {
3734 public StateMachine StateMachine {
3736 return state_machine;
3740 public ToplevelBlock TopBlock {
3749 public bool Resolved {
3751 return (flags & Flags.Resolved) != 0;
3755 public int TemporaryLocalsCount { get; set; }
3760 // Checks whether all `out' parameters have been assigned.
3762 public void CheckControlExit (FlowAnalysisContext fc)
3764 CheckControlExit (fc, fc.DefiniteAssignment);
3767 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3769 if (parameter_info == null)
3772 foreach (var p in parameter_info) {
3773 if (p.VariableInfo == null)
3776 if (p.VariableInfo.IsAssigned (dat))
3779 fc.Report.Error (177, p.Location,
3780 "The out parameter `{0}' must be assigned to before control leaves the current method",
3785 protected override void CloneTo (CloneContext clonectx, Statement t)
3787 base.CloneTo (clonectx, t);
3789 var target = (ParametersBlock) t;
3792 // Clone label statements as well as they contain block reference
3796 if (pb.labels != null) {
3797 target.labels = new Dictionary<string, object> ();
3799 foreach (var entry in pb.labels) {
3800 var list = entry.Value as List<LabeledStatement>;
3803 var list_clone = new List<LabeledStatement> ();
3804 foreach (var lentry in list) {
3805 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3808 target.labels.Add (entry.Key, list_clone);
3810 var labeled = (LabeledStatement) entry.Value;
3811 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3818 if (pb.Parent == null)
3821 pb = pb.Parent.ParametersBlock;
3825 public override Expression CreateExpressionTree (ResolveContext ec)
3827 if (statements.Count == 1) {
3828 Expression expr = statements[0].CreateExpressionTree (ec);
3829 if (scope_initializers != null)
3830 expr = new BlockScopeExpression (expr, this);
3835 return base.CreateExpressionTree (ec);
3838 public override void Emit (EmitContext ec)
3840 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3841 DefineStoreyContainer (ec, state_machine);
3842 state_machine.EmitStoreyInstantiation (ec, this);
3848 public void EmitEmbedded (EmitContext ec)
3850 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3851 DefineStoreyContainer (ec, state_machine);
3852 state_machine.EmitStoreyInstantiation (ec, this);
3858 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3860 var res = base.DoFlowAnalysis (fc);
3862 if (HasReachableClosingBrace)
3863 CheckControlExit (fc);
3868 public override int GetDebugSymbolScopeIndex ()
3873 public LabeledStatement GetLabel (string name, Block block)
3876 // Cloned parameters blocks can have their own cloned version of top-level labels
3878 if (labels == null) {
3880 return Parent.ParametersBlock.GetLabel (name, block);
3886 if (!labels.TryGetValue (name, out value)) {
3890 var label = value as LabeledStatement;
3892 if (label != null) {
3893 if (IsLabelVisible (label, b))
3897 List<LabeledStatement> list = (List<LabeledStatement>) value;
3898 for (int i = 0; i < list.Count; ++i) {
3900 if (IsLabelVisible (label, b))
3908 static bool IsLabelVisible (LabeledStatement label, Block b)
3911 if (label.Block == b)
3914 } while (b != null);
3919 public ParameterInfo GetParameterInfo (Parameter p)
3921 for (int i = 0; i < parameters.Count; ++i) {
3922 if (parameters[i] == p)
3923 return parameter_info[i];
3926 throw new ArgumentException ("Invalid parameter");
3929 public ParameterReference GetParameterReference (int index, Location loc)
3931 return new ParameterReference (parameter_info[index], loc);
3934 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3936 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3938 CloneContext clonectx = new CloneContext ();
3939 return Clone (clonectx);
3942 protected void ProcessParameters ()
3944 if (parameters.Count == 0)
3947 parameter_info = new ParameterInfo[parameters.Count];
3948 for (int i = 0; i < parameter_info.Length; ++i) {
3949 var p = parameters.FixedParameters[i];
3953 // TODO: Should use Parameter only and more block there
3954 parameter_info[i] = new ParameterInfo (this, i);
3956 AddLocalName (p.Name, parameter_info[i]);
3960 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3962 var src = stmt.Block;
3965 // Cannot remap label block if the label was not yet cloned which
3966 // can happen in case of anonymous method inside anoynymous method
3967 // with a label. But in this case we don't care because goto cannot
3968 // jump of out anonymous method
3970 if (src.ParametersBlock != this)
3973 var src_stmts = src.Statements;
3974 for (int i = 0; i < src_stmts.Count; ++i) {
3975 if (src_stmts[i] == stmt)
3976 return (LabeledStatement) dst.Statements[i];
3979 throw new InternalErrorException ("Should never be reached");
3982 public override bool Resolve (BlockContext bc)
3984 // TODO: if ((flags & Flags.Resolved) != 0)
3991 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3992 flags |= Flags.IsExpressionTree;
3995 PrepareAssignmentAnalysis (bc);
3997 if (!base.Resolve (bc))
4000 } catch (Exception e) {
4001 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
4004 if (bc.CurrentBlock != null) {
4005 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
4007 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4012 // If an asynchronous body of F is either an expression classified as nothing, or a
4013 // statement block where no return statements have expressions, the inferred return type is Task
4016 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4017 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4018 am.ReturnTypeInference = null;
4019 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4027 void PrepareAssignmentAnalysis (BlockContext bc)
4029 for (int i = 0; i < parameters.Count; ++i) {
4030 var par = parameters.FixedParameters[i];
4032 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4035 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4039 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4041 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4042 var stateMachine = new IteratorStorey (iterator);
4044 state_machine = stateMachine;
4045 iterator.SetStateMachine (stateMachine);
4047 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4048 tlb.Original = this;
4049 tlb.state_machine = stateMachine;
4050 tlb.AddStatement (new Return (iterator, iterator.Location));
4054 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4056 for (int i = 0; i < parameters.Count; i++) {
4057 Parameter p = parameters[i];
4058 Parameter.Modifier mod = p.ModFlags;
4059 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4060 host.Compiler.Report.Error (1988, p.Location,
4061 "Async methods cannot have ref or out parameters");
4065 if (p is ArglistParameter) {
4066 host.Compiler.Report.Error (4006, p.Location,
4067 "__arglist is not allowed in parameter list of async methods");
4071 if (parameters.Types[i].IsPointer) {
4072 host.Compiler.Report.Error (4005, p.Location,
4073 "Async methods cannot have unsafe parameters");
4079 host.Compiler.Report.Warning (1998, 1, loc,
4080 "Async block lacks `await' operator and will run synchronously");
4083 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4084 var initializer = new AsyncInitializer (this, host, block_type);
4085 initializer.Type = block_type;
4086 initializer.DelegateType = delegateType;
4088 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4090 state_machine = stateMachine;
4091 initializer.SetStateMachine (stateMachine);
4093 const Flags flags = Flags.CompilerGenerated;
4095 var b = this is ToplevelBlock ?
4096 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4097 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4100 b.state_machine = stateMachine;
4101 b.AddStatement (new AsyncInitializerStatement (initializer));
4109 public class ToplevelBlock : ParametersBlock
4111 LocalVariable this_variable;
4112 CompilerContext compiler;
4113 Dictionary<string, object> names;
4115 List<ExplicitBlock> this_references;
4117 public ToplevelBlock (CompilerContext ctx, Location loc)
4118 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4122 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4123 : base (parameters, start)
4125 this.compiler = ctx;
4129 ProcessParameters ();
4133 // Recreates a top level block from parameters block. Used for
4134 // compiler generated methods where the original block comes from
4135 // explicit child block. This works for already resolved blocks
4136 // only to ensure we resolve them in the correct flow order
4138 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4139 : base (source, parameters)
4141 this.compiler = source.TopBlock.compiler;
4145 public bool IsIterator {
4147 return (flags & Flags.Iterator) != 0;
4150 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4154 public Report Report {
4156 return compiler.Report;
4161 // Used by anonymous blocks to track references of `this' variable
4163 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4165 return this_references;
4170 // Returns the "this" instance variable of this block.
4171 // See AddThisVariable() for more information.
4173 public LocalVariable ThisVariable {
4175 return this_variable;
4179 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4182 names = new Dictionary<string, object> ();
4185 if (!names.TryGetValue (name, out value)) {
4186 names.Add (name, li);
4190 INamedBlockVariable existing = value as INamedBlockVariable;
4191 List<INamedBlockVariable> existing_list;
4192 if (existing != null) {
4193 existing_list = new List<INamedBlockVariable> ();
4194 existing_list.Add (existing);
4195 names[name] = existing_list;
4197 existing_list = (List<INamedBlockVariable>) value;
4201 // A collision checking between local names
4203 var variable_block = li.Block.Explicit;
4204 for (int i = 0; i < existing_list.Count; ++i) {
4205 existing = existing_list[i];
4206 Block b = existing.Block.Explicit;
4208 // Collision at same level
4209 if (variable_block == b) {
4210 li.Block.Error_AlreadyDeclared (name, li);
4214 // Collision with parent
4215 Block parent = variable_block;
4216 while ((parent = parent.Parent) != null) {
4218 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4219 i = existing_list.Count;
4224 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4225 // Collision with children
4226 while ((b = b.Parent) != null) {
4227 if (variable_block == b) {
4228 li.Block.Error_AlreadyDeclared (name, li, "child");
4229 i = existing_list.Count;
4236 existing_list.Add (li);
4239 public void AddLabel (string name, LabeledStatement label)
4242 labels = new Dictionary<string, object> ();
4245 if (!labels.TryGetValue (name, out value)) {
4246 labels.Add (name, label);
4250 LabeledStatement existing = value as LabeledStatement;
4251 List<LabeledStatement> existing_list;
4252 if (existing != null) {
4253 existing_list = new List<LabeledStatement> ();
4254 existing_list.Add (existing);
4255 labels[name] = existing_list;
4257 existing_list = (List<LabeledStatement>) value;
4261 // A collision checking between labels
4263 for (int i = 0; i < existing_list.Count; ++i) {
4264 existing = existing_list[i];
4265 Block b = existing.Block;
4267 // Collision at same level
4268 if (label.Block == b) {
4269 Report.SymbolRelatedToPreviousError (existing.loc, name);
4270 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4274 // Collision with parent
4276 while ((b = b.Parent) != null) {
4277 if (existing.Block == b) {
4278 Report.Error (158, label.loc,
4279 "The label `{0}' shadows another label by the same name in a contained scope", name);
4280 i = existing_list.Count;
4285 // Collision with with children
4287 while ((b = b.Parent) != null) {
4288 if (label.Block == b) {
4289 Report.Error (158, label.loc,
4290 "The label `{0}' shadows another label by the same name in a contained scope", name);
4291 i = existing_list.Count;
4297 existing_list.Add (label);
4300 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4302 if (this_references == null)
4303 this_references = new List<ExplicitBlock> ();
4305 if (!this_references.Contains (block))
4306 this_references.Add (block);
4309 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4311 this_references.Remove (block);
4315 // Creates an arguments set from all parameters, useful for method proxy calls
4317 public Arguments GetAllParametersArguments ()
4319 int count = parameters.Count;
4320 Arguments args = new Arguments (count);
4321 for (int i = 0; i < count; ++i) {
4322 var pi = parameter_info[i];
4323 var arg_expr = GetParameterReference (i, pi.Location);
4325 Argument.AType atype_modifier;
4326 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4327 case Parameter.Modifier.REF:
4328 atype_modifier = Argument.AType.Ref;
4330 case Parameter.Modifier.OUT:
4331 atype_modifier = Argument.AType.Out;
4338 args.Add (new Argument (arg_expr, atype_modifier));
4345 // Lookup inside a block, the returned value can represent 3 states
4347 // true+variable: A local name was found and it's valid
4348 // false+variable: A local name was found in a child block only
4349 // false+null: No local name was found
4351 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4357 if (!names.TryGetValue (name, out value))
4360 variable = value as INamedBlockVariable;
4362 if (variable != null) {
4364 if (variable.Block == b.Original)
4368 } while (b != null);
4376 } while (b != null);
4378 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4379 for (int i = 0; i < list.Count; ++i) {
4382 if (variable.Block == b.Original)
4386 } while (b != null);
4394 } while (b != null);
4404 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4406 if (block.names != null) {
4407 foreach (var n in block.names) {
4408 var variable = n.Value as INamedBlockVariable;
4409 if (variable != null) {
4410 if (variable.Block.ParametersBlock == pb)
4411 AddLocalName (n.Key, variable, false);
4415 foreach (var v in (List<INamedBlockVariable>) n.Value)
4416 if (v.Block.ParametersBlock == pb)
4417 AddLocalName (n.Key, v, false);
4423 // This is used by non-static `struct' constructors which do not have an
4424 // initializer - in this case, the constructor must initialize all of the
4425 // struct's fields. To do this, we add a "this" variable and use the flow
4426 // analysis code to ensure that it's been fully initialized before control
4427 // leaves the constructor.
4429 public void AddThisVariable (BlockContext bc)
4431 if (this_variable != null)
4432 throw new InternalErrorException (StartLocation.ToString ());
4434 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4435 this_variable.Type = bc.CurrentType;
4436 this_variable.PrepareAssignmentAnalysis (bc);
4439 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4442 // If we're a non-static struct constructor which doesn't have an
4443 // initializer, then we must initialize all of the struct's fields.
4445 if (this_variable != null)
4446 this_variable.IsThisAssigned (fc, this);
4448 base.CheckControlExit (fc, dat);
4451 public HashSet<LocalVariable> GetUndeclaredVariables ()
4456 HashSet<LocalVariable> variables = null;
4458 foreach (var entry in names) {
4459 var complex = entry.Value as List<INamedBlockVariable>;
4460 if (complex != null) {
4461 foreach (var centry in complex) {
4462 if (IsUndeclaredVariable (centry)) {
4463 if (variables == null)
4464 variables = new HashSet<LocalVariable> ();
4466 variables.Add ((LocalVariable) centry);
4469 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4470 if (variables == null)
4471 variables = new HashSet<LocalVariable> ();
4473 variables.Add ((LocalVariable)entry.Value);
4480 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4482 var lv = namedBlockVariable as LocalVariable;
4483 return lv != null && !lv.IsDeclared;
4486 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4491 foreach (var entry in names) {
4492 var complex = entry.Value as List<INamedBlockVariable>;
4493 if (complex != null) {
4494 foreach (var centry in complex) {
4495 var lv = centry as LocalVariable;
4496 if (lv != null && undeclaredVariables.Contains (lv)) {
4501 var lv = entry.Value as LocalVariable;
4502 if (lv != null && undeclaredVariables.Contains (lv))
4508 public override void Emit (EmitContext ec)
4510 if (Report.Errors > 0)
4514 if (IsCompilerGenerated) {
4515 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4523 // If `HasReturnLabel' is set, then we already emitted a
4524 // jump to the end of the method, so we must emit a `ret'
4527 // Unfortunately, System.Reflection.Emit automatically emits
4528 // a leave to the end of a finally block. This is a problem
4529 // if no code is following the try/finally block since we may
4530 // jump to a point after the end of the method.
4531 // As a workaround, we're always creating a return label in
4534 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4535 if (ec.HasReturnLabel)
4536 ec.MarkLabel (ec.ReturnLabel);
4538 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4539 ec.Mark (EndLocation);
4541 if (ec.ReturnType.Kind != MemberKind.Void)
4542 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4544 ec.Emit (OpCodes.Ret);
4547 } catch (Exception e) {
4548 throw new InternalErrorException (e, StartLocation);
4552 public bool Resolve (BlockContext bc, IMethodData md)
4557 var errors = bc.Report.Errors;
4561 if (bc.Report.Errors > errors)
4564 MarkReachable (new Reachability ());
4566 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4567 // TODO: var md = bc.CurrentMemberDefinition;
4568 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4571 if ((flags & Flags.NoFlowAnalysis) != 0)
4574 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4577 } catch (Exception e) {
4578 throw new InternalErrorException (e, StartLocation);
4585 public class SwitchLabel : Statement
4593 // if expr == null, then it is the default case.
4595 public SwitchLabel (Expression expr, Location l)
4601 public bool IsDefault {
4603 return label == null;
4607 public Expression Label {
4613 public Location Location {
4619 public Constant Converted {
4628 public bool PatternMatching { get; set; }
4630 public bool SectionStart { get; set; }
4632 public Label GetILLabel (EmitContext ec)
4634 if (il_label == null){
4635 il_label = ec.DefineLabel ();
4638 return il_label.Value;
4641 protected override void DoEmit (EmitContext ec)
4643 ec.MarkLabel (GetILLabel (ec));
4646 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4651 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4655 public override bool Resolve (BlockContext bc)
4657 if (ResolveAndReduce (bc))
4658 bc.Switch.RegisterLabel (bc, this);
4664 // Resolves the expression, reduces it to a literal if possible
4665 // and then converts it to the requested type.
4667 bool ResolveAndReduce (BlockContext bc)
4672 var switch_statement = bc.Switch;
4674 if (PatternMatching) {
4675 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4676 return label != null;
4679 var c = label.ResolveLabelConstant (bc);
4683 if (switch_statement.IsNullable && c is NullLiteral) {
4688 if (switch_statement.IsPatternMatching) {
4689 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4693 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4694 return converted != null;
4697 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4699 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4700 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4703 protected override void CloneTo (CloneContext clonectx, Statement target)
4705 var t = (SwitchLabel) target;
4707 t.label = label.Clone (clonectx);
4710 public override object Accept (StructuralVisitor visitor)
4712 return visitor.Visit (this);
4715 public string GetSignatureForError ()
4718 if (converted == null)
4721 label = converted.GetValueAsLiteral ();
4723 return string.Format ("case {0}:", label);
4727 public class Switch : LoopStatement
4729 // structure used to hold blocks of keys while calculating table switch
4730 sealed class LabelsRange : IComparable<LabelsRange>
4732 public readonly long min;
4734 public readonly List<long> label_values;
4736 public LabelsRange (long value)
4739 label_values = new List<long> ();
4740 label_values.Add (value);
4743 public LabelsRange (long min, long max, ICollection<long> values)
4747 this.label_values = new List<long> (values);
4752 return max - min + 1;
4756 public bool AddValue (long value)
4758 var gap = value - min + 1;
4759 // Ensure the range has > 50% occupancy
4760 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4764 label_values.Add (value);
4768 public int CompareTo (LabelsRange other)
4770 int nLength = label_values.Count;
4771 int nLengthOther = other.label_values.Count;
4772 if (nLengthOther == nLength)
4773 return (int) (other.min - min);
4775 return nLength - nLengthOther;
4779 sealed class DispatchStatement : Statement
4781 readonly Switch body;
4783 public DispatchStatement (Switch body)
4788 protected override void CloneTo (CloneContext clonectx, Statement target)
4790 throw new NotImplementedException ();
4793 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4798 protected override void DoEmit (EmitContext ec)
4800 body.EmitDispatch (ec);
4804 class MissingBreak : Statement
4806 readonly SwitchLabel label;
4808 public MissingBreak (SwitchLabel sl)
4814 public bool FallOut { get; set; }
4816 protected override void DoEmit (EmitContext ec)
4820 protected override void CloneTo (CloneContext clonectx, Statement target)
4824 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4827 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4828 label.GetSignatureForError ());
4830 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4831 label.GetSignatureForError ());
4837 public Expression Expr;
4840 // Mapping of all labels to their SwitchLabels
4842 Dictionary<long, SwitchLabel> labels;
4843 Dictionary<string, SwitchLabel> string_labels;
4844 List<SwitchLabel> case_labels;
4846 List<Tuple<GotoCase, Constant>> goto_cases;
4847 List<DefiniteAssignmentBitSet> end_reachable_das;
4850 /// The governing switch type
4852 public TypeSpec SwitchType;
4854 Expression new_expr;
4856 SwitchLabel case_null;
4857 SwitchLabel case_default;
4859 Label defaultLabel, nullLabel;
4860 VariableReference value;
4861 ExpressionStatement string_dictionary;
4862 FieldExpr switch_cache_field;
4863 ExplicitBlock block;
4867 // Nullable Types support
4869 Nullable.Unwrap unwrap;
4871 public Switch (Expression e, ExplicitBlock block, Location l)
4879 public SwitchLabel ActiveLabel { get; set; }
4881 public ExplicitBlock Block {
4887 public SwitchLabel DefaultLabel {
4889 return case_default;
4893 public bool IsNullable {
4895 return unwrap != null;
4899 public bool IsPatternMatching {
4901 return new_expr == null && SwitchType != null;
4905 public List<SwitchLabel> RegisteredLabels {
4911 public VariableReference ExpressionValue {
4918 // Determines the governing type for a switch. The returned
4919 // expression might be the expression from the switch, or an
4920 // expression that includes any potential conversions to
4922 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4924 switch (expr.Type.BuiltinType) {
4925 case BuiltinTypeSpec.Type.Byte:
4926 case BuiltinTypeSpec.Type.SByte:
4927 case BuiltinTypeSpec.Type.UShort:
4928 case BuiltinTypeSpec.Type.Short:
4929 case BuiltinTypeSpec.Type.UInt:
4930 case BuiltinTypeSpec.Type.Int:
4931 case BuiltinTypeSpec.Type.ULong:
4932 case BuiltinTypeSpec.Type.Long:
4933 case BuiltinTypeSpec.Type.Char:
4934 case BuiltinTypeSpec.Type.String:
4935 case BuiltinTypeSpec.Type.Bool:
4939 if (expr.Type.IsEnum)
4943 // Try to find a *user* defined implicit conversion.
4945 // If there is no implicit conversion, or if there are multiple
4946 // conversions, we have to report an error
4948 Expression converted = null;
4949 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4951 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4954 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4955 Convert.UserConversionRestriction.ProbingOnly;
4958 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4960 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4965 // Ignore over-worked ImplicitUserConversions that do
4966 // an implicit conversion in addition to the user conversion.
4968 var uc = e as UserCast;
4972 if (converted != null){
4973 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4982 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4984 var types = module.Compiler.BuiltinTypes;
4986 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4987 TypeSpec[] stypes = new[] {
5000 if (nullable != null) {
5002 Array.Resize (ref stypes, stypes.Length + 9);
5004 for (int i = 0; i < 9; ++i) {
5005 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5012 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5014 case_labels.Add (sl);
5017 if (case_default != null) {
5018 sl.Error_AlreadyOccurs (rc, case_default);
5026 if (sl.Converted == null)
5030 if (string_labels != null) {
5031 string string_value = sl.Converted.GetValue () as string;
5032 if (string_value == null)
5035 string_labels.Add (string_value, sl);
5037 if (sl.Converted.IsNull) {
5040 labels.Add (sl.Converted.GetValueAsLong (), sl);
5043 } catch (ArgumentException) {
5044 if (string_labels != null)
5045 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5047 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5052 // This method emits code for a lookup-based switch statement (non-string)
5053 // Basically it groups the cases into blocks that are at least half full,
5054 // and then spits out individual lookup opcodes for each block.
5055 // It emits the longest blocks first, and short blocks are just
5056 // handled with direct compares.
5058 void EmitTableSwitch (EmitContext ec, Expression val)
5060 if (labels != null && labels.Count > 0) {
5061 List<LabelsRange> ranges;
5062 if (string_labels != null) {
5063 // We have done all hard work for string already
5064 // setup single range only
5065 ranges = new List<LabelsRange> (1);
5066 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5068 var element_keys = new long[labels.Count];
5069 labels.Keys.CopyTo (element_keys, 0);
5070 Array.Sort (element_keys);
5073 // Build possible ranges of switch labes to reduce number
5076 ranges = new List<LabelsRange> (element_keys.Length);
5077 var range = new LabelsRange (element_keys[0]);
5079 for (int i = 1; i < element_keys.Length; ++i) {
5080 var l = element_keys[i];
5081 if (range.AddValue (l))
5084 range = new LabelsRange (l);
5088 // sort the blocks so we can tackle the largest ones first
5092 Label lbl_default = defaultLabel;
5093 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5095 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5096 LabelsRange kb = ranges[range_index];
5097 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5099 // Optimize small ranges using simple equality check
5100 if (kb.Range <= 2) {
5101 foreach (var key in kb.label_values) {
5102 SwitchLabel sl = labels[key];
5103 if (sl == case_default || sl == case_null)
5106 if (sl.Converted.IsZeroInteger) {
5107 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5110 sl.Converted.Emit (ec);
5111 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5115 // TODO: if all the keys in the block are the same and there are
5116 // no gaps/defaults then just use a range-check.
5117 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5118 // TODO: optimize constant/I4 cases
5120 // check block range (could be > 2^31)
5122 ec.EmitLong (kb.min);
5123 ec.Emit (OpCodes.Blt, lbl_default);
5126 ec.EmitLong (kb.max);
5127 ec.Emit (OpCodes.Bgt, lbl_default);
5132 ec.EmitLong (kb.min);
5133 ec.Emit (OpCodes.Sub);
5136 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5140 int first = (int) kb.min;
5143 ec.Emit (OpCodes.Sub);
5144 } else if (first < 0) {
5145 ec.EmitInt (-first);
5146 ec.Emit (OpCodes.Add);
5150 // first, build the list of labels for the switch
5152 long cJumps = kb.Range;
5153 Label[] switch_labels = new Label[cJumps];
5154 for (int iJump = 0; iJump < cJumps; iJump++) {
5155 var key = kb.label_values[iKey];
5156 if (key == kb.min + iJump) {
5157 switch_labels[iJump] = labels[key].GetILLabel (ec);
5160 switch_labels[iJump] = lbl_default;
5164 // emit the switch opcode
5165 ec.Emit (OpCodes.Switch, switch_labels);
5168 // mark the default for this block
5169 if (range_index != 0)
5170 ec.MarkLabel (lbl_default);
5173 // the last default just goes to the end
5174 if (ranges.Count > 0)
5175 ec.Emit (OpCodes.Br, lbl_default);
5179 public SwitchLabel FindLabel (Constant value)
5181 SwitchLabel sl = null;
5183 if (string_labels != null) {
5184 string s = value.GetValue () as string;
5186 if (case_null != null)
5188 else if (case_default != null)
5191 string_labels.TryGetValue (s, out sl);
5194 if (value is NullLiteral) {
5197 labels.TryGetValue (value.GetValueAsLong (), out sl);
5201 if (sl == null || sl.SectionStart)
5205 // Always return section start, it simplifies handling of switch labels
5207 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5208 var cs = case_labels [idx];
5209 if (cs.SectionStart)
5214 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5216 Expr.FlowAnalysis (fc);
5218 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5219 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5220 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5222 block.FlowAnalysis (fc);
5224 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5226 if (end_reachable_das != null) {
5227 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5228 InitialDefinitiveAssignment |= sections_das;
5229 end_reachable_das = null;
5232 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5234 return case_default != null && !end_reachable;
5237 public override bool Resolve (BlockContext ec)
5239 Expr = Expr.Resolve (ec);
5244 // LAMESPEC: User conversion from non-nullable governing type has a priority
5246 new_expr = SwitchGoverningType (ec, Expr, false);
5248 if (new_expr == null) {
5249 if (Expr.Type.IsNullableType) {
5250 unwrap = Nullable.Unwrap.Create (Expr, false);
5255 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5256 // involving nullable Expr and nullable governing type is
5258 new_expr = SwitchGoverningType (ec, unwrap, true);
5262 Expression switch_expr;
5263 if (new_expr == null) {
5264 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5265 if (Expr.Type != InternalType.ErrorType) {
5266 ec.Report.Error (151, loc,
5267 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5268 Expr.Type.GetSignatureForError ());
5275 SwitchType = Expr.Type;
5277 switch_expr = new_expr;
5278 SwitchType = new_expr.Type;
5279 if (SwitchType.IsNullableType) {
5280 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5281 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5284 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5285 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5289 if (block.Statements.Count == 0)
5292 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5293 string_labels = new Dictionary<string, SwitchLabel> ();
5295 labels = new Dictionary<long, SwitchLabel> ();
5299 var constant = switch_expr as Constant;
5302 // Don't need extra variable for constant switch or switch with
5303 // only default case
5305 if (constant == null) {
5307 // Store switch expression for comparison purposes
5309 value = switch_expr as VariableReference;
5310 if (value == null && !HasOnlyDefaultSection ()) {
5311 var current_block = ec.CurrentBlock;
5312 ec.CurrentBlock = Block;
5313 // Create temporary variable inside switch scope
5314 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5316 ec.CurrentBlock = current_block;
5320 case_labels = new List<SwitchLabel> ();
5322 Switch old_switch = ec.Switch;
5324 var parent_los = ec.EnclosingLoopOrSwitch;
5325 ec.EnclosingLoopOrSwitch = this;
5327 var ok = Statement.Resolve (ec);
5329 ec.EnclosingLoopOrSwitch = parent_los;
5330 ec.Switch = old_switch;
5333 // Check if all goto cases are valid. Needs to be done after switch
5334 // is resolved because goto can jump forward in the scope.
5336 if (goto_cases != null) {
5337 foreach (var gc in goto_cases) {
5338 if (gc.Item1 == null) {
5339 if (DefaultLabel == null) {
5340 Goto.Error_UnknownLabel (ec, "default", loc);
5346 var sl = FindLabel (gc.Item2);
5348 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5350 gc.Item1.Label = sl;
5358 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5359 ResolveStringSwitchMap (ec);
5363 // Anonymous storey initialization has to happen before
5364 // any generated switch dispatch
5366 block.InsertStatement (0, new DispatchStatement (this));
5371 bool HasOnlyDefaultSection ()
5373 for (int i = 0; i < block.Statements.Count; ++i) {
5374 var s = block.Statements[i] as SwitchLabel;
5376 if (s == null || s.IsDefault)
5385 public override Reachability MarkReachable (Reachability rc)
5387 if (rc.IsUnreachable)
5390 base.MarkReachable (rc);
5392 block.MarkReachableScope (rc);
5394 if (block.Statements.Count == 0)
5397 SwitchLabel constant_label = null;
5398 var constant = new_expr as Constant;
5400 if (constant != null) {
5401 constant_label = FindLabel (constant) ?? case_default;
5402 if (constant_label == null) {
5403 block.Statements.RemoveAt (0);
5408 var section_rc = new Reachability ();
5409 SwitchLabel prev_label = null;
5411 for (int i = 0; i < block.Statements.Count; ++i) {
5412 var s = block.Statements[i];
5413 var sl = s as SwitchLabel;
5415 if (sl != null && sl.SectionStart) {
5417 // Section is marked already via goto case
5419 if (!sl.IsUnreachable) {
5420 section_rc = new Reachability ();
5424 if (section_rc.IsUnreachable) {
5426 // Common case. Previous label section end is unreachable as
5427 // it ends with break, return, etc. For next section revert
5428 // to reachable again unless we have constant switch block
5430 section_rc = constant_label != null && constant_label != sl ?
5431 Reachability.CreateUnreachable () :
5432 new Reachability ();
5433 } else if (prev_label != null) {
5435 // Error case as control cannot fall through from one case label
5437 sl.SectionStart = false;
5438 s = new MissingBreak (prev_label);
5439 s.MarkReachable (rc);
5440 block.Statements.Insert (i - 1, s);
5442 } else if (constant_label != null && constant_label != sl) {
5444 // Special case for the first unreachable label in constant
5447 section_rc = Reachability.CreateUnreachable ();
5453 section_rc = s.MarkReachable (section_rc);
5456 if (!section_rc.IsUnreachable && prev_label != null) {
5457 prev_label.SectionStart = false;
5458 var s = new MissingBreak (prev_label) {
5462 s.MarkReachable (rc);
5463 block.Statements.Add (s);
5467 // Reachability can affect parent only when all possible paths are handled but
5468 // we still need to run reachability check on switch body to check for fall-through
5470 if (case_default == null && constant_label == null)
5474 // We have at least one local exit from the switch
5479 return Reachability.CreateUnreachable ();
5482 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5484 if (goto_cases == null)
5485 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5487 goto_cases.Add (Tuple.Create (gotoCase, value));
5491 // Converts string switch into string hashtable
5493 void ResolveStringSwitchMap (ResolveContext ec)
5495 FullNamedExpression string_dictionary_type;
5496 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5497 string_dictionary_type = new TypeExpression (
5498 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5499 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5501 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5502 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5504 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5508 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5509 Field field = new Field (ctype, string_dictionary_type,
5510 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5511 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5512 if (!field.Define ())
5514 ctype.AddField (field);
5516 var init = new List<Expression> ();
5518 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5519 string value = null;
5521 foreach (SwitchLabel sl in case_labels) {
5523 if (sl.SectionStart)
5524 labels.Add (++counter, sl);
5526 if (sl == case_default || sl == case_null)
5529 value = (string) sl.Converted.GetValue ();
5530 var init_args = new List<Expression> (2);
5531 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5533 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5534 init_args.Add (sl.Converted);
5536 init.Add (new CollectionElementInitializer (init_args, loc));
5539 Arguments args = new Arguments (1);
5540 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5541 Expression initializer = new NewInitialize (string_dictionary_type, args,
5542 new CollectionOrObjectInitializers (init, loc), loc);
5544 switch_cache_field = new FieldExpr (field, loc);
5545 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5548 void DoEmitStringSwitch (EmitContext ec)
5550 Label l_initialized = ec.DefineLabel ();
5553 // Skip initialization when value is null
5555 value.EmitBranchable (ec, nullLabel, false);
5558 // Check if string dictionary is initialized and initialize
5560 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5561 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5562 string_dictionary.EmitStatement (ec);
5564 ec.MarkLabel (l_initialized);
5566 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5568 ResolveContext rc = new ResolveContext (ec.MemberContext);
5570 if (switch_cache_field.Type.IsGeneric) {
5571 Arguments get_value_args = new Arguments (2);
5572 get_value_args.Add (new Argument (value));
5573 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5574 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5575 if (get_item == null)
5579 // A value was not found, go to default case
5581 get_item.EmitBranchable (ec, defaultLabel, false);
5583 Arguments get_value_args = new Arguments (1);
5584 get_value_args.Add (new Argument (value));
5586 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5587 if (get_item == null)
5590 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5591 get_item_object.EmitAssign (ec, get_item, true, false);
5592 ec.Emit (OpCodes.Brfalse, defaultLabel);
5594 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5595 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5597 get_item_int.EmitStatement (ec);
5598 get_item_object.Release (ec);
5601 EmitTableSwitch (ec, string_switch_variable);
5602 string_switch_variable.Release (ec);
5606 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5608 void EmitShortSwitch (EmitContext ec)
5610 MethodSpec equal_method = null;
5611 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5612 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5615 if (equal_method != null) {
5616 value.EmitBranchable (ec, nullLabel, false);
5619 for (int i = 0; i < case_labels.Count; ++i) {
5620 var label = case_labels [i];
5621 if (label == case_default || label == case_null)
5624 var constant = label.Converted;
5626 if (constant == null) {
5627 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5631 if (equal_method != null) {
5635 var call = new CallEmitter ();
5636 call.EmitPredefined (ec, equal_method, new Arguments (0));
5637 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5641 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5642 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5648 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5651 ec.Emit (OpCodes.Br, defaultLabel);
5654 void EmitDispatch (EmitContext ec)
5656 if (IsPatternMatching) {
5657 EmitShortSwitch (ec);
5661 if (value == null) {
5663 // Constant switch, we've already done the work if there is only 1 label
5667 foreach (var sl in case_labels) {
5668 if (sl.IsUnreachable)
5671 if (reachable++ > 0) {
5672 var constant = (Constant) new_expr;
5673 var constant_label = FindLabel (constant) ?? case_default;
5675 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5683 if (string_dictionary != null) {
5684 DoEmitStringSwitch (ec);
5685 } else if (case_labels.Count < 4 || string_labels != null) {
5686 EmitShortSwitch (ec);
5688 EmitTableSwitch (ec, value);
5692 protected override void DoEmit (EmitContext ec)
5695 // Setup the codegen context
5697 Label old_end = ec.LoopEnd;
5698 Switch old_switch = ec.Switch;
5700 ec.LoopEnd = ec.DefineLabel ();
5703 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5704 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5706 if (value != null) {
5709 var switch_expr = new_expr ?? Expr;
5711 unwrap.EmitCheck (ec);
5712 ec.Emit (OpCodes.Brfalse, nullLabel);
5713 value.EmitAssign (ec, switch_expr, false, false);
5714 } else if (switch_expr != value) {
5715 value.EmitAssign (ec, switch_expr, false, false);
5720 // Next statement is compiler generated we don't need extra
5721 // nop when we can use the statement for sequence point
5723 ec.Mark (block.StartLocation);
5724 block.IsCompilerGenerated = true;
5726 new_expr.EmitSideEffect (ec);
5731 // Restore context state.
5732 ec.MarkLabel (ec.LoopEnd);
5735 // Restore the previous context
5737 ec.LoopEnd = old_end;
5738 ec.Switch = old_switch;
5741 protected override void CloneTo (CloneContext clonectx, Statement t)
5743 Switch target = (Switch) t;
5745 target.Expr = Expr.Clone (clonectx);
5746 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5749 public override object Accept (StructuralVisitor visitor)
5751 return visitor.Visit (this);
5754 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5756 if (case_default == null && !(new_expr is Constant))
5759 if (end_reachable_das == null)
5760 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5762 end_reachable_das.Add (fc.DefiniteAssignment);
5765 public override void SetEndReachable ()
5767 end_reachable = true;
5771 // A place where execution can restart in a state machine
5772 public abstract class ResumableStatement : Statement
5775 protected Label resume_point;
5777 public Label PrepareForEmit (EmitContext ec)
5781 resume_point = ec.DefineLabel ();
5783 return resume_point;
5786 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5791 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5796 public abstract class TryFinallyBlock : ExceptionStatement
5798 protected Statement stmt;
5799 Label dispose_try_block;
5800 bool prepared_for_dispose, emitted_dispose;
5801 Method finally_host;
5803 protected TryFinallyBlock (Statement stmt, Location loc)
5811 public Statement Statement {
5819 protected abstract void EmitTryBody (EmitContext ec);
5820 public abstract void EmitFinallyBody (EmitContext ec);
5822 public override Label PrepareForDispose (EmitContext ec, Label end)
5824 if (!prepared_for_dispose) {
5825 prepared_for_dispose = true;
5826 dispose_try_block = ec.DefineLabel ();
5828 return dispose_try_block;
5831 protected sealed override void DoEmit (EmitContext ec)
5833 EmitTryBodyPrepare (ec);
5836 bool beginFinally = EmitBeginFinallyBlock (ec);
5838 Label start_finally = ec.DefineLabel ();
5839 if (resume_points != null && beginFinally) {
5840 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5842 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5843 ec.Emit (OpCodes.Brfalse_S, start_finally);
5844 ec.Emit (OpCodes.Endfinally);
5847 ec.MarkLabel (start_finally);
5849 if (finally_host != null) {
5850 finally_host.Define ();
5851 finally_host.PrepareEmit ();
5852 finally_host.Emit ();
5854 // Now it's safe to add, to close it properly and emit sequence points
5855 finally_host.Parent.AddMember (finally_host);
5857 var ce = new CallEmitter ();
5858 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5859 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5861 EmitFinallyBody (ec);
5865 ec.EndExceptionBlock ();
5868 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5870 if (emitted_dispose)
5873 emitted_dispose = true;
5875 Label end_of_try = ec.DefineLabel ();
5877 // Ensure that the only way we can get into this code is through a dispatcher
5878 if (have_dispatcher)
5879 ec.Emit (OpCodes.Br, end);
5881 ec.BeginExceptionBlock ();
5883 ec.MarkLabel (dispose_try_block);
5885 Label[] labels = null;
5886 for (int i = 0; i < resume_points.Count; ++i) {
5887 ResumableStatement s = resume_points[i];
5888 Label ret = s.PrepareForDispose (ec, end_of_try);
5889 if (ret.Equals (end_of_try) && labels == null)
5891 if (labels == null) {
5892 labels = new Label[resume_points.Count];
5893 for (int j = 0; j < i; ++j)
5894 labels[j] = end_of_try;
5899 if (labels != null) {
5901 for (j = 1; j < labels.Length; ++j)
5902 if (!labels[0].Equals (labels[j]))
5904 bool emit_dispatcher = j < labels.Length;
5906 if (emit_dispatcher) {
5907 ec.Emit (OpCodes.Ldloc, pc);
5908 ec.EmitInt (first_resume_pc);
5909 ec.Emit (OpCodes.Sub);
5910 ec.Emit (OpCodes.Switch, labels);
5913 foreach (ResumableStatement s in resume_points)
5914 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5917 ec.MarkLabel (end_of_try);
5919 ec.BeginFinallyBlock ();
5921 if (finally_host != null) {
5922 var ce = new CallEmitter ();
5923 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5924 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5926 EmitFinallyBody (ec);
5929 ec.EndExceptionBlock ();
5932 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5934 var res = stmt.FlowAnalysis (fc);
5935 parent_try_block = null;
5939 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5941 ec.BeginFinallyBlock ();
5945 public override Reachability MarkReachable (Reachability rc)
5947 base.MarkReachable (rc);
5948 return Statement.MarkReachable (rc);
5951 public override bool Resolve (BlockContext bc)
5955 parent_try_block = bc.CurrentTryBlock;
5956 bc.CurrentTryBlock = this;
5958 if (stmt is TryCatch) {
5959 ok = stmt.Resolve (bc);
5961 using (bc.Set (ResolveContext.Options.TryScope)) {
5962 ok = stmt.Resolve (bc);
5966 bc.CurrentTryBlock = parent_try_block;
5969 // Finally block inside iterator is called from MoveNext and
5970 // Dispose methods that means we need to lift the block into
5971 // newly created host method to emit the body only once. The
5972 // original block then simply calls the newly generated method.
5974 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5975 var b = stmt as Block;
5976 if (b != null && b.Explicit.HasYield) {
5977 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5981 return base.Resolve (bc) && ok;
5986 // Base class for blocks using exception handling
5988 public abstract class ExceptionStatement : ResumableStatement
5990 protected List<ResumableStatement> resume_points;
5991 protected int first_resume_pc;
5992 protected ExceptionStatement parent_try_block;
5993 protected int first_catch_resume_pc = -1;
5995 protected ExceptionStatement (Location loc)
6000 protected virtual void EmitTryBodyPrepare (EmitContext ec)
6002 StateMachineInitializer state_machine = null;
6003 if (resume_points != null) {
6004 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
6006 ec.EmitInt ((int) IteratorStorey.State.Running);
6007 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6011 // The resume points in catch section when this is try-catch-finally
6013 if (IsRewrittenTryCatchFinally ()) {
6014 ec.BeginExceptionBlock ();
6016 if (first_catch_resume_pc >= 0) {
6018 ec.MarkLabel (resume_point);
6020 // For normal control flow, we want to fall-through the Switch
6021 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6022 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6023 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6024 ec.Emit (OpCodes.Sub);
6026 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6027 for (int i = 0; i < labels.Length; ++i)
6028 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6029 ec.Emit (OpCodes.Switch, labels);
6033 ec.BeginExceptionBlock ();
6036 // The resume points for try section
6038 if (resume_points != null && first_catch_resume_pc != 0) {
6039 if (first_catch_resume_pc < 0)
6040 ec.MarkLabel (resume_point);
6042 // For normal control flow, we want to fall-through the Switch
6043 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6044 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6045 ec.EmitInt (first_resume_pc);
6046 ec.Emit (OpCodes.Sub);
6048 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6049 for (int i = 0; i < labels.Length; ++i)
6050 labels[i] = resume_points[i].PrepareForEmit (ec);
6051 ec.Emit (OpCodes.Switch, labels);
6055 bool IsRewrittenTryCatchFinally ()
6057 var tf = this as TryFinally;
6061 var tc = tf.Statement as TryCatch;
6065 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6068 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6070 if (parent_try_block != null) {
6071 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6073 pc = stateMachine.AddResumePoint (this);
6076 if (resume_points == null) {
6077 resume_points = new List<ResumableStatement> ();
6078 first_resume_pc = pc;
6081 if (pc != first_resume_pc + resume_points.Count)
6082 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6084 var tf = this as TryFinally;
6085 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6086 first_catch_resume_pc = resume_points.Count;
6089 resume_points.Add (stmt);
6094 public class Lock : TryFinallyBlock
6097 TemporaryVariableReference expr_copy;
6098 TemporaryVariableReference lock_taken;
6100 public Lock (Expression expr, Statement stmt, Location loc)
6106 public Expression Expr {
6112 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6114 expr.FlowAnalysis (fc);
6115 return base.DoFlowAnalysis (fc);
6118 public override bool Resolve (BlockContext ec)
6120 expr = expr.Resolve (ec);
6124 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6125 ec.Report.Error (185, loc,
6126 "`{0}' is not a reference type as required by the lock statement",
6127 expr.Type.GetSignatureForError ());
6130 if (expr.Type.IsGenericParameter) {
6131 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6134 VariableReference lv = expr as VariableReference;
6137 locked = lv.IsLockedByStatement;
6138 lv.IsLockedByStatement = true;
6145 // Have to keep original lock value around to unlock same location
6146 // in the case of original value has changed or is null
6148 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6149 expr_copy.Resolve (ec);
6152 // Ensure Monitor methods are available
6154 if (ResolvePredefinedMethods (ec) > 1) {
6155 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6156 lock_taken.Resolve (ec);
6159 using (ec.Set (ResolveContext.Options.LockScope)) {
6164 lv.IsLockedByStatement = locked;
6170 protected override void EmitTryBodyPrepare (EmitContext ec)
6172 expr_copy.EmitAssign (ec, expr);
6174 if (lock_taken != null) {
6176 // Initialize ref variable
6178 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6181 // Monitor.Enter (expr_copy)
6183 expr_copy.Emit (ec);
6184 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6187 base.EmitTryBodyPrepare (ec);
6190 protected override void EmitTryBody (EmitContext ec)
6193 // Monitor.Enter (expr_copy, ref lock_taken)
6195 if (lock_taken != null) {
6196 expr_copy.Emit (ec);
6197 lock_taken.LocalInfo.CreateBuilder (ec);
6198 lock_taken.AddressOf (ec, AddressOp.Load);
6199 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6202 Statement.Emit (ec);
6205 public override void EmitFinallyBody (EmitContext ec)
6208 // if (lock_taken) Monitor.Exit (expr_copy)
6210 Label skip = ec.DefineLabel ();
6212 if (lock_taken != null) {
6213 lock_taken.Emit (ec);
6214 ec.Emit (OpCodes.Brfalse_S, skip);
6217 expr_copy.Emit (ec);
6218 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6220 ec.Emit (OpCodes.Call, m);
6222 ec.MarkLabel (skip);
6225 int ResolvePredefinedMethods (ResolveContext rc)
6227 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6228 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6232 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6236 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6240 protected override void CloneTo (CloneContext clonectx, Statement t)
6242 Lock target = (Lock) t;
6244 target.expr = expr.Clone (clonectx);
6245 target.stmt = Statement.Clone (clonectx);
6248 public override object Accept (StructuralVisitor visitor)
6250 return visitor.Visit (this);
6255 public class Unchecked : Statement {
6258 public Unchecked (Block b, Location loc)
6265 public override bool Resolve (BlockContext ec)
6267 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6268 return Block.Resolve (ec);
6271 protected override void DoEmit (EmitContext ec)
6273 using (ec.With (EmitContext.Options.CheckedScope, false))
6277 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6279 return Block.FlowAnalysis (fc);
6282 public override Reachability MarkReachable (Reachability rc)
6284 base.MarkReachable (rc);
6285 return Block.MarkReachable (rc);
6288 protected override void CloneTo (CloneContext clonectx, Statement t)
6290 Unchecked target = (Unchecked) t;
6292 target.Block = clonectx.LookupBlock (Block);
6295 public override object Accept (StructuralVisitor visitor)
6297 return visitor.Visit (this);
6301 public class Checked : Statement {
6304 public Checked (Block b, Location loc)
6307 b.Unchecked = false;
6311 public override bool Resolve (BlockContext ec)
6313 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6314 return Block.Resolve (ec);
6317 protected override void DoEmit (EmitContext ec)
6319 using (ec.With (EmitContext.Options.CheckedScope, true))
6323 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6325 return Block.FlowAnalysis (fc);
6328 public override Reachability MarkReachable (Reachability rc)
6330 base.MarkReachable (rc);
6331 return Block.MarkReachable (rc);
6334 protected override void CloneTo (CloneContext clonectx, Statement t)
6336 Checked target = (Checked) t;
6338 target.Block = clonectx.LookupBlock (Block);
6341 public override object Accept (StructuralVisitor visitor)
6343 return visitor.Visit (this);
6347 public class Unsafe : Statement {
6350 public Unsafe (Block b, Location loc)
6353 Block.Unsafe = true;
6357 public override bool Resolve (BlockContext ec)
6359 if (ec.CurrentIterator != null)
6360 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6362 using (ec.Set (ResolveContext.Options.UnsafeScope))
6363 return Block.Resolve (ec);
6366 protected override void DoEmit (EmitContext ec)
6371 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6373 return Block.FlowAnalysis (fc);
6376 public override Reachability MarkReachable (Reachability rc)
6378 base.MarkReachable (rc);
6379 return Block.MarkReachable (rc);
6382 protected override void CloneTo (CloneContext clonectx, Statement t)
6384 Unsafe target = (Unsafe) t;
6386 target.Block = clonectx.LookupBlock (Block);
6389 public override object Accept (StructuralVisitor visitor)
6391 return visitor.Visit (this);
6398 public class Fixed : Statement
6400 abstract class Emitter : ShimExpression
6402 protected LocalVariable vi;
6404 protected Emitter (Expression expr, LocalVariable li)
6410 public abstract void EmitExit (EmitContext ec);
6412 public override void FlowAnalysis (FlowAnalysisContext fc)
6414 expr.FlowAnalysis (fc);
6418 sealed class ExpressionEmitter : Emitter {
6419 public ExpressionEmitter (Expression converted, LocalVariable li)
6420 : base (converted, li)
6424 protected override Expression DoResolve (ResolveContext rc)
6426 throw new NotImplementedException ();
6429 public override void Emit (EmitContext ec) {
6431 // Store pointer in pinned location
6437 public override void EmitExit (EmitContext ec)
6440 ec.Emit (OpCodes.Conv_U);
6445 class StringEmitter : Emitter
6447 LocalVariable pinned_string;
6449 public StringEmitter (Expression expr, LocalVariable li)
6454 protected override Expression DoResolve (ResolveContext rc)
6456 pinned_string = new LocalVariable (vi.Block, "$pinned",
6457 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6459 pinned_string.Type = rc.BuiltinTypes.String;
6462 eclass = ExprClass.Variable;
6463 type = rc.BuiltinTypes.Int;
6467 public override void Emit (EmitContext ec)
6469 pinned_string.CreateBuilder (ec);
6472 pinned_string.EmitAssign (ec);
6474 // TODO: Should use Binary::Add
6475 pinned_string.Emit (ec);
6476 ec.Emit (OpCodes.Conv_I);
6478 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6482 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6483 //pe.InstanceExpression = pinned_string;
6484 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6486 ec.Emit (OpCodes.Add);
6490 public override void EmitExit (EmitContext ec)
6493 pinned_string.EmitAssign (ec);
6497 public class VariableDeclaration : BlockVariable
6499 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6504 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6506 if (!Variable.Type.IsPointer && li == Variable) {
6507 bc.Report.Error (209, TypeExpression.Location,
6508 "The type of locals declared in a fixed statement must be a pointer type");
6512 var res = initializer.Resolve (bc);
6519 var ac = res.Type as ArrayContainer;
6521 TypeSpec array_type = ac.Element;
6524 // Provided that array_type is unmanaged,
6526 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6529 Expression res_init;
6530 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6533 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6534 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6535 res = expr_variable.CreateReferenceExpression (bc, loc);
6539 // and T* is implicitly convertible to the
6540 // pointer type given in the fixed statement.
6542 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6544 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6545 if (converted == null)
6549 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6551 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6552 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6553 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6554 new NullLiteral (loc),
6557 converted = converted.Resolve (bc);
6559 return new ExpressionEmitter (converted, li);
6565 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6566 return new StringEmitter (res, li).Resolve (bc);
6569 // Case 3: fixed buffer
6570 if (res is FixedBufferPtr) {
6571 return new ExpressionEmitter (res, li);
6574 bool already_fixed = true;
6577 // Case 4: & object.
6579 Unary u = res as Unary;
6581 if (u.Oper == Unary.Operator.AddressOf) {
6582 IVariableReference vr = u.Expr as IVariableReference;
6583 if (vr == null || !vr.IsFixed) {
6584 already_fixed = false;
6587 } else if (initializer is Cast) {
6588 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6592 if (already_fixed) {
6593 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6596 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6597 return new ExpressionEmitter (res, li);
6602 VariableDeclaration decl;
6603 Statement statement;
6606 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6615 public Statement Statement {
6621 public BlockVariable Variables {
6629 public override bool Resolve (BlockContext bc)
6631 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6632 if (!decl.Resolve (bc))
6636 return statement.Resolve (bc);
6639 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6641 decl.FlowAnalysis (fc);
6642 return statement.FlowAnalysis (fc);
6645 protected override void DoEmit (EmitContext ec)
6647 decl.Variable.CreateBuilder (ec);
6648 decl.Initializer.Emit (ec);
6649 if (decl.Declarators != null) {
6650 foreach (var d in decl.Declarators) {
6651 d.Variable.CreateBuilder (ec);
6652 d.Initializer.Emit (ec);
6656 statement.Emit (ec);
6662 // Clear the pinned variable
6664 ((Emitter) decl.Initializer).EmitExit (ec);
6665 if (decl.Declarators != null) {
6666 foreach (var d in decl.Declarators) {
6667 ((Emitter)d.Initializer).EmitExit (ec);
6672 public override Reachability MarkReachable (Reachability rc)
6674 base.MarkReachable (rc);
6676 decl.MarkReachable (rc);
6678 rc = statement.MarkReachable (rc);
6680 // TODO: What if there is local exit?
6681 has_ret = rc.IsUnreachable;
6685 protected override void CloneTo (CloneContext clonectx, Statement t)
6687 Fixed target = (Fixed) t;
6689 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6690 target.statement = statement.Clone (clonectx);
6693 public override object Accept (StructuralVisitor visitor)
6695 return visitor.Visit (this);
6699 public class Catch : Statement
6701 class CatchVariableStore : Statement
6703 readonly Catch ctch;
6705 public CatchVariableStore (Catch ctch)
6710 protected override void CloneTo (CloneContext clonectx, Statement target)
6714 protected override void DoEmit (EmitContext ec)
6716 // Emits catch variable debug information inside correct block
6717 ctch.EmitCatchVariableStore (ec);
6720 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6726 class FilterStatement : Statement
6728 readonly Catch ctch;
6730 public FilterStatement (Catch ctch)
6735 protected override void CloneTo (CloneContext clonectx, Statement target)
6739 protected override void DoEmit (EmitContext ec)
6741 if (ctch.li != null) {
6742 if (ctch.hoisted_temp != null)
6743 ctch.hoisted_temp.Emit (ec);
6747 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6748 ec.Emit (OpCodes.Box, ctch.type);
6751 var expr_start = ec.DefineLabel ();
6752 var end = ec.DefineLabel ();
6754 ec.Emit (OpCodes.Brtrue_S, expr_start);
6756 ec.Emit (OpCodes.Br, end);
6757 ec.MarkLabel (expr_start);
6759 ctch.Filter.Emit (ec);
6762 ec.Emit (OpCodes.Endfilter);
6763 ec.BeginFilterHandler ();
6764 ec.Emit (OpCodes.Pop);
6767 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6769 ctch.Filter.FlowAnalysis (fc);
6773 public override bool Resolve (BlockContext bc)
6775 ctch.Filter = ctch.Filter.Resolve (bc);
6777 if (ctch.Filter != null) {
6778 if (ctch.Filter.ContainsEmitWithAwait ()) {
6779 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6782 var c = ctch.Filter as Constant;
6783 if (c != null && !c.IsDefaultValue) {
6784 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6792 ExplicitBlock block;
6794 FullNamedExpression type_expr;
6795 CompilerAssign assign;
6797 LocalTemporary hoisted_temp;
6799 public Catch (ExplicitBlock block, Location loc)
6807 public ExplicitBlock Block {
6813 public TypeSpec CatchType {
6819 public Expression Filter {
6823 public bool IsGeneral {
6825 return type_expr == null;
6829 public FullNamedExpression TypeExpression {
6838 public LocalVariable Variable {
6849 protected override void DoEmit (EmitContext ec)
6851 if (Filter != null) {
6852 ec.BeginExceptionFilterBlock ();
6853 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6855 if (Block.HasAwait) {
6856 Block.EmitScopeInitialization (ec);
6865 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6867 ec.BeginCatchBlock (CatchType);
6870 ec.Emit (OpCodes.Pop);
6872 if (Block.HasAwait) {
6874 EmitCatchVariableStore (ec);
6880 void EmitCatchVariableStore (EmitContext ec)
6882 li.CreateBuilder (ec);
6885 // For hoisted catch variable we have to use a temporary local variable
6886 // for captured variable initialization during storey setup because variable
6887 // needs to be on the stack after storey instance for stfld operation
6889 if (li.HoistedVariant != null) {
6890 hoisted_temp = new LocalTemporary (li.Type);
6891 hoisted_temp.Store (ec);
6893 // switch to assignment from temporary variable and not from top of the stack
6894 assign.UpdateSource (hoisted_temp);
6898 public override bool Resolve (BlockContext bc)
6900 using (bc.Set (ResolveContext.Options.CatchScope)) {
6901 if (type_expr == null) {
6902 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6903 if (!block.HasAwait || Filter != null)
6904 block.AddScopeStatement (new CatchVariableStore (this));
6906 Expression source = new EmptyExpression (li.Type);
6907 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6908 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6911 type = type_expr.ResolveAsType (bc);
6916 CreateExceptionVariable (type);
6918 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6919 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6920 } else if (li != null) {
6922 li.PrepareAssignmentAnalysis (bc);
6924 // source variable is at the top of the stack
6925 Expression source = new EmptyExpression (li.Type);
6926 if (li.Type.IsGenericParameter)
6927 source = new UnboxCast (source, li.Type);
6929 if (!block.HasAwait || Filter != null)
6930 block.AddScopeStatement (new CatchVariableStore (this));
6933 // Uses Location.Null to hide from symbol file
6935 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6936 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6940 if (Filter != null) {
6941 Block.AddScopeStatement (new FilterStatement (this));
6944 Block.SetCatchBlock ();
6945 return Block.Resolve (bc);
6949 bool CreateExceptionVariable (TypeSpec type)
6951 if (!Block.HasAwait)
6954 // TODO: Scan the block for rethrow expression
6955 //if (!Block.HasRethrow)
6958 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6962 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6964 if (li != null && !li.IsCompilerGenerated) {
6965 fc.SetVariableAssigned (li.VariableInfo, true);
6968 return block.FlowAnalysis (fc);
6971 public override Reachability MarkReachable (Reachability rc)
6973 base.MarkReachable (rc);
6975 var c = Filter as Constant;
6976 if (c != null && c.IsDefaultValue)
6977 return Reachability.CreateUnreachable ();
6979 return block.MarkReachable (rc);
6982 protected override void CloneTo (CloneContext clonectx, Statement t)
6984 Catch target = (Catch) t;
6986 if (type_expr != null)
6987 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6990 target.Filter = Filter.Clone (clonectx);
6992 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6996 public class TryFinally : TryFinallyBlock
6999 List<DefiniteAssignmentBitSet> try_exit_dat;
7000 List<Label> redirected_jumps;
7001 Label? start_fin_label;
7003 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
7009 public ExplicitBlock FinallyBlock {
7015 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7017 if (try_exit_dat == null)
7018 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7020 try_exit_dat.Add (vector);
7023 public override bool Resolve (BlockContext bc)
7025 bool ok = base.Resolve (bc);
7027 fini.SetFinallyBlock ();
7028 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7029 ok &= fini.Resolve (bc);
7035 protected override void EmitTryBody (EmitContext ec)
7037 if (fini.HasAwait) {
7038 if (ec.TryFinallyUnwind == null)
7039 ec.TryFinallyUnwind = new List<TryFinally> ();
7041 ec.TryFinallyUnwind.Add (this);
7044 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7045 ec.EndExceptionBlock ();
7047 ec.TryFinallyUnwind.Remove (this);
7049 if (start_fin_label != null)
7050 ec.MarkLabel (start_fin_label.Value);
7058 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7063 return base.EmitBeginFinallyBlock (ec);
7066 public override void EmitFinallyBody (EmitContext ec)
7068 if (!fini.HasAwait) {
7074 // Emits catch block like
7076 // catch (object temp) {
7077 // this.exception_field = temp;
7080 var type = ec.BuiltinTypes.Object;
7081 ec.BeginCatchBlock (type);
7083 var temp = ec.GetTemporaryLocal (type);
7084 ec.Emit (OpCodes.Stloc, temp);
7086 var exception_field = ec.GetTemporaryField (type);
7087 exception_field.AutomaticallyReuse = false;
7089 ec.Emit (OpCodes.Ldloc, temp);
7090 exception_field.EmitAssignFromStack (ec);
7092 ec.EndExceptionBlock ();
7094 ec.FreeTemporaryLocal (temp, type);
7099 // Emits exception rethrow
7101 // if (this.exception_field != null)
7102 // throw this.exception_field;
7104 exception_field.Emit (ec);
7105 var skip_throw = ec.DefineLabel ();
7106 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7107 exception_field.Emit (ec);
7108 ec.Emit (OpCodes.Throw);
7109 ec.MarkLabel (skip_throw);
7111 exception_field.PrepareCleanup (ec);
7113 EmitUnwindFinallyTable (ec);
7116 bool IsParentBlock (Block block)
7118 for (Block b = fini; b != null; b = b.Parent) {
7126 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7129 if (labelBlock != null) {
7130 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7131 var fin = ec.TryFinallyUnwind [idx - 1];
7132 if (!fin.IsParentBlock (labelBlock))
7139 bool set_return_state = true;
7141 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7142 var fin = ec.TryFinallyUnwind [idx];
7143 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7146 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7147 set_return_state = false;
7149 if (fin.start_fin_label == null) {
7150 fin.start_fin_label = ec.DefineLabel ();
7153 label = fin.start_fin_label.Value;
7159 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7161 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7164 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7166 if (redirected_jumps == null) {
7167 redirected_jumps = new List<Label> ();
7169 // Add fallthrough label
7170 redirected_jumps.Add (ec.DefineLabel ());
7173 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7176 int index = redirected_jumps.IndexOf (label);
7178 redirected_jumps.Add (label);
7179 index = redirected_jumps.Count - 1;
7183 // Indicates we have captured exit jump
7185 if (setReturnState) {
7186 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7187 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7192 // Emits state table of jumps outside of try block and reload of return
7193 // value when try block returns value
7195 void EmitUnwindFinallyTable (EmitContext ec)
7197 if (redirected_jumps == null)
7200 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7201 initializer.HoistedReturnState.EmitLoad (ec);
7202 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7204 // Mark fallthrough label
7205 ec.MarkLabel (redirected_jumps [0]);
7208 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7210 var da = fc.BranchDefiniteAssignment ();
7212 var tf = fc.TryFinally;
7213 fc.TryFinally = this;
7215 var res_stmt = Statement.FlowAnalysis (fc);
7219 var try_da = fc.DefiniteAssignment;
7220 fc.DefiniteAssignment = da;
7222 var res_fin = fini.FlowAnalysis (fc);
7224 if (try_exit_dat != null) {
7226 // try block has global exit but we need to run definite assignment check
7227 // for parameter block out parameter after finally block because it's always
7228 // executed before exit
7230 foreach (var try_da_part in try_exit_dat)
7231 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7233 try_exit_dat = null;
7236 fc.DefiniteAssignment |= try_da;
7237 return res_stmt | res_fin;
7240 public override Reachability MarkReachable (Reachability rc)
7243 // Mark finally block first for any exit statement in try block
7244 // to know whether the code which follows finally is reachable
7246 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7249 protected override void CloneTo (CloneContext clonectx, Statement t)
7251 TryFinally target = (TryFinally) t;
7253 target.stmt = stmt.Clone (clonectx);
7255 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7258 public override object Accept (StructuralVisitor visitor)
7260 return visitor.Visit (this);
7264 public class TryCatch : ExceptionStatement
7267 List<Catch> clauses;
7268 readonly bool inside_try_finally;
7269 List<Catch> catch_sm;
7271 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7275 this.clauses = catch_clauses;
7276 this.inside_try_finally = inside_try_finally;
7279 public List<Catch> Clauses {
7285 public bool HasClauseWithAwait {
7287 return catch_sm != null;
7291 public bool IsTryCatchFinally {
7293 return inside_try_finally;
7297 public override bool Resolve (BlockContext bc)
7301 using (bc.Set (ResolveContext.Options.TryScope)) {
7303 parent_try_block = bc.CurrentTryBlock;
7305 if (IsTryCatchFinally) {
7306 ok = Block.Resolve (bc);
7308 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7309 bc.CurrentTryBlock = this;
7310 ok = Block.Resolve (bc);
7311 bc.CurrentTryBlock = parent_try_block;
7316 var prev_catch = bc.CurrentTryCatch;
7317 bc.CurrentTryCatch = this;
7319 for (int i = 0; i < clauses.Count; ++i) {
7322 ok &= c.Resolve (bc);
7324 if (c.Block.HasAwait) {
7325 if (catch_sm == null)
7326 catch_sm = new List<Catch> ();
7331 if (c.Filter != null)
7334 TypeSpec resolved_type = c.CatchType;
7335 if (resolved_type == null)
7338 for (int ii = 0; ii < clauses.Count; ++ii) {
7342 if (clauses[ii].Filter != null)
7345 if (clauses[ii].IsGeneral) {
7346 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7349 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7352 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7355 bc.Report.Warning (1058, 1, c.loc,
7356 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7364 var ct = clauses[ii].CatchType;
7368 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7369 bc.Report.Error (160, c.loc,
7370 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7371 ct.GetSignatureForError ());
7377 bc.CurrentTryCatch = prev_catch;
7379 return base.Resolve (bc) && ok;
7382 protected sealed override void DoEmit (EmitContext ec)
7384 if (!inside_try_finally)
7385 EmitTryBodyPrepare (ec);
7389 LocalBuilder state_variable = null;
7390 foreach (Catch c in clauses) {
7393 if (catch_sm != null) {
7394 if (state_variable == null) {
7396 // Cannot reuse temp variable because non-catch path assumes the value is 0
7397 // which may not be true for reused local variable
7399 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7402 var index = catch_sm.IndexOf (c);
7406 ec.EmitInt (index + 1);
7407 ec.Emit (OpCodes.Stloc, state_variable);
7411 if (state_variable == null) {
7412 if (!inside_try_finally)
7413 ec.EndExceptionBlock ();
7415 ec.EndExceptionBlock ();
7417 ec.Emit (OpCodes.Ldloc, state_variable);
7419 var labels = new Label [catch_sm.Count + 1];
7420 for (int i = 0; i < labels.Length; ++i) {
7421 labels [i] = ec.DefineLabel ();
7424 var end = ec.DefineLabel ();
7425 ec.Emit (OpCodes.Switch, labels);
7427 // 0 value is default label
7428 ec.MarkLabel (labels [0]);
7429 ec.Emit (OpCodes.Br, end);
7431 var atv = ec.AsyncThrowVariable;
7433 for (int i = 0; i < catch_sm.Count; ++i) {
7434 if (c != null && c.Block.HasReachableClosingBrace)
7435 ec.Emit (OpCodes.Br, end);
7437 ec.MarkLabel (labels [i + 1]);
7439 ec.AsyncThrowVariable = c.Variable;
7442 ec.AsyncThrowVariable = atv;
7448 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7450 var start_fc = fc.BranchDefiniteAssignment ();
7451 var res = Block.FlowAnalysis (fc);
7453 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7455 foreach (var c in clauses) {
7456 fc.BranchDefiniteAssignment (start_fc);
7457 if (!c.FlowAnalysis (fc)) {
7459 try_fc = fc.DefiniteAssignment;
7461 try_fc &= fc.DefiniteAssignment;
7467 fc.DefiniteAssignment = try_fc ?? start_fc;
7468 parent_try_block = null;
7472 public override Reachability MarkReachable (Reachability rc)
7474 if (rc.IsUnreachable)
7477 base.MarkReachable (rc);
7479 var tc_rc = Block.MarkReachable (rc);
7481 foreach (var c in clauses)
7482 tc_rc &= c.MarkReachable (rc);
7487 protected override void CloneTo (CloneContext clonectx, Statement t)
7489 TryCatch target = (TryCatch) t;
7491 target.Block = clonectx.LookupBlock (Block);
7492 if (clauses != null){
7493 target.clauses = new List<Catch> ();
7494 foreach (Catch c in clauses)
7495 target.clauses.Add ((Catch) c.Clone (clonectx));
7499 public override object Accept (StructuralVisitor visitor)
7501 return visitor.Visit (this);
7505 public class Using : TryFinallyBlock
7507 public class VariableDeclaration : BlockVariable
7509 Statement dispose_call;
7511 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7516 public VariableDeclaration (LocalVariable li, Location loc)
7523 public VariableDeclaration (Expression expr)
7526 loc = expr.Location;
7532 public bool IsNested { get; private set; }
7536 public void EmitDispose (EmitContext ec)
7538 dispose_call.Emit (ec);
7541 public override bool Resolve (BlockContext bc)
7546 return base.Resolve (bc, false);
7549 public Expression ResolveExpression (BlockContext bc)
7551 var e = Initializer.Resolve (bc);
7555 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7556 Initializer = ResolveInitializer (bc, Variable, e);
7560 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7562 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7563 initializer = initializer.Resolve (bc);
7564 if (initializer == null)
7567 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7568 Arguments args = new Arguments (1);
7569 args.Add (new Argument (initializer));
7570 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7571 if (initializer == null)
7574 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7575 dispose_call = CreateDisposeCall (bc, var);
7576 dispose_call.Resolve (bc);
7578 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7581 if (li == Variable) {
7582 CheckIDiposableConversion (bc, li, initializer);
7583 dispose_call = CreateDisposeCall (bc, li);
7584 dispose_call.Resolve (bc);
7587 return base.ResolveInitializer (bc, li, initializer);
7590 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7594 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7595 if (type.IsNullableType) {
7596 // it's handled in CreateDisposeCall
7600 if (type != InternalType.ErrorType) {
7601 bc.Report.SymbolRelatedToPreviousError (type);
7602 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7603 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7604 type.GetSignatureForError ());
7611 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7613 var target = bc.BuiltinTypes.IDisposable;
7614 var tp = type as TypeParameterSpec;
7616 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7618 return type.ImplementsInterface (target, false);
7621 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7623 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7625 var loc = lv.Location;
7627 var idt = bc.BuiltinTypes.IDisposable;
7628 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7630 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7631 dispose_mg.InstanceExpression = type.IsNullableType ?
7632 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7636 // Hide it from symbol file via null location
7638 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7640 // Add conditional call when disposing possible null variable
7641 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7642 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7647 public void ResolveDeclaratorInitializer (BlockContext bc)
7649 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7652 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7654 for (int i = declarators.Count - 1; i >= 0; --i) {
7655 var d = declarators [i];
7656 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7657 vd.Initializer = d.Initializer;
7659 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7660 vd.dispose_call.Resolve (bc);
7662 stmt = new Using (vd, stmt, d.Variable.Location);
7669 public override object Accept (StructuralVisitor visitor)
7671 return visitor.Visit (this);
7675 VariableDeclaration decl;
7677 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7683 public Using (Expression expr, Statement stmt, Location loc)
7686 this.decl = new VariableDeclaration (expr);
7691 public Expression Expr {
7693 return decl.Variable == null ? decl.Initializer : null;
7697 public BlockVariable Variables {
7705 public override void Emit (EmitContext ec)
7708 // Don't emit sequence point it will be set on variable declaration
7713 protected override void EmitTryBodyPrepare (EmitContext ec)
7716 base.EmitTryBodyPrepare (ec);
7719 protected override void EmitTryBody (EmitContext ec)
7724 public override void EmitFinallyBody (EmitContext ec)
7726 decl.EmitDispose (ec);
7729 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7731 decl.FlowAnalysis (fc);
7732 return stmt.FlowAnalysis (fc);
7735 public override Reachability MarkReachable (Reachability rc)
7737 decl.MarkReachable (rc);
7738 return base.MarkReachable (rc);
7741 public override bool Resolve (BlockContext ec)
7743 VariableReference vr;
7744 bool vr_locked = false;
7746 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7747 if (decl.Variable == null) {
7748 vr = decl.ResolveExpression (ec) as VariableReference;
7750 vr_locked = vr.IsLockedByStatement;
7751 vr.IsLockedByStatement = true;
7754 if (decl.IsNested) {
7755 decl.ResolveDeclaratorInitializer (ec);
7757 if (!decl.Resolve (ec))
7760 if (decl.Declarators != null) {
7761 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7769 var ok = base.Resolve (ec);
7772 vr.IsLockedByStatement = vr_locked;
7777 protected override void CloneTo (CloneContext clonectx, Statement t)
7779 Using target = (Using) t;
7781 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7782 target.stmt = stmt.Clone (clonectx);
7785 public override object Accept (StructuralVisitor visitor)
7787 return visitor.Visit (this);
7792 /// Implementation of the foreach C# statement
7794 public class Foreach : LoopStatement
7796 abstract class IteratorStatement : Statement
7798 protected readonly Foreach for_each;
7800 protected IteratorStatement (Foreach @foreach)
7802 this.for_each = @foreach;
7803 this.loc = @foreach.expr.Location;
7806 protected override void CloneTo (CloneContext clonectx, Statement target)
7808 throw new NotImplementedException ();
7811 public override void Emit (EmitContext ec)
7813 if (ec.EmitAccurateDebugInfo) {
7814 ec.Emit (OpCodes.Nop);
7820 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7822 throw new NotImplementedException ();
7826 sealed class ArrayForeach : IteratorStatement
7828 TemporaryVariableReference[] lengths;
7829 Expression [] length_exprs;
7830 StatementExpression[] counter;
7831 TemporaryVariableReference[] variables;
7833 TemporaryVariableReference copy;
7835 public ArrayForeach (Foreach @foreach, int rank)
7838 counter = new StatementExpression[rank];
7839 variables = new TemporaryVariableReference[rank];
7840 length_exprs = new Expression [rank];
7843 // Only use temporary length variables when dealing with
7844 // multi-dimensional arrays
7847 lengths = new TemporaryVariableReference [rank];
7850 public override bool Resolve (BlockContext ec)
7852 Block variables_block = for_each.variable.Block;
7853 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7856 int rank = length_exprs.Length;
7857 Arguments list = new Arguments (rank);
7858 for (int i = 0; i < rank; i++) {
7859 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7861 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7862 counter[i].Resolve (ec);
7865 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7867 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7868 lengths[i].Resolve (ec);
7870 Arguments args = new Arguments (1);
7871 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7872 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7875 list.Add (new Argument (v));
7878 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7883 if (for_each.type is VarExpr) {
7884 // Infer implicitly typed local variable from foreach array type
7885 var_type = access.Type;
7887 var_type = for_each.type.ResolveAsType (ec);
7889 if (var_type == null)
7892 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7897 for_each.variable.Type = var_type;
7899 var prev_block = ec.CurrentBlock;
7900 ec.CurrentBlock = variables_block;
7901 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7902 ec.CurrentBlock = prev_block;
7904 if (variable_ref == null)
7907 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7909 return for_each.body.Resolve (ec);
7912 protected override void DoEmit (EmitContext ec)
7914 copy.EmitAssign (ec, for_each.expr);
7916 int rank = length_exprs.Length;
7917 Label[] test = new Label [rank];
7918 Label[] loop = new Label [rank];
7920 for (int i = 0; i < rank; i++) {
7921 test [i] = ec.DefineLabel ();
7922 loop [i] = ec.DefineLabel ();
7924 if (lengths != null)
7925 lengths [i].EmitAssign (ec, length_exprs [i]);
7928 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7929 for (int i = 0; i < rank; i++) {
7930 variables [i].EmitAssign (ec, zero);
7932 ec.Emit (OpCodes.Br, test [i]);
7933 ec.MarkLabel (loop [i]);
7936 for_each.body.Emit (ec);
7938 ec.MarkLabel (ec.LoopBegin);
7939 ec.Mark (for_each.expr.Location);
7941 for (int i = rank - 1; i >= 0; i--){
7942 counter [i].Emit (ec);
7944 ec.MarkLabel (test [i]);
7945 variables [i].Emit (ec);
7947 if (lengths != null)
7948 lengths [i].Emit (ec);
7950 length_exprs [i].Emit (ec);
7952 ec.Emit (OpCodes.Blt, loop [i]);
7955 ec.MarkLabel (ec.LoopEnd);
7959 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7961 class RuntimeDispose : Using.VariableDeclaration
7963 public RuntimeDispose (LocalVariable lv, Location loc)
7969 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7971 // Defered to runtime check
7974 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7976 var idt = bc.BuiltinTypes.IDisposable;
7979 // Fabricates code like
7981 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7984 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7986 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7987 dispose_variable.CreateReferenceExpression (bc, loc),
7988 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7989 loc), new NullLiteral (loc));
7991 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7993 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7994 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7996 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7997 return new If (idisaposable_test, dispose, loc);
8001 LocalVariable variable;
8003 Statement statement;
8004 ExpressionStatement init;
8005 TemporaryVariableReference enumerator_variable;
8006 bool ambiguous_getenumerator_name;
8008 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8011 this.variable = var;
8015 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8017 rc.Report.SymbolRelatedToPreviousError (enumerator);
8018 rc.Report.Error (202, loc,
8019 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8020 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8023 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8026 // Option 1: Try to match by name GetEnumerator first
8028 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8029 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8031 var mg = mexpr as MethodGroupExpr;
8033 mg.InstanceExpression = expr;
8034 Arguments args = new Arguments (0);
8035 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8037 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8038 if (ambiguous_getenumerator_name)
8041 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8047 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8050 PredefinedMember<MethodSpec> iface_candidate = null;
8051 var ptypes = rc.Module.PredefinedTypes;
8052 var gen_ienumerable = ptypes.IEnumerableGeneric;
8053 if (!gen_ienumerable.Define ())
8054 gen_ienumerable = null;
8056 var ifaces = t.Interfaces;
8057 if (ifaces != null) {
8058 foreach (var iface in ifaces) {
8059 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8060 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8061 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8062 rc.Report.Error (1640, loc,
8063 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8064 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8069 // TODO: Cache this somehow
8070 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8071 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8076 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8077 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8082 if (iface_candidate == null) {
8083 if (expr.Type != InternalType.ErrorType) {
8084 rc.Report.Error (1579, loc,
8085 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8086 expr.Type.GetSignatureForError (), "GetEnumerator");
8092 var method = iface_candidate.Resolve (loc);
8096 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8097 mg.InstanceExpression = expr;
8101 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8103 var ms = MemberCache.FindMember (enumerator.ReturnType,
8104 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8105 BindingRestriction.InstanceOnly) as MethodSpec;
8107 if (ms == null || !ms.IsPublic) {
8108 Error_WrongEnumerator (rc, enumerator);
8112 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8115 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8117 var ps = MemberCache.FindMember (enumerator.ReturnType,
8118 MemberFilter.Property ("Current", null),
8119 BindingRestriction.InstanceOnly) as PropertySpec;
8121 if (ps == null || !ps.IsPublic) {
8122 Error_WrongEnumerator (rc, enumerator);
8129 public override bool Resolve (BlockContext ec)
8131 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8134 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8135 } else if (expr.Type.IsNullableType) {
8136 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8139 var get_enumerator_mg = ResolveGetEnumerator (ec);
8140 if (get_enumerator_mg == null) {
8144 var get_enumerator = get_enumerator_mg.BestCandidate;
8145 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8146 enumerator_variable.Resolve (ec);
8148 // Prepare bool MoveNext ()
8149 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8150 if (move_next_mg == null) {
8154 move_next_mg.InstanceExpression = enumerator_variable;
8156 // Prepare ~T~ Current { get; }
8157 var current_prop = ResolveCurrent (ec, get_enumerator);
8158 if (current_prop == null) {
8162 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8163 if (current_pe == null)
8166 VarExpr ve = for_each.type as VarExpr;
8170 // Source type is dynamic, set element type to dynamic too
8171 variable.Type = ec.BuiltinTypes.Dynamic;
8173 // Infer implicitly typed local variable from foreach enumerable type
8174 variable.Type = current_pe.Type;
8178 // Explicit cast of dynamic collection elements has to be done at runtime
8179 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8182 variable.Type = for_each.type.ResolveAsType (ec);
8184 if (variable.Type == null)
8187 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8188 if (current_pe == null)
8192 var prev_block = ec.CurrentBlock;
8193 ec.CurrentBlock = for_each.variable.Block;
8194 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8195 ec.CurrentBlock = prev_block;
8196 if (variable_ref == null)
8199 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8201 var init = new Invocation.Predefined (get_enumerator_mg, null);
8203 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8204 for_each.body, Location.Null);
8206 var enum_type = enumerator_variable.Type;
8209 // Add Dispose method call when enumerator can be IDisposable
8211 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8212 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8214 // Runtime Dispose check
8216 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8217 vd.Initializer = init;
8218 statement = new Using (vd, statement, Location.Null);
8221 // No Dispose call needed
8223 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8224 this.init.Resolve (ec);
8228 // Static Dispose check
8230 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8231 vd.Initializer = init;
8232 statement = new Using (vd, statement, Location.Null);
8235 return statement.Resolve (ec);
8238 protected override void DoEmit (EmitContext ec)
8240 enumerator_variable.LocalInfo.CreateBuilder (ec);
8243 init.EmitStatement (ec);
8245 statement.Emit (ec);
8248 #region IErrorHandler Members
8250 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8252 ec.Report.SymbolRelatedToPreviousError (best);
8253 ec.Report.Warning (278, 2, expr.Location,
8254 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8255 expr.Type.GetSignatureForError (), "enumerable",
8256 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8258 ambiguous_getenumerator_name = true;
8262 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8267 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8272 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8281 LocalVariable variable;
8285 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8289 this.variable = var;
8295 public Expression Expr {
8296 get { return expr; }
8299 public Expression TypeExpression {
8300 get { return type; }
8303 public LocalVariable Variable {
8304 get { return variable; }
8307 public override Reachability MarkReachable (Reachability rc)
8309 base.MarkReachable (rc);
8311 body.MarkReachable (rc);
8316 public override bool Resolve (BlockContext ec)
8318 expr = expr.Resolve (ec);
8323 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8327 body.AddStatement (Statement);
8329 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8330 Statement = new ArrayForeach (this, 1);
8331 } else if (expr.Type is ArrayContainer) {
8332 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8334 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8335 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8336 expr.ExprClassName);
8340 Statement = new CollectionForeach (this, variable, expr);
8343 return base.Resolve (ec);
8346 protected override void DoEmit (EmitContext ec)
8348 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8349 ec.LoopBegin = ec.DefineLabel ();
8350 ec.LoopEnd = ec.DefineLabel ();
8352 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8353 body.Explicit.DisableDebugScopeIndex ();
8355 variable.CreateBuilder (ec);
8357 Statement.Emit (ec);
8361 ec.LoopBegin = old_begin;
8362 ec.LoopEnd = old_end;
8365 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8367 expr.FlowAnalysis (fc);
8369 var da = fc.BranchDefiniteAssignment ();
8370 body.FlowAnalysis (fc);
8371 fc.DefiniteAssignment = da;
8375 protected override void CloneTo (CloneContext clonectx, Statement t)
8377 Foreach target = (Foreach) t;
8379 target.type = type.Clone (clonectx);
8380 target.expr = expr.Clone (clonectx);
8381 target.body = (Block) body.Clone (clonectx);
8382 target.Statement = Statement.Clone (clonectx);
8385 public override object Accept (StructuralVisitor visitor)
8387 return visitor.Visit (this);
8391 class SentinelStatement: Statement
8393 protected override void CloneTo (CloneContext clonectx, Statement target)
8397 protected override void DoEmit (EmitContext ec)
8399 var l = ec.DefineLabel ();
8401 ec.Emit (OpCodes.Br_S, l);
8404 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8406 throw new NotImplementedException ();