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 return DoFlowAnalysis (fc);
96 // Special handling cases
98 if (this is Block || this is SwitchLabel) {
99 return DoFlowAnalysis (fc);
102 if (this is EmptyStatement)
105 if (fc.UnreachableReported)
108 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
109 fc.UnreachableReported = true;
113 public virtual Reachability MarkReachable (Reachability rc)
115 if (!rc.IsUnreachable)
121 protected void CheckExitBoundaries (BlockContext bc, Block scope)
123 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
124 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
128 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
129 if (b.IsFinallyBlock) {
130 Error_FinallyClauseExit (bc);
136 protected void Error_FinallyClauseExit (BlockContext bc)
138 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
142 public sealed class EmptyStatement : Statement
144 public EmptyStatement (Location loc)
149 public override bool Resolve (BlockContext ec)
154 public override void Emit (EmitContext ec)
158 protected override void DoEmit (EmitContext ec)
160 throw new NotSupportedException ();
163 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
168 protected override void CloneTo (CloneContext clonectx, Statement target)
173 public override object Accept (StructuralVisitor visitor)
175 return visitor.Visit (this);
179 public class If : Statement {
181 public Statement TrueStatement;
182 public Statement FalseStatement;
184 bool true_returns, false_returns;
186 public If (Expression bool_expr, Statement true_statement, Location l)
187 : this (bool_expr, true_statement, null, l)
191 public If (Expression bool_expr,
192 Statement true_statement,
193 Statement false_statement,
196 this.expr = bool_expr;
197 TrueStatement = true_statement;
198 FalseStatement = false_statement;
202 public Expression Expr {
208 public override bool Resolve (BlockContext ec)
210 expr = expr.Resolve (ec);
212 var ok = TrueStatement.Resolve (ec);
214 if (FalseStatement != null) {
215 ok &= FalseStatement.Resolve (ec);
221 protected override void DoEmit (EmitContext ec)
223 Label false_target = ec.DefineLabel ();
227 // If we're a boolean constant, Resolve() already
228 // eliminated dead code for us.
230 Constant c = expr as Constant;
232 c.EmitSideEffect (ec);
234 if (!c.IsDefaultValue)
235 TrueStatement.Emit (ec);
236 else if (FalseStatement != null)
237 FalseStatement.Emit (ec);
242 expr.EmitBranchable (ec, false_target, false);
244 TrueStatement.Emit (ec);
246 if (FalseStatement != null){
247 bool branch_emitted = false;
249 end = ec.DefineLabel ();
251 ec.Emit (OpCodes.Br, end);
252 branch_emitted = true;
255 ec.MarkLabel (false_target);
256 FalseStatement.Emit (ec);
261 ec.MarkLabel (false_target);
265 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
267 expr.FlowAnalysis (fc);
269 var da = fc.BranchDefiniteAssignment ();
271 var res = TrueStatement.FlowAnalysis (fc);
273 if (FalseStatement == null) {
274 fc.DefiniteAssignment = da;
278 var da_true = fc.DefiniteAssignment;
280 fc.DefiniteAssignment = da;
281 return FalseStatement.FlowAnalysis (fc);
284 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (da);
285 res &= FalseStatement.FlowAnalysis (fc);
288 fc.DefiniteAssignment = da_true;
290 fc.DefiniteAssignment &= da_true;
295 public override Reachability MarkReachable (Reachability rc)
297 if (rc.IsUnreachable)
300 base.MarkReachable (rc);
302 var c = expr as Constant;
304 bool take = !c.IsDefaultValue;
306 rc = TrueStatement.MarkReachable (rc);
308 if (FalseStatement != null)
309 rc = FalseStatement.MarkReachable (rc);
315 var true_rc = TrueStatement.MarkReachable (rc);
316 true_returns = true_rc.IsUnreachable;
318 if (FalseStatement == null)
321 var false_rc = FalseStatement.MarkReachable (rc);
322 false_returns = false_rc.IsUnreachable;
324 return true_rc & false_rc;
327 protected override void CloneTo (CloneContext clonectx, Statement t)
331 target.expr = expr.Clone (clonectx);
332 target.TrueStatement = TrueStatement.Clone (clonectx);
333 if (FalseStatement != null)
334 target.FalseStatement = FalseStatement.Clone (clonectx);
337 public override object Accept (StructuralVisitor visitor)
339 return visitor.Visit (this);
343 public class Do : LoopStatement
345 public Expression expr;
346 bool iterator_reachable, end_reachable;
348 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
353 WhileLocation = whileLocation;
356 public Location WhileLocation {
360 public override bool Resolve (BlockContext bc)
362 var ok = base.Resolve (bc);
364 expr = expr.Resolve (bc);
369 protected override void DoEmit (EmitContext ec)
371 Label loop = ec.DefineLabel ();
372 Label old_begin = ec.LoopBegin;
373 Label old_end = ec.LoopEnd;
375 ec.LoopBegin = ec.DefineLabel ();
376 ec.LoopEnd = ec.DefineLabel ();
380 ec.MarkLabel (ec.LoopBegin);
382 // Mark start of while condition
383 ec.Mark (WhileLocation);
386 // Dead code elimination
388 if (expr is Constant) {
389 bool res = !((Constant) expr).IsDefaultValue;
391 expr.EmitSideEffect (ec);
393 ec.Emit (OpCodes.Br, loop);
395 expr.EmitBranchable (ec, loop, true);
398 ec.MarkLabel (ec.LoopEnd);
400 ec.LoopBegin = old_begin;
401 ec.LoopEnd = old_end;
404 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
406 var dat = fc.BranchDefiniteAssignment ();
408 var res = Statement.FlowAnalysis (fc);
410 expr.FlowAnalysis (fc);
412 fc.DefiniteAssignment = dat | fc.DefiniteAssignment;
414 if (res && !iterator_reachable)
415 return !end_reachable;
417 if (!end_reachable) {
418 var c = expr as Constant;
419 if (c != null && !c.IsDefaultValue)
426 public override Reachability MarkReachable (Reachability rc)
428 base.MarkReachable (rc);
430 var body_rc = Statement.MarkReachable (rc);
432 if (body_rc.IsUnreachable && !iterator_reachable) {
433 expr = new UnreachableExpression (expr);
434 return end_reachable ? rc : Reachability.CreateUnreachable ();
437 if (!end_reachable) {
438 var c = expr as Constant;
439 if (c != null && !c.IsDefaultValue)
440 return Reachability.CreateUnreachable ();
446 protected override void CloneTo (CloneContext clonectx, Statement t)
450 target.Statement = Statement.Clone (clonectx);
451 target.expr = expr.Clone (clonectx);
454 public override object Accept (StructuralVisitor visitor)
456 return visitor.Visit (this);
459 public override void SetEndReachable ()
461 end_reachable = true;
464 public override void SetIteratorReachable ()
466 iterator_reachable = true;
470 public class While : LoopStatement
472 public Expression expr;
473 bool empty, infinite, end_reachable;
474 List<DefiniteAssignmentBitSet> end_reachable_das;
476 public While (BooleanExpression bool_expr, Statement statement, Location l)
479 this.expr = bool_expr;
483 public override bool Resolve (BlockContext bc)
487 expr = expr.Resolve (bc);
491 var c = expr as Constant;
493 empty = c.IsDefaultValue;
497 ok &= base.Resolve (bc);
501 protected override void DoEmit (EmitContext ec)
504 expr.EmitSideEffect (ec);
508 Label old_begin = ec.LoopBegin;
509 Label old_end = ec.LoopEnd;
511 ec.LoopBegin = ec.DefineLabel ();
512 ec.LoopEnd = ec.DefineLabel ();
515 // Inform whether we are infinite or not
517 if (expr is Constant) {
518 // expr is 'true', since the 'empty' case above handles the 'false' case
519 ec.MarkLabel (ec.LoopBegin);
521 if (ec.EmitAccurateDebugInfo)
522 ec.Emit (OpCodes.Nop);
524 expr.EmitSideEffect (ec);
526 ec.Emit (OpCodes.Br, ec.LoopBegin);
529 // Inform that we are infinite (ie, `we return'), only
530 // if we do not `break' inside the code.
532 ec.MarkLabel (ec.LoopEnd);
534 Label while_loop = ec.DefineLabel ();
536 ec.Emit (OpCodes.Br, ec.LoopBegin);
537 ec.MarkLabel (while_loop);
541 ec.MarkLabel (ec.LoopBegin);
544 expr.EmitBranchable (ec, while_loop, true);
546 ec.MarkLabel (ec.LoopEnd);
549 ec.LoopBegin = old_begin;
550 ec.LoopEnd = old_end;
553 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
555 expr.FlowAnalysis (fc);
557 DefiniteAssignmentBitSet das;
559 das = fc.BranchDefiniteAssignment ();
561 Statement.FlowAnalysis (fc);
564 // Special case infinite while with breaks
566 if (end_reachable_das != null) {
567 das = DefiniteAssignmentBitSet.And (end_reachable_das);
568 end_reachable_das = null;
571 fc.DefiniteAssignment = das;
573 if (infinite && !end_reachable)
579 public override Reachability MarkReachable (Reachability rc)
581 if (rc.IsUnreachable)
584 base.MarkReachable (rc);
587 // Special case unreachable while body
590 Statement.MarkReachable (Reachability.CreateUnreachable ());
594 Statement.MarkReachable (rc);
597 // When infinite while end is unreachable via break anything what follows is unreachable too
599 if (infinite && !end_reachable)
600 return Reachability.CreateUnreachable ();
605 protected override void CloneTo (CloneContext clonectx, Statement t)
607 While target = (While) t;
609 target.expr = expr.Clone (clonectx);
610 target.Statement = Statement.Clone (clonectx);
613 public override object Accept (StructuralVisitor visitor)
615 return visitor.Visit (this);
618 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
623 if (end_reachable_das == null)
624 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
626 end_reachable_das.Add (fc.DefiniteAssignment);
629 public override void SetEndReachable ()
631 end_reachable = true;
635 public class For : LoopStatement
637 bool infinite, empty, iterator_reachable, end_reachable;
638 List<DefiniteAssignmentBitSet> end_reachability;
640 public For (Location l)
646 public Statement Initializer {
650 public Expression Condition {
654 public Statement Iterator {
658 public override bool Resolve (BlockContext bc)
660 Initializer.Resolve (bc);
662 if (Condition != null) {
663 Condition = Condition.Resolve (bc);
664 var condition_constant = Condition as Constant;
665 if (condition_constant != null) {
666 if (condition_constant.IsDefaultValue) {
678 Iterator.Resolve (bc);
683 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
685 Initializer.FlowAnalysis (fc);
687 if (Condition != null)
688 Condition.FlowAnalysis (fc);
690 var das = fc.BranchDefiniteAssignment ();
692 Statement.FlowAnalysis (fc);
694 Iterator.FlowAnalysis (fc);
696 fc.DefiniteAssignment = das;
698 return infinite && !end_reachable;
701 public override Reachability MarkReachable (Reachability rc)
703 base.MarkReachable (rc);
705 Initializer.MarkReachable (rc);
707 var body_rc = Statement.MarkReachable (rc);
708 if (!body_rc.IsUnreachable || iterator_reachable) {
709 Iterator.MarkReachable (rc);
713 // When infinite for end is unreachable via break anything what follows is unreachable too
715 if (infinite && !end_reachable) {
716 return Reachability.CreateUnreachable ();
722 protected override void DoEmit (EmitContext ec)
724 if (Initializer != null)
725 Initializer.Emit (ec);
728 Condition.EmitSideEffect (ec);
732 Label old_begin = ec.LoopBegin;
733 Label old_end = ec.LoopEnd;
734 Label loop = ec.DefineLabel ();
735 Label test = ec.DefineLabel ();
737 ec.LoopBegin = ec.DefineLabel ();
738 ec.LoopEnd = ec.DefineLabel ();
740 ec.Emit (OpCodes.Br, test);
744 ec.MarkLabel (ec.LoopBegin);
749 // If test is null, there is no test, and we are just
752 if (Condition != null) {
753 ec.Mark (Condition.Location);
756 // The Resolve code already catches the case for
757 // Test == Constant (false) so we know that
760 if (Condition is Constant) {
761 Condition.EmitSideEffect (ec);
762 ec.Emit (OpCodes.Br, loop);
764 Condition.EmitBranchable (ec, loop, true);
768 ec.Emit (OpCodes.Br, loop);
769 ec.MarkLabel (ec.LoopEnd);
771 ec.LoopBegin = old_begin;
772 ec.LoopEnd = old_end;
775 protected override void CloneTo (CloneContext clonectx, Statement t)
777 For target = (For) t;
779 if (Initializer != null)
780 target.Initializer = Initializer.Clone (clonectx);
781 if (Condition != null)
782 target.Condition = Condition.Clone (clonectx);
783 if (Iterator != null)
784 target.Iterator = Iterator.Clone (clonectx);
785 target.Statement = Statement.Clone (clonectx);
788 public override object Accept (StructuralVisitor visitor)
790 return visitor.Visit (this);
793 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
798 if (end_reachability == null)
799 end_reachability = new List<DefiniteAssignmentBitSet> ();
801 end_reachability.Add (fc.DefiniteAssignment);
804 public override void SetEndReachable ()
806 end_reachable = true;
809 public override void SetIteratorReachable ()
811 iterator_reachable = true;
815 public abstract class LoopStatement : Statement
817 protected LoopStatement (Statement statement)
819 Statement = statement;
822 public Statement Statement { get; set; }
824 public override bool Resolve (BlockContext bc)
826 var prev_loop = bc.EnclosingLoop;
827 var prev_los = bc.EnclosingLoopOrSwitch;
828 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
829 Statement.Resolve (bc);
830 bc.EnclosingLoopOrSwitch = prev_los;
831 bc.EnclosingLoop = prev_loop;
837 // Needed by possibly infinite loops statements (for, while) and switch statment
839 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
843 public virtual void SetEndReachable ()
847 public virtual void SetIteratorReachable ()
852 public class StatementExpression : Statement
854 ExpressionStatement expr;
856 public StatementExpression (ExpressionStatement expr)
859 loc = expr.StartLocation;
862 public StatementExpression (ExpressionStatement expr, Location loc)
868 public ExpressionStatement Expr {
874 protected override void CloneTo (CloneContext clonectx, Statement t)
876 StatementExpression target = (StatementExpression) t;
877 target.expr = (ExpressionStatement) expr.Clone (clonectx);
880 protected override void DoEmit (EmitContext ec)
882 expr.EmitStatement (ec);
885 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
887 expr.FlowAnalysis (fc);
891 public override Reachability MarkReachable (Reachability rc)
893 base.MarkReachable (rc);
894 expr.MarkReachable (rc);
898 public override bool Resolve (BlockContext ec)
900 expr = expr.ResolveStatement (ec);
904 public override object Accept (StructuralVisitor visitor)
906 return visitor.Visit (this);
910 public class StatementErrorExpression : Statement
914 public StatementErrorExpression (Expression expr)
917 this.loc = expr.StartLocation;
920 public Expression Expr {
926 public override bool Resolve (BlockContext bc)
928 expr.Error_InvalidExpressionStatement (bc);
932 protected override void DoEmit (EmitContext ec)
934 throw new NotSupportedException ();
937 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
942 protected override void CloneTo (CloneContext clonectx, Statement target)
944 var t = (StatementErrorExpression) target;
946 t.expr = expr.Clone (clonectx);
949 public override object Accept (StructuralVisitor visitor)
951 return visitor.Visit (this);
956 // Simple version of statement list not requiring a block
958 public class StatementList : Statement
960 List<Statement> statements;
962 public StatementList (Statement first, Statement second)
964 statements = new List<Statement> { first, second };
968 public IList<Statement> Statements {
975 public void Add (Statement statement)
977 statements.Add (statement);
980 public override bool Resolve (BlockContext ec)
982 foreach (var s in statements)
988 protected override void DoEmit (EmitContext ec)
990 foreach (var s in statements)
994 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
996 foreach (var s in statements)
1002 public override Reachability MarkReachable (Reachability rc)
1004 base.MarkReachable (rc);
1006 Reachability res = rc;
1007 foreach (var s in statements)
1008 res = s.MarkReachable (rc);
1013 protected override void CloneTo (CloneContext clonectx, Statement target)
1015 StatementList t = (StatementList) target;
1017 t.statements = new List<Statement> (statements.Count);
1018 foreach (Statement s in statements)
1019 t.statements.Add (s.Clone (clonectx));
1022 public override object Accept (StructuralVisitor visitor)
1024 return visitor.Visit (this);
1029 // For statements which require special handling when inside try or catch block
1031 public abstract class ExitStatement : Statement
1033 protected bool unwind_protect;
1035 protected abstract bool DoResolve (BlockContext bc);
1036 protected abstract bool IsLocalExit { get; }
1038 public override bool Resolve (BlockContext bc)
1040 var res = DoResolve (bc);
1044 // We are inside finally scope but is it the scope we are exiting
1046 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1048 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1049 if (b.IsFinallyBlock) {
1050 Error_FinallyClauseExit (bc);
1054 if (b is ParametersBlock)
1060 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1064 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1069 if (fc.TryFinally != null) {
1070 fc.TryFinally.RegisterOutParametersCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1072 fc.ParametersBlock.CheckOutParametersAssignment (fc);
1080 /// Implements the return statement
1082 public class Return : ExitStatement
1086 public Return (Expression expr, Location l)
1094 public Expression Expr {
1103 protected override bool IsLocalExit {
1111 protected override bool DoResolve (BlockContext ec)
1113 var block_return_type = ec.ReturnType;
1116 if (block_return_type.Kind == MemberKind.Void)
1120 // Return must not be followed by an expression when
1121 // the method return type is Task
1123 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1124 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1125 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1127 // Extra trick not to emit ret/leave inside awaiter body
1129 expr = EmptyExpression.Null;
1134 if (ec.CurrentIterator != null) {
1135 Error_ReturnFromIterator (ec);
1136 } else if (ec.ReturnType != InternalType.ErrorType) {
1137 ec.Report.Error (126, loc,
1138 "An object of a type convertible to `{0}' is required for the return statement",
1139 ec.ReturnType.GetSignatureForError ());
1145 expr = expr.Resolve (ec);
1147 AnonymousExpression am = ec.CurrentAnonymousMethod;
1149 if (block_return_type.Kind == MemberKind.Void) {
1150 ec.Report.Error (127, loc,
1151 "`{0}': A return keyword must not be followed by any expression when method returns void",
1152 ec.GetSignatureForError ());
1157 if (am.IsIterator) {
1158 Error_ReturnFromIterator (ec);
1162 var async_block = am as AsyncInitializer;
1163 if (async_block != null) {
1165 var storey = (AsyncTaskStorey) am.Storey;
1166 var async_type = storey.ReturnType;
1168 if (async_type == null && async_block.ReturnTypeInference != null) {
1169 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1173 if (async_type.Kind == MemberKind.Void) {
1174 ec.Report.Error (127, loc,
1175 "`{0}': A return keyword must not be followed by any expression when method returns void",
1176 ec.GetSignatureForError ());
1181 if (!async_type.IsGenericTask) {
1182 if (this is ContextualReturn)
1185 // Same error code as .NET but better error message
1186 if (async_block.DelegateType != null) {
1187 ec.Report.Error (1997, loc,
1188 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
1189 async_block.DelegateType.GetSignatureForError ());
1191 ec.Report.Error (1997, loc,
1192 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1193 ec.GetSignatureForError ());
1201 // The return type is actually Task<T> type argument
1203 if (expr.Type == async_type) {
1204 ec.Report.Error (4016, loc,
1205 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1206 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1208 block_return_type = async_type.TypeArguments[0];
1212 // Same error code as .NET but better error message
1213 if (block_return_type.Kind == MemberKind.Void) {
1214 ec.Report.Error (127, loc,
1215 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
1216 am.GetSignatureForError ());
1221 var l = am as AnonymousMethodBody;
1222 if (l != null && expr != null) {
1223 if (l.ReturnTypeInference != null) {
1224 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1229 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1230 // unexpected debugging experience
1232 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1233 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1242 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1243 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1246 if (am != null && block_return_type == ec.ReturnType) {
1247 ec.Report.Error (1662, loc,
1248 "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",
1249 am.ContainerType, am.GetSignatureForError ());
1258 protected override void DoEmit (EmitContext ec)
1263 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1264 if (async_body != null) {
1265 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
1267 // It's null for await without async
1268 if (async_return != null) {
1269 async_return.EmitAssign (ec);
1274 ec.Emit (OpCodes.Leave, async_body.BodyEnd);
1280 if (unwind_protect || ec.EmitAccurateDebugInfo)
1281 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1284 if (unwind_protect) {
1285 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1286 } else if (ec.EmitAccurateDebugInfo) {
1287 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1289 ec.Emit (OpCodes.Ret);
1293 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1296 expr.FlowAnalysis (fc);
1298 base.DoFlowAnalysis (fc);
1302 void Error_ReturnFromIterator (ResolveContext rc)
1304 rc.Report.Error (1622, loc,
1305 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1308 public override Reachability MarkReachable (Reachability rc)
1310 base.MarkReachable (rc);
1311 return Reachability.CreateUnreachable ();
1314 protected override void CloneTo (CloneContext clonectx, Statement t)
1316 Return target = (Return) t;
1317 // It's null for simple return;
1319 target.expr = expr.Clone (clonectx);
1322 public override object Accept (StructuralVisitor visitor)
1324 return visitor.Visit (this);
1328 public class Goto : ExitStatement
1331 LabeledStatement label;
1332 TryFinally try_finally;
1334 public Goto (string label, Location l)
1340 public string Target {
1341 get { return target; }
1344 protected override bool IsLocalExit {
1350 protected override bool DoResolve (BlockContext bc)
1352 label = bc.CurrentBlock.LookupLabel (target);
1353 if (label == null) {
1354 Error_UnknownLabel (bc, target, loc);
1358 try_finally = bc.CurrentTryBlock as TryFinally;
1360 CheckExitBoundaries (bc, label.Block);
1365 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1367 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1371 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1373 if (fc.LabelStack == null) {
1374 fc.LabelStack = new List<LabeledStatement> ();
1375 } else if (fc.LabelStack.Contains (label)) {
1379 fc.LabelStack.Add (label);
1380 label.Block.ScanGotoJump (label, fc);
1381 fc.LabelStack.Remove (label);
1385 public override Reachability MarkReachable (Reachability rc)
1387 if (rc.IsUnreachable)
1390 base.MarkReachable (rc);
1392 if (try_finally != null) {
1393 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1394 label.AddGotoReference (rc, false);
1396 label.AddGotoReference (rc, true);
1401 label.AddGotoReference (rc, false);
1404 return Reachability.CreateUnreachable ();
1407 protected override void CloneTo (CloneContext clonectx, Statement target)
1412 protected override void DoEmit (EmitContext ec)
1415 throw new InternalErrorException ("goto emitted before target resolved");
1417 Label l = label.LabelTarget (ec);
1418 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1421 public override object Accept (StructuralVisitor visitor)
1423 return visitor.Visit (this);
1427 public class LabeledStatement : Statement {
1435 public LabeledStatement (string name, Block block, Location l)
1442 public Label LabelTarget (EmitContext ec)
1447 label = ec.DefineLabel ();
1452 public Block Block {
1458 public string Name {
1459 get { return name; }
1462 protected override void CloneTo (CloneContext clonectx, Statement target)
1467 public override bool Resolve (BlockContext bc)
1472 protected override void DoEmit (EmitContext ec)
1475 ec.MarkLabel (label);
1478 ec.Emit (OpCodes.Br_S, label);
1481 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1484 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1490 public override Reachability MarkReachable (Reachability rc)
1492 base.MarkReachable (rc);
1495 rc = new Reachability ();
1500 public void AddGotoReference (Reachability rc, bool finalTarget)
1508 // Label is final target when goto jumps out of try block with
1509 // finally clause. In that case we need leave with target but in C#
1510 // terms the label is unreachable. Using finalTarget we emit
1511 // explicit label not just marker
1515 this.finalTarget = true;
1519 block.ScanGotoJump (this);
1522 public override object Accept (StructuralVisitor visitor)
1524 return visitor.Visit (this);
1530 /// `goto default' statement
1532 public class GotoDefault : SwitchGoto
1534 public GotoDefault (Location l)
1539 public override bool Resolve (BlockContext bc)
1541 if (bc.Switch == null) {
1542 Error_GotoCaseRequiresSwitchBlock (bc);
1546 bc.Switch.RegisterGotoCase (null, null);
1552 protected override void DoEmit (EmitContext ec)
1554 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1557 public override object Accept (StructuralVisitor visitor)
1559 return visitor.Visit (this);
1564 /// `goto case' statement
1566 public class GotoCase : SwitchGoto
1570 public GotoCase (Expression e, Location l)
1576 public Expression Expr {
1582 public SwitchLabel Label { get; set; }
1584 public override bool Resolve (BlockContext ec)
1586 if (ec.Switch == null) {
1587 Error_GotoCaseRequiresSwitchBlock (ec);
1591 Constant c = expr.ResolveLabelConstant (ec);
1597 if (ec.Switch.IsNullable && c is NullLiteral) {
1600 TypeSpec type = ec.Switch.SwitchType;
1601 res = c.Reduce (ec, type);
1603 c.Error_ValueCannotBeConverted (ec, type, true);
1607 if (!Convert.ImplicitStandardConversionExists (c, type))
1608 ec.Report.Warning (469, 2, loc,
1609 "The `goto case' value is not implicitly convertible to type `{0}'",
1610 type.GetSignatureForError ());
1614 ec.Switch.RegisterGotoCase (this, res);
1620 protected override void DoEmit (EmitContext ec)
1622 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1625 protected override void CloneTo (CloneContext clonectx, Statement t)
1627 GotoCase target = (GotoCase) t;
1629 target.expr = expr.Clone (clonectx);
1632 public override object Accept (StructuralVisitor visitor)
1634 return visitor.Visit (this);
1638 public abstract class SwitchGoto : Statement
1640 protected bool unwind_protect;
1642 protected SwitchGoto (Location loc)
1647 protected override void CloneTo (CloneContext clonectx, Statement target)
1652 public override bool Resolve (BlockContext bc)
1654 CheckExitBoundaries (bc, bc.Switch.Block);
1656 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1661 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1666 public override Reachability MarkReachable (Reachability rc)
1668 base.MarkReachable (rc);
1669 return Reachability.CreateUnreachable ();
1672 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1674 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1678 public class Throw : Statement {
1681 public Throw (Expression expr, Location l)
1687 public Expression Expr {
1693 public override bool Resolve (BlockContext ec)
1696 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1697 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1698 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1699 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1700 if (b.IsFinallyBlock) {
1701 ec.Report.Error (724, loc,
1702 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1711 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1716 var et = ec.BuiltinTypes.Exception;
1717 if (Convert.ImplicitConversionExists (ec, expr, et))
1718 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1720 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1725 protected override void DoEmit (EmitContext ec)
1728 ec.Emit (OpCodes.Rethrow);
1732 ec.Emit (OpCodes.Throw);
1736 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1739 expr.FlowAnalysis (fc);
1744 public override Reachability MarkReachable (Reachability rc)
1746 base.MarkReachable (rc);
1747 return Reachability.CreateUnreachable ();
1750 protected override void CloneTo (CloneContext clonectx, Statement t)
1752 Throw target = (Throw) t;
1755 target.expr = expr.Clone (clonectx);
1758 public override object Accept (StructuralVisitor visitor)
1760 return visitor.Visit (this);
1764 public class Break : LocalExitStatement
1766 public Break (Location l)
1771 public override object Accept (StructuralVisitor visitor)
1773 return visitor.Visit (this);
1776 protected override void DoEmit (EmitContext ec)
1778 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1781 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1783 enclosing_loop.AddEndDefiniteAssignment (fc);
1787 protected override bool DoResolve (BlockContext bc)
1789 enclosing_loop = bc.EnclosingLoopOrSwitch;
1790 return base.DoResolve (bc);
1793 public override Reachability MarkReachable (Reachability rc)
1795 base.MarkReachable (rc);
1797 if (!rc.IsUnreachable)
1798 enclosing_loop.SetEndReachable ();
1800 return Reachability.CreateUnreachable ();
1804 public class Continue : LocalExitStatement
1806 public Continue (Location l)
1811 public override object Accept (StructuralVisitor visitor)
1813 return visitor.Visit (this);
1817 protected override void DoEmit (EmitContext ec)
1819 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1822 protected override bool DoResolve (BlockContext bc)
1824 enclosing_loop = bc.EnclosingLoop;
1825 return base.DoResolve (bc);
1828 public override Reachability MarkReachable (Reachability rc)
1830 base.MarkReachable (rc);
1832 if (!rc.IsUnreachable)
1833 enclosing_loop.SetIteratorReachable ();
1835 return Reachability.CreateUnreachable ();
1839 public abstract class LocalExitStatement : ExitStatement
1841 protected LoopStatement enclosing_loop;
1843 protected LocalExitStatement (Location loc)
1848 protected override bool IsLocalExit {
1854 protected override void CloneTo (CloneContext clonectx, Statement t)
1859 protected override bool DoResolve (BlockContext bc)
1861 if (enclosing_loop == null) {
1862 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1866 var block = enclosing_loop.Statement as Block;
1868 // Don't need to do extra checks for simple statements loops
1869 if (block != null) {
1870 CheckExitBoundaries (bc, block);
1877 public interface ILocalVariable
1879 void Emit (EmitContext ec);
1880 void EmitAssign (EmitContext ec);
1881 void EmitAddressOf (EmitContext ec);
1884 public interface INamedBlockVariable
1886 Block Block { get; }
1887 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1888 bool IsDeclared { get; }
1889 bool IsParameter { get; }
1890 Location Location { get; }
1893 public class BlockVariableDeclarator
1896 Expression initializer;
1898 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1900 if (li.Type != null)
1901 throw new ArgumentException ("Expected null variable type");
1904 this.initializer = initializer;
1909 public LocalVariable Variable {
1915 public Expression Initializer {
1920 initializer = value;
1926 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1928 var t = (BlockVariableDeclarator) MemberwiseClone ();
1929 if (initializer != null)
1930 t.initializer = initializer.Clone (cloneCtx);
1936 public class BlockVariable : Statement
1938 Expression initializer;
1939 protected FullNamedExpression type_expr;
1940 protected LocalVariable li;
1941 protected List<BlockVariableDeclarator> declarators;
1944 public BlockVariable (FullNamedExpression type, LocalVariable li)
1946 this.type_expr = type;
1948 this.loc = type_expr.Location;
1951 protected BlockVariable (LocalVariable li)
1958 public List<BlockVariableDeclarator> Declarators {
1964 public Expression Initializer {
1969 initializer = value;
1973 public FullNamedExpression TypeExpression {
1979 public LocalVariable Variable {
1987 public void AddDeclarator (BlockVariableDeclarator decl)
1989 if (declarators == null)
1990 declarators = new List<BlockVariableDeclarator> ();
1992 declarators.Add (decl);
1995 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1997 if (bc.Report.Errors != 0)
2000 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2002 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2003 new MemberName (li.Name, li.Location), null);
2005 container.AddField (f);
2008 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2012 public override bool Resolve (BlockContext bc)
2014 return Resolve (bc, true);
2017 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2019 if (type == null && !li.IsCompilerGenerated) {
2020 var vexpr = type_expr as VarExpr;
2023 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2024 // same name exists or as a keyword when no type was found
2026 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2027 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2028 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2031 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2035 if (li.IsConstant) {
2036 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2040 if (Initializer == null) {
2041 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2045 if (declarators != null) {
2046 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2050 Initializer = Initializer.Resolve (bc);
2051 if (Initializer != null) {
2052 ((VarExpr) type_expr).InferType (bc, Initializer);
2053 type = type_expr.Type;
2055 // Set error type to indicate the var was placed correctly but could
2058 // var a = missing ();
2060 type = InternalType.ErrorType;
2065 type = type_expr.ResolveAsType (bc);
2069 if (li.IsConstant && !type.IsConstantCompatible) {
2070 Const.Error_InvalidConstantType (type, loc, bc.Report);
2075 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2080 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2082 CreateEvaluatorVariable (bc, li);
2083 } else if (type != InternalType.ErrorType) {
2084 li.PrepareAssignmentAnalysis (bc);
2087 if (initializer != null) {
2088 initializer = ResolveInitializer (bc, li, initializer);
2089 // li.Variable.DefinitelyAssigned
2092 if (declarators != null) {
2093 foreach (var d in declarators) {
2094 d.Variable.Type = li.Type;
2096 CreateEvaluatorVariable (bc, d.Variable);
2097 } else if (type != InternalType.ErrorType) {
2098 d.Variable.PrepareAssignmentAnalysis (bc);
2101 if (d.Initializer != null && resolveDeclaratorInitializers) {
2102 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2103 // d.Variable.DefinitelyAssigned
2111 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2113 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2114 return a.ResolveStatement (bc);
2117 protected override void DoEmit (EmitContext ec)
2119 li.CreateBuilder (ec);
2121 if (Initializer != null)
2122 ((ExpressionStatement) Initializer).EmitStatement (ec);
2124 if (declarators != null) {
2125 foreach (var d in declarators) {
2126 d.Variable.CreateBuilder (ec);
2127 if (d.Initializer != null) {
2128 ec.Mark (d.Variable.Location);
2129 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2135 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2137 if (Initializer != null)
2138 Initializer.FlowAnalysis (fc);
2140 if (declarators != null) {
2141 foreach (var d in declarators) {
2142 if (d.Initializer != null)
2143 d.Initializer.FlowAnalysis (fc);
2150 public override Reachability MarkReachable (Reachability rc)
2152 var init = initializer as ExpressionStatement;
2154 init.MarkReachable (rc);
2156 return base.MarkReachable (rc);
2159 protected override void CloneTo (CloneContext clonectx, Statement target)
2161 BlockVariable t = (BlockVariable) target;
2163 if (type_expr != null)
2164 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2166 if (initializer != null)
2167 t.initializer = initializer.Clone (clonectx);
2169 if (declarators != null) {
2170 t.declarators = null;
2171 foreach (var d in declarators)
2172 t.AddDeclarator (d.Clone (clonectx));
2176 public override object Accept (StructuralVisitor visitor)
2178 return visitor.Visit (this);
2182 public class BlockConstant : BlockVariable
2184 public BlockConstant (FullNamedExpression type, LocalVariable li)
2189 public override void Emit (EmitContext ec)
2191 // Nothing to emit, not even sequence point
2194 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2196 initializer = initializer.Resolve (bc);
2197 if (initializer == null)
2200 var c = initializer as Constant;
2202 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2206 c = c.ConvertImplicitly (li.Type);
2208 if (TypeSpec.IsReferenceType (li.Type))
2209 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2211 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2216 li.ConstantValue = c;
2220 public override object Accept (StructuralVisitor visitor)
2222 return visitor.Visit (this);
2227 // The information about a user-perceived local variable
2229 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2236 AddressTaken = 1 << 2,
2237 CompilerGenerated = 1 << 3,
2239 ForeachVariable = 1 << 5,
2240 FixedVariable = 1 << 6,
2241 UsingVariable = 1 << 7,
2244 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2248 readonly string name;
2249 readonly Location loc;
2250 readonly Block block;
2252 Constant const_value;
2254 public VariableInfo VariableInfo;
2255 HoistedVariable hoisted_variant;
2257 LocalBuilder builder;
2259 public LocalVariable (Block block, string name, Location loc)
2266 public LocalVariable (Block block, string name, Flags flags, Location loc)
2267 : this (block, name, loc)
2273 // Used by variable declarators
2275 public LocalVariable (LocalVariable li, string name, Location loc)
2276 : this (li.block, name, li.flags, loc)
2282 public bool AddressTaken {
2284 return (flags & Flags.AddressTaken) != 0;
2288 public Block Block {
2294 public Constant ConstantValue {
2299 const_value = value;
2304 // Hoisted local variable variant
2306 public HoistedVariable HoistedVariant {
2308 return hoisted_variant;
2311 hoisted_variant = value;
2315 public bool IsDeclared {
2317 return type != null;
2321 public bool IsCompilerGenerated {
2323 return (flags & Flags.CompilerGenerated) != 0;
2327 public bool IsConstant {
2329 return (flags & Flags.Constant) != 0;
2333 public bool IsLocked {
2335 return (flags & Flags.IsLocked) != 0;
2338 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2342 public bool IsThis {
2344 return (flags & Flags.IsThis) != 0;
2348 public bool IsFixed {
2350 return (flags & Flags.FixedVariable) != 0;
2354 bool INamedBlockVariable.IsParameter {
2360 public bool IsReadonly {
2362 return (flags & Flags.ReadonlyMask) != 0;
2366 public Location Location {
2372 public string Name {
2378 public TypeSpec Type {
2389 public void CreateBuilder (EmitContext ec)
2391 if ((flags & Flags.Used) == 0) {
2392 if (VariableInfo == null) {
2393 // Missing flow analysis or wrong variable flags
2394 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2397 if (VariableInfo.IsEverAssigned)
2398 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2400 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2403 if (HoistedVariant != null)
2406 if (builder != null) {
2407 if ((flags & Flags.CompilerGenerated) != 0)
2410 // To avoid Used warning duplicates
2411 throw new InternalErrorException ("Already created variable `{0}'", name);
2415 // All fixed variabled are pinned, a slot has to be alocated
2417 builder = ec.DeclareLocal (Type, IsFixed);
2418 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2419 ec.DefineLocalVariable (name, builder);
2422 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2424 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2429 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2431 if (IsConstant && const_value != null)
2432 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2434 return new LocalVariableReference (this, loc);
2437 public void Emit (EmitContext ec)
2439 // TODO: Need something better for temporary variables
2440 if ((flags & Flags.CompilerGenerated) != 0)
2443 ec.Emit (OpCodes.Ldloc, builder);
2446 public void EmitAssign (EmitContext ec)
2448 // TODO: Need something better for temporary variables
2449 if ((flags & Flags.CompilerGenerated) != 0)
2452 ec.Emit (OpCodes.Stloc, builder);
2455 public void EmitAddressOf (EmitContext ec)
2457 ec.Emit (OpCodes.Ldloca, builder);
2460 public static string GetCompilerGeneratedName (Block block)
2462 // HACK: Debugger depends on the name semantics
2463 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2466 public string GetReadOnlyContext ()
2468 switch (flags & Flags.ReadonlyMask) {
2469 case Flags.FixedVariable:
2470 return "fixed variable";
2471 case Flags.ForeachVariable:
2472 return "foreach iteration variable";
2473 case Flags.UsingVariable:
2474 return "using variable";
2477 throw new InternalErrorException ("Variable is not readonly");
2480 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2482 if (VariableInfo == null)
2483 throw new Exception ();
2485 if (IsAssigned (fc))
2488 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2491 public bool IsAssigned (FlowAnalysisContext fc)
2493 return fc.IsDefinitelyAssigned (VariableInfo);
2496 public void PrepareAssignmentAnalysis (BlockContext bc)
2499 // No need to run assignment analysis for these guys
2501 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2504 VariableInfo = VariableInfo.Create (bc, this);
2508 // Mark the variables as referenced in the user code
2510 public void SetIsUsed ()
2512 flags |= Flags.Used;
2515 public void SetHasAddressTaken ()
2517 flags |= (Flags.AddressTaken | Flags.Used);
2520 public override string ToString ()
2522 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2527 /// Block represents a C# block.
2531 /// This class is used in a number of places: either to represent
2532 /// explicit blocks that the programmer places or implicit blocks.
2534 /// Implicit blocks are used as labels or to introduce variable
2537 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2538 /// they contain extra information that is not necessary on normal blocks.
2540 public class Block : Statement {
2547 HasCapturedVariable = 64,
2548 HasCapturedThis = 1 << 7,
2549 IsExpressionTree = 1 << 8,
2550 CompilerGenerated = 1 << 9,
2551 HasAsyncModifier = 1 << 10,
2553 YieldBlock = 1 << 12,
2554 AwaitBlock = 1 << 13,
2555 FinallyBlock = 1 << 14,
2556 CatchBlock = 1 << 15,
2558 NoFlowAnalysis = 1 << 21
2561 public Block Parent;
2562 public Location StartLocation;
2563 public Location EndLocation;
2565 public ExplicitBlock Explicit;
2566 public ParametersBlock ParametersBlock;
2568 protected Flags flags;
2571 // The statements in this block
2573 protected List<Statement> statements;
2575 protected List<Statement> scope_initializers;
2577 int? resolving_init_idx;
2583 public int ID = id++;
2585 static int clone_id_counter;
2589 // int assignable_slots;
2591 public Block (Block parent, Location start, Location end)
2592 : this (parent, 0, start, end)
2596 public Block (Block parent, Flags flags, Location start, Location end)
2598 if (parent != null) {
2599 // the appropriate constructors will fixup these fields
2600 ParametersBlock = parent.ParametersBlock;
2601 Explicit = parent.Explicit;
2604 this.Parent = parent;
2606 this.StartLocation = start;
2607 this.EndLocation = end;
2609 statements = new List<Statement> (4);
2611 this.original = this;
2616 public Block Original {
2625 public bool IsCompilerGenerated {
2626 get { return (flags & Flags.CompilerGenerated) != 0; }
2627 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2631 public bool IsCatchBlock {
2633 return (flags & Flags.CatchBlock) != 0;
2637 public bool IsFinallyBlock {
2639 return (flags & Flags.FinallyBlock) != 0;
2643 public bool Unchecked {
2644 get { return (flags & Flags.Unchecked) != 0; }
2645 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2648 public bool Unsafe {
2649 get { return (flags & Flags.Unsafe) != 0; }
2650 set { flags |= Flags.Unsafe; }
2653 public List<Statement> Statements {
2654 get { return statements; }
2659 public void SetEndLocation (Location loc)
2664 public void AddLabel (LabeledStatement target)
2666 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2669 public void AddLocalName (LocalVariable li)
2671 AddLocalName (li.Name, li);
2674 public void AddLocalName (string name, INamedBlockVariable li)
2676 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2679 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2681 if (reason == null) {
2682 Error_AlreadyDeclared (name, variable);
2686 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2687 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2688 "to `{0}', which is already used in a `{1}' scope to denote something else",
2692 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2694 var pi = variable as ParametersBlock.ParameterInfo;
2696 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2698 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2699 "A local variable named `{0}' is already defined in this scope", name);
2703 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2705 ParametersBlock.TopBlock.Report.Error (412, loc,
2706 "The type parameter name `{0}' is the same as local variable or parameter name",
2711 // It should be used by expressions which require to
2712 // register a statement during resolve process.
2714 public void AddScopeStatement (Statement s)
2716 if (scope_initializers == null)
2717 scope_initializers = new List<Statement> ();
2720 // Simple recursive helper, when resolve scope initializer another
2721 // new scope initializer can be added, this ensures it's initialized
2722 // before existing one. For now this can happen with expression trees
2723 // in base ctor initializer only
2725 if (resolving_init_idx.HasValue) {
2726 scope_initializers.Insert (resolving_init_idx.Value, s);
2727 ++resolving_init_idx;
2729 scope_initializers.Add (s);
2733 public void InsertStatement (int index, Statement s)
2735 statements.Insert (index, s);
2738 public void AddStatement (Statement s)
2743 public int AssignableSlots {
2745 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2747 // return assignable_slots;
2751 public LabeledStatement LookupLabel (string name)
2753 return ParametersBlock.TopBlock.GetLabel (name, this);
2756 public override Reachability MarkReachable (Reachability rc)
2758 if (rc.IsUnreachable)
2761 base.MarkReachable (rc);
2763 if (scope_initializers != null) {
2764 foreach (var si in scope_initializers)
2765 si.MarkReachable (rc);
2768 foreach (var s in statements) {
2769 rc = s.MarkReachable (rc);
2770 if (rc.IsUnreachable) {
2771 if ((flags & Flags.ReachableEnd) != 0)
2772 return new Reachability ();
2778 flags |= Flags.ReachableEnd;
2783 public override bool Resolve (BlockContext bc)
2785 if ((flags & Flags.Resolved) != 0)
2788 Block prev_block = bc.CurrentBlock;
2789 bc.CurrentBlock = this;
2792 // Compiler generated scope statements
2794 if (scope_initializers != null) {
2795 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2796 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2799 resolving_init_idx = null;
2803 int statement_count = statements.Count;
2804 for (int ix = 0; ix < statement_count; ix++){
2805 Statement s = statements [ix];
2807 if (!s.Resolve (bc)) {
2809 if (!bc.IsInProbingMode)
2810 statements [ix] = new EmptyStatement (s.loc);
2816 bc.CurrentBlock = prev_block;
2818 flags |= Flags.Resolved;
2822 protected override void DoEmit (EmitContext ec)
2824 for (int ix = 0; ix < statements.Count; ix++){
2825 statements [ix].Emit (ec);
2829 public override void Emit (EmitContext ec)
2831 if (scope_initializers != null)
2832 EmitScopeInitializers (ec);
2837 protected void EmitScopeInitializers (EmitContext ec)
2839 foreach (Statement s in scope_initializers)
2843 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2845 if (scope_initializers != null) {
2846 foreach (var si in scope_initializers)
2847 si.FlowAnalysis (fc);
2850 return DoFlowAnalysis (fc, 0);
2853 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2855 bool end_unreachable = !reachable;
2856 for (; startIndex < statements.Count; ++startIndex) {
2857 var s = statements[startIndex];
2859 end_unreachable = s.FlowAnalysis (fc);
2860 if (s.IsUnreachable) {
2862 // This is kind of a hack, switch label is unreachable because we need to mark
2863 // switch section end but at the same time we need to run Emit on it
2865 if (s is SwitchLabel)
2868 statements[startIndex] = new EmptyStatement (s.loc);
2873 // Statement end reachability is needed mostly due to goto support. Consider
2882 // X label is reachable only via goto not as another statement after if. We need
2883 // this for flow-analysis only to carry variable info correctly.
2885 if (end_unreachable) { // s is ExitStatement) {
2886 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2887 s = statements[startIndex];
2888 if (s is SwitchLabel) {
2889 s.FlowAnalysis (fc);
2893 if (s.IsUnreachable) {
2894 s.FlowAnalysis (fc);
2895 statements[startIndex] = new EmptyStatement (s.loc);
2902 // The condition should be true unless there is forward jumping goto
2904 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
2907 return !Explicit.HasReachableClosingBrace;
2910 public void ScanGotoJump (Statement label)
2913 for (i = 0; i < statements.Count; ++i) {
2914 if (statements[i] == label)
2918 var rc = new Reachability ();
2919 for (; i < statements.Count; ++i) {
2920 var s = statements[i];
2921 rc = s.MarkReachable (rc);
2922 if (rc.IsUnreachable)
2926 flags |= Flags.ReachableEnd;
2929 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
2932 for (i = 0; i < statements.Count; ++i) {
2933 if (statements[i] == label)
2937 DoFlowAnalysis (fc, ++i);
2941 public override string ToString ()
2943 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2947 protected override void CloneTo (CloneContext clonectx, Statement t)
2949 Block target = (Block) t;
2951 target.clone_id = clone_id_counter++;
2954 clonectx.AddBlockMap (this, target);
2955 if (original != this)
2956 clonectx.AddBlockMap (original, target);
2958 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2959 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2962 target.Parent = clonectx.RemapBlockCopy (Parent);
2964 target.statements = new List<Statement> (statements.Count);
2965 foreach (Statement s in statements)
2966 target.statements.Add (s.Clone (clonectx));
2969 public override object Accept (StructuralVisitor visitor)
2971 return visitor.Visit (this);
2975 public class ExplicitBlock : Block
2977 protected AnonymousMethodStorey am_storey;
2979 public ExplicitBlock (Block parent, Location start, Location end)
2980 : this (parent, (Flags) 0, start, end)
2984 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2985 : base (parent, flags, start, end)
2987 this.Explicit = this;
2992 public AnonymousMethodStorey AnonymousMethodStorey {
2998 public bool HasAwait {
3000 return (flags & Flags.AwaitBlock) != 0;
3004 public bool HasCapturedThis {
3006 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3009 return (flags & Flags.HasCapturedThis) != 0;
3014 // Used to indicate that the block has reference to parent
3015 // block and cannot be made static when defining anonymous method
3017 public bool HasCapturedVariable {
3019 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3022 return (flags & Flags.HasCapturedVariable) != 0;
3026 public bool HasReachableClosingBrace {
3028 return (flags & Flags.ReachableEnd) != 0;
3031 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3035 public bool HasYield {
3037 return (flags & Flags.YieldBlock) != 0;
3044 // Creates anonymous method storey in current block
3046 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3049 // Return same story for iterator and async blocks unless we are
3050 // in nested anonymous method
3052 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3053 return ec.CurrentAnonymousMethod.Storey;
3055 if (am_storey == null) {
3056 MemberBase mc = ec.MemberContext as MemberBase;
3059 // Creates anonymous method storey for this block
3061 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3067 public override void Emit (EmitContext ec)
3069 if (am_storey != null) {
3070 DefineStoreyContainer (ec, am_storey);
3071 am_storey.EmitStoreyInstantiation (ec, this);
3074 if (scope_initializers != null)
3075 EmitScopeInitializers (ec);
3077 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3078 ec.Emit (OpCodes.Nop);
3089 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3090 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3091 ec.Emit (OpCodes.Nop);
3095 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3097 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3098 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3099 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3103 // Creates anonymous method storey
3105 storey.CreateContainer ();
3106 storey.DefineContainer ();
3108 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3111 // Only first storey in path will hold this reference. All children blocks will
3112 // reference it indirectly using $ref field
3114 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3115 if (b.Parent != null) {
3116 var s = b.Parent.Explicit.AnonymousMethodStorey;
3118 storey.HoistedThis = s.HoistedThis;
3123 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3124 if (storey.HoistedThis == null)
3125 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3127 if (storey.HoistedThis != null)
3133 // We are the first storey on path and 'this' has to be hoisted
3135 if (storey.HoistedThis == null) {
3136 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3138 // ThisReferencesFromChildrenBlock holds all reference even if they
3139 // are not on this path. It saves some memory otherwise it'd have to
3140 // be in every explicit block. We run this check to see if the reference
3141 // is valid for this storey
3143 Block block_on_path = ref_block;
3144 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3146 if (block_on_path == null)
3149 if (storey.HoistedThis == null) {
3150 storey.AddCapturedThisField (ec, null);
3153 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3156 if (b.AnonymousMethodStorey != null) {
3158 // Don't add storey cross reference for `this' when the storey ends up not
3159 // beeing attached to any parent
3161 if (b.ParametersBlock.StateMachine == null) {
3162 AnonymousMethodStorey s = null;
3163 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3164 s = ab.Explicit.AnonymousMethodStorey;
3169 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3171 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3172 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3177 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3178 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
3181 // Stop propagation inside same top block
3183 if (b.ParametersBlock == ParametersBlock.Original)
3186 b = b.ParametersBlock;
3189 pb = b as ParametersBlock;
3190 if (pb != null && pb.StateMachine != null) {
3191 if (pb.StateMachine == storey)
3195 // If we are state machine with no parent. We can hook into parent without additional
3196 // reference and capture this directly
3198 ExplicitBlock parent_storey_block = pb;
3199 while (parent_storey_block.Parent != null) {
3200 parent_storey_block = parent_storey_block.Parent.Explicit;
3201 if (parent_storey_block.AnonymousMethodStorey != null) {
3206 if (parent_storey_block.AnonymousMethodStorey == null) {
3207 pb.StateMachine.AddCapturedThisField (ec, null);
3208 b.HasCapturedThis = true;
3212 pb.StateMachine.AddParentStoreyReference (ec, storey);
3215 b.HasCapturedVariable = true;
3221 var ref_blocks = storey.ReferencesFromChildrenBlock;
3222 if (ref_blocks != null) {
3223 foreach (ExplicitBlock ref_block in ref_blocks) {
3224 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3225 if (b.AnonymousMethodStorey != null) {
3226 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3229 // Stop propagation inside same top block
3231 if (b.ParametersBlock == ParametersBlock.Original)
3234 b = b.ParametersBlock;
3237 var pb = b as ParametersBlock;
3238 if (pb != null && pb.StateMachine != null) {
3239 if (pb.StateMachine == storey)
3242 pb.StateMachine.AddParentStoreyReference (ec, storey);
3245 b.HasCapturedVariable = true;
3251 storey.PrepareEmit ();
3252 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3255 public void RegisterAsyncAwait ()
3258 while ((block.flags & Flags.AwaitBlock) == 0) {
3259 block.flags |= Flags.AwaitBlock;
3261 if (block is ParametersBlock)
3264 block = block.Parent.Explicit;
3268 public void RegisterIteratorYield ()
3270 ParametersBlock.TopBlock.IsIterator = true;
3273 while ((block.flags & Flags.YieldBlock) == 0) {
3274 block.flags |= Flags.YieldBlock;
3276 if (block.Parent == null)
3279 block = block.Parent.Explicit;
3283 public void SetCatchBlock ()
3285 flags |= Flags.CatchBlock;
3288 public void SetFinallyBlock ()
3290 flags |= Flags.FinallyBlock;
3293 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3295 tryBlock.statements = statements;
3296 statements = new List<Statement> (1);
3297 statements.Add (tf);
3302 // ParametersBlock was introduced to support anonymous methods
3303 // and lambda expressions
3305 public class ParametersBlock : ExplicitBlock
3307 public class ParameterInfo : INamedBlockVariable
3309 readonly ParametersBlock block;
3311 public VariableInfo VariableInfo;
3314 public ParameterInfo (ParametersBlock block, int index)
3322 public ParametersBlock Block {
3328 Block INamedBlockVariable.Block {
3334 public bool IsDeclared {
3340 public bool IsParameter {
3346 public bool IsLocked {
3355 public Location Location {
3357 return Parameter.Location;
3361 public Parameter Parameter {
3363 return block.Parameters [index];
3367 public TypeSpec ParameterType {
3369 return Parameter.Type;
3375 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3377 return new ParameterReference (this, loc);
3382 // Block is converted into an expression
3384 sealed class BlockScopeExpression : Expression
3387 readonly ParametersBlock block;
3389 public BlockScopeExpression (Expression child, ParametersBlock block)
3395 public override bool ContainsEmitWithAwait ()
3397 return child.ContainsEmitWithAwait ();
3400 public override Expression CreateExpressionTree (ResolveContext ec)
3402 throw new NotSupportedException ();
3405 protected override Expression DoResolve (ResolveContext ec)
3410 child = child.Resolve (ec);
3414 eclass = child.eclass;
3419 public override void Emit (EmitContext ec)
3421 block.EmitScopeInitializers (ec);
3426 protected ParametersCompiled parameters;
3427 protected ParameterInfo[] parameter_info;
3428 protected bool resolved;
3429 protected ToplevelBlock top_block;
3430 protected StateMachine state_machine;
3432 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3433 : base (parent, 0, start, start)
3435 if (parameters == null)
3436 throw new ArgumentNullException ("parameters");
3438 this.parameters = parameters;
3439 ParametersBlock = this;
3441 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3443 this.top_block = parent.ParametersBlock.top_block;
3444 ProcessParameters ();
3447 protected ParametersBlock (ParametersCompiled parameters, Location start)
3448 : base (null, 0, start, start)
3450 if (parameters == null)
3451 throw new ArgumentNullException ("parameters");
3453 this.parameters = parameters;
3454 ParametersBlock = this;
3458 // It's supposed to be used by method body implementation of anonymous methods
3460 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3461 : base (null, 0, source.StartLocation, source.EndLocation)
3463 this.parameters = parameters;
3464 this.statements = source.statements;
3465 this.scope_initializers = source.scope_initializers;
3467 this.resolved = true;
3468 this.reachable = source.reachable;
3469 this.am_storey = source.am_storey;
3470 this.state_machine = source.state_machine;
3471 this.flags = source.flags & Flags.ReachableEnd;
3473 ParametersBlock = this;
3476 // Overwrite original for comparison purposes when linking cross references
3477 // between anonymous methods
3479 Original = source.Original;
3484 public bool IsAsync {
3486 return (flags & Flags.HasAsyncModifier) != 0;
3489 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3494 // Block has been converted to expression tree
3496 public bool IsExpressionTree {
3498 return (flags & Flags.IsExpressionTree) != 0;
3503 // The parameters for the block.
3505 public ParametersCompiled Parameters {
3511 public StateMachine StateMachine {
3513 return state_machine;
3517 public ToplevelBlock TopBlock {
3523 public bool Resolved {
3525 return (flags & Flags.Resolved) != 0;
3529 public int TemporaryLocalsCount { get; set; }
3534 // Checks whether all `out' parameters have been assigned.
3536 public void CheckOutParametersAssignment (FlowAnalysisContext fc)
3538 CheckOutParametersAssignment (fc, fc.DefiniteAssignment);
3541 public void CheckOutParametersAssignment (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3543 if (parameter_info == null)
3546 foreach (var p in parameter_info) {
3547 if (p.VariableInfo == null)
3550 if (p.VariableInfo.IsAssigned (dat))
3553 fc.Report.Error (177, p.Location,
3554 "The out parameter `{0}' must be assigned to before control leaves the current method",
3559 public override Expression CreateExpressionTree (ResolveContext ec)
3561 if (statements.Count == 1) {
3562 Expression expr = statements[0].CreateExpressionTree (ec);
3563 if (scope_initializers != null)
3564 expr = new BlockScopeExpression (expr, this);
3569 return base.CreateExpressionTree (ec);
3572 public override void Emit (EmitContext ec)
3574 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3575 DefineStoreyContainer (ec, state_machine);
3576 state_machine.EmitStoreyInstantiation (ec, this);
3582 public void EmitEmbedded (EmitContext ec)
3584 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3585 DefineStoreyContainer (ec, state_machine);
3586 state_machine.EmitStoreyInstantiation (ec, this);
3592 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3594 var res = base.DoFlowAnalysis (fc);
3596 if (HasReachableClosingBrace)
3597 CheckOutParametersAssignment (fc);
3602 public ParameterInfo GetParameterInfo (Parameter p)
3604 for (int i = 0; i < parameters.Count; ++i) {
3605 if (parameters[i] == p)
3606 return parameter_info[i];
3609 throw new ArgumentException ("Invalid parameter");
3612 public ParameterReference GetParameterReference (int index, Location loc)
3614 return new ParameterReference (parameter_info[index], loc);
3617 public Statement PerformClone ()
3619 CloneContext clonectx = new CloneContext ();
3620 return Clone (clonectx);
3623 protected void ProcessParameters ()
3625 if (parameters.Count == 0)
3628 parameter_info = new ParameterInfo[parameters.Count];
3629 for (int i = 0; i < parameter_info.Length; ++i) {
3630 var p = parameters.FixedParameters[i];
3634 // TODO: Should use Parameter only and more block there
3635 parameter_info[i] = new ParameterInfo (this, i);
3637 AddLocalName (p.Name, parameter_info[i]);
3641 public override bool Resolve (BlockContext bc)
3643 // TODO: if ((flags & Flags.Resolved) != 0)
3650 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3651 flags |= Flags.IsExpressionTree;
3654 PrepareAssignmentAnalysis (bc);
3656 if (!base.Resolve (bc))
3659 } catch (Exception e) {
3660 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter)
3663 if (bc.CurrentBlock != null) {
3664 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3666 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3669 if (bc.Module.Compiler.Settings.DebugFlags > 0)
3674 // If an asynchronous body of F is either an expression classified as nothing, or a
3675 // statement block where no return statements have expressions, the inferred return type is Task
3678 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3679 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3680 am.ReturnTypeInference = null;
3681 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3689 void PrepareAssignmentAnalysis (BlockContext bc)
3691 for (int i = 0; i < parameters.Count; ++i) {
3692 var par = parameters.FixedParameters[i];
3694 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3697 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3701 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3703 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3704 var stateMachine = new IteratorStorey (iterator);
3706 state_machine = stateMachine;
3707 iterator.SetStateMachine (stateMachine);
3709 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3710 tlb.Original = this;
3711 tlb.state_machine = stateMachine;
3712 tlb.AddStatement (new Return (iterator, iterator.Location));
3716 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3718 for (int i = 0; i < parameters.Count; i++) {
3719 Parameter p = parameters[i];
3720 Parameter.Modifier mod = p.ModFlags;
3721 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3722 host.Compiler.Report.Error (1988, p.Location,
3723 "Async methods cannot have ref or out parameters");
3727 if (p is ArglistParameter) {
3728 host.Compiler.Report.Error (4006, p.Location,
3729 "__arglist is not allowed in parameter list of async methods");
3733 if (parameters.Types[i].IsPointer) {
3734 host.Compiler.Report.Error (4005, p.Location,
3735 "Async methods cannot have unsafe parameters");
3741 host.Compiler.Report.Warning (1998, 1, loc,
3742 "Async block lacks `await' operator and will run synchronously");
3745 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3746 var initializer = new AsyncInitializer (this, host, block_type);
3747 initializer.Type = block_type;
3748 initializer.DelegateType = delegateType;
3750 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3752 state_machine = stateMachine;
3753 initializer.SetStateMachine (stateMachine);
3755 const Flags flags = Flags.CompilerGenerated;
3757 var b = this is ToplevelBlock ?
3758 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
3759 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
3762 b.state_machine = stateMachine;
3763 b.AddStatement (new AsyncInitializerStatement (initializer));
3771 public class ToplevelBlock : ParametersBlock
3773 LocalVariable this_variable;
3774 CompilerContext compiler;
3775 Dictionary<string, object> names;
3776 Dictionary<string, object> labels;
3778 List<ExplicitBlock> this_references;
3780 public ToplevelBlock (CompilerContext ctx, Location loc)
3781 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3785 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
3786 : base (parameters, start)
3788 this.compiler = ctx;
3792 ProcessParameters ();
3796 // Recreates a top level block from parameters block. Used for
3797 // compiler generated methods where the original block comes from
3798 // explicit child block. This works for already resolved blocks
3799 // only to ensure we resolve them in the correct flow order
3801 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3802 : base (source, parameters)
3804 this.compiler = source.TopBlock.compiler;
3808 public bool IsIterator {
3810 return (flags & Flags.Iterator) != 0;
3813 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3817 public Report Report {
3819 return compiler.Report;
3824 // Used by anonymous blocks to track references of `this' variable
3826 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3828 return this_references;
3833 // Returns the "this" instance variable of this block.
3834 // See AddThisVariable() for more information.
3836 public LocalVariable ThisVariable {
3838 return this_variable;
3842 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3845 names = new Dictionary<string, object> ();
3848 if (!names.TryGetValue (name, out value)) {
3849 names.Add (name, li);
3853 INamedBlockVariable existing = value as INamedBlockVariable;
3854 List<INamedBlockVariable> existing_list;
3855 if (existing != null) {
3856 existing_list = new List<INamedBlockVariable> ();
3857 existing_list.Add (existing);
3858 names[name] = existing_list;
3860 existing_list = (List<INamedBlockVariable>) value;
3864 // A collision checking between local names
3866 var variable_block = li.Block.Explicit;
3867 for (int i = 0; i < existing_list.Count; ++i) {
3868 existing = existing_list[i];
3869 Block b = existing.Block.Explicit;
3871 // Collision at same level
3872 if (variable_block == b) {
3873 li.Block.Error_AlreadyDeclared (name, li);
3877 // Collision with parent
3878 Block parent = variable_block;
3879 while ((parent = parent.Parent) != null) {
3881 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3882 i = existing_list.Count;
3887 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3888 // Collision with children
3889 while ((b = b.Parent) != null) {
3890 if (variable_block == b) {
3891 li.Block.Error_AlreadyDeclared (name, li, "child");
3892 i = existing_list.Count;
3899 existing_list.Add (li);
3902 public void AddLabel (string name, LabeledStatement label)
3905 labels = new Dictionary<string, object> ();
3908 if (!labels.TryGetValue (name, out value)) {
3909 labels.Add (name, label);
3913 LabeledStatement existing = value as LabeledStatement;
3914 List<LabeledStatement> existing_list;
3915 if (existing != null) {
3916 existing_list = new List<LabeledStatement> ();
3917 existing_list.Add (existing);
3918 labels[name] = existing_list;
3920 existing_list = (List<LabeledStatement>) value;
3924 // A collision checking between labels
3926 for (int i = 0; i < existing_list.Count; ++i) {
3927 existing = existing_list[i];
3928 Block b = existing.Block;
3930 // Collision at same level
3931 if (label.Block == b) {
3932 Report.SymbolRelatedToPreviousError (existing.loc, name);
3933 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3937 // Collision with parent
3939 while ((b = b.Parent) != null) {
3940 if (existing.Block == b) {
3941 Report.Error (158, label.loc,
3942 "The label `{0}' shadows another label by the same name in a contained scope", name);
3943 i = existing_list.Count;
3948 // Collision with with children
3950 while ((b = b.Parent) != null) {
3951 if (label.Block == b) {
3952 Report.Error (158, label.loc,
3953 "The label `{0}' shadows another label by the same name in a contained scope", name);
3954 i = existing_list.Count;
3960 existing_list.Add (label);
3963 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3965 if (this_references == null)
3966 this_references = new List<ExplicitBlock> ();
3968 if (!this_references.Contains (block))
3969 this_references.Add (block);
3972 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3974 this_references.Remove (block);
3978 // Creates an arguments set from all parameters, useful for method proxy calls
3980 public Arguments GetAllParametersArguments ()
3982 int count = parameters.Count;
3983 Arguments args = new Arguments (count);
3984 for (int i = 0; i < count; ++i) {
3985 var pi = parameter_info[i];
3986 var arg_expr = GetParameterReference (i, pi.Location);
3988 Argument.AType atype_modifier;
3989 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
3990 case Parameter.Modifier.REF:
3991 atype_modifier = Argument.AType.Ref;
3993 case Parameter.Modifier.OUT:
3994 atype_modifier = Argument.AType.Out;
4001 args.Add (new Argument (arg_expr, atype_modifier));
4008 // Lookup inside a block, the returned value can represent 3 states
4010 // true+variable: A local name was found and it's valid
4011 // false+variable: A local name was found in a child block only
4012 // false+null: No local name was found
4014 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4020 if (!names.TryGetValue (name, out value))
4023 variable = value as INamedBlockVariable;
4025 if (variable != null) {
4027 if (variable.Block == b.Original)
4031 } while (b != null);
4039 } while (b != null);
4041 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4042 for (int i = 0; i < list.Count; ++i) {
4045 if (variable.Block == b.Original)
4049 } while (b != null);
4057 } while (b != null);
4067 public LabeledStatement GetLabel (string name, Block block)
4073 if (!labels.TryGetValue (name, out value)) {
4077 var label = value as LabeledStatement;
4079 if (label != null) {
4081 if (label.Block == b.Original)
4084 } while (b != null);
4086 List<LabeledStatement> list = (List<LabeledStatement>) value;
4087 for (int i = 0; i < list.Count; ++i) {
4089 if (label.Block == b.Original)
4098 // This is used by non-static `struct' constructors which do not have an
4099 // initializer - in this case, the constructor must initialize all of the
4100 // struct's fields. To do this, we add a "this" variable and use the flow
4101 // analysis code to ensure that it's been fully initialized before control
4102 // leaves the constructor.
4104 public void AddThisVariable (BlockContext bc)
4106 if (this_variable != null)
4107 throw new InternalErrorException (StartLocation.ToString ());
4109 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4110 this_variable.Type = bc.CurrentType;
4111 this_variable.PrepareAssignmentAnalysis (bc);
4114 public bool IsThisAssigned (FlowAnalysisContext fc)
4116 return this_variable == null || this_variable.IsThisAssigned (fc, this);
4119 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4121 var res = base.DoFlowAnalysis (fc);
4124 // If we're a non-static struct constructor which doesn't have an
4125 // initializer, then we must initialize all of the struct's fields.
4127 IsThisAssigned (fc);
4131 public override void Emit (EmitContext ec)
4133 if (Report.Errors > 0)
4137 if (IsCompilerGenerated) {
4138 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4146 // If `HasReturnLabel' is set, then we already emitted a
4147 // jump to the end of the method, so we must emit a `ret'
4150 // Unfortunately, System.Reflection.Emit automatically emits
4151 // a leave to the end of a finally block. This is a problem
4152 // if no code is following the try/finally block since we may
4153 // jump to a point after the end of the method.
4154 // As a workaround, we're always creating a return label in
4157 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4158 if (ec.HasReturnLabel)
4159 ec.MarkLabel (ec.ReturnLabel);
4161 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4162 ec.Mark (EndLocation);
4164 if (ec.ReturnType.Kind != MemberKind.Void)
4165 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4167 ec.Emit (OpCodes.Ret);
4170 } catch (Exception e) {
4171 throw new InternalErrorException (e, StartLocation);
4175 public bool Resolve (BlockContext bc, IMethodData md)
4180 var errors = bc.Report.Errors;
4184 if (bc.Report.Errors > errors)
4187 MarkReachable (new Reachability ());
4189 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4190 // TODO: var md = bc.CurrentMemberDefinition;
4191 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4194 if ((flags & Flags.NoFlowAnalysis) != 0)
4197 var fc = new FlowAnalysisContext (bc.Module.Compiler, this);
4200 } catch (Exception e) {
4201 throw new InternalErrorException (e, StartLocation);
4208 public class SwitchLabel : Statement
4216 // if expr == null, then it is the default case.
4218 public SwitchLabel (Expression expr, Location l)
4224 public bool IsDefault {
4226 return label == null;
4230 public Expression Label {
4236 public Location Location {
4242 public Constant Converted {
4251 public bool SectionStart { get; set; }
4253 public Label GetILLabel (EmitContext ec)
4255 if (il_label == null){
4256 il_label = ec.DefineLabel ();
4259 return il_label.Value;
4262 protected override void DoEmit (EmitContext ec)
4264 ec.MarkLabel (GetILLabel (ec));
4267 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4272 if (IsUnreachable) {
4273 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
4275 fc.Report.Error (163, Location,
4276 "Control cannot fall through from one case label `{0}' to another", GetSignatureForError ());
4282 public override bool Resolve (BlockContext bc)
4284 if (ResolveAndReduce (bc))
4285 bc.Switch.RegisterLabel (bc, this);
4291 // Resolves the expression, reduces it to a literal if possible
4292 // and then converts it to the requested type.
4294 bool ResolveAndReduce (BlockContext rc)
4299 var c = label.ResolveLabelConstant (rc);
4303 if (rc.Switch.IsNullable && c is NullLiteral) {
4308 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType);
4309 return converted != null;
4312 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4314 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4315 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4318 protected override void CloneTo (CloneContext clonectx, Statement target)
4320 var t = (SwitchLabel) target;
4322 t.label = label.Clone (clonectx);
4325 public override object Accept (StructuralVisitor visitor)
4327 return visitor.Visit (this);
4330 string GetSignatureForError ()
4333 if (converted == null)
4336 label = converted.GetValueAsLiteral ();
4338 return string.Format ("case {0}:", label);
4342 public class Switch : LoopStatement
4344 // structure used to hold blocks of keys while calculating table switch
4345 sealed class LabelsRange : IComparable<LabelsRange>
4347 public readonly long min;
4349 public readonly List<long> label_values;
4351 public LabelsRange (long value)
4354 label_values = new List<long> ();
4355 label_values.Add (value);
4358 public LabelsRange (long min, long max, ICollection<long> values)
4362 this.label_values = new List<long> (values);
4367 return max - min + 1;
4371 public bool AddValue (long value)
4373 var gap = value - min + 1;
4374 // Ensure the range has > 50% occupancy
4375 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4379 label_values.Add (value);
4383 public int CompareTo (LabelsRange other)
4385 int nLength = label_values.Count;
4386 int nLengthOther = other.label_values.Count;
4387 if (nLengthOther == nLength)
4388 return (int) (other.min - min);
4390 return nLength - nLengthOther;
4394 sealed class DispatchStatement : Statement
4396 readonly Switch body;
4398 public DispatchStatement (Switch body)
4403 protected override void CloneTo (CloneContext clonectx, Statement target)
4405 throw new NotImplementedException ();
4408 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4413 protected override void DoEmit (EmitContext ec)
4415 body.EmitDispatch (ec);
4419 public Expression Expr;
4422 // Mapping of all labels to their SwitchLabels
4424 Dictionary<long, SwitchLabel> labels;
4425 Dictionary<string, SwitchLabel> string_labels;
4426 List<SwitchLabel> case_labels;
4428 List<Tuple<GotoCase, Constant>> goto_cases;
4429 List<DefiniteAssignmentBitSet> end_reachable_das;
4432 /// The governing switch type
4434 public TypeSpec SwitchType;
4436 Expression new_expr;
4438 SwitchLabel case_null;
4439 SwitchLabel case_default;
4441 Label defaultLabel, nullLabel;
4442 VariableReference value;
4443 ExpressionStatement string_dictionary;
4444 FieldExpr switch_cache_field;
4445 ExplicitBlock block;
4449 // Nullable Types support
4451 Nullable.Unwrap unwrap;
4453 public Switch (Expression e, ExplicitBlock block, Location l)
4461 public SwitchLabel ActiveLabel { get; set; }
4463 public ExplicitBlock Block {
4469 public SwitchLabel DefaultLabel {
4471 return case_default;
4475 public bool IsNullable {
4477 return unwrap != null;
4481 public List<SwitchLabel> RegisteredLabels {
4488 // Determines the governing type for a switch. The returned
4489 // expression might be the expression from the switch, or an
4490 // expression that includes any potential conversions to
4492 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
4494 switch (expr.Type.BuiltinType) {
4495 case BuiltinTypeSpec.Type.Byte:
4496 case BuiltinTypeSpec.Type.SByte:
4497 case BuiltinTypeSpec.Type.UShort:
4498 case BuiltinTypeSpec.Type.Short:
4499 case BuiltinTypeSpec.Type.UInt:
4500 case BuiltinTypeSpec.Type.Int:
4501 case BuiltinTypeSpec.Type.ULong:
4502 case BuiltinTypeSpec.Type.Long:
4503 case BuiltinTypeSpec.Type.Char:
4504 case BuiltinTypeSpec.Type.String:
4505 case BuiltinTypeSpec.Type.Bool:
4509 if (expr.Type.IsEnum)
4513 // Try to find a *user* defined implicit conversion.
4515 // If there is no implicit conversion, or if there are multiple
4516 // conversions, we have to report an error
4518 Expression converted = null;
4519 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
4522 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
4527 // Ignore over-worked ImplicitUserConversions that do
4528 // an implicit conversion in addition to the user conversion.
4530 if (!(e is UserCast))
4533 if (converted != null){
4534 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4543 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
4545 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4560 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4562 case_labels.Add (sl);
4565 if (case_default != null) {
4566 sl.Error_AlreadyOccurs (rc, case_default);
4575 if (string_labels != null) {
4576 string string_value = sl.Converted.GetValue () as string;
4577 if (string_value == null)
4580 string_labels.Add (string_value, sl);
4582 if (sl.Converted is NullLiteral) {
4585 labels.Add (sl.Converted.GetValueAsLong (), sl);
4588 } catch (ArgumentException) {
4589 if (string_labels != null)
4590 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4592 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4597 // This method emits code for a lookup-based switch statement (non-string)
4598 // Basically it groups the cases into blocks that are at least half full,
4599 // and then spits out individual lookup opcodes for each block.
4600 // It emits the longest blocks first, and short blocks are just
4601 // handled with direct compares.
4603 void EmitTableSwitch (EmitContext ec, Expression val)
4605 if (labels != null && labels.Count > 0) {
4606 List<LabelsRange> ranges;
4607 if (string_labels != null) {
4608 // We have done all hard work for string already
4609 // setup single range only
4610 ranges = new List<LabelsRange> (1);
4611 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4613 var element_keys = new long[labels.Count];
4614 labels.Keys.CopyTo (element_keys, 0);
4615 Array.Sort (element_keys);
4618 // Build possible ranges of switch labes to reduce number
4621 ranges = new List<LabelsRange> (element_keys.Length);
4622 var range = new LabelsRange (element_keys[0]);
4624 for (int i = 1; i < element_keys.Length; ++i) {
4625 var l = element_keys[i];
4626 if (range.AddValue (l))
4629 range = new LabelsRange (l);
4633 // sort the blocks so we can tackle the largest ones first
4637 Label lbl_default = defaultLabel;
4638 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4640 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4641 LabelsRange kb = ranges[range_index];
4642 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4644 // Optimize small ranges using simple equality check
4645 if (kb.Range <= 2) {
4646 foreach (var key in kb.label_values) {
4647 SwitchLabel sl = labels[key];
4648 if (sl == case_default || sl == case_null)
4651 if (sl.Converted.IsZeroInteger) {
4652 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4655 sl.Converted.Emit (ec);
4656 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4660 // TODO: if all the keys in the block are the same and there are
4661 // no gaps/defaults then just use a range-check.
4662 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4663 // TODO: optimize constant/I4 cases
4665 // check block range (could be > 2^31)
4667 ec.EmitLong (kb.min);
4668 ec.Emit (OpCodes.Blt, lbl_default);
4671 ec.EmitLong (kb.max);
4672 ec.Emit (OpCodes.Bgt, lbl_default);
4677 ec.EmitLong (kb.min);
4678 ec.Emit (OpCodes.Sub);
4681 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4685 int first = (int) kb.min;
4688 ec.Emit (OpCodes.Sub);
4689 } else if (first < 0) {
4690 ec.EmitInt (-first);
4691 ec.Emit (OpCodes.Add);
4695 // first, build the list of labels for the switch
4697 long cJumps = kb.Range;
4698 Label[] switch_labels = new Label[cJumps];
4699 for (int iJump = 0; iJump < cJumps; iJump++) {
4700 var key = kb.label_values[iKey];
4701 if (key == kb.min + iJump) {
4702 switch_labels[iJump] = labels[key].GetILLabel (ec);
4705 switch_labels[iJump] = lbl_default;
4709 // emit the switch opcode
4710 ec.Emit (OpCodes.Switch, switch_labels);
4713 // mark the default for this block
4714 if (range_index != 0)
4715 ec.MarkLabel (lbl_default);
4718 // the last default just goes to the end
4719 if (ranges.Count > 0)
4720 ec.Emit (OpCodes.Br, lbl_default);
4724 SwitchLabel FindLabel (Constant value)
4726 SwitchLabel sl = null;
4728 if (string_labels != null) {
4729 string s = value.GetValue () as string;
4731 if (case_null != null)
4733 else if (case_default != null)
4736 string_labels.TryGetValue (s, out sl);
4739 if (value is NullLiteral) {
4742 labels.TryGetValue (value.GetValueAsLong (), out sl);
4749 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4751 Expr.FlowAnalysis (fc);
4753 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
4754 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
4755 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
4757 block.FlowAnalysis (fc);
4759 fc.SwitchInitialDefinitiveAssignment = prev_switch;
4761 if (end_reachable_das != null) {
4762 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
4763 InitialDefinitiveAssignment |= sections_das;
4764 end_reachable_das = null;
4767 fc.DefiniteAssignment = InitialDefinitiveAssignment;
4769 return case_default != null && !end_reachable;
4772 public override bool Resolve (BlockContext ec)
4774 Expr = Expr.Resolve (ec);
4778 new_expr = SwitchGoverningType (ec, Expr);
4780 if (new_expr == null && Expr.Type.IsNullableType) {
4781 unwrap = Nullable.Unwrap.Create (Expr, false);
4785 new_expr = SwitchGoverningType (ec, unwrap);
4788 if (new_expr == null) {
4789 if (Expr.Type != InternalType.ErrorType) {
4790 ec.Report.Error (151, loc,
4791 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4792 Expr.Type.GetSignatureForError ());
4799 SwitchType = new_expr.Type;
4801 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4802 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4806 if (block.Statements.Count == 0)
4809 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4810 string_labels = new Dictionary<string, SwitchLabel> ();
4812 labels = new Dictionary<long, SwitchLabel> ();
4815 case_labels = new List<SwitchLabel> ();
4817 var constant = new_expr as Constant;
4820 // Don't need extra variable for constant switch or switch with
4821 // only default case
4823 if (constant == null) {
4825 // Store switch expression for comparison purposes
4827 value = new_expr as VariableReference;
4828 if (value == null && !HasOnlyDefaultSection ()) {
4829 var current_block = ec.CurrentBlock;
4830 ec.CurrentBlock = Block;
4831 // Create temporary variable inside switch scope
4832 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4834 ec.CurrentBlock = current_block;
4838 Switch old_switch = ec.Switch;
4840 var parent_los = ec.EnclosingLoopOrSwitch;
4841 ec.EnclosingLoopOrSwitch = this;
4843 var ok = Statement.Resolve (ec);
4845 ec.EnclosingLoopOrSwitch = parent_los;
4846 ec.Switch = old_switch;
4849 // Check if all goto cases are valid. Needs to be done after switch
4850 // is resolved because goto can jump forward in the scope.
4852 if (goto_cases != null) {
4853 foreach (var gc in goto_cases) {
4854 if (gc.Item1 == null) {
4855 if (DefaultLabel == null) {
4856 Goto.Error_UnknownLabel (ec, "default", loc);
4862 var sl = FindLabel (gc.Item2);
4864 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
4866 gc.Item1.Label = sl;
4874 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4875 ResolveStringSwitchMap (ec);
4879 // Anonymous storey initialization has to happen before
4880 // any generated switch dispatch
4882 block.InsertStatement (0, new DispatchStatement (this));
4887 bool HasOnlyDefaultSection ()
4889 for (int i = 0; i < block.Statements.Count; ++i) {
4890 var s = block.Statements[i] as SwitchLabel;
4892 if (s == null || s.IsDefault)
4901 public override Reachability MarkReachable (Reachability rc)
4903 if (rc.IsUnreachable)
4906 base.MarkReachable (rc);
4908 SwitchLabel constant_label = null;
4909 var constant = new_expr as Constant;
4910 if (constant != null) {
4911 constant_label = FindLabel (constant) ?? case_default;
4912 if (constant_label == null) {
4913 block.Statements.RemoveAt (0);
4919 var section_rc = new Reachability ();
4920 SwitchLabel prev_label = null;
4922 for (int i = 0; i < block.Statements.Count; ++i) {
4923 var s = block.Statements[i];
4924 var sl = s as SwitchLabel;
4927 if (!sl.SectionStart) {
4928 sl.MarkReachable (section_rc);
4932 if (prev_label == null) {
4934 switch_rc = Reachability.CreateUnreachable ();
4936 if (constant_label != null && sl != constant_label)
4937 section_rc = Reachability.CreateUnreachable ();
4943 // Small trick, using unreachable flag for label means
4944 // the label section does not fallthrough
4946 prev_label.MarkReachable (section_rc);
4949 switch_rc &= section_rc;
4950 section_rc = new Reachability ();
4952 if (constant_label != null && sl != constant_label)
4953 section_rc = Reachability.CreateUnreachable ();
4958 section_rc = s.MarkReachable (section_rc);
4961 if (prev_label != null) {
4962 prev_label.MarkReachable (section_rc);
4963 switch_rc &= section_rc;
4967 // Reachability can affect parent only when all possible paths are handled but
4968 // we still need to run reachability check on switch body to check for fall-through
4970 if (case_default == null && constant_label == null)
4974 // We have at least one local exit from the switch
4982 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
4984 if (goto_cases == null)
4985 goto_cases = new List<Tuple<GotoCase, Constant>> ();
4987 goto_cases.Add (Tuple.Create (gotoCase, value));
4991 // Converts string switch into string hashtable
4993 void ResolveStringSwitchMap (ResolveContext ec)
4995 FullNamedExpression string_dictionary_type;
4996 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4997 string_dictionary_type = new TypeExpression (
4998 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4999 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5001 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5002 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5004 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5008 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5009 Field field = new Field (ctype, string_dictionary_type,
5010 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5011 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5012 if (!field.Define ())
5014 ctype.AddField (field);
5016 var init = new List<Expression> ();
5018 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5019 string value = null;
5021 foreach (SwitchLabel sl in case_labels) {
5023 if (sl.SectionStart)
5024 labels.Add (++counter, sl);
5026 if (sl == case_default || sl == case_null)
5029 value = (string) sl.Converted.GetValue ();
5030 var init_args = new List<Expression> (2);
5031 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5033 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5034 init_args.Add (sl.Converted);
5036 init.Add (new CollectionElementInitializer (init_args, loc));
5039 Arguments args = new Arguments (1);
5040 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5041 Expression initializer = new NewInitialize (string_dictionary_type, args,
5042 new CollectionOrObjectInitializers (init, loc), loc);
5044 switch_cache_field = new FieldExpr (field, loc);
5045 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5048 void DoEmitStringSwitch (EmitContext ec)
5050 Label l_initialized = ec.DefineLabel ();
5053 // Skip initialization when value is null
5055 value.EmitBranchable (ec, nullLabel, false);
5058 // Check if string dictionary is initialized and initialize
5060 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5061 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5062 string_dictionary.EmitStatement (ec);
5064 ec.MarkLabel (l_initialized);
5066 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5068 ResolveContext rc = new ResolveContext (ec.MemberContext);
5070 if (switch_cache_field.Type.IsGeneric) {
5071 Arguments get_value_args = new Arguments (2);
5072 get_value_args.Add (new Argument (value));
5073 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5074 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5075 if (get_item == null)
5079 // A value was not found, go to default case
5081 get_item.EmitBranchable (ec, defaultLabel, false);
5083 Arguments get_value_args = new Arguments (1);
5084 get_value_args.Add (new Argument (value));
5086 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5087 if (get_item == null)
5090 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5091 get_item_object.EmitAssign (ec, get_item, true, false);
5092 ec.Emit (OpCodes.Brfalse, defaultLabel);
5094 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5095 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5097 get_item_int.EmitStatement (ec);
5098 get_item_object.Release (ec);
5101 EmitTableSwitch (ec, string_switch_variable);
5102 string_switch_variable.Release (ec);
5106 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5108 void EmitShortSwitch (EmitContext ec)
5110 MethodSpec equal_method = null;
5111 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5112 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5115 if (equal_method != null) {
5116 value.EmitBranchable (ec, nullLabel, false);
5119 for (int i = 0; i < case_labels.Count; ++i) {
5120 var label = case_labels [i];
5121 if (label == case_default || label == case_null)
5124 var constant = label.Converted;
5126 if (equal_method != null) {
5130 var call = new CallEmitter ();
5131 call.EmitPredefined (ec, equal_method, new Arguments (0));
5132 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5136 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5137 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5143 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5146 ec.Emit (OpCodes.Br, defaultLabel);
5149 void EmitDispatch (EmitContext ec)
5151 if (value == null) {
5153 // Constant switch, we already done the work
5158 if (string_dictionary != null) {
5159 DoEmitStringSwitch (ec);
5160 } else if (case_labels.Count < 4 || string_labels != null) {
5161 EmitShortSwitch (ec);
5163 EmitTableSwitch (ec, value);
5167 protected override void DoEmit (EmitContext ec)
5170 // Setup the codegen context
5172 Label old_end = ec.LoopEnd;
5173 Switch old_switch = ec.Switch;
5175 ec.LoopEnd = ec.DefineLabel ();
5178 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5179 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5181 if (value != null) {
5184 unwrap.EmitCheck (ec);
5185 ec.Emit (OpCodes.Brfalse, nullLabel);
5186 value.EmitAssign (ec, new_expr, false, false);
5187 } else if (new_expr != value) {
5188 value.EmitAssign (ec, new_expr, false, false);
5193 // Next statement is compiler generated we don't need extra
5194 // nop when we can use the statement for sequence point
5196 ec.Mark (block.StartLocation);
5197 block.IsCompilerGenerated = true;
5202 // Restore context state.
5203 ec.MarkLabel (ec.LoopEnd);
5206 // Restore the previous context
5208 ec.LoopEnd = old_end;
5209 ec.Switch = old_switch;
5212 protected override void CloneTo (CloneContext clonectx, Statement t)
5214 Switch target = (Switch) t;
5216 target.Expr = Expr.Clone (clonectx);
5217 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5220 public override object Accept (StructuralVisitor visitor)
5222 return visitor.Visit (this);
5225 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5227 if (case_default == null)
5230 if (end_reachable_das == null)
5231 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5233 end_reachable_das.Add (fc.DefiniteAssignment);
5236 public override void SetEndReachable ()
5238 end_reachable = true;
5242 // A place where execution can restart in a state machine
5243 public abstract class ResumableStatement : Statement
5246 protected Label resume_point;
5248 public Label PrepareForEmit (EmitContext ec)
5252 resume_point = ec.DefineLabel ();
5254 return resume_point;
5257 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5262 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5267 public abstract class TryFinallyBlock : ExceptionStatement
5269 protected Statement stmt;
5270 Label dispose_try_block;
5271 bool prepared_for_dispose, emitted_dispose;
5272 Method finally_host;
5274 protected TryFinallyBlock (Statement stmt, Location loc)
5282 public Statement Statement {
5290 protected abstract void EmitTryBody (EmitContext ec);
5291 public abstract void EmitFinallyBody (EmitContext ec);
5293 public override Label PrepareForDispose (EmitContext ec, Label end)
5295 if (!prepared_for_dispose) {
5296 prepared_for_dispose = true;
5297 dispose_try_block = ec.DefineLabel ();
5299 return dispose_try_block;
5302 protected sealed override void DoEmit (EmitContext ec)
5304 EmitTryBodyPrepare (ec);
5307 ec.BeginFinallyBlock ();
5309 Label start_finally = ec.DefineLabel ();
5310 if (resume_points != null) {
5311 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5313 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5314 ec.Emit (OpCodes.Brfalse_S, start_finally);
5315 ec.Emit (OpCodes.Endfinally);
5318 ec.MarkLabel (start_finally);
5320 if (finally_host != null) {
5321 finally_host.Define ();
5322 finally_host.PrepareEmit ();
5323 finally_host.Emit ();
5325 // Now it's safe to add, to close it properly and emit sequence points
5326 finally_host.Parent.AddMember (finally_host);
5328 var ce = new CallEmitter ();
5329 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5330 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5332 EmitFinallyBody (ec);
5335 ec.EndExceptionBlock ();
5338 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5340 if (emitted_dispose)
5343 emitted_dispose = true;
5345 Label end_of_try = ec.DefineLabel ();
5347 // Ensure that the only way we can get into this code is through a dispatcher
5348 if (have_dispatcher)
5349 ec.Emit (OpCodes.Br, end);
5351 ec.BeginExceptionBlock ();
5353 ec.MarkLabel (dispose_try_block);
5355 Label[] labels = null;
5356 for (int i = 0; i < resume_points.Count; ++i) {
5357 ResumableStatement s = resume_points[i];
5358 Label ret = s.PrepareForDispose (ec, end_of_try);
5359 if (ret.Equals (end_of_try) && labels == null)
5361 if (labels == null) {
5362 labels = new Label[resume_points.Count];
5363 for (int j = 0; j < i; ++j)
5364 labels[j] = end_of_try;
5369 if (labels != null) {
5371 for (j = 1; j < labels.Length; ++j)
5372 if (!labels[0].Equals (labels[j]))
5374 bool emit_dispatcher = j < labels.Length;
5376 if (emit_dispatcher) {
5377 ec.Emit (OpCodes.Ldloc, pc);
5378 ec.EmitInt (first_resume_pc);
5379 ec.Emit (OpCodes.Sub);
5380 ec.Emit (OpCodes.Switch, labels);
5383 foreach (ResumableStatement s in resume_points)
5384 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5387 ec.MarkLabel (end_of_try);
5389 ec.BeginFinallyBlock ();
5391 if (finally_host != null) {
5392 var ce = new CallEmitter ();
5393 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5394 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5396 EmitFinallyBody (ec);
5399 ec.EndExceptionBlock ();
5402 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5404 var res = stmt.FlowAnalysis (fc);
5409 public override Reachability MarkReachable (Reachability rc)
5411 base.MarkReachable (rc);
5412 return Statement.MarkReachable (rc);
5415 public override bool Resolve (BlockContext bc)
5419 parent = bc.CurrentTryBlock;
5420 bc.CurrentTryBlock = this;
5422 using (bc.Set (ResolveContext.Options.TryScope)) {
5423 ok = stmt.Resolve (bc);
5426 bc.CurrentTryBlock = parent;
5429 // Finally block inside iterator is called from MoveNext and
5430 // Dispose methods that means we need to lift the block into
5431 // newly created host method to emit the body only once. The
5432 // original block then simply calls the newly generated method.
5434 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5435 var b = stmt as Block;
5436 if (b != null && b.Explicit.HasYield) {
5437 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5441 return base.Resolve (bc) && ok;
5446 // Base class for blocks using exception handling
5448 public abstract class ExceptionStatement : ResumableStatement
5450 protected List<ResumableStatement> resume_points;
5451 protected int first_resume_pc;
5452 protected ExceptionStatement parent;
5454 protected ExceptionStatement (Location loc)
5459 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5461 StateMachineInitializer state_machine = null;
5462 if (resume_points != null) {
5463 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5465 ec.EmitInt ((int) IteratorStorey.State.Running);
5466 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5469 ec.BeginExceptionBlock ();
5471 if (resume_points != null) {
5472 ec.MarkLabel (resume_point);
5474 // For normal control flow, we want to fall-through the Switch
5475 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5476 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5477 ec.EmitInt (first_resume_pc);
5478 ec.Emit (OpCodes.Sub);
5480 Label[] labels = new Label[resume_points.Count];
5481 for (int i = 0; i < resume_points.Count; ++i)
5482 labels[i] = resume_points[i].PrepareForEmit (ec);
5483 ec.Emit (OpCodes.Switch, labels);
5487 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5489 if (parent != null) {
5490 // TODO: MOVE to virtual TryCatch
5491 var tc = this as TryCatch;
5492 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5494 pc = parent.AddResumePoint (s, pc, stateMachine);
5496 pc = stateMachine.AddResumePoint (this);
5499 if (resume_points == null) {
5500 resume_points = new List<ResumableStatement> ();
5501 first_resume_pc = pc;
5504 if (pc != first_resume_pc + resume_points.Count)
5505 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5507 resume_points.Add (stmt);
5512 public class Lock : TryFinallyBlock
5515 TemporaryVariableReference expr_copy;
5516 TemporaryVariableReference lock_taken;
5518 public Lock (Expression expr, Statement stmt, Location loc)
5524 public Expression Expr {
5530 public override bool Resolve (BlockContext ec)
5532 expr = expr.Resolve (ec);
5536 if (!TypeSpec.IsReferenceType (expr.Type)) {
5537 ec.Report.Error (185, loc,
5538 "`{0}' is not a reference type as required by the lock statement",
5539 expr.Type.GetSignatureForError ());
5542 if (expr.Type.IsGenericParameter) {
5543 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5546 VariableReference lv = expr as VariableReference;
5549 locked = lv.IsLockedByStatement;
5550 lv.IsLockedByStatement = true;
5557 // Have to keep original lock value around to unlock same location
5558 // in the case of original value has changed or is null
5560 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5561 expr_copy.Resolve (ec);
5564 // Ensure Monitor methods are available
5566 if (ResolvePredefinedMethods (ec) > 1) {
5567 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5568 lock_taken.Resolve (ec);
5571 using (ec.Set (ResolveContext.Options.LockScope)) {
5576 lv.IsLockedByStatement = locked;
5582 protected override void EmitTryBodyPrepare (EmitContext ec)
5584 expr_copy.EmitAssign (ec, expr);
5586 if (lock_taken != null) {
5588 // Initialize ref variable
5590 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5593 // Monitor.Enter (expr_copy)
5595 expr_copy.Emit (ec);
5596 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5599 base.EmitTryBodyPrepare (ec);
5602 protected override void EmitTryBody (EmitContext ec)
5605 // Monitor.Enter (expr_copy, ref lock_taken)
5607 if (lock_taken != null) {
5608 expr_copy.Emit (ec);
5609 lock_taken.LocalInfo.CreateBuilder (ec);
5610 lock_taken.AddressOf (ec, AddressOp.Load);
5611 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
5614 Statement.Emit (ec);
5617 public override void EmitFinallyBody (EmitContext ec)
5620 // if (lock_taken) Monitor.Exit (expr_copy)
5622 Label skip = ec.DefineLabel ();
5624 if (lock_taken != null) {
5625 lock_taken.Emit (ec);
5626 ec.Emit (OpCodes.Brfalse_S, skip);
5629 expr_copy.Emit (ec);
5630 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
5632 ec.Emit (OpCodes.Call, m);
5634 ec.MarkLabel (skip);
5637 int ResolvePredefinedMethods (ResolveContext rc)
5639 // Try 4.0 Monitor.Enter (object, ref bool) overload first
5640 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
5644 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
5648 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
5652 protected override void CloneTo (CloneContext clonectx, Statement t)
5654 Lock target = (Lock) t;
5656 target.expr = expr.Clone (clonectx);
5657 target.stmt = Statement.Clone (clonectx);
5660 public override object Accept (StructuralVisitor visitor)
5662 return visitor.Visit (this);
5667 public class Unchecked : Statement {
5670 public Unchecked (Block b, Location loc)
5677 public override bool Resolve (BlockContext ec)
5679 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
5680 return Block.Resolve (ec);
5683 protected override void DoEmit (EmitContext ec)
5685 using (ec.With (EmitContext.Options.CheckedScope, false))
5689 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5691 return Block.FlowAnalysis (fc);
5694 public override Reachability MarkReachable (Reachability rc)
5696 base.MarkReachable (rc);
5697 return Block.MarkReachable (rc);
5700 protected override void CloneTo (CloneContext clonectx, Statement t)
5702 Unchecked target = (Unchecked) t;
5704 target.Block = clonectx.LookupBlock (Block);
5707 public override object Accept (StructuralVisitor visitor)
5709 return visitor.Visit (this);
5713 public class Checked : Statement {
5716 public Checked (Block b, Location loc)
5719 b.Unchecked = false;
5723 public override bool Resolve (BlockContext ec)
5725 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
5726 return Block.Resolve (ec);
5729 protected override void DoEmit (EmitContext ec)
5731 using (ec.With (EmitContext.Options.CheckedScope, true))
5735 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5737 return Block.FlowAnalysis (fc);
5740 public override Reachability MarkReachable (Reachability rc)
5742 base.MarkReachable (rc);
5743 return Block.MarkReachable (rc);
5746 protected override void CloneTo (CloneContext clonectx, Statement t)
5748 Checked target = (Checked) t;
5750 target.Block = clonectx.LookupBlock (Block);
5753 public override object Accept (StructuralVisitor visitor)
5755 return visitor.Visit (this);
5759 public class Unsafe : Statement {
5762 public Unsafe (Block b, Location loc)
5765 Block.Unsafe = true;
5769 public override bool Resolve (BlockContext ec)
5771 if (ec.CurrentIterator != null)
5772 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5774 using (ec.Set (ResolveContext.Options.UnsafeScope))
5775 return Block.Resolve (ec);
5778 protected override void DoEmit (EmitContext ec)
5783 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5785 return Block.FlowAnalysis (fc);
5788 public override Reachability MarkReachable (Reachability rc)
5790 base.MarkReachable (rc);
5791 return Block.MarkReachable (rc);
5794 protected override void CloneTo (CloneContext clonectx, Statement t)
5796 Unsafe target = (Unsafe) t;
5798 target.Block = clonectx.LookupBlock (Block);
5801 public override object Accept (StructuralVisitor visitor)
5803 return visitor.Visit (this);
5810 public class Fixed : Statement
5812 abstract class Emitter : ShimExpression
5814 protected LocalVariable vi;
5816 protected Emitter (Expression expr, LocalVariable li)
5822 public abstract void EmitExit (EmitContext ec);
5824 public override void FlowAnalysis (FlowAnalysisContext fc)
5826 expr.FlowAnalysis (fc);
5830 class ExpressionEmitter : Emitter {
5831 public ExpressionEmitter (Expression converted, LocalVariable li) :
5832 base (converted, li)
5836 protected override Expression DoResolve (ResolveContext rc)
5838 throw new NotImplementedException ();
5841 public override void Emit (EmitContext ec) {
5843 // Store pointer in pinned location
5849 public override void EmitExit (EmitContext ec)
5852 ec.Emit (OpCodes.Conv_U);
5857 class StringEmitter : Emitter
5859 LocalVariable pinned_string;
5861 public StringEmitter (Expression expr, LocalVariable li)
5866 protected override Expression DoResolve (ResolveContext rc)
5868 pinned_string = new LocalVariable (vi.Block, "$pinned",
5869 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5871 pinned_string.Type = rc.BuiltinTypes.String;
5873 eclass = ExprClass.Variable;
5874 type = rc.BuiltinTypes.Int;
5878 public override void Emit (EmitContext ec)
5880 pinned_string.CreateBuilder (ec);
5883 pinned_string.EmitAssign (ec);
5885 // TODO: Should use Binary::Add
5886 pinned_string.Emit (ec);
5887 ec.Emit (OpCodes.Conv_I);
5889 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5893 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5894 //pe.InstanceExpression = pinned_string;
5895 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5897 ec.Emit (OpCodes.Add);
5901 public override void EmitExit (EmitContext ec)
5904 pinned_string.EmitAssign (ec);
5908 public class VariableDeclaration : BlockVariable
5910 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5915 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5917 if (!Variable.Type.IsPointer && li == Variable) {
5918 bc.Report.Error (209, TypeExpression.Location,
5919 "The type of locals declared in a fixed statement must be a pointer type");
5924 // The rules for the possible declarators are pretty wise,
5925 // but the production on the grammar is more concise.
5927 // So we have to enforce these rules here.
5929 // We do not resolve before doing the case 1 test,
5930 // because the grammar is explicit in that the token &
5931 // is present, so we need to test for this particular case.
5934 if (initializer is Cast) {
5935 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5939 initializer = initializer.Resolve (bc);
5941 if (initializer == null)
5947 if (initializer.Type.IsArray) {
5948 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5951 // Provided that array_type is unmanaged,
5953 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5957 // and T* is implicitly convertible to the
5958 // pointer type given in the fixed statement.
5960 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5962 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5963 if (converted == null)
5967 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5969 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5970 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5971 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5972 new NullLiteral (loc),
5975 converted = converted.Resolve (bc);
5977 return new ExpressionEmitter (converted, li);
5983 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5984 return new StringEmitter (initializer, li).Resolve (bc);
5987 // Case 3: fixed buffer
5988 if (initializer is FixedBufferPtr) {
5989 return new ExpressionEmitter (initializer, li);
5993 // Case 4: & object.
5995 bool already_fixed = true;
5996 Unary u = initializer as Unary;
5997 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5998 IVariableReference vr = u.Expr as IVariableReference;
5999 if (vr == null || !vr.IsFixed) {
6000 already_fixed = false;
6004 if (already_fixed) {
6005 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6008 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
6009 return new ExpressionEmitter (initializer, li);
6014 VariableDeclaration decl;
6015 Statement statement;
6018 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6027 public Statement Statement {
6033 public BlockVariable Variables {
6041 public override bool Resolve (BlockContext bc)
6043 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6044 if (!decl.Resolve (bc))
6048 return statement.Resolve (bc);
6051 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6053 decl.FlowAnalysis (fc);
6054 return statement.FlowAnalysis (fc);
6057 protected override void DoEmit (EmitContext ec)
6059 decl.Variable.CreateBuilder (ec);
6060 decl.Initializer.Emit (ec);
6061 if (decl.Declarators != null) {
6062 foreach (var d in decl.Declarators) {
6063 d.Variable.CreateBuilder (ec);
6064 d.Initializer.Emit (ec);
6068 statement.Emit (ec);
6074 // Clear the pinned variable
6076 ((Emitter) decl.Initializer).EmitExit (ec);
6077 if (decl.Declarators != null) {
6078 foreach (var d in decl.Declarators) {
6079 ((Emitter)d.Initializer).EmitExit (ec);
6084 public override Reachability MarkReachable (Reachability rc)
6086 base.MarkReachable (rc);
6088 decl.MarkReachable (rc);
6090 rc = statement.MarkReachable (rc);
6092 // TODO: What if there is local exit?
6093 has_ret = rc.IsUnreachable;
6097 protected override void CloneTo (CloneContext clonectx, Statement t)
6099 Fixed target = (Fixed) t;
6101 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6102 target.statement = statement.Clone (clonectx);
6105 public override object Accept (StructuralVisitor visitor)
6107 return visitor.Visit (this);
6111 public class Catch : Statement
6113 ExplicitBlock block;
6115 FullNamedExpression type_expr;
6116 CompilerAssign assign;
6119 public Catch (ExplicitBlock block, Location loc)
6127 public ExplicitBlock Block {
6133 public TypeSpec CatchType {
6139 public bool IsGeneral {
6141 return type_expr == null;
6145 public FullNamedExpression TypeExpression {
6154 public LocalVariable Variable {
6165 protected override void DoEmit (EmitContext ec)
6168 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6170 ec.BeginCatchBlock (CatchType);
6173 li.CreateBuilder (ec);
6176 // Special case hoisted catch variable, we have to use a temporary variable
6177 // to pass via anonymous storey initialization with the value still on top
6180 if (li.HoistedVariant != null) {
6181 LocalTemporary lt = new LocalTemporary (li.Type);
6184 // switch to assigning from the temporary variable and not from top of the stack
6185 assign.UpdateSource (lt);
6188 ec.Emit (OpCodes.Pop);
6194 public override bool Resolve (BlockContext ec)
6196 using (ec.Set (ResolveContext.Options.CatchScope)) {
6197 if (type_expr != null) {
6198 type = type_expr.ResolveAsType (ec);
6202 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
6203 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6204 } else if (li != null) {
6206 li.PrepareAssignmentAnalysis (ec);
6208 // source variable is at the top of the stack
6209 Expression source = new EmptyExpression (li.Type);
6210 if (li.Type.IsGenericParameter)
6211 source = new UnboxCast (source, li.Type);
6214 // Uses Location.Null to hide from symbol file
6216 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6217 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6221 Block.SetCatchBlock ();
6222 return Block.Resolve (ec);
6226 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6229 fc.SetVariableAssigned (li.VariableInfo, true);
6232 return block.FlowAnalysis (fc);
6235 public override Reachability MarkReachable (Reachability rc)
6237 base.MarkReachable (rc);
6239 return block.MarkReachable (rc);
6242 protected override void CloneTo (CloneContext clonectx, Statement t)
6244 Catch target = (Catch) t;
6246 if (type_expr != null)
6247 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6249 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6253 public class TryFinally : TryFinallyBlock
6256 List<DefiniteAssignmentBitSet> try_exit_dat;
6258 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6264 public ExplicitBlock FinallyBlock {
6270 public void RegisterOutParametersCheck (DefiniteAssignmentBitSet vector)
6272 if (try_exit_dat == null)
6273 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6275 try_exit_dat.Add (vector);
6278 public override bool Resolve (BlockContext bc)
6280 bool ok = base.Resolve (bc);
6282 fini.SetFinallyBlock ();
6283 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6284 ok &= fini.Resolve (bc);
6290 protected override void EmitTryBody (EmitContext ec)
6295 public override void EmitFinallyBody (EmitContext ec)
6300 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6302 var da = fc.BranchDefiniteAssignment ();
6304 var tf = fc.TryFinally;
6305 fc.TryFinally = this;
6307 var res_stmt = Statement.FlowAnalysis (fc);
6311 var try_da = fc.DefiniteAssignment;
6312 fc.DefiniteAssignment = da;
6314 var res_fin = fini.FlowAnalysis (fc);
6316 if (try_exit_dat != null) {
6318 // try block has global exit but we need to run definite assignment check
6319 // for parameter block out parameter after finally block because it's always
6320 // executed before exit
6322 foreach (var try_da_part in try_exit_dat)
6323 fc.ParametersBlock.CheckOutParametersAssignment (fc, fc.DefiniteAssignment | try_da_part);
6325 try_exit_dat = null;
6328 fc.DefiniteAssignment |= try_da;
6329 return res_stmt | res_fin;
6332 public override Reachability MarkReachable (Reachability rc)
6335 // Mark finally block first for any exit statement in try block
6336 // to know whether the code which follows finally is reachable
6338 return fini.MarkReachable (rc) | base.MarkReachable (rc);
6341 protected override void CloneTo (CloneContext clonectx, Statement t)
6343 TryFinally target = (TryFinally) t;
6345 target.stmt = stmt.Clone (clonectx);
6347 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
6350 public override object Accept (StructuralVisitor visitor)
6352 return visitor.Visit (this);
6356 public class TryCatch : ExceptionStatement
6359 List<Catch> clauses;
6360 readonly bool inside_try_finally;
6362 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
6366 this.clauses = catch_clauses;
6367 this.inside_try_finally = inside_try_finally;
6370 public List<Catch> Clauses {
6376 public bool IsTryCatchFinally {
6378 return inside_try_finally;
6382 public override bool Resolve (BlockContext bc)
6386 using (bc.Set (ResolveContext.Options.TryScope)) {
6387 parent = bc.CurrentTryBlock;
6389 if (IsTryCatchFinally) {
6390 ok = Block.Resolve (bc);
6392 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
6393 bc.CurrentTryBlock = this;
6394 ok = Block.Resolve (bc);
6395 bc.CurrentTryBlock = parent;
6400 for (int i = 0; i < clauses.Count; ++i) {
6403 ok &= c.Resolve (bc);
6405 TypeSpec resolved_type = c.CatchType;
6406 for (int ii = 0; ii < clauses.Count; ++ii) {
6410 if (clauses[ii].IsGeneral) {
6411 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
6414 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
6417 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
6420 bc.Report.Warning (1058, 1, c.loc,
6421 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
6429 var ct = clauses[ii].CatchType;
6433 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
6434 bc.Report.Error (160, c.loc,
6435 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
6436 ct.GetSignatureForError ());
6442 return base.Resolve (bc) && ok;
6445 protected sealed override void DoEmit (EmitContext ec)
6447 if (!inside_try_finally)
6448 EmitTryBodyPrepare (ec);
6452 foreach (Catch c in clauses)
6455 if (!inside_try_finally)
6456 ec.EndExceptionBlock ();
6459 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6461 var start_fc = fc.BranchDefiniteAssignment ();
6462 var res = Block.FlowAnalysis (fc);
6464 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
6466 foreach (var c in clauses) {
6467 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
6468 if (!c.FlowAnalysis (fc)) {
6470 try_fc = fc.DefiniteAssignment;
6472 try_fc &= fc.DefiniteAssignment;
6478 fc.DefiniteAssignment = try_fc ?? start_fc;
6483 public override Reachability MarkReachable (Reachability rc)
6485 if (rc.IsUnreachable)
6488 base.MarkReachable (rc);
6490 var tc_rc = Block.MarkReachable (rc);
6492 foreach (var c in clauses)
6493 tc_rc &= c.MarkReachable (rc);
6498 protected override void CloneTo (CloneContext clonectx, Statement t)
6500 TryCatch target = (TryCatch) t;
6502 target.Block = clonectx.LookupBlock (Block);
6503 if (clauses != null){
6504 target.clauses = new List<Catch> ();
6505 foreach (Catch c in clauses)
6506 target.clauses.Add ((Catch) c.Clone (clonectx));
6510 public override object Accept (StructuralVisitor visitor)
6512 return visitor.Visit (this);
6516 public class Using : TryFinallyBlock
6518 public class VariableDeclaration : BlockVariable
6520 Statement dispose_call;
6522 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6527 public VariableDeclaration (LocalVariable li, Location loc)
6533 public VariableDeclaration (Expression expr)
6536 loc = expr.Location;
6542 public bool IsNested { get; private set; }
6546 public void EmitDispose (EmitContext ec)
6548 dispose_call.Emit (ec);
6551 public override bool Resolve (BlockContext bc)
6556 return base.Resolve (bc, false);
6559 public Expression ResolveExpression (BlockContext bc)
6561 var e = Initializer.Resolve (bc);
6565 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
6566 Initializer = ResolveInitializer (bc, Variable, e);
6570 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6572 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
6573 initializer = initializer.Resolve (bc);
6574 if (initializer == null)
6577 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
6578 Arguments args = new Arguments (1);
6579 args.Add (new Argument (initializer));
6580 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
6581 if (initializer == null)
6584 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
6585 dispose_call = CreateDisposeCall (bc, var);
6586 dispose_call.Resolve (bc);
6588 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
6591 if (li == Variable) {
6592 CheckIDiposableConversion (bc, li, initializer);
6593 dispose_call = CreateDisposeCall (bc, li);
6594 dispose_call.Resolve (bc);
6597 return base.ResolveInitializer (bc, li, initializer);
6600 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6604 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
6605 if (type.IsNullableType) {
6606 // it's handled in CreateDisposeCall
6610 bc.Report.SymbolRelatedToPreviousError (type);
6611 var loc = type_expr == null ? initializer.Location : type_expr.Location;
6612 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
6613 type.GetSignatureForError ());
6619 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6621 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
6623 var loc = lv.Location;
6625 var idt = bc.BuiltinTypes.IDisposable;
6626 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6628 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6629 dispose_mg.InstanceExpression = type.IsNullableType ?
6630 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
6634 // Hide it from symbol file via null location
6636 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
6638 // Add conditional call when disposing possible null variable
6639 if (!type.IsStruct || type.IsNullableType)
6640 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
6645 public void ResolveDeclaratorInitializer (BlockContext bc)
6647 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
6650 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
6652 for (int i = declarators.Count - 1; i >= 0; --i) {
6653 var d = declarators [i];
6654 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
6655 vd.Initializer = d.Initializer;
6657 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
6658 vd.dispose_call.Resolve (bc);
6660 stmt = new Using (vd, stmt, d.Variable.Location);
6667 public override object Accept (StructuralVisitor visitor)
6669 return visitor.Visit (this);
6673 VariableDeclaration decl;
6675 public Using (VariableDeclaration decl, Statement stmt, Location loc)
6681 public Using (Expression expr, Statement stmt, Location loc)
6684 this.decl = new VariableDeclaration (expr);
6689 public Expression Expr {
6691 return decl.Variable == null ? decl.Initializer : null;
6695 public BlockVariable Variables {
6703 public override void Emit (EmitContext ec)
6706 // Don't emit sequence point it will be set on variable declaration
6711 protected override void EmitTryBodyPrepare (EmitContext ec)
6714 base.EmitTryBodyPrepare (ec);
6717 protected override void EmitTryBody (EmitContext ec)
6722 public override void EmitFinallyBody (EmitContext ec)
6724 decl.EmitDispose (ec);
6727 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6729 decl.FlowAnalysis (fc);
6730 return stmt.FlowAnalysis (fc);
6733 public override Reachability MarkReachable (Reachability rc)
6735 decl.MarkReachable (rc);
6736 return base.MarkReachable (rc);
6739 public override bool Resolve (BlockContext ec)
6741 VariableReference vr;
6742 bool vr_locked = false;
6744 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
6745 if (decl.Variable == null) {
6746 vr = decl.ResolveExpression (ec) as VariableReference;
6748 vr_locked = vr.IsLockedByStatement;
6749 vr.IsLockedByStatement = true;
6752 if (decl.IsNested) {
6753 decl.ResolveDeclaratorInitializer (ec);
6755 if (!decl.Resolve (ec))
6758 if (decl.Declarators != null) {
6759 stmt = decl.RewriteUsingDeclarators (ec, stmt);
6770 vr.IsLockedByStatement = vr_locked;
6775 protected override void CloneTo (CloneContext clonectx, Statement t)
6777 Using target = (Using) t;
6779 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6780 target.stmt = stmt.Clone (clonectx);
6783 public override object Accept (StructuralVisitor visitor)
6785 return visitor.Visit (this);
6790 /// Implementation of the foreach C# statement
6792 public class Foreach : LoopStatement
6794 abstract class IteratorStatement : Statement
6796 protected readonly Foreach for_each;
6798 protected IteratorStatement (Foreach @foreach)
6800 this.for_each = @foreach;
6801 this.loc = @foreach.expr.Location;
6804 protected override void CloneTo (CloneContext clonectx, Statement target)
6806 throw new NotImplementedException ();
6809 public override void Emit (EmitContext ec)
6811 if (ec.EmitAccurateDebugInfo) {
6812 ec.Emit (OpCodes.Nop);
6818 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6820 throw new NotImplementedException ();
6824 sealed class ArrayForeach : IteratorStatement
6826 TemporaryVariableReference[] lengths;
6827 Expression [] length_exprs;
6828 StatementExpression[] counter;
6829 TemporaryVariableReference[] variables;
6831 TemporaryVariableReference copy;
6833 public ArrayForeach (Foreach @foreach, int rank)
6836 counter = new StatementExpression[rank];
6837 variables = new TemporaryVariableReference[rank];
6838 length_exprs = new Expression [rank];
6841 // Only use temporary length variables when dealing with
6842 // multi-dimensional arrays
6845 lengths = new TemporaryVariableReference [rank];
6848 public override bool Resolve (BlockContext ec)
6850 Block variables_block = for_each.variable.Block;
6851 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
6854 int rank = length_exprs.Length;
6855 Arguments list = new Arguments (rank);
6856 for (int i = 0; i < rank; i++) {
6857 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6859 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
6860 counter[i].Resolve (ec);
6863 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
6865 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6866 lengths[i].Resolve (ec);
6868 Arguments args = new Arguments (1);
6869 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
6870 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
6873 list.Add (new Argument (v));
6876 var access = new ElementAccess (copy, list, loc).Resolve (ec);
6881 if (for_each.type is VarExpr) {
6882 // Infer implicitly typed local variable from foreach array type
6883 var_type = access.Type;
6885 var_type = for_each.type.ResolveAsType (ec);
6887 if (var_type == null)
6890 access = Convert.ExplicitConversion (ec, access, var_type, loc);
6895 for_each.variable.Type = var_type;
6897 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
6898 if (variable_ref == null)
6901 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
6903 return for_each.body.Resolve (ec);
6906 protected override void DoEmit (EmitContext ec)
6908 copy.EmitAssign (ec, for_each.expr);
6910 int rank = length_exprs.Length;
6911 Label[] test = new Label [rank];
6912 Label[] loop = new Label [rank];
6914 for (int i = 0; i < rank; i++) {
6915 test [i] = ec.DefineLabel ();
6916 loop [i] = ec.DefineLabel ();
6918 if (lengths != null)
6919 lengths [i].EmitAssign (ec, length_exprs [i]);
6922 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
6923 for (int i = 0; i < rank; i++) {
6924 variables [i].EmitAssign (ec, zero);
6926 ec.Emit (OpCodes.Br, test [i]);
6927 ec.MarkLabel (loop [i]);
6930 for_each.body.Emit (ec);
6932 ec.MarkLabel (ec.LoopBegin);
6933 ec.Mark (for_each.expr.Location);
6935 for (int i = rank - 1; i >= 0; i--){
6936 counter [i].Emit (ec);
6938 ec.MarkLabel (test [i]);
6939 variables [i].Emit (ec);
6941 if (lengths != null)
6942 lengths [i].Emit (ec);
6944 length_exprs [i].Emit (ec);
6946 ec.Emit (OpCodes.Blt, loop [i]);
6949 ec.MarkLabel (ec.LoopEnd);
6953 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6955 class RuntimeDispose : Using.VariableDeclaration
6957 public RuntimeDispose (LocalVariable lv, Location loc)
6962 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6964 // Defered to runtime check
6967 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6969 var idt = bc.BuiltinTypes.IDisposable;
6972 // Fabricates code like
6974 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6977 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6979 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6980 dispose_variable.CreateReferenceExpression (bc, loc),
6981 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6982 loc), new NullLiteral (loc));
6984 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6986 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6987 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6989 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6990 return new If (idisaposable_test, dispose, loc);
6994 LocalVariable variable;
6996 Statement statement;
6997 ExpressionStatement init;
6998 TemporaryVariableReference enumerator_variable;
6999 bool ambiguous_getenumerator_name;
7001 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7004 this.variable = var;
7008 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7010 rc.Report.SymbolRelatedToPreviousError (enumerator);
7011 rc.Report.Error (202, loc,
7012 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7013 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7016 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7019 // Option 1: Try to match by name GetEnumerator first
7021 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7022 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7024 var mg = mexpr as MethodGroupExpr;
7026 mg.InstanceExpression = expr;
7027 Arguments args = new Arguments (0);
7028 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
7030 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7031 if (ambiguous_getenumerator_name)
7034 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7040 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7043 PredefinedMember<MethodSpec> iface_candidate = null;
7044 var ptypes = rc.Module.PredefinedTypes;
7045 var gen_ienumerable = ptypes.IEnumerableGeneric;
7046 if (!gen_ienumerable.Define ())
7047 gen_ienumerable = null;
7049 var ifaces = t.Interfaces;
7050 if (ifaces != null) {
7051 foreach (var iface in ifaces) {
7052 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7053 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7054 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7055 rc.Report.Error (1640, loc,
7056 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7057 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7062 // TODO: Cache this somehow
7063 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7064 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7069 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7070 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7075 if (iface_candidate == null) {
7076 if (expr.Type != InternalType.ErrorType) {
7077 rc.Report.Error (1579, loc,
7078 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7079 expr.Type.GetSignatureForError (), "GetEnumerator");
7085 var method = iface_candidate.Resolve (loc);
7089 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7090 mg.InstanceExpression = expr;
7094 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7096 var ms = MemberCache.FindMember (enumerator.ReturnType,
7097 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7098 BindingRestriction.InstanceOnly) as MethodSpec;
7100 if (ms == null || !ms.IsPublic) {
7101 Error_WrongEnumerator (rc, enumerator);
7105 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7108 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7110 var ps = MemberCache.FindMember (enumerator.ReturnType,
7111 MemberFilter.Property ("Current", null),
7112 BindingRestriction.InstanceOnly) as PropertySpec;
7114 if (ps == null || !ps.IsPublic) {
7115 Error_WrongEnumerator (rc, enumerator);
7122 public override bool Resolve (BlockContext ec)
7124 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7127 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7128 } else if (expr.Type.IsNullableType) {
7129 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7132 var get_enumerator_mg = ResolveGetEnumerator (ec);
7133 if (get_enumerator_mg == null) {
7137 var get_enumerator = get_enumerator_mg.BestCandidate;
7138 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7139 enumerator_variable.Resolve (ec);
7141 // Prepare bool MoveNext ()
7142 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7143 if (move_next_mg == null) {
7147 move_next_mg.InstanceExpression = enumerator_variable;
7149 // Prepare ~T~ Current { get; }
7150 var current_prop = ResolveCurrent (ec, get_enumerator);
7151 if (current_prop == null) {
7155 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7156 if (current_pe == null)
7159 VarExpr ve = for_each.type as VarExpr;
7163 // Source type is dynamic, set element type to dynamic too
7164 variable.Type = ec.BuiltinTypes.Dynamic;
7166 // Infer implicitly typed local variable from foreach enumerable type
7167 variable.Type = current_pe.Type;
7171 // Explicit cast of dynamic collection elements has to be done at runtime
7172 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7175 variable.Type = for_each.type.ResolveAsType (ec);
7177 if (variable.Type == null)
7180 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7181 if (current_pe == null)
7185 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7186 if (variable_ref == null)
7189 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7191 var init = new Invocation (get_enumerator_mg, null);
7193 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7194 for_each.body, Location.Null);
7196 var enum_type = enumerator_variable.Type;
7199 // Add Dispose method call when enumerator can be IDisposable
7201 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7202 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7204 // Runtime Dispose check
7206 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7207 vd.Initializer = init;
7208 statement = new Using (vd, statement, Location.Null);
7211 // No Dispose call needed
7213 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7214 this.init.Resolve (ec);
7218 // Static Dispose check
7220 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
7221 vd.Initializer = init;
7222 statement = new Using (vd, statement, Location.Null);
7225 return statement.Resolve (ec);
7228 protected override void DoEmit (EmitContext ec)
7230 enumerator_variable.LocalInfo.CreateBuilder (ec);
7233 init.EmitStatement (ec);
7235 statement.Emit (ec);
7238 #region IErrorHandler Members
7240 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
7242 ec.Report.SymbolRelatedToPreviousError (best);
7243 ec.Report.Warning (278, 2, expr.Location,
7244 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
7245 expr.Type.GetSignatureForError (), "enumerable",
7246 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
7248 ambiguous_getenumerator_name = true;
7252 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
7257 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
7262 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
7271 LocalVariable variable;
7275 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
7279 this.variable = var;
7285 public Expression Expr {
7286 get { return expr; }
7289 public Expression TypeExpression {
7290 get { return type; }
7293 public LocalVariable Variable {
7294 get { return variable; }
7297 public override Reachability MarkReachable (Reachability rc)
7299 base.MarkReachable (rc);
7301 body.MarkReachable (rc);
7306 public override bool Resolve (BlockContext ec)
7308 expr = expr.Resolve (ec);
7313 ec.Report.Error (186, loc, "Use of null is not valid in this context");
7317 body.AddStatement (Statement);
7319 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
7320 Statement = new ArrayForeach (this, 1);
7321 } else if (expr.Type is ArrayContainer) {
7322 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
7324 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
7325 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
7326 expr.ExprClassName);
7330 Statement = new CollectionForeach (this, variable, expr);
7337 protected override void DoEmit (EmitContext ec)
7339 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
7340 ec.LoopBegin = ec.DefineLabel ();
7341 ec.LoopEnd = ec.DefineLabel ();
7343 if (!(Statement is Block))
7344 ec.BeginCompilerScope ();
7346 variable.CreateBuilder (ec);
7348 Statement.Emit (ec);
7350 if (!(Statement is Block))
7353 ec.LoopBegin = old_begin;
7354 ec.LoopEnd = old_end;
7357 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7359 expr.FlowAnalysis (fc);
7361 var da = fc.BranchDefiniteAssignment ();
7362 body.FlowAnalysis (fc);
7363 fc.DefiniteAssignment = da;
7367 protected override void CloneTo (CloneContext clonectx, Statement t)
7369 Foreach target = (Foreach) t;
7371 target.type = type.Clone (clonectx);
7372 target.expr = expr.Clone (clonectx);
7373 target.body = (Block) body.Clone (clonectx);
7374 target.Statement = Statement.Clone (clonectx);
7377 public override object Accept (StructuralVisitor visitor)
7379 return visitor.Visit (this);