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 if (Parent != null) {
3249 // TODO: It's needed only when scope has variable (normal or lifted)
3250 ec.BeginScope (GetDebugSymbolScopeIndex ());
3253 EmitScopeInitialization (ec);
3255 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3256 ec.Emit (OpCodes.Nop);
3264 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3265 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3266 ec.Emit (OpCodes.Nop);
3270 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3272 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3273 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3274 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3278 // Creates anonymous method storey
3280 storey.CreateContainer ();
3281 storey.DefineContainer ();
3282 storey.ExpandBaseInterfaces ();
3284 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3287 // Only first storey in path will hold this reference. All children blocks will
3288 // reference it indirectly using $ref field
3290 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3291 if (b.Parent != null) {
3292 var s = b.Parent.Explicit.AnonymousMethodStorey;
3294 storey.HoistedThis = s.HoistedThis;
3299 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3300 if (storey.HoistedThis == null)
3301 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3303 if (storey.HoistedThis != null)
3309 // We are the first storey on path and 'this' has to be hoisted
3311 if (storey.HoistedThis == null || !(storey.Parent is HoistedStoreyClass)) {
3312 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3314 // ThisReferencesFromChildrenBlock holds all reference even if they
3315 // are not on this path. It saves some memory otherwise it'd have to
3316 // be in every explicit block. We run this check to see if the reference
3317 // is valid for this storey
3319 Block block_on_path = ref_block;
3320 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3322 if (block_on_path == null)
3325 if (storey.HoistedThis == null) {
3326 storey.AddCapturedThisField (ec, null);
3329 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3331 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3333 if (b_storey != null) {
3335 // Don't add storey cross reference for `this' when the storey ends up not
3336 // beeing attached to any parent
3338 if (b.ParametersBlock.StateMachine == null) {
3339 AnonymousMethodStorey s = null;
3340 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3341 s = ab.Explicit.AnonymousMethodStorey;
3346 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3348 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3349 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3356 // Stop propagation inside same top block
3358 if (b.ParametersBlock == ParametersBlock.Original) {
3359 b_storey.AddParentStoreyReference (ec, storey);
3360 // b_storey.HoistedThis = storey.HoistedThis;
3364 b = pb = b.ParametersBlock;
3366 pb = b as ParametersBlock;
3369 if (pb != null && pb.StateMachine != null) {
3370 if (pb.StateMachine == storey)
3374 // If we are state machine with no parent. We can hook into parent without additional
3375 // reference and capture this directly
3377 ExplicitBlock parent_storey_block = pb;
3378 while (parent_storey_block.Parent != null) {
3379 parent_storey_block = parent_storey_block.Parent.Explicit;
3380 if (parent_storey_block.AnonymousMethodStorey != null) {
3385 if (parent_storey_block.AnonymousMethodStorey == null) {
3386 if (pb.StateMachine.HoistedThis == null) {
3387 pb.StateMachine.AddCapturedThisField (ec, null);
3388 b.HasCapturedThis = true;
3394 var parent_this_block = pb;
3395 while (parent_this_block.Parent != null) {
3396 parent_this_block = parent_this_block.Parent.ParametersBlock;
3397 if (parent_this_block.StateMachine != null && parent_this_block.StateMachine.HoistedThis != null) {
3403 // Add reference to closest storey which holds captured this
3405 pb.StateMachine.AddParentStoreyReference (ec, parent_this_block.StateMachine ?? storey);
3409 // Add parent storey reference only when this is not captured directly
3411 if (b_storey != null) {
3412 b_storey.AddParentStoreyReference (ec, storey);
3413 b_storey.HoistedThis = storey.HoistedThis;
3420 var ref_blocks = storey.ReferencesFromChildrenBlock;
3421 if (ref_blocks != null) {
3422 foreach (ExplicitBlock ref_block in ref_blocks) {
3423 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3424 if (b.AnonymousMethodStorey != null) {
3425 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3428 // Stop propagation inside same top block
3430 if (b.ParametersBlock == ParametersBlock.Original)
3433 b = b.ParametersBlock;
3436 var pb = b as ParametersBlock;
3437 if (pb != null && pb.StateMachine != null) {
3438 if (pb.StateMachine == storey)
3441 pb.StateMachine.AddParentStoreyReference (ec, storey);
3444 b.HasCapturedVariable = true;
3450 storey.PrepareEmit ();
3451 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3454 public int GetDebugSymbolScopeIndex ()
3456 if (debug_scope_index == 0)
3457 debug_scope_index = ++ParametersBlock.debug_scope_index;
3459 return debug_scope_index;
3462 public void RegisterAsyncAwait ()
3465 while ((block.flags & Flags.AwaitBlock) == 0) {
3466 block.flags |= Flags.AwaitBlock;
3468 if (block is ParametersBlock)
3471 block = block.Parent.Explicit;
3475 public void RegisterIteratorYield ()
3477 ParametersBlock.TopBlock.IsIterator = true;
3480 while ((block.flags & Flags.YieldBlock) == 0) {
3481 block.flags |= Flags.YieldBlock;
3483 if (block.Parent == null)
3486 block = block.Parent.Explicit;
3490 public void SetCatchBlock ()
3492 flags |= Flags.CatchBlock;
3495 public void SetFinallyBlock ()
3497 flags |= Flags.FinallyBlock;
3500 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3502 tryBlock.statements = statements;
3503 statements = new List<Statement> (1);
3504 statements.Add (tf);
3509 // ParametersBlock was introduced to support anonymous methods
3510 // and lambda expressions
3512 public class ParametersBlock : ExplicitBlock
3514 public class ParameterInfo : INamedBlockVariable
3516 readonly ParametersBlock block;
3518 public VariableInfo VariableInfo;
3521 public ParameterInfo (ParametersBlock block, int index)
3529 public ParametersBlock Block {
3535 Block INamedBlockVariable.Block {
3541 public bool IsDeclared {
3547 public bool IsParameter {
3553 public bool IsLocked {
3562 public Location Location {
3564 return Parameter.Location;
3568 public Parameter Parameter {
3570 return block.Parameters [index];
3574 public TypeSpec ParameterType {
3576 return Parameter.Type;
3582 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3584 return new ParameterReference (this, loc);
3589 // Block is converted into an expression
3591 sealed class BlockScopeExpression : Expression
3594 readonly ParametersBlock block;
3596 public BlockScopeExpression (Expression child, ParametersBlock block)
3602 public override bool ContainsEmitWithAwait ()
3604 return child.ContainsEmitWithAwait ();
3607 public override Expression CreateExpressionTree (ResolveContext ec)
3609 throw new NotSupportedException ();
3612 protected override Expression DoResolve (ResolveContext ec)
3617 child = child.Resolve (ec);
3621 eclass = child.eclass;
3626 public override void Emit (EmitContext ec)
3628 block.EmitScopeInitializers (ec);
3633 protected ParametersCompiled parameters;
3634 protected ParameterInfo[] parameter_info;
3635 protected bool resolved;
3636 protected ToplevelBlock top_block;
3637 protected StateMachine state_machine;
3638 protected Dictionary<string, object> labels;
3640 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3641 : base (parent, 0, start, start)
3643 if (parameters == null)
3644 throw new ArgumentNullException ("parameters");
3646 this.parameters = parameters;
3647 ParametersBlock = this;
3649 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3651 this.top_block = parent.ParametersBlock.top_block;
3652 ProcessParameters ();
3655 protected ParametersBlock (ParametersCompiled parameters, Location start)
3656 : base (null, 0, start, start)
3658 if (parameters == null)
3659 throw new ArgumentNullException ("parameters");
3661 this.parameters = parameters;
3662 ParametersBlock = this;
3666 // It's supposed to be used by method body implementation of anonymous methods
3668 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3669 : base (null, 0, source.StartLocation, source.EndLocation)
3671 this.parameters = parameters;
3672 this.statements = source.statements;
3673 this.scope_initializers = source.scope_initializers;
3675 this.resolved = true;
3676 this.reachable = source.reachable;
3677 this.am_storey = source.am_storey;
3678 this.state_machine = source.state_machine;
3679 this.flags = source.flags & Flags.ReachableEnd;
3681 ParametersBlock = this;
3684 // Overwrite original for comparison purposes when linking cross references
3685 // between anonymous methods
3687 Original = source.Original;
3692 public bool HasReferenceToStoreyForInstanceLambdas {
3694 return (flags & Flags.HasReferenceToStoreyForInstanceLambdas) != 0;
3697 flags = value ? flags | Flags.HasReferenceToStoreyForInstanceLambdas : flags & ~Flags.HasReferenceToStoreyForInstanceLambdas;
3701 public bool IsAsync {
3703 return (flags & Flags.HasAsyncModifier) != 0;
3706 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3711 // Block has been converted to expression tree
3713 public bool IsExpressionTree {
3715 return (flags & Flags.IsExpressionTree) != 0;
3720 // The parameters for the block.
3722 public ParametersCompiled Parameters {
3728 public StateMachine StateMachine {
3730 return state_machine;
3734 public ToplevelBlock TopBlock {
3743 public bool Resolved {
3745 return (flags & Flags.Resolved) != 0;
3749 public int TemporaryLocalsCount { get; set; }
3754 // Checks whether all `out' parameters have been assigned.
3756 public void CheckControlExit (FlowAnalysisContext fc)
3758 CheckControlExit (fc, fc.DefiniteAssignment);
3761 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3763 if (parameter_info == null)
3766 foreach (var p in parameter_info) {
3767 if (p.VariableInfo == null)
3770 if (p.VariableInfo.IsAssigned (dat))
3773 fc.Report.Error (177, p.Location,
3774 "The out parameter `{0}' must be assigned to before control leaves the current method",
3779 protected override void CloneTo (CloneContext clonectx, Statement t)
3781 base.CloneTo (clonectx, t);
3783 var target = (ParametersBlock) t;
3786 // Clone label statements as well as they contain block reference
3790 if (pb.labels != null) {
3791 target.labels = new Dictionary<string, object> ();
3793 foreach (var entry in pb.labels) {
3794 var list = entry.Value as List<LabeledStatement>;
3797 var list_clone = new List<LabeledStatement> ();
3798 foreach (var lentry in list) {
3799 list_clone.Add (RemapLabeledStatement (lentry, clonectx.RemapBlockCopy (lentry.Block)));
3802 target.labels.Add (entry.Key, list_clone);
3804 var labeled = (LabeledStatement) entry.Value;
3805 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, clonectx.RemapBlockCopy (labeled.Block)));
3812 if (pb.Parent == null)
3815 pb = pb.Parent.ParametersBlock;
3819 public override Expression CreateExpressionTree (ResolveContext ec)
3821 if (statements.Count == 1) {
3822 Expression expr = statements[0].CreateExpressionTree (ec);
3823 if (scope_initializers != null)
3824 expr = new BlockScopeExpression (expr, this);
3829 return base.CreateExpressionTree (ec);
3832 public override void Emit (EmitContext ec)
3834 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3835 DefineStoreyContainer (ec, state_machine);
3836 state_machine.EmitStoreyInstantiation (ec, this);
3842 public void EmitEmbedded (EmitContext ec)
3844 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3845 DefineStoreyContainer (ec, state_machine);
3846 state_machine.EmitStoreyInstantiation (ec, this);
3852 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3854 var res = base.DoFlowAnalysis (fc);
3856 if (HasReachableClosingBrace)
3857 CheckControlExit (fc);
3862 public LabeledStatement GetLabel (string name, Block block)
3865 // Cloned parameters blocks can have their own cloned version of top-level labels
3867 if (labels == null) {
3869 return Parent.ParametersBlock.GetLabel (name, block);
3875 if (!labels.TryGetValue (name, out value)) {
3879 var label = value as LabeledStatement;
3881 if (label != null) {
3882 if (IsLabelVisible (label, b))
3886 List<LabeledStatement> list = (List<LabeledStatement>) value;
3887 for (int i = 0; i < list.Count; ++i) {
3889 if (IsLabelVisible (label, b))
3897 static bool IsLabelVisible (LabeledStatement label, Block b)
3900 if (label.Block == b)
3903 } while (b != null);
3908 public ParameterInfo GetParameterInfo (Parameter p)
3910 for (int i = 0; i < parameters.Count; ++i) {
3911 if (parameters[i] == p)
3912 return parameter_info[i];
3915 throw new ArgumentException ("Invalid parameter");
3918 public ParameterReference GetParameterReference (int index, Location loc)
3920 return new ParameterReference (parameter_info[index], loc);
3923 public Statement PerformClone (ref HashSet<LocalVariable> undeclaredVariables)
3925 undeclaredVariables = TopBlock.GetUndeclaredVariables ();
3927 CloneContext clonectx = new CloneContext ();
3928 return Clone (clonectx);
3931 protected void ProcessParameters ()
3933 if (parameters.Count == 0)
3936 parameter_info = new ParameterInfo[parameters.Count];
3937 for (int i = 0; i < parameter_info.Length; ++i) {
3938 var p = parameters.FixedParameters[i];
3942 // TODO: Should use Parameter only and more block there
3943 parameter_info[i] = new ParameterInfo (this, i);
3945 AddLocalName (p.Name, parameter_info[i]);
3949 LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block dst)
3951 var src = stmt.Block;
3954 // Cannot remap label block if the label was not yet cloned which
3955 // can happen in case of anonymous method inside anoynymous method
3956 // with a label. But in this case we don't care because goto cannot
3957 // jump of out anonymous method
3959 if (src.ParametersBlock != this)
3962 var src_stmts = src.Statements;
3963 for (int i = 0; i < src_stmts.Count; ++i) {
3964 if (src_stmts[i] == stmt)
3965 return (LabeledStatement) dst.Statements[i];
3968 throw new InternalErrorException ("Should never be reached");
3971 public override bool Resolve (BlockContext bc)
3973 // TODO: if ((flags & Flags.Resolved) != 0)
3980 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3981 flags |= Flags.IsExpressionTree;
3984 PrepareAssignmentAnalysis (bc);
3986 if (!base.Resolve (bc))
3989 } catch (Exception e) {
3990 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3993 if (bc.CurrentBlock != null) {
3994 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3996 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
4001 // If an asynchronous body of F is either an expression classified as nothing, or a
4002 // statement block where no return statements have expressions, the inferred return type is Task
4005 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
4006 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
4007 am.ReturnTypeInference = null;
4008 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
4016 void PrepareAssignmentAnalysis (BlockContext bc)
4018 for (int i = 0; i < parameters.Count; ++i) {
4019 var par = parameters.FixedParameters[i];
4021 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
4024 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
4028 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
4030 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
4031 var stateMachine = new IteratorStorey (iterator);
4033 state_machine = stateMachine;
4034 iterator.SetStateMachine (stateMachine);
4036 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
4037 tlb.Original = this;
4038 tlb.state_machine = stateMachine;
4039 tlb.AddStatement (new Return (iterator, iterator.Location));
4043 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
4045 for (int i = 0; i < parameters.Count; i++) {
4046 Parameter p = parameters[i];
4047 Parameter.Modifier mod = p.ModFlags;
4048 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
4049 host.Compiler.Report.Error (1988, p.Location,
4050 "Async methods cannot have ref or out parameters");
4054 if (p is ArglistParameter) {
4055 host.Compiler.Report.Error (4006, p.Location,
4056 "__arglist is not allowed in parameter list of async methods");
4060 if (parameters.Types[i].IsPointer) {
4061 host.Compiler.Report.Error (4005, p.Location,
4062 "Async methods cannot have unsafe parameters");
4068 host.Compiler.Report.Warning (1998, 1, loc,
4069 "Async block lacks `await' operator and will run synchronously");
4072 var block_type = host.Module.Compiler.BuiltinTypes.Void;
4073 var initializer = new AsyncInitializer (this, host, block_type);
4074 initializer.Type = block_type;
4075 initializer.DelegateType = delegateType;
4077 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
4079 state_machine = stateMachine;
4080 initializer.SetStateMachine (stateMachine);
4082 const Flags flags = Flags.CompilerGenerated;
4084 var b = this is ToplevelBlock ?
4085 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
4086 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
4089 b.state_machine = stateMachine;
4090 b.AddStatement (new AsyncInitializerStatement (initializer));
4098 public class ToplevelBlock : ParametersBlock
4100 LocalVariable this_variable;
4101 CompilerContext compiler;
4102 Dictionary<string, object> names;
4104 List<ExplicitBlock> this_references;
4106 public ToplevelBlock (CompilerContext ctx, Location loc)
4107 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
4111 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
4112 : base (parameters, start)
4114 this.compiler = ctx;
4118 ProcessParameters ();
4122 // Recreates a top level block from parameters block. Used for
4123 // compiler generated methods where the original block comes from
4124 // explicit child block. This works for already resolved blocks
4125 // only to ensure we resolve them in the correct flow order
4127 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
4128 : base (source, parameters)
4130 this.compiler = source.TopBlock.compiler;
4134 public bool IsIterator {
4136 return (flags & Flags.Iterator) != 0;
4139 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
4143 public Report Report {
4145 return compiler.Report;
4150 // Used by anonymous blocks to track references of `this' variable
4152 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
4154 return this_references;
4159 // Returns the "this" instance variable of this block.
4160 // See AddThisVariable() for more information.
4162 public LocalVariable ThisVariable {
4164 return this_variable;
4168 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4171 names = new Dictionary<string, object> ();
4174 if (!names.TryGetValue (name, out value)) {
4175 names.Add (name, li);
4179 INamedBlockVariable existing = value as INamedBlockVariable;
4180 List<INamedBlockVariable> existing_list;
4181 if (existing != null) {
4182 existing_list = new List<INamedBlockVariable> ();
4183 existing_list.Add (existing);
4184 names[name] = existing_list;
4186 existing_list = (List<INamedBlockVariable>) value;
4190 // A collision checking between local names
4192 var variable_block = li.Block.Explicit;
4193 for (int i = 0; i < existing_list.Count; ++i) {
4194 existing = existing_list[i];
4195 Block b = existing.Block.Explicit;
4197 // Collision at same level
4198 if (variable_block == b) {
4199 li.Block.Error_AlreadyDeclared (name, li);
4203 // Collision with parent
4204 Block parent = variable_block;
4205 while ((parent = parent.Parent) != null) {
4207 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4208 i = existing_list.Count;
4213 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4214 // Collision with children
4215 while ((b = b.Parent) != null) {
4216 if (variable_block == b) {
4217 li.Block.Error_AlreadyDeclared (name, li, "child");
4218 i = existing_list.Count;
4225 existing_list.Add (li);
4228 public void AddLabel (string name, LabeledStatement label)
4231 labels = new Dictionary<string, object> ();
4234 if (!labels.TryGetValue (name, out value)) {
4235 labels.Add (name, label);
4239 LabeledStatement existing = value as LabeledStatement;
4240 List<LabeledStatement> existing_list;
4241 if (existing != null) {
4242 existing_list = new List<LabeledStatement> ();
4243 existing_list.Add (existing);
4244 labels[name] = existing_list;
4246 existing_list = (List<LabeledStatement>) value;
4250 // A collision checking between labels
4252 for (int i = 0; i < existing_list.Count; ++i) {
4253 existing = existing_list[i];
4254 Block b = existing.Block;
4256 // Collision at same level
4257 if (label.Block == b) {
4258 Report.SymbolRelatedToPreviousError (existing.loc, name);
4259 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4263 // Collision with parent
4265 while ((b = b.Parent) != null) {
4266 if (existing.Block == b) {
4267 Report.Error (158, label.loc,
4268 "The label `{0}' shadows another label by the same name in a contained scope", name);
4269 i = existing_list.Count;
4274 // Collision with with children
4276 while ((b = b.Parent) != null) {
4277 if (label.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;
4286 existing_list.Add (label);
4289 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4291 if (this_references == null)
4292 this_references = new List<ExplicitBlock> ();
4294 if (!this_references.Contains (block))
4295 this_references.Add (block);
4298 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4300 this_references.Remove (block);
4304 // Creates an arguments set from all parameters, useful for method proxy calls
4306 public Arguments GetAllParametersArguments ()
4308 int count = parameters.Count;
4309 Arguments args = new Arguments (count);
4310 for (int i = 0; i < count; ++i) {
4311 var pi = parameter_info[i];
4312 var arg_expr = GetParameterReference (i, pi.Location);
4314 Argument.AType atype_modifier;
4315 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4316 case Parameter.Modifier.REF:
4317 atype_modifier = Argument.AType.Ref;
4319 case Parameter.Modifier.OUT:
4320 atype_modifier = Argument.AType.Out;
4327 args.Add (new Argument (arg_expr, atype_modifier));
4334 // Lookup inside a block, the returned value can represent 3 states
4336 // true+variable: A local name was found and it's valid
4337 // false+variable: A local name was found in a child block only
4338 // false+null: No local name was found
4340 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4346 if (!names.TryGetValue (name, out value))
4349 variable = value as INamedBlockVariable;
4351 if (variable != null) {
4353 if (variable.Block == b.Original)
4357 } while (b != null);
4365 } while (b != null);
4367 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4368 for (int i = 0; i < list.Count; ++i) {
4371 if (variable.Block == b.Original)
4375 } while (b != null);
4383 } while (b != null);
4393 public void IncludeBlock (ParametersBlock pb, ToplevelBlock block)
4395 if (block.names != null) {
4396 foreach (var n in block.names) {
4397 var variable = n.Value as INamedBlockVariable;
4398 if (variable != null) {
4399 if (variable.Block.ParametersBlock == pb)
4400 AddLocalName (n.Key, variable, false);
4404 foreach (var v in (List<INamedBlockVariable>) n.Value)
4405 if (v.Block.ParametersBlock == pb)
4406 AddLocalName (n.Key, v, false);
4412 // This is used by non-static `struct' constructors which do not have an
4413 // initializer - in this case, the constructor must initialize all of the
4414 // struct's fields. To do this, we add a "this" variable and use the flow
4415 // analysis code to ensure that it's been fully initialized before control
4416 // leaves the constructor.
4418 public void AddThisVariable (BlockContext bc)
4420 if (this_variable != null)
4421 throw new InternalErrorException (StartLocation.ToString ());
4423 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4424 this_variable.Type = bc.CurrentType;
4425 this_variable.PrepareAssignmentAnalysis (bc);
4428 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4431 // If we're a non-static struct constructor which doesn't have an
4432 // initializer, then we must initialize all of the struct's fields.
4434 if (this_variable != null)
4435 this_variable.IsThisAssigned (fc, this);
4437 base.CheckControlExit (fc, dat);
4440 public HashSet<LocalVariable> GetUndeclaredVariables ()
4445 HashSet<LocalVariable> variables = null;
4447 foreach (var entry in names) {
4448 var complex = entry.Value as List<INamedBlockVariable>;
4449 if (complex != null) {
4450 foreach (var centry in complex) {
4451 if (IsUndeclaredVariable (centry)) {
4452 if (variables == null)
4453 variables = new HashSet<LocalVariable> ();
4455 variables.Add ((LocalVariable) centry);
4458 } else if (IsUndeclaredVariable ((INamedBlockVariable)entry.Value)) {
4459 if (variables == null)
4460 variables = new HashSet<LocalVariable> ();
4462 variables.Add ((LocalVariable)entry.Value);
4469 static bool IsUndeclaredVariable (INamedBlockVariable namedBlockVariable)
4471 var lv = namedBlockVariable as LocalVariable;
4472 return lv != null && !lv.IsDeclared;
4475 public void SetUndeclaredVariables (HashSet<LocalVariable> undeclaredVariables)
4480 foreach (var entry in names) {
4481 var complex = entry.Value as List<INamedBlockVariable>;
4482 if (complex != null) {
4483 foreach (var centry in complex) {
4484 var lv = centry as LocalVariable;
4485 if (lv != null && undeclaredVariables.Contains (lv)) {
4490 var lv = entry.Value as LocalVariable;
4491 if (lv != null && undeclaredVariables.Contains (lv))
4497 public override void Emit (EmitContext ec)
4499 if (Report.Errors > 0)
4503 if (IsCompilerGenerated) {
4504 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4512 // If `HasReturnLabel' is set, then we already emitted a
4513 // jump to the end of the method, so we must emit a `ret'
4516 // Unfortunately, System.Reflection.Emit automatically emits
4517 // a leave to the end of a finally block. This is a problem
4518 // if no code is following the try/finally block since we may
4519 // jump to a point after the end of the method.
4520 // As a workaround, we're always creating a return label in
4523 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4524 if (ec.HasReturnLabel)
4525 ec.MarkLabel (ec.ReturnLabel);
4527 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4528 ec.Mark (EndLocation);
4530 if (ec.ReturnType.Kind != MemberKind.Void)
4531 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4533 ec.Emit (OpCodes.Ret);
4536 } catch (Exception e) {
4537 throw new InternalErrorException (e, StartLocation);
4541 public bool Resolve (BlockContext bc, IMethodData md)
4546 var errors = bc.Report.Errors;
4550 if (bc.Report.Errors > errors)
4553 MarkReachable (new Reachability ());
4555 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4556 // TODO: var md = bc.CurrentMemberDefinition;
4557 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4560 if ((flags & Flags.NoFlowAnalysis) != 0)
4563 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4566 } catch (Exception e) {
4567 throw new InternalErrorException (e, StartLocation);
4574 public class SwitchLabel : Statement
4582 // if expr == null, then it is the default case.
4584 public SwitchLabel (Expression expr, Location l)
4590 public bool IsDefault {
4592 return label == null;
4596 public Expression Label {
4602 public Location Location {
4608 public Constant Converted {
4617 public bool PatternMatching { get; set; }
4619 public bool SectionStart { get; set; }
4621 public Label GetILLabel (EmitContext ec)
4623 if (il_label == null){
4624 il_label = ec.DefineLabel ();
4627 return il_label.Value;
4630 protected override void DoEmit (EmitContext ec)
4632 ec.MarkLabel (GetILLabel (ec));
4635 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4640 fc.BranchDefiniteAssignment (fc.SwitchInitialDefinitiveAssignment);
4644 public override bool Resolve (BlockContext bc)
4646 if (ResolveAndReduce (bc))
4647 bc.Switch.RegisterLabel (bc, this);
4653 // Resolves the expression, reduces it to a literal if possible
4654 // and then converts it to the requested type.
4656 bool ResolveAndReduce (BlockContext bc)
4661 var switch_statement = bc.Switch;
4663 if (PatternMatching) {
4664 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4665 return label != null;
4668 var c = label.ResolveLabelConstant (bc);
4672 if (switch_statement.IsNullable && c is NullLiteral) {
4677 if (switch_statement.IsPatternMatching) {
4678 label = new Is (switch_statement.ExpressionValue, label, loc).Resolve (bc);
4682 converted = c.ImplicitConversionRequired (bc, switch_statement.SwitchType);
4683 return converted != null;
4686 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4688 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4689 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4692 protected override void CloneTo (CloneContext clonectx, Statement target)
4694 var t = (SwitchLabel) target;
4696 t.label = label.Clone (clonectx);
4699 public override object Accept (StructuralVisitor visitor)
4701 return visitor.Visit (this);
4704 public string GetSignatureForError ()
4707 if (converted == null)
4710 label = converted.GetValueAsLiteral ();
4712 return string.Format ("case {0}:", label);
4716 public class Switch : LoopStatement
4718 // structure used to hold blocks of keys while calculating table switch
4719 sealed class LabelsRange : IComparable<LabelsRange>
4721 public readonly long min;
4723 public readonly List<long> label_values;
4725 public LabelsRange (long value)
4728 label_values = new List<long> ();
4729 label_values.Add (value);
4732 public LabelsRange (long min, long max, ICollection<long> values)
4736 this.label_values = new List<long> (values);
4741 return max - min + 1;
4745 public bool AddValue (long value)
4747 var gap = value - min + 1;
4748 // Ensure the range has > 50% occupancy
4749 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4753 label_values.Add (value);
4757 public int CompareTo (LabelsRange other)
4759 int nLength = label_values.Count;
4760 int nLengthOther = other.label_values.Count;
4761 if (nLengthOther == nLength)
4762 return (int) (other.min - min);
4764 return nLength - nLengthOther;
4768 sealed class DispatchStatement : Statement
4770 readonly Switch body;
4772 public DispatchStatement (Switch body)
4777 protected override void CloneTo (CloneContext clonectx, Statement target)
4779 throw new NotImplementedException ();
4782 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4787 protected override void DoEmit (EmitContext ec)
4789 body.EmitDispatch (ec);
4793 class MissingBreak : Statement
4795 readonly SwitchLabel label;
4797 public MissingBreak (SwitchLabel sl)
4803 public bool FallOut { get; set; }
4805 protected override void DoEmit (EmitContext ec)
4809 protected override void CloneTo (CloneContext clonectx, Statement target)
4813 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4816 fc.Report.Error (8070, loc, "Control cannot fall out of switch statement through final case label `{0}'",
4817 label.GetSignatureForError ());
4819 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4820 label.GetSignatureForError ());
4826 public Expression Expr;
4829 // Mapping of all labels to their SwitchLabels
4831 Dictionary<long, SwitchLabel> labels;
4832 Dictionary<string, SwitchLabel> string_labels;
4833 List<SwitchLabel> case_labels;
4835 List<Tuple<GotoCase, Constant>> goto_cases;
4836 List<DefiniteAssignmentBitSet> end_reachable_das;
4839 /// The governing switch type
4841 public TypeSpec SwitchType;
4843 Expression new_expr;
4845 SwitchLabel case_null;
4846 SwitchLabel case_default;
4848 Label defaultLabel, nullLabel;
4849 VariableReference value;
4850 ExpressionStatement string_dictionary;
4851 FieldExpr switch_cache_field;
4852 ExplicitBlock block;
4856 // Nullable Types support
4858 Nullable.Unwrap unwrap;
4860 public Switch (Expression e, ExplicitBlock block, Location l)
4868 public SwitchLabel ActiveLabel { get; set; }
4870 public ExplicitBlock Block {
4876 public SwitchLabel DefaultLabel {
4878 return case_default;
4882 public bool IsNullable {
4884 return unwrap != null;
4888 public bool IsPatternMatching {
4890 return new_expr == null && SwitchType != null;
4894 public List<SwitchLabel> RegisteredLabels {
4900 public VariableReference ExpressionValue {
4907 // Determines the governing type for a switch. The returned
4908 // expression might be the expression from the switch, or an
4909 // expression that includes any potential conversions to
4911 static Expression SwitchGoverningType (ResolveContext rc, Expression expr, bool unwrapExpr)
4913 switch (expr.Type.BuiltinType) {
4914 case BuiltinTypeSpec.Type.Byte:
4915 case BuiltinTypeSpec.Type.SByte:
4916 case BuiltinTypeSpec.Type.UShort:
4917 case BuiltinTypeSpec.Type.Short:
4918 case BuiltinTypeSpec.Type.UInt:
4919 case BuiltinTypeSpec.Type.Int:
4920 case BuiltinTypeSpec.Type.ULong:
4921 case BuiltinTypeSpec.Type.Long:
4922 case BuiltinTypeSpec.Type.Char:
4923 case BuiltinTypeSpec.Type.String:
4924 case BuiltinTypeSpec.Type.Bool:
4928 if (expr.Type.IsEnum)
4932 // Try to find a *user* defined implicit conversion.
4934 // If there is no implicit conversion, or if there are multiple
4935 // conversions, we have to report an error
4937 Expression converted = null;
4938 foreach (TypeSpec tt in rc.Module.PredefinedTypes.SwitchUserTypes) {
4940 if (!unwrapExpr && tt.IsNullableType && expr.Type.IsNullableType)
4943 var restr = Convert.UserConversionRestriction.ImplicitOnly |
4944 Convert.UserConversionRestriction.ProbingOnly;
4947 restr |= Convert.UserConversionRestriction.NullableSourceOnly;
4949 var e = Convert.UserDefinedConversion (rc, expr, tt, restr, Location.Null);
4954 // Ignore over-worked ImplicitUserConversions that do
4955 // an implicit conversion in addition to the user conversion.
4957 var uc = e as UserCast;
4961 if (converted != null){
4962 // rc.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4971 public static TypeSpec[] CreateSwitchUserTypes (ModuleContainer module, TypeSpec nullable)
4973 var types = module.Compiler.BuiltinTypes;
4975 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4976 TypeSpec[] stypes = new[] {
4989 if (nullable != null) {
4991 Array.Resize (ref stypes, stypes.Length + 9);
4993 for (int i = 0; i < 9; ++i) {
4994 stypes [10 + i] = nullable.MakeGenericType (module, new [] { stypes [i] });
5001 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
5003 case_labels.Add (sl);
5006 if (case_default != null) {
5007 sl.Error_AlreadyOccurs (rc, case_default);
5015 if (sl.Converted == null)
5019 if (string_labels != null) {
5020 string string_value = sl.Converted.GetValue () as string;
5021 if (string_value == null)
5024 string_labels.Add (string_value, sl);
5026 if (sl.Converted.IsNull) {
5029 labels.Add (sl.Converted.GetValueAsLong (), sl);
5032 } catch (ArgumentException) {
5033 if (string_labels != null)
5034 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
5036 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
5041 // This method emits code for a lookup-based switch statement (non-string)
5042 // Basically it groups the cases into blocks that are at least half full,
5043 // and then spits out individual lookup opcodes for each block.
5044 // It emits the longest blocks first, and short blocks are just
5045 // handled with direct compares.
5047 void EmitTableSwitch (EmitContext ec, Expression val)
5049 if (labels != null && labels.Count > 0) {
5050 List<LabelsRange> ranges;
5051 if (string_labels != null) {
5052 // We have done all hard work for string already
5053 // setup single range only
5054 ranges = new List<LabelsRange> (1);
5055 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
5057 var element_keys = new long[labels.Count];
5058 labels.Keys.CopyTo (element_keys, 0);
5059 Array.Sort (element_keys);
5062 // Build possible ranges of switch labes to reduce number
5065 ranges = new List<LabelsRange> (element_keys.Length);
5066 var range = new LabelsRange (element_keys[0]);
5068 for (int i = 1; i < element_keys.Length; ++i) {
5069 var l = element_keys[i];
5070 if (range.AddValue (l))
5073 range = new LabelsRange (l);
5077 // sort the blocks so we can tackle the largest ones first
5081 Label lbl_default = defaultLabel;
5082 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
5084 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
5085 LabelsRange kb = ranges[range_index];
5086 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
5088 // Optimize small ranges using simple equality check
5089 if (kb.Range <= 2) {
5090 foreach (var key in kb.label_values) {
5091 SwitchLabel sl = labels[key];
5092 if (sl == case_default || sl == case_null)
5095 if (sl.Converted.IsZeroInteger) {
5096 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
5099 sl.Converted.Emit (ec);
5100 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
5104 // TODO: if all the keys in the block are the same and there are
5105 // no gaps/defaults then just use a range-check.
5106 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
5107 // TODO: optimize constant/I4 cases
5109 // check block range (could be > 2^31)
5111 ec.EmitLong (kb.min);
5112 ec.Emit (OpCodes.Blt, lbl_default);
5115 ec.EmitLong (kb.max);
5116 ec.Emit (OpCodes.Bgt, lbl_default);
5121 ec.EmitLong (kb.min);
5122 ec.Emit (OpCodes.Sub);
5125 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
5129 int first = (int) kb.min;
5132 ec.Emit (OpCodes.Sub);
5133 } else if (first < 0) {
5134 ec.EmitInt (-first);
5135 ec.Emit (OpCodes.Add);
5139 // first, build the list of labels for the switch
5141 long cJumps = kb.Range;
5142 Label[] switch_labels = new Label[cJumps];
5143 for (int iJump = 0; iJump < cJumps; iJump++) {
5144 var key = kb.label_values[iKey];
5145 if (key == kb.min + iJump) {
5146 switch_labels[iJump] = labels[key].GetILLabel (ec);
5149 switch_labels[iJump] = lbl_default;
5153 // emit the switch opcode
5154 ec.Emit (OpCodes.Switch, switch_labels);
5157 // mark the default for this block
5158 if (range_index != 0)
5159 ec.MarkLabel (lbl_default);
5162 // the last default just goes to the end
5163 if (ranges.Count > 0)
5164 ec.Emit (OpCodes.Br, lbl_default);
5168 public SwitchLabel FindLabel (Constant value)
5170 SwitchLabel sl = null;
5172 if (string_labels != null) {
5173 string s = value.GetValue () as string;
5175 if (case_null != null)
5177 else if (case_default != null)
5180 string_labels.TryGetValue (s, out sl);
5183 if (value is NullLiteral) {
5186 labels.TryGetValue (value.GetValueAsLong (), out sl);
5190 if (sl == null || sl.SectionStart)
5194 // Always return section start, it simplifies handling of switch labels
5196 for (int idx = case_labels.IndexOf (sl); ; --idx) {
5197 var cs = case_labels [idx];
5198 if (cs.SectionStart)
5203 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5205 Expr.FlowAnalysis (fc);
5207 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
5208 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
5209 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
5211 block.FlowAnalysis (fc);
5213 fc.SwitchInitialDefinitiveAssignment = prev_switch;
5215 if (end_reachable_das != null) {
5216 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
5217 InitialDefinitiveAssignment |= sections_das;
5218 end_reachable_das = null;
5221 fc.DefiniteAssignment = InitialDefinitiveAssignment;
5223 return case_default != null && !end_reachable;
5226 public override bool Resolve (BlockContext ec)
5228 Expr = Expr.Resolve (ec);
5233 // LAMESPEC: User conversion from non-nullable governing type has a priority
5235 new_expr = SwitchGoverningType (ec, Expr, false);
5237 if (new_expr == null) {
5238 if (Expr.Type.IsNullableType) {
5239 unwrap = Nullable.Unwrap.Create (Expr, false);
5244 // Unwrap + user conversion using non-nullable type is not allowed but user operator
5245 // involving nullable Expr and nullable governing type is
5247 new_expr = SwitchGoverningType (ec, unwrap, true);
5251 Expression switch_expr;
5252 if (new_expr == null) {
5253 if (ec.Module.Compiler.Settings.Version != LanguageVersion.Experimental) {
5254 if (Expr.Type != InternalType.ErrorType) {
5255 ec.Report.Error (151, loc,
5256 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
5257 Expr.Type.GetSignatureForError ());
5264 SwitchType = Expr.Type;
5266 switch_expr = new_expr;
5267 SwitchType = new_expr.Type;
5268 if (SwitchType.IsNullableType) {
5269 new_expr = unwrap = Nullable.Unwrap.Create (new_expr, true);
5270 SwitchType = Nullable.NullableInfo.GetUnderlyingType (SwitchType);
5273 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
5274 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
5278 if (block.Statements.Count == 0)
5281 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5282 string_labels = new Dictionary<string, SwitchLabel> ();
5284 labels = new Dictionary<long, SwitchLabel> ();
5288 var constant = switch_expr as Constant;
5291 // Don't need extra variable for constant switch or switch with
5292 // only default case
5294 if (constant == null) {
5296 // Store switch expression for comparison purposes
5298 value = switch_expr as VariableReference;
5299 if (value == null && !HasOnlyDefaultSection ()) {
5300 var current_block = ec.CurrentBlock;
5301 ec.CurrentBlock = Block;
5302 // Create temporary variable inside switch scope
5303 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
5305 ec.CurrentBlock = current_block;
5309 case_labels = new List<SwitchLabel> ();
5311 Switch old_switch = ec.Switch;
5313 var parent_los = ec.EnclosingLoopOrSwitch;
5314 ec.EnclosingLoopOrSwitch = this;
5316 var ok = Statement.Resolve (ec);
5318 ec.EnclosingLoopOrSwitch = parent_los;
5319 ec.Switch = old_switch;
5322 // Check if all goto cases are valid. Needs to be done after switch
5323 // is resolved because goto can jump forward in the scope.
5325 if (goto_cases != null) {
5326 foreach (var gc in goto_cases) {
5327 if (gc.Item1 == null) {
5328 if (DefaultLabel == null) {
5329 Goto.Error_UnknownLabel (ec, "default", loc);
5335 var sl = FindLabel (gc.Item2);
5337 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5339 gc.Item1.Label = sl;
5347 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5348 ResolveStringSwitchMap (ec);
5352 // Anonymous storey initialization has to happen before
5353 // any generated switch dispatch
5355 block.InsertStatement (0, new DispatchStatement (this));
5360 bool HasOnlyDefaultSection ()
5362 for (int i = 0; i < block.Statements.Count; ++i) {
5363 var s = block.Statements[i] as SwitchLabel;
5365 if (s == null || s.IsDefault)
5374 public override Reachability MarkReachable (Reachability rc)
5376 if (rc.IsUnreachable)
5379 base.MarkReachable (rc);
5381 block.MarkReachableScope (rc);
5383 if (block.Statements.Count == 0)
5386 SwitchLabel constant_label = null;
5387 var constant = new_expr as Constant;
5389 if (constant != null) {
5390 constant_label = FindLabel (constant) ?? case_default;
5391 if (constant_label == null) {
5392 block.Statements.RemoveAt (0);
5397 var section_rc = new Reachability ();
5398 SwitchLabel prev_label = null;
5400 for (int i = 0; i < block.Statements.Count; ++i) {
5401 var s = block.Statements[i];
5402 var sl = s as SwitchLabel;
5404 if (sl != null && sl.SectionStart) {
5406 // Section is marked already via goto case
5408 if (!sl.IsUnreachable) {
5409 section_rc = new Reachability ();
5413 if (section_rc.IsUnreachable) {
5415 // Common case. Previous label section end is unreachable as
5416 // it ends with break, return, etc. For next section revert
5417 // to reachable again unless we have constant switch block
5419 section_rc = constant_label != null && constant_label != sl ?
5420 Reachability.CreateUnreachable () :
5421 new Reachability ();
5422 } else if (prev_label != null) {
5424 // Error case as control cannot fall through from one case label
5426 sl.SectionStart = false;
5427 s = new MissingBreak (prev_label);
5428 s.MarkReachable (rc);
5429 block.Statements.Insert (i - 1, s);
5431 } else if (constant_label != null && constant_label != sl) {
5433 // Special case for the first unreachable label in constant
5436 section_rc = Reachability.CreateUnreachable ();
5442 section_rc = s.MarkReachable (section_rc);
5445 if (!section_rc.IsUnreachable && prev_label != null) {
5446 prev_label.SectionStart = false;
5447 var s = new MissingBreak (prev_label) {
5451 s.MarkReachable (rc);
5452 block.Statements.Add (s);
5456 // Reachability can affect parent only when all possible paths are handled but
5457 // we still need to run reachability check on switch body to check for fall-through
5459 if (case_default == null && constant_label == null)
5463 // We have at least one local exit from the switch
5468 return Reachability.CreateUnreachable ();
5471 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5473 if (goto_cases == null)
5474 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5476 goto_cases.Add (Tuple.Create (gotoCase, value));
5480 // Converts string switch into string hashtable
5482 void ResolveStringSwitchMap (ResolveContext ec)
5484 FullNamedExpression string_dictionary_type;
5485 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5486 string_dictionary_type = new TypeExpression (
5487 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5488 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5490 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5491 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5493 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5497 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5498 Field field = new Field (ctype, string_dictionary_type,
5499 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5500 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5501 if (!field.Define ())
5503 ctype.AddField (field);
5505 var init = new List<Expression> ();
5507 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5508 string value = null;
5510 foreach (SwitchLabel sl in case_labels) {
5512 if (sl.SectionStart)
5513 labels.Add (++counter, sl);
5515 if (sl == case_default || sl == case_null)
5518 value = (string) sl.Converted.GetValue ();
5519 var init_args = new List<Expression> (2);
5520 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5522 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5523 init_args.Add (sl.Converted);
5525 init.Add (new CollectionElementInitializer (init_args, loc));
5528 Arguments args = new Arguments (1);
5529 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5530 Expression initializer = new NewInitialize (string_dictionary_type, args,
5531 new CollectionOrObjectInitializers (init, loc), loc);
5533 switch_cache_field = new FieldExpr (field, loc);
5534 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5537 void DoEmitStringSwitch (EmitContext ec)
5539 Label l_initialized = ec.DefineLabel ();
5542 // Skip initialization when value is null
5544 value.EmitBranchable (ec, nullLabel, false);
5547 // Check if string dictionary is initialized and initialize
5549 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5550 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5551 string_dictionary.EmitStatement (ec);
5553 ec.MarkLabel (l_initialized);
5555 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5557 ResolveContext rc = new ResolveContext (ec.MemberContext);
5559 if (switch_cache_field.Type.IsGeneric) {
5560 Arguments get_value_args = new Arguments (2);
5561 get_value_args.Add (new Argument (value));
5562 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5563 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5564 if (get_item == null)
5568 // A value was not found, go to default case
5570 get_item.EmitBranchable (ec, defaultLabel, false);
5572 Arguments get_value_args = new Arguments (1);
5573 get_value_args.Add (new Argument (value));
5575 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5576 if (get_item == null)
5579 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5580 get_item_object.EmitAssign (ec, get_item, true, false);
5581 ec.Emit (OpCodes.Brfalse, defaultLabel);
5583 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5584 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5586 get_item_int.EmitStatement (ec);
5587 get_item_object.Release (ec);
5590 EmitTableSwitch (ec, string_switch_variable);
5591 string_switch_variable.Release (ec);
5595 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5597 void EmitShortSwitch (EmitContext ec)
5599 MethodSpec equal_method = null;
5600 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5601 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5604 if (equal_method != null) {
5605 value.EmitBranchable (ec, nullLabel, false);
5608 for (int i = 0; i < case_labels.Count; ++i) {
5609 var label = case_labels [i];
5610 if (label == case_default || label == case_null)
5613 var constant = label.Converted;
5615 if (constant == null) {
5616 label.Label.EmitBranchable (ec, label.GetILLabel (ec), true);
5620 if (equal_method != null) {
5624 var call = new CallEmitter ();
5625 call.EmitPredefined (ec, equal_method, new Arguments (0));
5626 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5630 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5631 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5637 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5640 ec.Emit (OpCodes.Br, defaultLabel);
5643 void EmitDispatch (EmitContext ec)
5645 if (IsPatternMatching) {
5646 EmitShortSwitch (ec);
5650 if (value == null) {
5652 // Constant switch, we've already done the work if there is only 1 label
5656 foreach (var sl in case_labels) {
5657 if (sl.IsUnreachable)
5660 if (reachable++ > 0) {
5661 var constant = (Constant) new_expr;
5662 var constant_label = FindLabel (constant) ?? case_default;
5664 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5672 if (string_dictionary != null) {
5673 DoEmitStringSwitch (ec);
5674 } else if (case_labels.Count < 4 || string_labels != null) {
5675 EmitShortSwitch (ec);
5677 EmitTableSwitch (ec, value);
5681 protected override void DoEmit (EmitContext ec)
5684 // Setup the codegen context
5686 Label old_end = ec.LoopEnd;
5687 Switch old_switch = ec.Switch;
5689 ec.LoopEnd = ec.DefineLabel ();
5692 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5693 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5695 if (value != null) {
5698 var switch_expr = new_expr ?? Expr;
5700 unwrap.EmitCheck (ec);
5701 ec.Emit (OpCodes.Brfalse, nullLabel);
5702 value.EmitAssign (ec, switch_expr, false, false);
5703 } else if (switch_expr != value) {
5704 value.EmitAssign (ec, switch_expr, false, false);
5709 // Next statement is compiler generated we don't need extra
5710 // nop when we can use the statement for sequence point
5712 ec.Mark (block.StartLocation);
5713 block.IsCompilerGenerated = true;
5715 new_expr.EmitSideEffect (ec);
5720 // Restore context state.
5721 ec.MarkLabel (ec.LoopEnd);
5724 // Restore the previous context
5726 ec.LoopEnd = old_end;
5727 ec.Switch = old_switch;
5730 protected override void CloneTo (CloneContext clonectx, Statement t)
5732 Switch target = (Switch) t;
5734 target.Expr = Expr.Clone (clonectx);
5735 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5738 public override object Accept (StructuralVisitor visitor)
5740 return visitor.Visit (this);
5743 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5745 if (case_default == null && !(new_expr is Constant))
5748 if (end_reachable_das == null)
5749 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5751 end_reachable_das.Add (fc.DefiniteAssignment);
5754 public override void SetEndReachable ()
5756 end_reachable = true;
5760 // A place where execution can restart in a state machine
5761 public abstract class ResumableStatement : Statement
5764 protected Label resume_point;
5766 public Label PrepareForEmit (EmitContext ec)
5770 resume_point = ec.DefineLabel ();
5772 return resume_point;
5775 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5780 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5785 public abstract class TryFinallyBlock : ExceptionStatement
5787 protected Statement stmt;
5788 Label dispose_try_block;
5789 bool prepared_for_dispose, emitted_dispose;
5790 Method finally_host;
5792 protected TryFinallyBlock (Statement stmt, Location loc)
5800 public Statement Statement {
5808 protected abstract void EmitTryBody (EmitContext ec);
5809 public abstract void EmitFinallyBody (EmitContext ec);
5811 public override Label PrepareForDispose (EmitContext ec, Label end)
5813 if (!prepared_for_dispose) {
5814 prepared_for_dispose = true;
5815 dispose_try_block = ec.DefineLabel ();
5817 return dispose_try_block;
5820 protected sealed override void DoEmit (EmitContext ec)
5822 EmitTryBodyPrepare (ec);
5825 bool beginFinally = EmitBeginFinallyBlock (ec);
5827 Label start_finally = ec.DefineLabel ();
5828 if (resume_points != null && beginFinally) {
5829 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5831 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5832 ec.Emit (OpCodes.Brfalse_S, start_finally);
5833 ec.Emit (OpCodes.Endfinally);
5836 ec.MarkLabel (start_finally);
5838 if (finally_host != null) {
5839 finally_host.Define ();
5840 finally_host.PrepareEmit ();
5841 finally_host.Emit ();
5843 // Now it's safe to add, to close it properly and emit sequence points
5844 finally_host.Parent.AddMember (finally_host);
5846 var ce = new CallEmitter ();
5847 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5848 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5850 EmitFinallyBody (ec);
5854 ec.EndExceptionBlock ();
5857 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5859 if (emitted_dispose)
5862 emitted_dispose = true;
5864 Label end_of_try = ec.DefineLabel ();
5866 // Ensure that the only way we can get into this code is through a dispatcher
5867 if (have_dispatcher)
5868 ec.Emit (OpCodes.Br, end);
5870 ec.BeginExceptionBlock ();
5872 ec.MarkLabel (dispose_try_block);
5874 Label[] labels = null;
5875 for (int i = 0; i < resume_points.Count; ++i) {
5876 ResumableStatement s = resume_points[i];
5877 Label ret = s.PrepareForDispose (ec, end_of_try);
5878 if (ret.Equals (end_of_try) && labels == null)
5880 if (labels == null) {
5881 labels = new Label[resume_points.Count];
5882 for (int j = 0; j < i; ++j)
5883 labels[j] = end_of_try;
5888 if (labels != null) {
5890 for (j = 1; j < labels.Length; ++j)
5891 if (!labels[0].Equals (labels[j]))
5893 bool emit_dispatcher = j < labels.Length;
5895 if (emit_dispatcher) {
5896 ec.Emit (OpCodes.Ldloc, pc);
5897 ec.EmitInt (first_resume_pc);
5898 ec.Emit (OpCodes.Sub);
5899 ec.Emit (OpCodes.Switch, labels);
5902 foreach (ResumableStatement s in resume_points)
5903 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5906 ec.MarkLabel (end_of_try);
5908 ec.BeginFinallyBlock ();
5910 if (finally_host != null) {
5911 var ce = new CallEmitter ();
5912 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5913 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0), true);
5915 EmitFinallyBody (ec);
5918 ec.EndExceptionBlock ();
5921 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5923 var res = stmt.FlowAnalysis (fc);
5924 parent_try_block = null;
5928 protected virtual bool EmitBeginFinallyBlock (EmitContext ec)
5930 ec.BeginFinallyBlock ();
5934 public override Reachability MarkReachable (Reachability rc)
5936 base.MarkReachable (rc);
5937 return Statement.MarkReachable (rc);
5940 public override bool Resolve (BlockContext bc)
5944 parent_try_block = bc.CurrentTryBlock;
5945 bc.CurrentTryBlock = this;
5947 if (stmt is TryCatch) {
5948 ok = stmt.Resolve (bc);
5950 using (bc.Set (ResolveContext.Options.TryScope)) {
5951 ok = stmt.Resolve (bc);
5955 bc.CurrentTryBlock = parent_try_block;
5958 // Finally block inside iterator is called from MoveNext and
5959 // Dispose methods that means we need to lift the block into
5960 // newly created host method to emit the body only once. The
5961 // original block then simply calls the newly generated method.
5963 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5964 var b = stmt as Block;
5965 if (b != null && b.Explicit.HasYield) {
5966 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5970 return base.Resolve (bc) && ok;
5975 // Base class for blocks using exception handling
5977 public abstract class ExceptionStatement : ResumableStatement
5979 protected List<ResumableStatement> resume_points;
5980 protected int first_resume_pc;
5981 protected ExceptionStatement parent_try_block;
5982 protected int first_catch_resume_pc = -1;
5984 protected ExceptionStatement (Location loc)
5989 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5991 StateMachineInitializer state_machine = null;
5992 if (resume_points != null) {
5993 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5995 ec.EmitInt ((int) IteratorStorey.State.Running);
5996 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
6000 // The resume points in catch section when this is try-catch-finally
6002 if (IsRewrittenTryCatchFinally ()) {
6003 ec.BeginExceptionBlock ();
6005 if (first_catch_resume_pc >= 0) {
6007 ec.MarkLabel (resume_point);
6009 // For normal control flow, we want to fall-through the Switch
6010 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6011 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6012 ec.EmitInt (first_resume_pc + first_catch_resume_pc);
6013 ec.Emit (OpCodes.Sub);
6015 var labels = new Label [resume_points.Count - first_catch_resume_pc];
6016 for (int i = 0; i < labels.Length; ++i)
6017 labels [i] = resume_points [i + first_catch_resume_pc].PrepareForEmit (ec);
6018 ec.Emit (OpCodes.Switch, labels);
6022 ec.BeginExceptionBlock ();
6025 // The resume points for try section
6027 if (resume_points != null && first_catch_resume_pc != 0) {
6028 if (first_catch_resume_pc < 0)
6029 ec.MarkLabel (resume_point);
6031 // For normal control flow, we want to fall-through the Switch
6032 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
6033 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
6034 ec.EmitInt (first_resume_pc);
6035 ec.Emit (OpCodes.Sub);
6037 var labels = new Label [first_catch_resume_pc > 0 ? first_catch_resume_pc : resume_points.Count];
6038 for (int i = 0; i < labels.Length; ++i)
6039 labels[i] = resume_points[i].PrepareForEmit (ec);
6040 ec.Emit (OpCodes.Switch, labels);
6044 bool IsRewrittenTryCatchFinally ()
6046 var tf = this as TryFinally;
6050 var tc = tf.Statement as TryCatch;
6054 return tf.FinallyBlock.HasAwait || tc.HasClauseWithAwait;
6057 public int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine, TryCatch catchBlock)
6059 if (parent_try_block != null) {
6060 pc = parent_try_block.AddResumePoint (this, pc, stateMachine, catchBlock);
6062 pc = stateMachine.AddResumePoint (this);
6065 if (resume_points == null) {
6066 resume_points = new List<ResumableStatement> ();
6067 first_resume_pc = pc;
6070 if (pc != first_resume_pc + resume_points.Count)
6071 throw new InternalErrorException ("missed an intervening AddResumePoint?");
6073 var tf = this as TryFinally;
6074 if (tf != null && tf.Statement == catchBlock && first_catch_resume_pc < 0) {
6075 first_catch_resume_pc = resume_points.Count;
6078 resume_points.Add (stmt);
6083 public class Lock : TryFinallyBlock
6086 TemporaryVariableReference expr_copy;
6087 TemporaryVariableReference lock_taken;
6089 public Lock (Expression expr, Statement stmt, Location loc)
6095 public Expression Expr {
6101 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6103 expr.FlowAnalysis (fc);
6104 return base.DoFlowAnalysis (fc);
6107 public override bool Resolve (BlockContext ec)
6109 expr = expr.Resolve (ec);
6113 if (!TypeSpec.IsReferenceType (expr.Type) && expr.Type != InternalType.ErrorType) {
6114 ec.Report.Error (185, loc,
6115 "`{0}' is not a reference type as required by the lock statement",
6116 expr.Type.GetSignatureForError ());
6119 if (expr.Type.IsGenericParameter) {
6120 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
6123 VariableReference lv = expr as VariableReference;
6126 locked = lv.IsLockedByStatement;
6127 lv.IsLockedByStatement = true;
6134 // Have to keep original lock value around to unlock same location
6135 // in the case of original value has changed or is null
6137 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
6138 expr_copy.Resolve (ec);
6141 // Ensure Monitor methods are available
6143 if (ResolvePredefinedMethods (ec) > 1) {
6144 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
6145 lock_taken.Resolve (ec);
6148 using (ec.Set (ResolveContext.Options.LockScope)) {
6153 lv.IsLockedByStatement = locked;
6159 protected override void EmitTryBodyPrepare (EmitContext ec)
6161 expr_copy.EmitAssign (ec, expr);
6163 if (lock_taken != null) {
6165 // Initialize ref variable
6167 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
6170 // Monitor.Enter (expr_copy)
6172 expr_copy.Emit (ec);
6173 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
6176 base.EmitTryBodyPrepare (ec);
6179 protected override void EmitTryBody (EmitContext ec)
6182 // Monitor.Enter (expr_copy, ref lock_taken)
6184 if (lock_taken != null) {
6185 expr_copy.Emit (ec);
6186 lock_taken.LocalInfo.CreateBuilder (ec);
6187 lock_taken.AddressOf (ec, AddressOp.Load);
6188 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
6191 Statement.Emit (ec);
6194 public override void EmitFinallyBody (EmitContext ec)
6197 // if (lock_taken) Monitor.Exit (expr_copy)
6199 Label skip = ec.DefineLabel ();
6201 if (lock_taken != null) {
6202 lock_taken.Emit (ec);
6203 ec.Emit (OpCodes.Brfalse_S, skip);
6206 expr_copy.Emit (ec);
6207 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
6209 ec.Emit (OpCodes.Call, m);
6211 ec.MarkLabel (skip);
6214 int ResolvePredefinedMethods (ResolveContext rc)
6216 // Try 4.0 Monitor.Enter (object, ref bool) overload first
6217 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
6221 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
6225 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
6229 protected override void CloneTo (CloneContext clonectx, Statement t)
6231 Lock target = (Lock) t;
6233 target.expr = expr.Clone (clonectx);
6234 target.stmt = Statement.Clone (clonectx);
6237 public override object Accept (StructuralVisitor visitor)
6239 return visitor.Visit (this);
6244 public class Unchecked : Statement {
6247 public Unchecked (Block b, Location loc)
6254 public override bool Resolve (BlockContext ec)
6256 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
6257 return Block.Resolve (ec);
6260 protected override void DoEmit (EmitContext ec)
6262 using (ec.With (EmitContext.Options.CheckedScope, false))
6266 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6268 return Block.FlowAnalysis (fc);
6271 public override Reachability MarkReachable (Reachability rc)
6273 base.MarkReachable (rc);
6274 return Block.MarkReachable (rc);
6277 protected override void CloneTo (CloneContext clonectx, Statement t)
6279 Unchecked target = (Unchecked) t;
6281 target.Block = clonectx.LookupBlock (Block);
6284 public override object Accept (StructuralVisitor visitor)
6286 return visitor.Visit (this);
6290 public class Checked : Statement {
6293 public Checked (Block b, Location loc)
6296 b.Unchecked = false;
6300 public override bool Resolve (BlockContext ec)
6302 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
6303 return Block.Resolve (ec);
6306 protected override void DoEmit (EmitContext ec)
6308 using (ec.With (EmitContext.Options.CheckedScope, true))
6312 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6314 return Block.FlowAnalysis (fc);
6317 public override Reachability MarkReachable (Reachability rc)
6319 base.MarkReachable (rc);
6320 return Block.MarkReachable (rc);
6323 protected override void CloneTo (CloneContext clonectx, Statement t)
6325 Checked target = (Checked) t;
6327 target.Block = clonectx.LookupBlock (Block);
6330 public override object Accept (StructuralVisitor visitor)
6332 return visitor.Visit (this);
6336 public class Unsafe : Statement {
6339 public Unsafe (Block b, Location loc)
6342 Block.Unsafe = true;
6346 public override bool Resolve (BlockContext ec)
6348 if (ec.CurrentIterator != null)
6349 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
6351 using (ec.Set (ResolveContext.Options.UnsafeScope))
6352 return Block.Resolve (ec);
6355 protected override void DoEmit (EmitContext ec)
6360 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6362 return Block.FlowAnalysis (fc);
6365 public override Reachability MarkReachable (Reachability rc)
6367 base.MarkReachable (rc);
6368 return Block.MarkReachable (rc);
6371 protected override void CloneTo (CloneContext clonectx, Statement t)
6373 Unsafe target = (Unsafe) t;
6375 target.Block = clonectx.LookupBlock (Block);
6378 public override object Accept (StructuralVisitor visitor)
6380 return visitor.Visit (this);
6387 public class Fixed : Statement
6389 abstract class Emitter : ShimExpression
6391 protected LocalVariable vi;
6393 protected Emitter (Expression expr, LocalVariable li)
6399 public abstract void EmitExit (EmitContext ec);
6401 public override void FlowAnalysis (FlowAnalysisContext fc)
6403 expr.FlowAnalysis (fc);
6407 sealed class ExpressionEmitter : Emitter {
6408 public ExpressionEmitter (Expression converted, LocalVariable li)
6409 : base (converted, li)
6413 protected override Expression DoResolve (ResolveContext rc)
6415 throw new NotImplementedException ();
6418 public override void Emit (EmitContext ec) {
6420 // Store pointer in pinned location
6426 public override void EmitExit (EmitContext ec)
6429 ec.Emit (OpCodes.Conv_U);
6434 class StringEmitter : Emitter
6436 LocalVariable pinned_string;
6438 public StringEmitter (Expression expr, LocalVariable li)
6443 protected override Expression DoResolve (ResolveContext rc)
6445 pinned_string = new LocalVariable (vi.Block, "$pinned",
6446 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6448 pinned_string.Type = rc.BuiltinTypes.String;
6451 eclass = ExprClass.Variable;
6452 type = rc.BuiltinTypes.Int;
6456 public override void Emit (EmitContext ec)
6458 pinned_string.CreateBuilder (ec);
6461 pinned_string.EmitAssign (ec);
6463 // TODO: Should use Binary::Add
6464 pinned_string.Emit (ec);
6465 ec.Emit (OpCodes.Conv_I);
6467 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6471 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6472 //pe.InstanceExpression = pinned_string;
6473 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6475 ec.Emit (OpCodes.Add);
6479 public override void EmitExit (EmitContext ec)
6482 pinned_string.EmitAssign (ec);
6486 public class VariableDeclaration : BlockVariable
6488 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6493 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6495 if (!Variable.Type.IsPointer && li == Variable) {
6496 bc.Report.Error (209, TypeExpression.Location,
6497 "The type of locals declared in a fixed statement must be a pointer type");
6501 var res = initializer.Resolve (bc);
6508 var ac = res.Type as ArrayContainer;
6510 TypeSpec array_type = ac.Element;
6513 // Provided that array_type is unmanaged,
6515 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6518 Expression res_init;
6519 if (ExpressionAnalyzer.IsInexpensiveLoad (res)) {
6522 var expr_variable = LocalVariable.CreateCompilerGenerated (ac, bc.CurrentBlock, loc);
6523 res_init = new CompilerAssign (expr_variable.CreateReferenceExpression (bc, loc), res, loc);
6524 res = expr_variable.CreateReferenceExpression (bc, loc);
6528 // and T* is implicitly convertible to the
6529 // pointer type given in the fixed statement.
6531 ArrayPtr array_ptr = new ArrayPtr (res, array_type, loc);
6533 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6534 if (converted == null)
6538 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6540 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6541 new Binary (Binary.Operator.Equality, res_init, new NullLiteral (loc)),
6542 new Binary (Binary.Operator.Equality, new MemberAccess (res, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6543 new NullLiteral (loc),
6546 converted = converted.Resolve (bc);
6548 return new ExpressionEmitter (converted, li);
6554 if (res.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6555 return new StringEmitter (res, li).Resolve (bc);
6558 // Case 3: fixed buffer
6559 if (res is FixedBufferPtr) {
6560 return new ExpressionEmitter (res, li);
6563 bool already_fixed = true;
6566 // Case 4: & object.
6568 Unary u = res as Unary;
6570 if (u.Oper == Unary.Operator.AddressOf) {
6571 IVariableReference vr = u.Expr as IVariableReference;
6572 if (vr == null || !vr.IsFixed) {
6573 already_fixed = false;
6576 } else if (initializer is Cast) {
6577 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6581 if (already_fixed) {
6582 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6585 res = Convert.ImplicitConversionRequired (bc, res, li.Type, loc);
6586 return new ExpressionEmitter (res, li);
6591 VariableDeclaration decl;
6592 Statement statement;
6595 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6604 public Statement Statement {
6610 public BlockVariable Variables {
6618 public override bool Resolve (BlockContext bc)
6620 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6621 if (!decl.Resolve (bc))
6625 return statement.Resolve (bc);
6628 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6630 decl.FlowAnalysis (fc);
6631 return statement.FlowAnalysis (fc);
6634 protected override void DoEmit (EmitContext ec)
6636 decl.Variable.CreateBuilder (ec);
6637 decl.Initializer.Emit (ec);
6638 if (decl.Declarators != null) {
6639 foreach (var d in decl.Declarators) {
6640 d.Variable.CreateBuilder (ec);
6641 d.Initializer.Emit (ec);
6645 statement.Emit (ec);
6651 // Clear the pinned variable
6653 ((Emitter) decl.Initializer).EmitExit (ec);
6654 if (decl.Declarators != null) {
6655 foreach (var d in decl.Declarators) {
6656 ((Emitter)d.Initializer).EmitExit (ec);
6661 public override Reachability MarkReachable (Reachability rc)
6663 base.MarkReachable (rc);
6665 decl.MarkReachable (rc);
6667 rc = statement.MarkReachable (rc);
6669 // TODO: What if there is local exit?
6670 has_ret = rc.IsUnreachable;
6674 protected override void CloneTo (CloneContext clonectx, Statement t)
6676 Fixed target = (Fixed) t;
6678 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6679 target.statement = statement.Clone (clonectx);
6682 public override object Accept (StructuralVisitor visitor)
6684 return visitor.Visit (this);
6688 public class Catch : Statement
6690 class CatchVariableStore : Statement
6692 readonly Catch ctch;
6694 public CatchVariableStore (Catch ctch)
6699 protected override void CloneTo (CloneContext clonectx, Statement target)
6703 protected override void DoEmit (EmitContext ec)
6705 // Emits catch variable debug information inside correct block
6706 ctch.EmitCatchVariableStore (ec);
6709 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6715 class FilterStatement : Statement
6717 readonly Catch ctch;
6719 public FilterStatement (Catch ctch)
6724 protected override void CloneTo (CloneContext clonectx, Statement target)
6728 protected override void DoEmit (EmitContext ec)
6730 if (ctch.li != null) {
6731 if (ctch.hoisted_temp != null)
6732 ctch.hoisted_temp.Emit (ec);
6736 if (!ctch.IsGeneral && ctch.type.Kind == MemberKind.TypeParameter)
6737 ec.Emit (OpCodes.Box, ctch.type);
6740 var expr_start = ec.DefineLabel ();
6741 var end = ec.DefineLabel ();
6743 ec.Emit (OpCodes.Brtrue_S, expr_start);
6745 ec.Emit (OpCodes.Br, end);
6746 ec.MarkLabel (expr_start);
6748 ctch.Filter.Emit (ec);
6751 ec.Emit (OpCodes.Endfilter);
6752 ec.BeginFilterHandler ();
6753 ec.Emit (OpCodes.Pop);
6756 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6758 ctch.Filter.FlowAnalysis (fc);
6762 public override bool Resolve (BlockContext bc)
6764 ctch.Filter = ctch.Filter.Resolve (bc);
6766 if (ctch.Filter != null) {
6767 if (ctch.Filter.ContainsEmitWithAwait ()) {
6768 bc.Report.Error (7094, ctch.Filter.Location, "The `await' operator cannot be used in the filter expression of a catch clause");
6771 var c = ctch.Filter as Constant;
6772 if (c != null && !c.IsDefaultValue) {
6773 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6781 ExplicitBlock block;
6783 FullNamedExpression type_expr;
6784 CompilerAssign assign;
6786 LocalTemporary hoisted_temp;
6788 public Catch (ExplicitBlock block, Location loc)
6796 public ExplicitBlock Block {
6802 public TypeSpec CatchType {
6808 public Expression Filter {
6812 public bool IsGeneral {
6814 return type_expr == null;
6818 public FullNamedExpression TypeExpression {
6827 public LocalVariable Variable {
6838 protected override void DoEmit (EmitContext ec)
6840 if (Filter != null) {
6841 ec.BeginExceptionFilterBlock ();
6842 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6844 if (Block.HasAwait) {
6845 Block.EmitScopeInitialization (ec);
6854 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6856 ec.BeginCatchBlock (CatchType);
6859 ec.Emit (OpCodes.Pop);
6861 if (Block.HasAwait) {
6863 EmitCatchVariableStore (ec);
6869 void EmitCatchVariableStore (EmitContext ec)
6871 li.CreateBuilder (ec);
6874 // For hoisted catch variable we have to use a temporary local variable
6875 // for captured variable initialization during storey setup because variable
6876 // needs to be on the stack after storey instance for stfld operation
6878 if (li.HoistedVariant != null) {
6879 hoisted_temp = new LocalTemporary (li.Type);
6880 hoisted_temp.Store (ec);
6882 // switch to assignment from temporary variable and not from top of the stack
6883 assign.UpdateSource (hoisted_temp);
6887 public override bool Resolve (BlockContext bc)
6889 using (bc.Set (ResolveContext.Options.CatchScope)) {
6890 if (type_expr == null) {
6891 if (CreateExceptionVariable (bc.Module.Compiler.BuiltinTypes.Object)) {
6892 if (!block.HasAwait || Filter != null)
6893 block.AddScopeStatement (new CatchVariableStore (this));
6895 Expression source = new EmptyExpression (li.Type);
6896 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6897 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6900 type = type_expr.ResolveAsType (bc);
6905 CreateExceptionVariable (type);
6907 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, bc.BuiltinTypes.Exception, false)) {
6908 bc.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6909 } else if (li != null) {
6911 li.PrepareAssignmentAnalysis (bc);
6913 // source variable is at the top of the stack
6914 Expression source = new EmptyExpression (li.Type);
6915 if (li.Type.IsGenericParameter)
6916 source = new UnboxCast (source, li.Type);
6918 if (!block.HasAwait || Filter != null)
6919 block.AddScopeStatement (new CatchVariableStore (this));
6922 // Uses Location.Null to hide from symbol file
6924 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6925 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6929 if (Filter != null) {
6930 Block.AddScopeStatement (new FilterStatement (this));
6933 Block.SetCatchBlock ();
6934 return Block.Resolve (bc);
6938 bool CreateExceptionVariable (TypeSpec type)
6940 if (!Block.HasAwait)
6943 // TODO: Scan the block for rethrow expression
6944 //if (!Block.HasRethrow)
6947 li = LocalVariable.CreateCompilerGenerated (type, block, Location.Null);
6951 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6953 if (li != null && !li.IsCompilerGenerated) {
6954 fc.SetVariableAssigned (li.VariableInfo, true);
6957 return block.FlowAnalysis (fc);
6960 public override Reachability MarkReachable (Reachability rc)
6962 base.MarkReachable (rc);
6964 var c = Filter as Constant;
6965 if (c != null && c.IsDefaultValue)
6966 return Reachability.CreateUnreachable ();
6968 return block.MarkReachable (rc);
6971 protected override void CloneTo (CloneContext clonectx, Statement t)
6973 Catch target = (Catch) t;
6975 if (type_expr != null)
6976 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6979 target.Filter = Filter.Clone (clonectx);
6981 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6985 public class TryFinally : TryFinallyBlock
6988 List<DefiniteAssignmentBitSet> try_exit_dat;
6989 List<Label> redirected_jumps;
6990 Label? start_fin_label;
6992 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6998 public ExplicitBlock FinallyBlock {
7004 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
7006 if (try_exit_dat == null)
7007 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
7009 try_exit_dat.Add (vector);
7012 public override bool Resolve (BlockContext bc)
7014 bool ok = base.Resolve (bc);
7016 fini.SetFinallyBlock ();
7017 using (bc.Set (ResolveContext.Options.FinallyScope)) {
7018 ok &= fini.Resolve (bc);
7024 protected override void EmitTryBody (EmitContext ec)
7026 if (fini.HasAwait) {
7027 if (ec.TryFinallyUnwind == null)
7028 ec.TryFinallyUnwind = new List<TryFinally> ();
7030 ec.TryFinallyUnwind.Add (this);
7033 if (first_catch_resume_pc < 0 && stmt is TryCatch)
7034 ec.EndExceptionBlock ();
7036 ec.TryFinallyUnwind.Remove (this);
7038 if (start_fin_label != null)
7039 ec.MarkLabel (start_fin_label.Value);
7047 protected override bool EmitBeginFinallyBlock (EmitContext ec)
7052 return base.EmitBeginFinallyBlock (ec);
7055 public override void EmitFinallyBody (EmitContext ec)
7057 if (!fini.HasAwait) {
7063 // Emits catch block like
7065 // catch (object temp) {
7066 // this.exception_field = temp;
7069 var type = ec.BuiltinTypes.Object;
7070 ec.BeginCatchBlock (type);
7072 var temp = ec.GetTemporaryLocal (type);
7073 ec.Emit (OpCodes.Stloc, temp);
7075 var exception_field = ec.GetTemporaryField (type);
7076 exception_field.AutomaticallyReuse = false;
7078 ec.Emit (OpCodes.Ldloc, temp);
7079 exception_field.EmitAssignFromStack (ec);
7081 ec.EndExceptionBlock ();
7083 ec.FreeTemporaryLocal (temp, type);
7088 // Emits exception rethrow
7090 // if (this.exception_field != null)
7091 // throw this.exception_field;
7093 exception_field.Emit (ec);
7094 var skip_throw = ec.DefineLabel ();
7095 ec.Emit (OpCodes.Brfalse_S, skip_throw);
7096 exception_field.Emit (ec);
7097 ec.Emit (OpCodes.Throw);
7098 ec.MarkLabel (skip_throw);
7100 exception_field.PrepareCleanup (ec);
7102 EmitUnwindFinallyTable (ec);
7105 bool IsParentBlock (Block block)
7107 for (Block b = fini; b != null; b = b.Parent) {
7115 public static Label EmitRedirectedJump (EmitContext ec, AsyncInitializer initializer, Label label, Block labelBlock)
7118 if (labelBlock != null) {
7119 for (idx = ec.TryFinallyUnwind.Count; idx != 0; --idx) {
7120 var fin = ec.TryFinallyUnwind [idx - 1];
7121 if (!fin.IsParentBlock (labelBlock))
7128 bool set_return_state = true;
7130 for (; idx < ec.TryFinallyUnwind.Count; ++idx) {
7131 var fin = ec.TryFinallyUnwind [idx];
7132 if (labelBlock != null && !fin.IsParentBlock (labelBlock))
7135 fin.EmitRedirectedExit (ec, label, initializer, set_return_state);
7136 set_return_state = false;
7138 if (fin.start_fin_label == null) {
7139 fin.start_fin_label = ec.DefineLabel ();
7142 label = fin.start_fin_label.Value;
7148 public static Label EmitRedirectedReturn (EmitContext ec, AsyncInitializer initializer)
7150 return EmitRedirectedJump (ec, initializer, initializer.BodyEnd, null);
7153 void EmitRedirectedExit (EmitContext ec, Label label, AsyncInitializer initializer, bool setReturnState)
7155 if (redirected_jumps == null) {
7156 redirected_jumps = new List<Label> ();
7158 // Add fallthrough label
7159 redirected_jumps.Add (ec.DefineLabel ());
7162 initializer.HoistedReturnState = ec.GetTemporaryField (ec.Module.Compiler.BuiltinTypes.Int, true);
7165 int index = redirected_jumps.IndexOf (label);
7167 redirected_jumps.Add (label);
7168 index = redirected_jumps.Count - 1;
7172 // Indicates we have captured exit jump
7174 if (setReturnState) {
7175 var value = new IntConstant (initializer.HoistedReturnState.Type, index, Location.Null);
7176 initializer.HoistedReturnState.EmitAssign (ec, value, false, false);
7181 // Emits state table of jumps outside of try block and reload of return
7182 // value when try block returns value
7184 void EmitUnwindFinallyTable (EmitContext ec)
7186 if (redirected_jumps == null)
7189 var initializer = (AsyncInitializer)ec.CurrentAnonymousMethod;
7190 initializer.HoistedReturnState.EmitLoad (ec);
7191 ec.Emit (OpCodes.Switch, redirected_jumps.ToArray ());
7193 // Mark fallthrough label
7194 ec.MarkLabel (redirected_jumps [0]);
7197 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7199 var da = fc.BranchDefiniteAssignment ();
7201 var tf = fc.TryFinally;
7202 fc.TryFinally = this;
7204 var res_stmt = Statement.FlowAnalysis (fc);
7208 var try_da = fc.DefiniteAssignment;
7209 fc.DefiniteAssignment = da;
7211 var res_fin = fini.FlowAnalysis (fc);
7213 if (try_exit_dat != null) {
7215 // try block has global exit but we need to run definite assignment check
7216 // for parameter block out parameter after finally block because it's always
7217 // executed before exit
7219 foreach (var try_da_part in try_exit_dat)
7220 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
7222 try_exit_dat = null;
7225 fc.DefiniteAssignment |= try_da;
7226 return res_stmt | res_fin;
7229 public override Reachability MarkReachable (Reachability rc)
7232 // Mark finally block first for any exit statement in try block
7233 // to know whether the code which follows finally is reachable
7235 return fini.MarkReachable (rc) | base.MarkReachable (rc);
7238 protected override void CloneTo (CloneContext clonectx, Statement t)
7240 TryFinally target = (TryFinally) t;
7242 target.stmt = stmt.Clone (clonectx);
7244 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
7247 public override object Accept (StructuralVisitor visitor)
7249 return visitor.Visit (this);
7253 public class TryCatch : ExceptionStatement
7256 List<Catch> clauses;
7257 readonly bool inside_try_finally;
7258 List<Catch> catch_sm;
7260 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
7264 this.clauses = catch_clauses;
7265 this.inside_try_finally = inside_try_finally;
7268 public List<Catch> Clauses {
7274 public bool HasClauseWithAwait {
7276 return catch_sm != null;
7280 public bool IsTryCatchFinally {
7282 return inside_try_finally;
7286 public override bool Resolve (BlockContext bc)
7290 using (bc.Set (ResolveContext.Options.TryScope)) {
7292 parent_try_block = bc.CurrentTryBlock;
7294 if (IsTryCatchFinally) {
7295 ok = Block.Resolve (bc);
7297 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
7298 bc.CurrentTryBlock = this;
7299 ok = Block.Resolve (bc);
7300 bc.CurrentTryBlock = parent_try_block;
7305 var prev_catch = bc.CurrentTryCatch;
7306 bc.CurrentTryCatch = this;
7308 for (int i = 0; i < clauses.Count; ++i) {
7311 ok &= c.Resolve (bc);
7313 if (c.Block.HasAwait) {
7314 if (catch_sm == null)
7315 catch_sm = new List<Catch> ();
7320 if (c.Filter != null)
7323 TypeSpec resolved_type = c.CatchType;
7324 if (resolved_type == null)
7327 for (int ii = 0; ii < clauses.Count; ++ii) {
7331 if (clauses[ii].Filter != null)
7334 if (clauses[ii].IsGeneral) {
7335 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
7338 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
7341 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
7344 bc.Report.Warning (1058, 1, c.loc,
7345 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
7353 var ct = clauses[ii].CatchType;
7357 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
7358 bc.Report.Error (160, c.loc,
7359 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
7360 ct.GetSignatureForError ());
7366 bc.CurrentTryCatch = prev_catch;
7368 return base.Resolve (bc) && ok;
7371 protected sealed override void DoEmit (EmitContext ec)
7373 if (!inside_try_finally)
7374 EmitTryBodyPrepare (ec);
7378 LocalBuilder state_variable = null;
7379 foreach (Catch c in clauses) {
7382 if (catch_sm != null) {
7383 if (state_variable == null) {
7385 // Cannot reuse temp variable because non-catch path assumes the value is 0
7386 // which may not be true for reused local variable
7388 state_variable = ec.DeclareLocal (ec.Module.Compiler.BuiltinTypes.Int, false);
7391 var index = catch_sm.IndexOf (c);
7395 ec.EmitInt (index + 1);
7396 ec.Emit (OpCodes.Stloc, state_variable);
7400 if (state_variable == null) {
7401 if (!inside_try_finally)
7402 ec.EndExceptionBlock ();
7404 ec.EndExceptionBlock ();
7406 ec.Emit (OpCodes.Ldloc, state_variable);
7408 var labels = new Label [catch_sm.Count + 1];
7409 for (int i = 0; i < labels.Length; ++i) {
7410 labels [i] = ec.DefineLabel ();
7413 var end = ec.DefineLabel ();
7414 ec.Emit (OpCodes.Switch, labels);
7416 // 0 value is default label
7417 ec.MarkLabel (labels [0]);
7418 ec.Emit (OpCodes.Br, end);
7420 var atv = ec.AsyncThrowVariable;
7422 for (int i = 0; i < catch_sm.Count; ++i) {
7423 if (c != null && c.Block.HasReachableClosingBrace)
7424 ec.Emit (OpCodes.Br, end);
7426 ec.MarkLabel (labels [i + 1]);
7428 ec.AsyncThrowVariable = c.Variable;
7431 ec.AsyncThrowVariable = atv;
7437 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7439 var start_fc = fc.BranchDefiniteAssignment ();
7440 var res = Block.FlowAnalysis (fc);
7442 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
7444 foreach (var c in clauses) {
7445 fc.BranchDefiniteAssignment (start_fc);
7446 if (!c.FlowAnalysis (fc)) {
7448 try_fc = fc.DefiniteAssignment;
7450 try_fc &= fc.DefiniteAssignment;
7456 fc.DefiniteAssignment = try_fc ?? start_fc;
7457 parent_try_block = null;
7461 public override Reachability MarkReachable (Reachability rc)
7463 if (rc.IsUnreachable)
7466 base.MarkReachable (rc);
7468 var tc_rc = Block.MarkReachable (rc);
7470 foreach (var c in clauses)
7471 tc_rc &= c.MarkReachable (rc);
7476 protected override void CloneTo (CloneContext clonectx, Statement t)
7478 TryCatch target = (TryCatch) t;
7480 target.Block = clonectx.LookupBlock (Block);
7481 if (clauses != null){
7482 target.clauses = new List<Catch> ();
7483 foreach (Catch c in clauses)
7484 target.clauses.Add ((Catch) c.Clone (clonectx));
7488 public override object Accept (StructuralVisitor visitor)
7490 return visitor.Visit (this);
7494 public class Using : TryFinallyBlock
7496 public class VariableDeclaration : BlockVariable
7498 Statement dispose_call;
7500 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
7505 public VariableDeclaration (LocalVariable li, Location loc)
7512 public VariableDeclaration (Expression expr)
7515 loc = expr.Location;
7521 public bool IsNested { get; private set; }
7525 public void EmitDispose (EmitContext ec)
7527 dispose_call.Emit (ec);
7530 public override bool Resolve (BlockContext bc)
7535 return base.Resolve (bc, false);
7538 public Expression ResolveExpression (BlockContext bc)
7540 var e = Initializer.Resolve (bc);
7544 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
7545 Initializer = ResolveInitializer (bc, Variable, e);
7549 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
7551 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
7552 initializer = initializer.Resolve (bc);
7553 if (initializer == null)
7556 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
7557 Arguments args = new Arguments (1);
7558 args.Add (new Argument (initializer));
7559 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
7560 if (initializer == null)
7563 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
7564 dispose_call = CreateDisposeCall (bc, var);
7565 dispose_call.Resolve (bc);
7567 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
7570 if (li == Variable) {
7571 CheckIDiposableConversion (bc, li, initializer);
7572 dispose_call = CreateDisposeCall (bc, li);
7573 dispose_call.Resolve (bc);
7576 return base.ResolveInitializer (bc, li, initializer);
7579 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7583 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
7584 if (type.IsNullableType) {
7585 // it's handled in CreateDisposeCall
7589 if (type != InternalType.ErrorType) {
7590 bc.Report.SymbolRelatedToPreviousError (type);
7591 var loc = type_expr == null ? initializer.Location : type_expr.Location;
7592 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
7593 type.GetSignatureForError ());
7600 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
7602 var target = bc.BuiltinTypes.IDisposable;
7603 var tp = type as TypeParameterSpec;
7605 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
7607 return type.ImplementsInterface (target, false);
7610 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7612 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
7614 var loc = lv.Location;
7616 var idt = bc.BuiltinTypes.IDisposable;
7617 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7619 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7620 dispose_mg.InstanceExpression = type.IsNullableType ?
7621 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
7625 // Hide it from symbol file via null location
7627 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
7629 // Add conditional call when disposing possible null variable
7630 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
7631 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
7636 public void ResolveDeclaratorInitializer (BlockContext bc)
7638 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
7641 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
7643 for (int i = declarators.Count - 1; i >= 0; --i) {
7644 var d = declarators [i];
7645 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
7646 vd.Initializer = d.Initializer;
7648 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
7649 vd.dispose_call.Resolve (bc);
7651 stmt = new Using (vd, stmt, d.Variable.Location);
7658 public override object Accept (StructuralVisitor visitor)
7660 return visitor.Visit (this);
7664 VariableDeclaration decl;
7666 public Using (VariableDeclaration decl, Statement stmt, Location loc)
7672 public Using (Expression expr, Statement stmt, Location loc)
7675 this.decl = new VariableDeclaration (expr);
7680 public Expression Expr {
7682 return decl.Variable == null ? decl.Initializer : null;
7686 public BlockVariable Variables {
7694 public override void Emit (EmitContext ec)
7697 // Don't emit sequence point it will be set on variable declaration
7702 protected override void EmitTryBodyPrepare (EmitContext ec)
7705 base.EmitTryBodyPrepare (ec);
7708 protected override void EmitTryBody (EmitContext ec)
7713 public override void EmitFinallyBody (EmitContext ec)
7715 decl.EmitDispose (ec);
7718 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7720 decl.FlowAnalysis (fc);
7721 return stmt.FlowAnalysis (fc);
7724 public override Reachability MarkReachable (Reachability rc)
7726 decl.MarkReachable (rc);
7727 return base.MarkReachable (rc);
7730 public override bool Resolve (BlockContext ec)
7732 VariableReference vr;
7733 bool vr_locked = false;
7735 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7736 if (decl.Variable == null) {
7737 vr = decl.ResolveExpression (ec) as VariableReference;
7739 vr_locked = vr.IsLockedByStatement;
7740 vr.IsLockedByStatement = true;
7743 if (decl.IsNested) {
7744 decl.ResolveDeclaratorInitializer (ec);
7746 if (!decl.Resolve (ec))
7749 if (decl.Declarators != null) {
7750 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7758 var ok = base.Resolve (ec);
7761 vr.IsLockedByStatement = vr_locked;
7766 protected override void CloneTo (CloneContext clonectx, Statement t)
7768 Using target = (Using) t;
7770 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7771 target.stmt = stmt.Clone (clonectx);
7774 public override object Accept (StructuralVisitor visitor)
7776 return visitor.Visit (this);
7781 /// Implementation of the foreach C# statement
7783 public class Foreach : LoopStatement
7785 abstract class IteratorStatement : Statement
7787 protected readonly Foreach for_each;
7789 protected IteratorStatement (Foreach @foreach)
7791 this.for_each = @foreach;
7792 this.loc = @foreach.expr.Location;
7795 protected override void CloneTo (CloneContext clonectx, Statement target)
7797 throw new NotImplementedException ();
7800 public override void Emit (EmitContext ec)
7802 if (ec.EmitAccurateDebugInfo) {
7803 ec.Emit (OpCodes.Nop);
7809 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7811 throw new NotImplementedException ();
7815 sealed class ArrayForeach : IteratorStatement
7817 TemporaryVariableReference[] lengths;
7818 Expression [] length_exprs;
7819 StatementExpression[] counter;
7820 TemporaryVariableReference[] variables;
7822 TemporaryVariableReference copy;
7824 public ArrayForeach (Foreach @foreach, int rank)
7827 counter = new StatementExpression[rank];
7828 variables = new TemporaryVariableReference[rank];
7829 length_exprs = new Expression [rank];
7832 // Only use temporary length variables when dealing with
7833 // multi-dimensional arrays
7836 lengths = new TemporaryVariableReference [rank];
7839 public override bool Resolve (BlockContext ec)
7841 Block variables_block = for_each.variable.Block;
7842 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7845 int rank = length_exprs.Length;
7846 Arguments list = new Arguments (rank);
7847 for (int i = 0; i < rank; i++) {
7848 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7850 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7851 counter[i].Resolve (ec);
7854 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7856 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7857 lengths[i].Resolve (ec);
7859 Arguments args = new Arguments (1);
7860 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7861 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7864 list.Add (new Argument (v));
7867 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7872 if (for_each.type is VarExpr) {
7873 // Infer implicitly typed local variable from foreach array type
7874 var_type = access.Type;
7876 var_type = for_each.type.ResolveAsType (ec);
7878 if (var_type == null)
7881 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7886 for_each.variable.Type = var_type;
7888 var prev_block = ec.CurrentBlock;
7889 ec.CurrentBlock = variables_block;
7890 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7891 ec.CurrentBlock = prev_block;
7893 if (variable_ref == null)
7896 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7898 return for_each.body.Resolve (ec);
7901 protected override void DoEmit (EmitContext ec)
7903 copy.EmitAssign (ec, for_each.expr);
7905 int rank = length_exprs.Length;
7906 Label[] test = new Label [rank];
7907 Label[] loop = new Label [rank];
7909 for (int i = 0; i < rank; i++) {
7910 test [i] = ec.DefineLabel ();
7911 loop [i] = ec.DefineLabel ();
7913 if (lengths != null)
7914 lengths [i].EmitAssign (ec, length_exprs [i]);
7917 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7918 for (int i = 0; i < rank; i++) {
7919 variables [i].EmitAssign (ec, zero);
7921 ec.Emit (OpCodes.Br, test [i]);
7922 ec.MarkLabel (loop [i]);
7925 for_each.body.Emit (ec);
7927 ec.MarkLabel (ec.LoopBegin);
7928 ec.Mark (for_each.expr.Location);
7930 for (int i = rank - 1; i >= 0; i--){
7931 counter [i].Emit (ec);
7933 ec.MarkLabel (test [i]);
7934 variables [i].Emit (ec);
7936 if (lengths != null)
7937 lengths [i].Emit (ec);
7939 length_exprs [i].Emit (ec);
7941 ec.Emit (OpCodes.Blt, loop [i]);
7944 ec.MarkLabel (ec.LoopEnd);
7948 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7950 class RuntimeDispose : Using.VariableDeclaration
7952 public RuntimeDispose (LocalVariable lv, Location loc)
7958 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7960 // Defered to runtime check
7963 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7965 var idt = bc.BuiltinTypes.IDisposable;
7968 // Fabricates code like
7970 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7973 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7975 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7976 dispose_variable.CreateReferenceExpression (bc, loc),
7977 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7978 loc), new NullLiteral (loc));
7980 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7982 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7983 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7985 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7986 return new If (idisaposable_test, dispose, loc);
7990 LocalVariable variable;
7992 Statement statement;
7993 ExpressionStatement init;
7994 TemporaryVariableReference enumerator_variable;
7995 bool ambiguous_getenumerator_name;
7997 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
8000 this.variable = var;
8004 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
8006 rc.Report.SymbolRelatedToPreviousError (enumerator);
8007 rc.Report.Error (202, loc,
8008 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
8009 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
8012 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
8015 // Option 1: Try to match by name GetEnumerator first
8017 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
8018 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
8020 var mg = mexpr as MethodGroupExpr;
8022 mg.InstanceExpression = expr;
8023 Arguments args = new Arguments (0);
8024 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly | OverloadResolver.Restrictions.GetEnumeratorLookup);
8026 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
8027 if (ambiguous_getenumerator_name)
8030 if (mg != null && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
8036 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
8039 PredefinedMember<MethodSpec> iface_candidate = null;
8040 var ptypes = rc.Module.PredefinedTypes;
8041 var gen_ienumerable = ptypes.IEnumerableGeneric;
8042 if (!gen_ienumerable.Define ())
8043 gen_ienumerable = null;
8045 var ifaces = t.Interfaces;
8046 if (ifaces != null) {
8047 foreach (var iface in ifaces) {
8048 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
8049 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
8050 rc.Report.SymbolRelatedToPreviousError (expr.Type);
8051 rc.Report.Error (1640, loc,
8052 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
8053 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
8058 // TODO: Cache this somehow
8059 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
8060 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
8065 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
8066 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
8071 if (iface_candidate == null) {
8072 if (expr.Type != InternalType.ErrorType) {
8073 rc.Report.Error (1579, loc,
8074 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
8075 expr.Type.GetSignatureForError (), "GetEnumerator");
8081 var method = iface_candidate.Resolve (loc);
8085 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
8086 mg.InstanceExpression = expr;
8090 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
8092 var ms = MemberCache.FindMember (enumerator.ReturnType,
8093 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
8094 BindingRestriction.InstanceOnly) as MethodSpec;
8096 if (ms == null || !ms.IsPublic) {
8097 Error_WrongEnumerator (rc, enumerator);
8101 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
8104 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
8106 var ps = MemberCache.FindMember (enumerator.ReturnType,
8107 MemberFilter.Property ("Current", null),
8108 BindingRestriction.InstanceOnly) as PropertySpec;
8110 if (ps == null || !ps.IsPublic) {
8111 Error_WrongEnumerator (rc, enumerator);
8118 public override bool Resolve (BlockContext ec)
8120 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
8123 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
8124 } else if (expr.Type.IsNullableType) {
8125 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
8128 var get_enumerator_mg = ResolveGetEnumerator (ec);
8129 if (get_enumerator_mg == null) {
8133 var get_enumerator = get_enumerator_mg.BestCandidate;
8134 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
8135 enumerator_variable.Resolve (ec);
8137 // Prepare bool MoveNext ()
8138 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
8139 if (move_next_mg == null) {
8143 move_next_mg.InstanceExpression = enumerator_variable;
8145 // Prepare ~T~ Current { get; }
8146 var current_prop = ResolveCurrent (ec, get_enumerator);
8147 if (current_prop == null) {
8151 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
8152 if (current_pe == null)
8155 VarExpr ve = for_each.type as VarExpr;
8159 // Source type is dynamic, set element type to dynamic too
8160 variable.Type = ec.BuiltinTypes.Dynamic;
8162 // Infer implicitly typed local variable from foreach enumerable type
8163 variable.Type = current_pe.Type;
8167 // Explicit cast of dynamic collection elements has to be done at runtime
8168 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
8171 variable.Type = for_each.type.ResolveAsType (ec);
8173 if (variable.Type == null)
8176 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
8177 if (current_pe == null)
8181 var prev_block = ec.CurrentBlock;
8182 ec.CurrentBlock = for_each.variable.Block;
8183 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
8184 ec.CurrentBlock = prev_block;
8185 if (variable_ref == null)
8188 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
8190 var init = new Invocation.Predefined (get_enumerator_mg, null);
8192 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
8193 for_each.body, Location.Null);
8195 var enum_type = enumerator_variable.Type;
8198 // Add Dispose method call when enumerator can be IDisposable
8200 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
8201 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
8203 // Runtime Dispose check
8205 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
8206 vd.Initializer = init;
8207 statement = new Using (vd, statement, Location.Null);
8210 // No Dispose call needed
8212 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
8213 this.init.Resolve (ec);
8217 // Static Dispose check
8219 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
8220 vd.Initializer = init;
8221 statement = new Using (vd, statement, Location.Null);
8224 return statement.Resolve (ec);
8227 protected override void DoEmit (EmitContext ec)
8229 enumerator_variable.LocalInfo.CreateBuilder (ec);
8232 init.EmitStatement (ec);
8234 statement.Emit (ec);
8237 #region IErrorHandler Members
8239 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
8241 ec.Report.SymbolRelatedToPreviousError (best);
8242 ec.Report.Warning (278, 2, expr.Location,
8243 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
8244 expr.Type.GetSignatureForError (), "enumerable",
8245 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
8247 ambiguous_getenumerator_name = true;
8251 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
8256 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
8261 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
8270 LocalVariable variable;
8274 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
8278 this.variable = var;
8284 public Expression Expr {
8285 get { return expr; }
8288 public Expression TypeExpression {
8289 get { return type; }
8292 public LocalVariable Variable {
8293 get { return variable; }
8296 public override Reachability MarkReachable (Reachability rc)
8298 base.MarkReachable (rc);
8300 body.MarkReachable (rc);
8305 public override bool Resolve (BlockContext ec)
8307 expr = expr.Resolve (ec);
8312 ec.Report.Error (186, loc, "Use of null is not valid in this context");
8316 body.AddStatement (Statement);
8318 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
8319 Statement = new ArrayForeach (this, 1);
8320 } else if (expr.Type is ArrayContainer) {
8321 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
8323 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
8324 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
8325 expr.ExprClassName);
8329 Statement = new CollectionForeach (this, variable, expr);
8332 return base.Resolve (ec);
8335 protected override void DoEmit (EmitContext ec)
8337 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
8338 ec.LoopBegin = ec.DefineLabel ();
8339 ec.LoopEnd = ec.DefineLabel ();
8341 if (!(Statement is Block))
8342 ec.BeginCompilerScope (variable.Block.Explicit.GetDebugSymbolScopeIndex ());
8344 variable.CreateBuilder (ec);
8346 Statement.Emit (ec);
8348 if (!(Statement is Block))
8351 ec.LoopBegin = old_begin;
8352 ec.LoopEnd = old_end;
8355 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8357 expr.FlowAnalysis (fc);
8359 var da = fc.BranchDefiniteAssignment ();
8360 body.FlowAnalysis (fc);
8361 fc.DefiniteAssignment = da;
8365 protected override void CloneTo (CloneContext clonectx, Statement t)
8367 Foreach target = (Foreach) t;
8369 target.type = type.Clone (clonectx);
8370 target.expr = expr.Clone (clonectx);
8371 target.body = (Block) body.Clone (clonectx);
8372 target.Statement = Statement.Clone (clonectx);
8375 public override object Accept (StructuralVisitor visitor)
8377 return visitor.Visit (this);
8381 class SentinelStatement: Statement
8383 protected override void CloneTo (CloneContext clonectx, Statement target)
8387 protected override void DoEmit (EmitContext ec)
8389 var l = ec.DefineLabel ();
8391 ec.Emit (OpCodes.Br_S, l);
8394 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
8396 throw new NotImplementedException ();