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;
1133 if (storey.ReturnType.IsGenericTask)
1134 block_return_type = storey.ReturnType.TypeArguments[0];
1137 if (ec.CurrentIterator != null) {
1138 Error_ReturnFromIterator (ec);
1139 } else if (block_return_type != InternalType.ErrorType) {
1140 ec.Report.Error (126, loc,
1141 "An object of a type convertible to `{0}' is required for the return statement",
1142 block_return_type.GetSignatureForError ());
1148 expr = expr.Resolve (ec);
1150 AnonymousExpression am = ec.CurrentAnonymousMethod;
1152 if (block_return_type.Kind == MemberKind.Void) {
1153 ec.Report.Error (127, loc,
1154 "`{0}': A return keyword must not be followed by any expression when method returns void",
1155 ec.GetSignatureForError ());
1160 if (am.IsIterator) {
1161 Error_ReturnFromIterator (ec);
1165 var async_block = am as AsyncInitializer;
1166 if (async_block != null) {
1168 var storey = (AsyncTaskStorey) am.Storey;
1169 var async_type = storey.ReturnType;
1171 if (async_type == null && async_block.ReturnTypeInference != null) {
1172 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1176 if (async_type.Kind == MemberKind.Void) {
1177 ec.Report.Error (127, loc,
1178 "`{0}': A return keyword must not be followed by any expression when method returns void",
1179 ec.GetSignatureForError ());
1184 if (!async_type.IsGenericTask) {
1185 if (this is ContextualReturn)
1188 // Same error code as .NET but better error message
1189 if (async_block.DelegateType != null) {
1190 ec.Report.Error (1997, loc,
1191 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
1192 async_block.DelegateType.GetSignatureForError ());
1194 ec.Report.Error (1997, loc,
1195 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1196 ec.GetSignatureForError ());
1204 // The return type is actually Task<T> type argument
1206 if (expr.Type == async_type) {
1207 ec.Report.Error (4016, loc,
1208 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1209 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1211 block_return_type = async_type.TypeArguments[0];
1215 // Same error code as .NET but better error message
1216 if (block_return_type.Kind == MemberKind.Void) {
1217 ec.Report.Error (127, loc,
1218 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
1219 am.GetSignatureForError ());
1224 var l = am as AnonymousMethodBody;
1225 if (l != null && expr != null) {
1226 if (l.ReturnTypeInference != null) {
1227 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1232 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1233 // unexpected debugging experience
1235 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1236 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1245 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1246 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1249 if (am != null && block_return_type == ec.ReturnType) {
1250 ec.Report.Error (1662, loc,
1251 "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",
1252 am.ContainerType, am.GetSignatureForError ());
1261 protected override void DoEmit (EmitContext ec)
1266 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1267 if (async_body != null) {
1268 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
1270 // It's null for await without async
1271 if (async_return != null) {
1272 async_return.EmitAssign (ec);
1277 ec.Emit (OpCodes.Leave, async_body.BodyEnd);
1283 if (unwind_protect || ec.EmitAccurateDebugInfo)
1284 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1287 if (unwind_protect) {
1288 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1289 } else if (ec.EmitAccurateDebugInfo) {
1290 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1292 ec.Emit (OpCodes.Ret);
1296 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1299 expr.FlowAnalysis (fc);
1301 base.DoFlowAnalysis (fc);
1305 void Error_ReturnFromIterator (ResolveContext rc)
1307 rc.Report.Error (1622, loc,
1308 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1311 public override Reachability MarkReachable (Reachability rc)
1313 base.MarkReachable (rc);
1314 return Reachability.CreateUnreachable ();
1317 protected override void CloneTo (CloneContext clonectx, Statement t)
1319 Return target = (Return) t;
1320 // It's null for simple return;
1322 target.expr = expr.Clone (clonectx);
1325 public override object Accept (StructuralVisitor visitor)
1327 return visitor.Visit (this);
1331 public class Goto : ExitStatement
1334 LabeledStatement label;
1335 TryFinally try_finally;
1337 public Goto (string label, Location l)
1343 public string Target {
1344 get { return target; }
1347 protected override bool IsLocalExit {
1353 protected override bool DoResolve (BlockContext bc)
1355 label = bc.CurrentBlock.LookupLabel (target);
1356 if (label == null) {
1357 Error_UnknownLabel (bc, target, loc);
1361 try_finally = bc.CurrentTryBlock as TryFinally;
1363 CheckExitBoundaries (bc, label.Block);
1368 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1370 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1374 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1376 if (fc.LabelStack == null) {
1377 fc.LabelStack = new List<LabeledStatement> ();
1378 } else if (fc.LabelStack.Contains (label)) {
1382 fc.LabelStack.Add (label);
1383 label.Block.ScanGotoJump (label, fc);
1384 fc.LabelStack.Remove (label);
1388 public override Reachability MarkReachable (Reachability rc)
1390 if (rc.IsUnreachable)
1393 base.MarkReachable (rc);
1395 if (try_finally != null) {
1396 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1397 label.AddGotoReference (rc, false);
1399 label.AddGotoReference (rc, true);
1404 label.AddGotoReference (rc, false);
1407 return Reachability.CreateUnreachable ();
1410 protected override void CloneTo (CloneContext clonectx, Statement target)
1415 protected override void DoEmit (EmitContext ec)
1418 throw new InternalErrorException ("goto emitted before target resolved");
1420 Label l = label.LabelTarget (ec);
1421 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1424 public override object Accept (StructuralVisitor visitor)
1426 return visitor.Visit (this);
1430 public class LabeledStatement : Statement {
1438 public LabeledStatement (string name, Block block, Location l)
1445 public Label LabelTarget (EmitContext ec)
1450 label = ec.DefineLabel ();
1455 public Block Block {
1461 public string Name {
1462 get { return name; }
1465 protected override void CloneTo (CloneContext clonectx, Statement target)
1470 public override bool Resolve (BlockContext bc)
1475 protected override void DoEmit (EmitContext ec)
1478 ec.MarkLabel (label);
1481 ec.Emit (OpCodes.Br_S, label);
1484 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1487 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1493 public override Reachability MarkReachable (Reachability rc)
1495 base.MarkReachable (rc);
1498 rc = new Reachability ();
1503 public void AddGotoReference (Reachability rc, bool finalTarget)
1511 // Label is final target when goto jumps out of try block with
1512 // finally clause. In that case we need leave with target but in C#
1513 // terms the label is unreachable. Using finalTarget we emit
1514 // explicit label not just marker
1518 this.finalTarget = true;
1522 block.ScanGotoJump (this);
1525 public override object Accept (StructuralVisitor visitor)
1527 return visitor.Visit (this);
1533 /// `goto default' statement
1535 public class GotoDefault : SwitchGoto
1537 public GotoDefault (Location l)
1542 public override bool Resolve (BlockContext bc)
1544 if (bc.Switch == null) {
1545 Error_GotoCaseRequiresSwitchBlock (bc);
1549 bc.Switch.RegisterGotoCase (null, null);
1555 protected override void DoEmit (EmitContext ec)
1557 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1560 public override object Accept (StructuralVisitor visitor)
1562 return visitor.Visit (this);
1567 /// `goto case' statement
1569 public class GotoCase : SwitchGoto
1573 public GotoCase (Expression e, Location l)
1579 public Expression Expr {
1585 public SwitchLabel Label { get; set; }
1587 public override bool Resolve (BlockContext ec)
1589 if (ec.Switch == null) {
1590 Error_GotoCaseRequiresSwitchBlock (ec);
1594 Constant c = expr.ResolveLabelConstant (ec);
1600 if (ec.Switch.IsNullable && c is NullLiteral) {
1603 TypeSpec type = ec.Switch.SwitchType;
1604 res = c.Reduce (ec, type);
1606 c.Error_ValueCannotBeConverted (ec, type, true);
1610 if (!Convert.ImplicitStandardConversionExists (c, type))
1611 ec.Report.Warning (469, 2, loc,
1612 "The `goto case' value is not implicitly convertible to type `{0}'",
1613 type.GetSignatureForError ());
1617 ec.Switch.RegisterGotoCase (this, res);
1623 protected override void DoEmit (EmitContext ec)
1625 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1628 protected override void CloneTo (CloneContext clonectx, Statement t)
1630 GotoCase target = (GotoCase) t;
1632 target.expr = expr.Clone (clonectx);
1635 public override object Accept (StructuralVisitor visitor)
1637 return visitor.Visit (this);
1641 public abstract class SwitchGoto : Statement
1643 protected bool unwind_protect;
1645 protected SwitchGoto (Location loc)
1650 protected override void CloneTo (CloneContext clonectx, Statement target)
1655 public override bool Resolve (BlockContext bc)
1657 CheckExitBoundaries (bc, bc.Switch.Block);
1659 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1664 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1669 public override Reachability MarkReachable (Reachability rc)
1671 base.MarkReachable (rc);
1672 return Reachability.CreateUnreachable ();
1675 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1677 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1681 public class Throw : Statement {
1684 public Throw (Expression expr, Location l)
1690 public Expression Expr {
1696 public override bool Resolve (BlockContext ec)
1699 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1700 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1701 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1702 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1703 if (b.IsFinallyBlock) {
1704 ec.Report.Error (724, loc,
1705 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1714 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1719 var et = ec.BuiltinTypes.Exception;
1720 if (Convert.ImplicitConversionExists (ec, expr, et))
1721 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1723 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1728 protected override void DoEmit (EmitContext ec)
1731 ec.Emit (OpCodes.Rethrow);
1735 ec.Emit (OpCodes.Throw);
1739 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1742 expr.FlowAnalysis (fc);
1747 public override Reachability MarkReachable (Reachability rc)
1749 base.MarkReachable (rc);
1750 return Reachability.CreateUnreachable ();
1753 protected override void CloneTo (CloneContext clonectx, Statement t)
1755 Throw target = (Throw) t;
1758 target.expr = expr.Clone (clonectx);
1761 public override object Accept (StructuralVisitor visitor)
1763 return visitor.Visit (this);
1767 public class Break : LocalExitStatement
1769 public Break (Location l)
1774 public override object Accept (StructuralVisitor visitor)
1776 return visitor.Visit (this);
1779 protected override void DoEmit (EmitContext ec)
1781 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1784 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1786 enclosing_loop.AddEndDefiniteAssignment (fc);
1790 protected override bool DoResolve (BlockContext bc)
1792 enclosing_loop = bc.EnclosingLoopOrSwitch;
1793 return base.DoResolve (bc);
1796 public override Reachability MarkReachable (Reachability rc)
1798 base.MarkReachable (rc);
1800 if (!rc.IsUnreachable)
1801 enclosing_loop.SetEndReachable ();
1803 return Reachability.CreateUnreachable ();
1807 public class Continue : LocalExitStatement
1809 public Continue (Location l)
1814 public override object Accept (StructuralVisitor visitor)
1816 return visitor.Visit (this);
1820 protected override void DoEmit (EmitContext ec)
1822 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1825 protected override bool DoResolve (BlockContext bc)
1827 enclosing_loop = bc.EnclosingLoop;
1828 return base.DoResolve (bc);
1831 public override Reachability MarkReachable (Reachability rc)
1833 base.MarkReachable (rc);
1835 if (!rc.IsUnreachable)
1836 enclosing_loop.SetIteratorReachable ();
1838 return Reachability.CreateUnreachable ();
1842 public abstract class LocalExitStatement : ExitStatement
1844 protected LoopStatement enclosing_loop;
1846 protected LocalExitStatement (Location loc)
1851 protected override bool IsLocalExit {
1857 protected override void CloneTo (CloneContext clonectx, Statement t)
1862 protected override bool DoResolve (BlockContext bc)
1864 if (enclosing_loop == null) {
1865 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1869 var block = enclosing_loop.Statement as Block;
1871 // Don't need to do extra checks for simple statements loops
1872 if (block != null) {
1873 CheckExitBoundaries (bc, block);
1880 public interface ILocalVariable
1882 void Emit (EmitContext ec);
1883 void EmitAssign (EmitContext ec);
1884 void EmitAddressOf (EmitContext ec);
1887 public interface INamedBlockVariable
1889 Block Block { get; }
1890 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1891 bool IsDeclared { get; }
1892 bool IsParameter { get; }
1893 Location Location { get; }
1896 public class BlockVariableDeclarator
1899 Expression initializer;
1901 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1903 if (li.Type != null)
1904 throw new ArgumentException ("Expected null variable type");
1907 this.initializer = initializer;
1912 public LocalVariable Variable {
1918 public Expression Initializer {
1923 initializer = value;
1929 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1931 var t = (BlockVariableDeclarator) MemberwiseClone ();
1932 if (initializer != null)
1933 t.initializer = initializer.Clone (cloneCtx);
1939 public class BlockVariable : Statement
1941 Expression initializer;
1942 protected FullNamedExpression type_expr;
1943 protected LocalVariable li;
1944 protected List<BlockVariableDeclarator> declarators;
1947 public BlockVariable (FullNamedExpression type, LocalVariable li)
1949 this.type_expr = type;
1951 this.loc = type_expr.Location;
1954 protected BlockVariable (LocalVariable li)
1961 public List<BlockVariableDeclarator> Declarators {
1967 public Expression Initializer {
1972 initializer = value;
1976 public FullNamedExpression TypeExpression {
1982 public LocalVariable Variable {
1990 public void AddDeclarator (BlockVariableDeclarator decl)
1992 if (declarators == null)
1993 declarators = new List<BlockVariableDeclarator> ();
1995 declarators.Add (decl);
1998 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2000 if (bc.Report.Errors != 0)
2003 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2005 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2006 new MemberName (li.Name, li.Location), null);
2008 container.AddField (f);
2011 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2015 public override bool Resolve (BlockContext bc)
2017 return Resolve (bc, true);
2020 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2022 if (type == null && !li.IsCompilerGenerated) {
2023 var vexpr = type_expr as VarExpr;
2026 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2027 // same name exists or as a keyword when no type was found
2029 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2030 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2031 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2034 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2038 if (li.IsConstant) {
2039 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2043 if (Initializer == null) {
2044 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2048 if (declarators != null) {
2049 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2053 Initializer = Initializer.Resolve (bc);
2054 if (Initializer != null) {
2055 ((VarExpr) type_expr).InferType (bc, Initializer);
2056 type = type_expr.Type;
2058 // Set error type to indicate the var was placed correctly but could
2061 // var a = missing ();
2063 type = InternalType.ErrorType;
2068 type = type_expr.ResolveAsType (bc);
2072 if (li.IsConstant && !type.IsConstantCompatible) {
2073 Const.Error_InvalidConstantType (type, loc, bc.Report);
2078 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2083 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2085 CreateEvaluatorVariable (bc, li);
2086 } else if (type != InternalType.ErrorType) {
2087 li.PrepareAssignmentAnalysis (bc);
2090 if (initializer != null) {
2091 initializer = ResolveInitializer (bc, li, initializer);
2092 // li.Variable.DefinitelyAssigned
2095 if (declarators != null) {
2096 foreach (var d in declarators) {
2097 d.Variable.Type = li.Type;
2099 CreateEvaluatorVariable (bc, d.Variable);
2100 } else if (type != InternalType.ErrorType) {
2101 d.Variable.PrepareAssignmentAnalysis (bc);
2104 if (d.Initializer != null && resolveDeclaratorInitializers) {
2105 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2106 // d.Variable.DefinitelyAssigned
2114 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2116 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2117 return a.ResolveStatement (bc);
2120 protected override void DoEmit (EmitContext ec)
2122 li.CreateBuilder (ec);
2124 if (Initializer != null)
2125 ((ExpressionStatement) Initializer).EmitStatement (ec);
2127 if (declarators != null) {
2128 foreach (var d in declarators) {
2129 d.Variable.CreateBuilder (ec);
2130 if (d.Initializer != null) {
2131 ec.Mark (d.Variable.Location);
2132 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2138 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2140 if (Initializer != null)
2141 Initializer.FlowAnalysis (fc);
2143 if (declarators != null) {
2144 foreach (var d in declarators) {
2145 if (d.Initializer != null)
2146 d.Initializer.FlowAnalysis (fc);
2153 public override Reachability MarkReachable (Reachability rc)
2155 var init = initializer as ExpressionStatement;
2157 init.MarkReachable (rc);
2159 return base.MarkReachable (rc);
2162 protected override void CloneTo (CloneContext clonectx, Statement target)
2164 BlockVariable t = (BlockVariable) target;
2166 if (type_expr != null)
2167 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2169 if (initializer != null)
2170 t.initializer = initializer.Clone (clonectx);
2172 if (declarators != null) {
2173 t.declarators = null;
2174 foreach (var d in declarators)
2175 t.AddDeclarator (d.Clone (clonectx));
2179 public override object Accept (StructuralVisitor visitor)
2181 return visitor.Visit (this);
2185 public class BlockConstant : BlockVariable
2187 public BlockConstant (FullNamedExpression type, LocalVariable li)
2192 public override void Emit (EmitContext ec)
2194 // Nothing to emit, not even sequence point
2197 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2199 initializer = initializer.Resolve (bc);
2200 if (initializer == null)
2203 var c = initializer as Constant;
2205 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2209 c = c.ConvertImplicitly (li.Type);
2211 if (TypeSpec.IsReferenceType (li.Type))
2212 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2214 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2219 li.ConstantValue = c;
2223 public override object Accept (StructuralVisitor visitor)
2225 return visitor.Visit (this);
2230 // The information about a user-perceived local variable
2232 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2239 AddressTaken = 1 << 2,
2240 CompilerGenerated = 1 << 3,
2242 ForeachVariable = 1 << 5,
2243 FixedVariable = 1 << 6,
2244 UsingVariable = 1 << 7,
2247 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2251 readonly string name;
2252 readonly Location loc;
2253 readonly Block block;
2255 Constant const_value;
2257 public VariableInfo VariableInfo;
2258 HoistedVariable hoisted_variant;
2260 LocalBuilder builder;
2262 public LocalVariable (Block block, string name, Location loc)
2269 public LocalVariable (Block block, string name, Flags flags, Location loc)
2270 : this (block, name, loc)
2276 // Used by variable declarators
2278 public LocalVariable (LocalVariable li, string name, Location loc)
2279 : this (li.block, name, li.flags, loc)
2285 public bool AddressTaken {
2287 return (flags & Flags.AddressTaken) != 0;
2291 public Block Block {
2297 public Constant ConstantValue {
2302 const_value = value;
2307 // Hoisted local variable variant
2309 public HoistedVariable HoistedVariant {
2311 return hoisted_variant;
2314 hoisted_variant = value;
2318 public bool IsDeclared {
2320 return type != null;
2324 public bool IsCompilerGenerated {
2326 return (flags & Flags.CompilerGenerated) != 0;
2330 public bool IsConstant {
2332 return (flags & Flags.Constant) != 0;
2336 public bool IsLocked {
2338 return (flags & Flags.IsLocked) != 0;
2341 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2345 public bool IsThis {
2347 return (flags & Flags.IsThis) != 0;
2351 public bool IsFixed {
2353 return (flags & Flags.FixedVariable) != 0;
2357 bool INamedBlockVariable.IsParameter {
2363 public bool IsReadonly {
2365 return (flags & Flags.ReadonlyMask) != 0;
2369 public Location Location {
2375 public string Name {
2381 public TypeSpec Type {
2392 public void CreateBuilder (EmitContext ec)
2394 if ((flags & Flags.Used) == 0) {
2395 if (VariableInfo == null) {
2396 // Missing flow analysis or wrong variable flags
2397 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2400 if (VariableInfo.IsEverAssigned)
2401 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2403 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2406 if (HoistedVariant != null)
2409 if (builder != null) {
2410 if ((flags & Flags.CompilerGenerated) != 0)
2413 // To avoid Used warning duplicates
2414 throw new InternalErrorException ("Already created variable `{0}'", name);
2418 // All fixed variabled are pinned, a slot has to be alocated
2420 builder = ec.DeclareLocal (Type, IsFixed);
2421 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2422 ec.DefineLocalVariable (name, builder);
2425 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2427 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2432 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2434 if (IsConstant && const_value != null)
2435 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2437 return new LocalVariableReference (this, loc);
2440 public void Emit (EmitContext ec)
2442 // TODO: Need something better for temporary variables
2443 if ((flags & Flags.CompilerGenerated) != 0)
2446 ec.Emit (OpCodes.Ldloc, builder);
2449 public void EmitAssign (EmitContext ec)
2451 // TODO: Need something better for temporary variables
2452 if ((flags & Flags.CompilerGenerated) != 0)
2455 ec.Emit (OpCodes.Stloc, builder);
2458 public void EmitAddressOf (EmitContext ec)
2460 ec.Emit (OpCodes.Ldloca, builder);
2463 public static string GetCompilerGeneratedName (Block block)
2465 // HACK: Debugger depends on the name semantics
2466 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2469 public string GetReadOnlyContext ()
2471 switch (flags & Flags.ReadonlyMask) {
2472 case Flags.FixedVariable:
2473 return "fixed variable";
2474 case Flags.ForeachVariable:
2475 return "foreach iteration variable";
2476 case Flags.UsingVariable:
2477 return "using variable";
2480 throw new InternalErrorException ("Variable is not readonly");
2483 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2485 if (VariableInfo == null)
2486 throw new Exception ();
2488 if (IsAssigned (fc))
2491 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2494 public bool IsAssigned (FlowAnalysisContext fc)
2496 return fc.IsDefinitelyAssigned (VariableInfo);
2499 public void PrepareAssignmentAnalysis (BlockContext bc)
2502 // No need to run assignment analysis for these guys
2504 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2507 VariableInfo = VariableInfo.Create (bc, this);
2511 // Mark the variables as referenced in the user code
2513 public void SetIsUsed ()
2515 flags |= Flags.Used;
2518 public void SetHasAddressTaken ()
2520 flags |= (Flags.AddressTaken | Flags.Used);
2523 public override string ToString ()
2525 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2530 /// Block represents a C# block.
2534 /// This class is used in a number of places: either to represent
2535 /// explicit blocks that the programmer places or implicit blocks.
2537 /// Implicit blocks are used as labels or to introduce variable
2540 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2541 /// they contain extra information that is not necessary on normal blocks.
2543 public class Block : Statement {
2550 HasCapturedVariable = 64,
2551 HasCapturedThis = 1 << 7,
2552 IsExpressionTree = 1 << 8,
2553 CompilerGenerated = 1 << 9,
2554 HasAsyncModifier = 1 << 10,
2556 YieldBlock = 1 << 12,
2557 AwaitBlock = 1 << 13,
2558 FinallyBlock = 1 << 14,
2559 CatchBlock = 1 << 15,
2561 NoFlowAnalysis = 1 << 21
2564 public Block Parent;
2565 public Location StartLocation;
2566 public Location EndLocation;
2568 public ExplicitBlock Explicit;
2569 public ParametersBlock ParametersBlock;
2571 protected Flags flags;
2574 // The statements in this block
2576 protected List<Statement> statements;
2578 protected List<Statement> scope_initializers;
2580 int? resolving_init_idx;
2586 public int ID = id++;
2588 static int clone_id_counter;
2592 // int assignable_slots;
2594 public Block (Block parent, Location start, Location end)
2595 : this (parent, 0, start, end)
2599 public Block (Block parent, Flags flags, Location start, Location end)
2601 if (parent != null) {
2602 // the appropriate constructors will fixup these fields
2603 ParametersBlock = parent.ParametersBlock;
2604 Explicit = parent.Explicit;
2607 this.Parent = parent;
2609 this.StartLocation = start;
2610 this.EndLocation = end;
2612 statements = new List<Statement> (4);
2614 this.original = this;
2619 public Block Original {
2628 public bool IsCompilerGenerated {
2629 get { return (flags & Flags.CompilerGenerated) != 0; }
2630 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2634 public bool IsCatchBlock {
2636 return (flags & Flags.CatchBlock) != 0;
2640 public bool IsFinallyBlock {
2642 return (flags & Flags.FinallyBlock) != 0;
2646 public bool Unchecked {
2647 get { return (flags & Flags.Unchecked) != 0; }
2648 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2651 public bool Unsafe {
2652 get { return (flags & Flags.Unsafe) != 0; }
2653 set { flags |= Flags.Unsafe; }
2656 public List<Statement> Statements {
2657 get { return statements; }
2662 public void SetEndLocation (Location loc)
2667 public void AddLabel (LabeledStatement target)
2669 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2672 public void AddLocalName (LocalVariable li)
2674 AddLocalName (li.Name, li);
2677 public void AddLocalName (string name, INamedBlockVariable li)
2679 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2682 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2684 if (reason == null) {
2685 Error_AlreadyDeclared (name, variable);
2689 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2690 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2691 "to `{0}', which is already used in a `{1}' scope to denote something else",
2695 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2697 var pi = variable as ParametersBlock.ParameterInfo;
2699 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2701 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2702 "A local variable named `{0}' is already defined in this scope", name);
2706 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2708 ParametersBlock.TopBlock.Report.Error (412, loc,
2709 "The type parameter name `{0}' is the same as local variable or parameter name",
2714 // It should be used by expressions which require to
2715 // register a statement during resolve process.
2717 public void AddScopeStatement (Statement s)
2719 if (scope_initializers == null)
2720 scope_initializers = new List<Statement> ();
2723 // Simple recursive helper, when resolve scope initializer another
2724 // new scope initializer can be added, this ensures it's initialized
2725 // before existing one. For now this can happen with expression trees
2726 // in base ctor initializer only
2728 if (resolving_init_idx.HasValue) {
2729 scope_initializers.Insert (resolving_init_idx.Value, s);
2730 ++resolving_init_idx;
2732 scope_initializers.Add (s);
2736 public void InsertStatement (int index, Statement s)
2738 statements.Insert (index, s);
2741 public void AddStatement (Statement s)
2746 public int AssignableSlots {
2748 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2750 // return assignable_slots;
2754 public LabeledStatement LookupLabel (string name)
2756 return ParametersBlock.TopBlock.GetLabel (name, this);
2759 public override Reachability MarkReachable (Reachability rc)
2761 if (rc.IsUnreachable)
2764 base.MarkReachable (rc);
2766 if (scope_initializers != null) {
2767 foreach (var si in scope_initializers)
2768 si.MarkReachable (rc);
2771 foreach (var s in statements) {
2772 rc = s.MarkReachable (rc);
2773 if (rc.IsUnreachable) {
2774 if ((flags & Flags.ReachableEnd) != 0)
2775 return new Reachability ();
2781 flags |= Flags.ReachableEnd;
2786 public override bool Resolve (BlockContext bc)
2788 if ((flags & Flags.Resolved) != 0)
2791 Block prev_block = bc.CurrentBlock;
2792 bc.CurrentBlock = this;
2795 // Compiler generated scope statements
2797 if (scope_initializers != null) {
2798 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2799 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2802 resolving_init_idx = null;
2806 int statement_count = statements.Count;
2807 for (int ix = 0; ix < statement_count; ix++){
2808 Statement s = statements [ix];
2810 if (!s.Resolve (bc)) {
2812 if (!bc.IsInProbingMode)
2813 statements [ix] = new EmptyStatement (s.loc);
2819 bc.CurrentBlock = prev_block;
2821 flags |= Flags.Resolved;
2825 protected override void DoEmit (EmitContext ec)
2827 for (int ix = 0; ix < statements.Count; ix++){
2828 statements [ix].Emit (ec);
2832 public override void Emit (EmitContext ec)
2834 if (scope_initializers != null)
2835 EmitScopeInitializers (ec);
2840 protected void EmitScopeInitializers (EmitContext ec)
2842 foreach (Statement s in scope_initializers)
2846 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2848 if (scope_initializers != null) {
2849 foreach (var si in scope_initializers)
2850 si.FlowAnalysis (fc);
2853 return DoFlowAnalysis (fc, 0);
2856 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2858 bool end_unreachable = !reachable;
2859 for (; startIndex < statements.Count; ++startIndex) {
2860 var s = statements[startIndex];
2862 end_unreachable = s.FlowAnalysis (fc);
2863 if (s.IsUnreachable) {
2865 // This is kind of a hack, switch label is unreachable because we need to mark
2866 // switch section end but at the same time we need to run Emit on it
2868 if (s is SwitchLabel)
2871 statements[startIndex] = new EmptyStatement (s.loc);
2876 // Statement end reachability is needed mostly due to goto support. Consider
2885 // X label is reachable only via goto not as another statement after if. We need
2886 // this for flow-analysis only to carry variable info correctly.
2888 if (end_unreachable) { // s is ExitStatement) {
2889 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2890 s = statements[startIndex];
2891 if (s is SwitchLabel) {
2892 s.FlowAnalysis (fc);
2896 if (s.IsUnreachable) {
2897 s.FlowAnalysis (fc);
2898 statements[startIndex] = new EmptyStatement (s.loc);
2905 // The condition should be true unless there is forward jumping goto
2907 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
2910 return !Explicit.HasReachableClosingBrace;
2913 public void ScanGotoJump (Statement label)
2916 for (i = 0; i < statements.Count; ++i) {
2917 if (statements[i] == label)
2921 var rc = new Reachability ();
2922 for (; i < statements.Count; ++i) {
2923 var s = statements[i];
2924 rc = s.MarkReachable (rc);
2925 if (rc.IsUnreachable)
2929 flags |= Flags.ReachableEnd;
2932 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
2935 for (i = 0; i < statements.Count; ++i) {
2936 if (statements[i] == label)
2940 DoFlowAnalysis (fc, ++i);
2944 public override string ToString ()
2946 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2950 protected override void CloneTo (CloneContext clonectx, Statement t)
2952 Block target = (Block) t;
2954 target.clone_id = clone_id_counter++;
2957 clonectx.AddBlockMap (this, target);
2958 if (original != this)
2959 clonectx.AddBlockMap (original, target);
2961 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2962 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2965 target.Parent = clonectx.RemapBlockCopy (Parent);
2967 target.statements = new List<Statement> (statements.Count);
2968 foreach (Statement s in statements)
2969 target.statements.Add (s.Clone (clonectx));
2972 public override object Accept (StructuralVisitor visitor)
2974 return visitor.Visit (this);
2978 public class ExplicitBlock : Block
2980 protected AnonymousMethodStorey am_storey;
2982 public ExplicitBlock (Block parent, Location start, Location end)
2983 : this (parent, (Flags) 0, start, end)
2987 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2988 : base (parent, flags, start, end)
2990 this.Explicit = this;
2995 public AnonymousMethodStorey AnonymousMethodStorey {
3001 public bool HasAwait {
3003 return (flags & Flags.AwaitBlock) != 0;
3007 public bool HasCapturedThis {
3009 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3012 return (flags & Flags.HasCapturedThis) != 0;
3017 // Used to indicate that the block has reference to parent
3018 // block and cannot be made static when defining anonymous method
3020 public bool HasCapturedVariable {
3022 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3025 return (flags & Flags.HasCapturedVariable) != 0;
3029 public bool HasReachableClosingBrace {
3031 return (flags & Flags.ReachableEnd) != 0;
3034 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3038 public bool HasYield {
3040 return (flags & Flags.YieldBlock) != 0;
3047 // Creates anonymous method storey in current block
3049 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3052 // Return same story for iterator and async blocks unless we are
3053 // in nested anonymous method
3055 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3056 return ec.CurrentAnonymousMethod.Storey;
3058 if (am_storey == null) {
3059 MemberBase mc = ec.MemberContext as MemberBase;
3062 // Creates anonymous method storey for this block
3064 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3070 public override void Emit (EmitContext ec)
3072 if (am_storey != null) {
3073 DefineStoreyContainer (ec, am_storey);
3074 am_storey.EmitStoreyInstantiation (ec, this);
3077 if (scope_initializers != null)
3078 EmitScopeInitializers (ec);
3080 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3081 ec.Emit (OpCodes.Nop);
3092 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3093 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3094 ec.Emit (OpCodes.Nop);
3098 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3100 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3101 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3102 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3106 // Creates anonymous method storey
3108 storey.CreateContainer ();
3109 storey.DefineContainer ();
3111 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3114 // Only first storey in path will hold this reference. All children blocks will
3115 // reference it indirectly using $ref field
3117 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3118 if (b.Parent != null) {
3119 var s = b.Parent.Explicit.AnonymousMethodStorey;
3121 storey.HoistedThis = s.HoistedThis;
3126 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3127 if (storey.HoistedThis == null)
3128 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3130 if (storey.HoistedThis != null)
3136 // We are the first storey on path and 'this' has to be hoisted
3138 if (storey.HoistedThis == null) {
3139 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3141 // ThisReferencesFromChildrenBlock holds all reference even if they
3142 // are not on this path. It saves some memory otherwise it'd have to
3143 // be in every explicit block. We run this check to see if the reference
3144 // is valid for this storey
3146 Block block_on_path = ref_block;
3147 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3149 if (block_on_path == null)
3152 if (storey.HoistedThis == null) {
3153 storey.AddCapturedThisField (ec, null);
3156 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3159 if (b.AnonymousMethodStorey != null) {
3161 // Don't add storey cross reference for `this' when the storey ends up not
3162 // beeing attached to any parent
3164 if (b.ParametersBlock.StateMachine == null) {
3165 AnonymousMethodStorey s = null;
3166 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3167 s = ab.Explicit.AnonymousMethodStorey;
3172 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3174 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3175 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3180 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3181 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
3184 // Stop propagation inside same top block
3186 if (b.ParametersBlock == ParametersBlock.Original)
3189 b = b.ParametersBlock;
3192 pb = b as ParametersBlock;
3193 if (pb != null && pb.StateMachine != null) {
3194 if (pb.StateMachine == storey)
3198 // If we are state machine with no parent. We can hook into parent without additional
3199 // reference and capture this directly
3201 ExplicitBlock parent_storey_block = pb;
3202 while (parent_storey_block.Parent != null) {
3203 parent_storey_block = parent_storey_block.Parent.Explicit;
3204 if (parent_storey_block.AnonymousMethodStorey != null) {
3209 if (parent_storey_block.AnonymousMethodStorey == null) {
3210 pb.StateMachine.AddCapturedThisField (ec, null);
3211 b.HasCapturedThis = true;
3215 pb.StateMachine.AddParentStoreyReference (ec, storey);
3218 b.HasCapturedVariable = true;
3224 var ref_blocks = storey.ReferencesFromChildrenBlock;
3225 if (ref_blocks != null) {
3226 foreach (ExplicitBlock ref_block in ref_blocks) {
3227 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3228 if (b.AnonymousMethodStorey != null) {
3229 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3232 // Stop propagation inside same top block
3234 if (b.ParametersBlock == ParametersBlock.Original)
3237 b = b.ParametersBlock;
3240 var pb = b as ParametersBlock;
3241 if (pb != null && pb.StateMachine != null) {
3242 if (pb.StateMachine == storey)
3245 pb.StateMachine.AddParentStoreyReference (ec, storey);
3248 b.HasCapturedVariable = true;
3254 storey.PrepareEmit ();
3255 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3258 public void RegisterAsyncAwait ()
3261 while ((block.flags & Flags.AwaitBlock) == 0) {
3262 block.flags |= Flags.AwaitBlock;
3264 if (block is ParametersBlock)
3267 block = block.Parent.Explicit;
3271 public void RegisterIteratorYield ()
3273 ParametersBlock.TopBlock.IsIterator = true;
3276 while ((block.flags & Flags.YieldBlock) == 0) {
3277 block.flags |= Flags.YieldBlock;
3279 if (block.Parent == null)
3282 block = block.Parent.Explicit;
3286 public void SetCatchBlock ()
3288 flags |= Flags.CatchBlock;
3291 public void SetFinallyBlock ()
3293 flags |= Flags.FinallyBlock;
3296 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3298 tryBlock.statements = statements;
3299 statements = new List<Statement> (1);
3300 statements.Add (tf);
3305 // ParametersBlock was introduced to support anonymous methods
3306 // and lambda expressions
3308 public class ParametersBlock : ExplicitBlock
3310 public class ParameterInfo : INamedBlockVariable
3312 readonly ParametersBlock block;
3314 public VariableInfo VariableInfo;
3317 public ParameterInfo (ParametersBlock block, int index)
3325 public ParametersBlock Block {
3331 Block INamedBlockVariable.Block {
3337 public bool IsDeclared {
3343 public bool IsParameter {
3349 public bool IsLocked {
3358 public Location Location {
3360 return Parameter.Location;
3364 public Parameter Parameter {
3366 return block.Parameters [index];
3370 public TypeSpec ParameterType {
3372 return Parameter.Type;
3378 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3380 return new ParameterReference (this, loc);
3385 // Block is converted into an expression
3387 sealed class BlockScopeExpression : Expression
3390 readonly ParametersBlock block;
3392 public BlockScopeExpression (Expression child, ParametersBlock block)
3398 public override bool ContainsEmitWithAwait ()
3400 return child.ContainsEmitWithAwait ();
3403 public override Expression CreateExpressionTree (ResolveContext ec)
3405 throw new NotSupportedException ();
3408 protected override Expression DoResolve (ResolveContext ec)
3413 child = child.Resolve (ec);
3417 eclass = child.eclass;
3422 public override void Emit (EmitContext ec)
3424 block.EmitScopeInitializers (ec);
3429 protected ParametersCompiled parameters;
3430 protected ParameterInfo[] parameter_info;
3431 protected bool resolved;
3432 protected ToplevelBlock top_block;
3433 protected StateMachine state_machine;
3435 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3436 : base (parent, 0, start, start)
3438 if (parameters == null)
3439 throw new ArgumentNullException ("parameters");
3441 this.parameters = parameters;
3442 ParametersBlock = this;
3444 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3446 this.top_block = parent.ParametersBlock.top_block;
3447 ProcessParameters ();
3450 protected ParametersBlock (ParametersCompiled parameters, Location start)
3451 : base (null, 0, start, start)
3453 if (parameters == null)
3454 throw new ArgumentNullException ("parameters");
3456 this.parameters = parameters;
3457 ParametersBlock = this;
3461 // It's supposed to be used by method body implementation of anonymous methods
3463 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3464 : base (null, 0, source.StartLocation, source.EndLocation)
3466 this.parameters = parameters;
3467 this.statements = source.statements;
3468 this.scope_initializers = source.scope_initializers;
3470 this.resolved = true;
3471 this.reachable = source.reachable;
3472 this.am_storey = source.am_storey;
3473 this.state_machine = source.state_machine;
3474 this.flags = source.flags & Flags.ReachableEnd;
3476 ParametersBlock = this;
3479 // Overwrite original for comparison purposes when linking cross references
3480 // between anonymous methods
3482 Original = source.Original;
3487 public bool IsAsync {
3489 return (flags & Flags.HasAsyncModifier) != 0;
3492 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3497 // Block has been converted to expression tree
3499 public bool IsExpressionTree {
3501 return (flags & Flags.IsExpressionTree) != 0;
3506 // The parameters for the block.
3508 public ParametersCompiled Parameters {
3514 public StateMachine StateMachine {
3516 return state_machine;
3520 public ToplevelBlock TopBlock {
3526 public bool Resolved {
3528 return (flags & Flags.Resolved) != 0;
3532 public int TemporaryLocalsCount { get; set; }
3537 // Checks whether all `out' parameters have been assigned.
3539 public void CheckOutParametersAssignment (FlowAnalysisContext fc)
3541 CheckOutParametersAssignment (fc, fc.DefiniteAssignment);
3544 public void CheckOutParametersAssignment (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3546 if (parameter_info == null)
3549 foreach (var p in parameter_info) {
3550 if (p.VariableInfo == null)
3553 if (p.VariableInfo.IsAssigned (dat))
3556 fc.Report.Error (177, p.Location,
3557 "The out parameter `{0}' must be assigned to before control leaves the current method",
3562 public override Expression CreateExpressionTree (ResolveContext ec)
3564 if (statements.Count == 1) {
3565 Expression expr = statements[0].CreateExpressionTree (ec);
3566 if (scope_initializers != null)
3567 expr = new BlockScopeExpression (expr, this);
3572 return base.CreateExpressionTree (ec);
3575 public override void Emit (EmitContext ec)
3577 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3578 DefineStoreyContainer (ec, state_machine);
3579 state_machine.EmitStoreyInstantiation (ec, this);
3585 public void EmitEmbedded (EmitContext ec)
3587 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3588 DefineStoreyContainer (ec, state_machine);
3589 state_machine.EmitStoreyInstantiation (ec, this);
3595 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3597 var res = base.DoFlowAnalysis (fc);
3599 if (HasReachableClosingBrace)
3600 CheckOutParametersAssignment (fc);
3605 public ParameterInfo GetParameterInfo (Parameter p)
3607 for (int i = 0; i < parameters.Count; ++i) {
3608 if (parameters[i] == p)
3609 return parameter_info[i];
3612 throw new ArgumentException ("Invalid parameter");
3615 public ParameterReference GetParameterReference (int index, Location loc)
3617 return new ParameterReference (parameter_info[index], loc);
3620 public Statement PerformClone ()
3622 CloneContext clonectx = new CloneContext ();
3623 return Clone (clonectx);
3626 protected void ProcessParameters ()
3628 if (parameters.Count == 0)
3631 parameter_info = new ParameterInfo[parameters.Count];
3632 for (int i = 0; i < parameter_info.Length; ++i) {
3633 var p = parameters.FixedParameters[i];
3637 // TODO: Should use Parameter only and more block there
3638 parameter_info[i] = new ParameterInfo (this, i);
3640 AddLocalName (p.Name, parameter_info[i]);
3644 public override bool Resolve (BlockContext bc)
3646 // TODO: if ((flags & Flags.Resolved) != 0)
3653 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3654 flags |= Flags.IsExpressionTree;
3657 PrepareAssignmentAnalysis (bc);
3659 if (!base.Resolve (bc))
3662 } catch (Exception e) {
3663 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter)
3666 if (bc.CurrentBlock != null) {
3667 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3669 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3672 if (bc.Module.Compiler.Settings.DebugFlags > 0)
3677 // If an asynchronous body of F is either an expression classified as nothing, or a
3678 // statement block where no return statements have expressions, the inferred return type is Task
3681 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3682 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3683 am.ReturnTypeInference = null;
3684 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3692 void PrepareAssignmentAnalysis (BlockContext bc)
3694 for (int i = 0; i < parameters.Count; ++i) {
3695 var par = parameters.FixedParameters[i];
3697 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3700 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3704 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3706 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3707 var stateMachine = new IteratorStorey (iterator);
3709 state_machine = stateMachine;
3710 iterator.SetStateMachine (stateMachine);
3712 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3713 tlb.Original = this;
3714 tlb.state_machine = stateMachine;
3715 tlb.AddStatement (new Return (iterator, iterator.Location));
3719 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3721 for (int i = 0; i < parameters.Count; i++) {
3722 Parameter p = parameters[i];
3723 Parameter.Modifier mod = p.ModFlags;
3724 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3725 host.Compiler.Report.Error (1988, p.Location,
3726 "Async methods cannot have ref or out parameters");
3730 if (p is ArglistParameter) {
3731 host.Compiler.Report.Error (4006, p.Location,
3732 "__arglist is not allowed in parameter list of async methods");
3736 if (parameters.Types[i].IsPointer) {
3737 host.Compiler.Report.Error (4005, p.Location,
3738 "Async methods cannot have unsafe parameters");
3744 host.Compiler.Report.Warning (1998, 1, loc,
3745 "Async block lacks `await' operator and will run synchronously");
3748 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3749 var initializer = new AsyncInitializer (this, host, block_type);
3750 initializer.Type = block_type;
3751 initializer.DelegateType = delegateType;
3753 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3755 state_machine = stateMachine;
3756 initializer.SetStateMachine (stateMachine);
3758 const Flags flags = Flags.CompilerGenerated;
3760 var b = this is ToplevelBlock ?
3761 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
3762 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
3765 b.state_machine = stateMachine;
3766 b.AddStatement (new AsyncInitializerStatement (initializer));
3774 public class ToplevelBlock : ParametersBlock
3776 LocalVariable this_variable;
3777 CompilerContext compiler;
3778 Dictionary<string, object> names;
3779 Dictionary<string, object> labels;
3781 List<ExplicitBlock> this_references;
3783 public ToplevelBlock (CompilerContext ctx, Location loc)
3784 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3788 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
3789 : base (parameters, start)
3791 this.compiler = ctx;
3795 ProcessParameters ();
3799 // Recreates a top level block from parameters block. Used for
3800 // compiler generated methods where the original block comes from
3801 // explicit child block. This works for already resolved blocks
3802 // only to ensure we resolve them in the correct flow order
3804 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3805 : base (source, parameters)
3807 this.compiler = source.TopBlock.compiler;
3811 public bool IsIterator {
3813 return (flags & Flags.Iterator) != 0;
3816 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3820 public Report Report {
3822 return compiler.Report;
3827 // Used by anonymous blocks to track references of `this' variable
3829 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3831 return this_references;
3836 // Returns the "this" instance variable of this block.
3837 // See AddThisVariable() for more information.
3839 public LocalVariable ThisVariable {
3841 return this_variable;
3845 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3848 names = new Dictionary<string, object> ();
3851 if (!names.TryGetValue (name, out value)) {
3852 names.Add (name, li);
3856 INamedBlockVariable existing = value as INamedBlockVariable;
3857 List<INamedBlockVariable> existing_list;
3858 if (existing != null) {
3859 existing_list = new List<INamedBlockVariable> ();
3860 existing_list.Add (existing);
3861 names[name] = existing_list;
3863 existing_list = (List<INamedBlockVariable>) value;
3867 // A collision checking between local names
3869 var variable_block = li.Block.Explicit;
3870 for (int i = 0; i < existing_list.Count; ++i) {
3871 existing = existing_list[i];
3872 Block b = existing.Block.Explicit;
3874 // Collision at same level
3875 if (variable_block == b) {
3876 li.Block.Error_AlreadyDeclared (name, li);
3880 // Collision with parent
3881 Block parent = variable_block;
3882 while ((parent = parent.Parent) != null) {
3884 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3885 i = existing_list.Count;
3890 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3891 // Collision with children
3892 while ((b = b.Parent) != null) {
3893 if (variable_block == b) {
3894 li.Block.Error_AlreadyDeclared (name, li, "child");
3895 i = existing_list.Count;
3902 existing_list.Add (li);
3905 public void AddLabel (string name, LabeledStatement label)
3908 labels = new Dictionary<string, object> ();
3911 if (!labels.TryGetValue (name, out value)) {
3912 labels.Add (name, label);
3916 LabeledStatement existing = value as LabeledStatement;
3917 List<LabeledStatement> existing_list;
3918 if (existing != null) {
3919 existing_list = new List<LabeledStatement> ();
3920 existing_list.Add (existing);
3921 labels[name] = existing_list;
3923 existing_list = (List<LabeledStatement>) value;
3927 // A collision checking between labels
3929 for (int i = 0; i < existing_list.Count; ++i) {
3930 existing = existing_list[i];
3931 Block b = existing.Block;
3933 // Collision at same level
3934 if (label.Block == b) {
3935 Report.SymbolRelatedToPreviousError (existing.loc, name);
3936 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3940 // Collision with parent
3942 while ((b = b.Parent) != null) {
3943 if (existing.Block == b) {
3944 Report.Error (158, label.loc,
3945 "The label `{0}' shadows another label by the same name in a contained scope", name);
3946 i = existing_list.Count;
3951 // Collision with with children
3953 while ((b = b.Parent) != null) {
3954 if (label.Block == b) {
3955 Report.Error (158, label.loc,
3956 "The label `{0}' shadows another label by the same name in a contained scope", name);
3957 i = existing_list.Count;
3963 existing_list.Add (label);
3966 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3968 if (this_references == null)
3969 this_references = new List<ExplicitBlock> ();
3971 if (!this_references.Contains (block))
3972 this_references.Add (block);
3975 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3977 this_references.Remove (block);
3981 // Creates an arguments set from all parameters, useful for method proxy calls
3983 public Arguments GetAllParametersArguments ()
3985 int count = parameters.Count;
3986 Arguments args = new Arguments (count);
3987 for (int i = 0; i < count; ++i) {
3988 var pi = parameter_info[i];
3989 var arg_expr = GetParameterReference (i, pi.Location);
3991 Argument.AType atype_modifier;
3992 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
3993 case Parameter.Modifier.REF:
3994 atype_modifier = Argument.AType.Ref;
3996 case Parameter.Modifier.OUT:
3997 atype_modifier = Argument.AType.Out;
4004 args.Add (new Argument (arg_expr, atype_modifier));
4011 // Lookup inside a block, the returned value can represent 3 states
4013 // true+variable: A local name was found and it's valid
4014 // false+variable: A local name was found in a child block only
4015 // false+null: No local name was found
4017 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4023 if (!names.TryGetValue (name, out value))
4026 variable = value as INamedBlockVariable;
4028 if (variable != null) {
4030 if (variable.Block == b.Original)
4034 } while (b != null);
4042 } while (b != null);
4044 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4045 for (int i = 0; i < list.Count; ++i) {
4048 if (variable.Block == b.Original)
4052 } while (b != null);
4060 } while (b != null);
4070 public LabeledStatement GetLabel (string name, Block block)
4076 if (!labels.TryGetValue (name, out value)) {
4080 var label = value as LabeledStatement;
4082 if (label != null) {
4084 if (label.Block == b.Original)
4087 } while (b != null);
4089 List<LabeledStatement> list = (List<LabeledStatement>) value;
4090 for (int i = 0; i < list.Count; ++i) {
4092 if (label.Block == b.Original)
4101 // This is used by non-static `struct' constructors which do not have an
4102 // initializer - in this case, the constructor must initialize all of the
4103 // struct's fields. To do this, we add a "this" variable and use the flow
4104 // analysis code to ensure that it's been fully initialized before control
4105 // leaves the constructor.
4107 public void AddThisVariable (BlockContext bc)
4109 if (this_variable != null)
4110 throw new InternalErrorException (StartLocation.ToString ());
4112 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4113 this_variable.Type = bc.CurrentType;
4114 this_variable.PrepareAssignmentAnalysis (bc);
4117 public bool IsThisAssigned (FlowAnalysisContext fc)
4119 return this_variable == null || this_variable.IsThisAssigned (fc, this);
4122 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4124 var res = base.DoFlowAnalysis (fc);
4127 // If we're a non-static struct constructor which doesn't have an
4128 // initializer, then we must initialize all of the struct's fields.
4130 IsThisAssigned (fc);
4134 public override void Emit (EmitContext ec)
4136 if (Report.Errors > 0)
4140 if (IsCompilerGenerated) {
4141 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4149 // If `HasReturnLabel' is set, then we already emitted a
4150 // jump to the end of the method, so we must emit a `ret'
4153 // Unfortunately, System.Reflection.Emit automatically emits
4154 // a leave to the end of a finally block. This is a problem
4155 // if no code is following the try/finally block since we may
4156 // jump to a point after the end of the method.
4157 // As a workaround, we're always creating a return label in
4160 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4161 if (ec.HasReturnLabel)
4162 ec.MarkLabel (ec.ReturnLabel);
4164 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4165 ec.Mark (EndLocation);
4167 if (ec.ReturnType.Kind != MemberKind.Void)
4168 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4170 ec.Emit (OpCodes.Ret);
4173 } catch (Exception e) {
4174 throw new InternalErrorException (e, StartLocation);
4178 public bool Resolve (BlockContext bc, IMethodData md)
4183 var errors = bc.Report.Errors;
4187 if (bc.Report.Errors > errors)
4190 MarkReachable (new Reachability ());
4192 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4193 // TODO: var md = bc.CurrentMemberDefinition;
4194 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4197 if ((flags & Flags.NoFlowAnalysis) != 0)
4200 var fc = new FlowAnalysisContext (bc.Module.Compiler, this);
4203 } catch (Exception e) {
4204 throw new InternalErrorException (e, StartLocation);
4211 public class SwitchLabel : Statement
4219 // if expr == null, then it is the default case.
4221 public SwitchLabel (Expression expr, Location l)
4227 public bool IsDefault {
4229 return label == null;
4233 public Expression Label {
4239 public Location Location {
4245 public Constant Converted {
4254 public bool SectionStart { get; set; }
4256 public Label GetILLabel (EmitContext ec)
4258 if (il_label == null){
4259 il_label = ec.DefineLabel ();
4262 return il_label.Value;
4265 protected override void DoEmit (EmitContext ec)
4267 ec.MarkLabel (GetILLabel (ec));
4270 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4275 if (IsUnreachable) {
4276 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
4278 fc.Report.Error (163, Location,
4279 "Control cannot fall through from one case label `{0}' to another", GetSignatureForError ());
4285 public override bool Resolve (BlockContext bc)
4287 if (ResolveAndReduce (bc))
4288 bc.Switch.RegisterLabel (bc, this);
4294 // Resolves the expression, reduces it to a literal if possible
4295 // and then converts it to the requested type.
4297 bool ResolveAndReduce (BlockContext rc)
4302 var c = label.ResolveLabelConstant (rc);
4306 if (rc.Switch.IsNullable && c is NullLiteral) {
4311 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType);
4312 return converted != null;
4315 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4317 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4318 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4321 protected override void CloneTo (CloneContext clonectx, Statement target)
4323 var t = (SwitchLabel) target;
4325 t.label = label.Clone (clonectx);
4328 public override object Accept (StructuralVisitor visitor)
4330 return visitor.Visit (this);
4333 string GetSignatureForError ()
4336 if (converted == null)
4339 label = converted.GetValueAsLiteral ();
4341 return string.Format ("case {0}:", label);
4345 public class Switch : LoopStatement
4347 // structure used to hold blocks of keys while calculating table switch
4348 sealed class LabelsRange : IComparable<LabelsRange>
4350 public readonly long min;
4352 public readonly List<long> label_values;
4354 public LabelsRange (long value)
4357 label_values = new List<long> ();
4358 label_values.Add (value);
4361 public LabelsRange (long min, long max, ICollection<long> values)
4365 this.label_values = new List<long> (values);
4370 return max - min + 1;
4374 public bool AddValue (long value)
4376 var gap = value - min + 1;
4377 // Ensure the range has > 50% occupancy
4378 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4382 label_values.Add (value);
4386 public int CompareTo (LabelsRange other)
4388 int nLength = label_values.Count;
4389 int nLengthOther = other.label_values.Count;
4390 if (nLengthOther == nLength)
4391 return (int) (other.min - min);
4393 return nLength - nLengthOther;
4397 sealed class DispatchStatement : Statement
4399 readonly Switch body;
4401 public DispatchStatement (Switch body)
4406 protected override void CloneTo (CloneContext clonectx, Statement target)
4408 throw new NotImplementedException ();
4411 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4416 protected override void DoEmit (EmitContext ec)
4418 body.EmitDispatch (ec);
4422 public Expression Expr;
4425 // Mapping of all labels to their SwitchLabels
4427 Dictionary<long, SwitchLabel> labels;
4428 Dictionary<string, SwitchLabel> string_labels;
4429 List<SwitchLabel> case_labels;
4431 List<Tuple<GotoCase, Constant>> goto_cases;
4432 List<DefiniteAssignmentBitSet> end_reachable_das;
4435 /// The governing switch type
4437 public TypeSpec SwitchType;
4439 Expression new_expr;
4441 SwitchLabel case_null;
4442 SwitchLabel case_default;
4444 Label defaultLabel, nullLabel;
4445 VariableReference value;
4446 ExpressionStatement string_dictionary;
4447 FieldExpr switch_cache_field;
4448 ExplicitBlock block;
4452 // Nullable Types support
4454 Nullable.Unwrap unwrap;
4456 public Switch (Expression e, ExplicitBlock block, Location l)
4464 public SwitchLabel ActiveLabel { get; set; }
4466 public ExplicitBlock Block {
4472 public SwitchLabel DefaultLabel {
4474 return case_default;
4478 public bool IsNullable {
4480 return unwrap != null;
4484 public List<SwitchLabel> RegisteredLabels {
4491 // Determines the governing type for a switch. The returned
4492 // expression might be the expression from the switch, or an
4493 // expression that includes any potential conversions to
4495 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
4497 switch (expr.Type.BuiltinType) {
4498 case BuiltinTypeSpec.Type.Byte:
4499 case BuiltinTypeSpec.Type.SByte:
4500 case BuiltinTypeSpec.Type.UShort:
4501 case BuiltinTypeSpec.Type.Short:
4502 case BuiltinTypeSpec.Type.UInt:
4503 case BuiltinTypeSpec.Type.Int:
4504 case BuiltinTypeSpec.Type.ULong:
4505 case BuiltinTypeSpec.Type.Long:
4506 case BuiltinTypeSpec.Type.Char:
4507 case BuiltinTypeSpec.Type.String:
4508 case BuiltinTypeSpec.Type.Bool:
4512 if (expr.Type.IsEnum)
4516 // Try to find a *user* defined implicit conversion.
4518 // If there is no implicit conversion, or if there are multiple
4519 // conversions, we have to report an error
4521 Expression converted = null;
4522 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
4525 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
4530 // Ignore over-worked ImplicitUserConversions that do
4531 // an implicit conversion in addition to the user conversion.
4533 if (!(e is UserCast))
4536 if (converted != null){
4537 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4546 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
4548 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4563 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4565 case_labels.Add (sl);
4568 if (case_default != null) {
4569 sl.Error_AlreadyOccurs (rc, case_default);
4578 if (string_labels != null) {
4579 string string_value = sl.Converted.GetValue () as string;
4580 if (string_value == null)
4583 string_labels.Add (string_value, sl);
4585 if (sl.Converted is NullLiteral) {
4588 labels.Add (sl.Converted.GetValueAsLong (), sl);
4591 } catch (ArgumentException) {
4592 if (string_labels != null)
4593 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4595 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4600 // This method emits code for a lookup-based switch statement (non-string)
4601 // Basically it groups the cases into blocks that are at least half full,
4602 // and then spits out individual lookup opcodes for each block.
4603 // It emits the longest blocks first, and short blocks are just
4604 // handled with direct compares.
4606 void EmitTableSwitch (EmitContext ec, Expression val)
4608 if (labels != null && labels.Count > 0) {
4609 List<LabelsRange> ranges;
4610 if (string_labels != null) {
4611 // We have done all hard work for string already
4612 // setup single range only
4613 ranges = new List<LabelsRange> (1);
4614 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4616 var element_keys = new long[labels.Count];
4617 labels.Keys.CopyTo (element_keys, 0);
4618 Array.Sort (element_keys);
4621 // Build possible ranges of switch labes to reduce number
4624 ranges = new List<LabelsRange> (element_keys.Length);
4625 var range = new LabelsRange (element_keys[0]);
4627 for (int i = 1; i < element_keys.Length; ++i) {
4628 var l = element_keys[i];
4629 if (range.AddValue (l))
4632 range = new LabelsRange (l);
4636 // sort the blocks so we can tackle the largest ones first
4640 Label lbl_default = defaultLabel;
4641 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4643 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4644 LabelsRange kb = ranges[range_index];
4645 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4647 // Optimize small ranges using simple equality check
4648 if (kb.Range <= 2) {
4649 foreach (var key in kb.label_values) {
4650 SwitchLabel sl = labels[key];
4651 if (sl == case_default || sl == case_null)
4654 if (sl.Converted.IsZeroInteger) {
4655 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4658 sl.Converted.Emit (ec);
4659 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4663 // TODO: if all the keys in the block are the same and there are
4664 // no gaps/defaults then just use a range-check.
4665 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4666 // TODO: optimize constant/I4 cases
4668 // check block range (could be > 2^31)
4670 ec.EmitLong (kb.min);
4671 ec.Emit (OpCodes.Blt, lbl_default);
4674 ec.EmitLong (kb.max);
4675 ec.Emit (OpCodes.Bgt, lbl_default);
4680 ec.EmitLong (kb.min);
4681 ec.Emit (OpCodes.Sub);
4684 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4688 int first = (int) kb.min;
4691 ec.Emit (OpCodes.Sub);
4692 } else if (first < 0) {
4693 ec.EmitInt (-first);
4694 ec.Emit (OpCodes.Add);
4698 // first, build the list of labels for the switch
4700 long cJumps = kb.Range;
4701 Label[] switch_labels = new Label[cJumps];
4702 for (int iJump = 0; iJump < cJumps; iJump++) {
4703 var key = kb.label_values[iKey];
4704 if (key == kb.min + iJump) {
4705 switch_labels[iJump] = labels[key].GetILLabel (ec);
4708 switch_labels[iJump] = lbl_default;
4712 // emit the switch opcode
4713 ec.Emit (OpCodes.Switch, switch_labels);
4716 // mark the default for this block
4717 if (range_index != 0)
4718 ec.MarkLabel (lbl_default);
4721 // the last default just goes to the end
4722 if (ranges.Count > 0)
4723 ec.Emit (OpCodes.Br, lbl_default);
4727 SwitchLabel FindLabel (Constant value)
4729 SwitchLabel sl = null;
4731 if (string_labels != null) {
4732 string s = value.GetValue () as string;
4734 if (case_null != null)
4736 else if (case_default != null)
4739 string_labels.TryGetValue (s, out sl);
4742 if (value is NullLiteral) {
4745 labels.TryGetValue (value.GetValueAsLong (), out sl);
4752 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4754 Expr.FlowAnalysis (fc);
4756 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
4757 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
4758 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
4760 block.FlowAnalysis (fc);
4762 fc.SwitchInitialDefinitiveAssignment = prev_switch;
4764 if (end_reachable_das != null) {
4765 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
4766 InitialDefinitiveAssignment |= sections_das;
4767 end_reachable_das = null;
4770 fc.DefiniteAssignment = InitialDefinitiveAssignment;
4772 return case_default != null && !end_reachable;
4775 public override bool Resolve (BlockContext ec)
4777 Expr = Expr.Resolve (ec);
4781 new_expr = SwitchGoverningType (ec, Expr);
4783 if (new_expr == null && Expr.Type.IsNullableType) {
4784 unwrap = Nullable.Unwrap.Create (Expr, false);
4788 new_expr = SwitchGoverningType (ec, unwrap);
4791 if (new_expr == null) {
4792 if (Expr.Type != InternalType.ErrorType) {
4793 ec.Report.Error (151, loc,
4794 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4795 Expr.Type.GetSignatureForError ());
4802 SwitchType = new_expr.Type;
4804 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4805 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4809 if (block.Statements.Count == 0)
4812 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4813 string_labels = new Dictionary<string, SwitchLabel> ();
4815 labels = new Dictionary<long, SwitchLabel> ();
4818 case_labels = new List<SwitchLabel> ();
4820 var constant = new_expr as Constant;
4823 // Don't need extra variable for constant switch or switch with
4824 // only default case
4826 if (constant == null) {
4828 // Store switch expression for comparison purposes
4830 value = new_expr as VariableReference;
4831 if (value == null && !HasOnlyDefaultSection ()) {
4832 var current_block = ec.CurrentBlock;
4833 ec.CurrentBlock = Block;
4834 // Create temporary variable inside switch scope
4835 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4837 ec.CurrentBlock = current_block;
4841 Switch old_switch = ec.Switch;
4843 var parent_los = ec.EnclosingLoopOrSwitch;
4844 ec.EnclosingLoopOrSwitch = this;
4846 var ok = Statement.Resolve (ec);
4848 ec.EnclosingLoopOrSwitch = parent_los;
4849 ec.Switch = old_switch;
4852 // Check if all goto cases are valid. Needs to be done after switch
4853 // is resolved because goto can jump forward in the scope.
4855 if (goto_cases != null) {
4856 foreach (var gc in goto_cases) {
4857 if (gc.Item1 == null) {
4858 if (DefaultLabel == null) {
4859 Goto.Error_UnknownLabel (ec, "default", loc);
4865 var sl = FindLabel (gc.Item2);
4867 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
4869 gc.Item1.Label = sl;
4877 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4878 ResolveStringSwitchMap (ec);
4882 // Anonymous storey initialization has to happen before
4883 // any generated switch dispatch
4885 block.InsertStatement (0, new DispatchStatement (this));
4890 bool HasOnlyDefaultSection ()
4892 for (int i = 0; i < block.Statements.Count; ++i) {
4893 var s = block.Statements[i] as SwitchLabel;
4895 if (s == null || s.IsDefault)
4904 public override Reachability MarkReachable (Reachability rc)
4906 if (rc.IsUnreachable)
4909 base.MarkReachable (rc);
4911 SwitchLabel constant_label = null;
4912 var constant = new_expr as Constant;
4913 if (constant != null) {
4914 constant_label = FindLabel (constant) ?? case_default;
4915 if (constant_label == null) {
4916 block.Statements.RemoveAt (0);
4922 var section_rc = new Reachability ();
4923 SwitchLabel prev_label = null;
4925 for (int i = 0; i < block.Statements.Count; ++i) {
4926 var s = block.Statements[i];
4927 var sl = s as SwitchLabel;
4930 if (!sl.SectionStart) {
4931 sl.MarkReachable (section_rc);
4935 if (prev_label == null) {
4937 switch_rc = Reachability.CreateUnreachable ();
4939 if (constant_label != null && sl != constant_label)
4940 section_rc = Reachability.CreateUnreachable ();
4946 // Small trick, using unreachable flag for label means
4947 // the label section does not fallthrough
4949 prev_label.MarkReachable (section_rc);
4952 switch_rc &= section_rc;
4953 section_rc = new Reachability ();
4955 if (constant_label != null && sl != constant_label)
4956 section_rc = Reachability.CreateUnreachable ();
4961 section_rc = s.MarkReachable (section_rc);
4964 if (prev_label != null) {
4965 prev_label.MarkReachable (section_rc);
4966 switch_rc &= section_rc;
4970 // Reachability can affect parent only when all possible paths are handled but
4971 // we still need to run reachability check on switch body to check for fall-through
4973 if (case_default == null && constant_label == null)
4977 // We have at least one local exit from the switch
4985 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
4987 if (goto_cases == null)
4988 goto_cases = new List<Tuple<GotoCase, Constant>> ();
4990 goto_cases.Add (Tuple.Create (gotoCase, value));
4994 // Converts string switch into string hashtable
4996 void ResolveStringSwitchMap (ResolveContext ec)
4998 FullNamedExpression string_dictionary_type;
4999 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5000 string_dictionary_type = new TypeExpression (
5001 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5002 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5004 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5005 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5007 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5011 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5012 Field field = new Field (ctype, string_dictionary_type,
5013 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5014 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5015 if (!field.Define ())
5017 ctype.AddField (field);
5019 var init = new List<Expression> ();
5021 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5022 string value = null;
5024 foreach (SwitchLabel sl in case_labels) {
5026 if (sl.SectionStart)
5027 labels.Add (++counter, sl);
5029 if (sl == case_default || sl == case_null)
5032 value = (string) sl.Converted.GetValue ();
5033 var init_args = new List<Expression> (2);
5034 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5036 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5037 init_args.Add (sl.Converted);
5039 init.Add (new CollectionElementInitializer (init_args, loc));
5042 Arguments args = new Arguments (1);
5043 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5044 Expression initializer = new NewInitialize (string_dictionary_type, args,
5045 new CollectionOrObjectInitializers (init, loc), loc);
5047 switch_cache_field = new FieldExpr (field, loc);
5048 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5051 void DoEmitStringSwitch (EmitContext ec)
5053 Label l_initialized = ec.DefineLabel ();
5056 // Skip initialization when value is null
5058 value.EmitBranchable (ec, nullLabel, false);
5061 // Check if string dictionary is initialized and initialize
5063 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5064 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5065 string_dictionary.EmitStatement (ec);
5067 ec.MarkLabel (l_initialized);
5069 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5071 ResolveContext rc = new ResolveContext (ec.MemberContext);
5073 if (switch_cache_field.Type.IsGeneric) {
5074 Arguments get_value_args = new Arguments (2);
5075 get_value_args.Add (new Argument (value));
5076 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5077 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5078 if (get_item == null)
5082 // A value was not found, go to default case
5084 get_item.EmitBranchable (ec, defaultLabel, false);
5086 Arguments get_value_args = new Arguments (1);
5087 get_value_args.Add (new Argument (value));
5089 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5090 if (get_item == null)
5093 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5094 get_item_object.EmitAssign (ec, get_item, true, false);
5095 ec.Emit (OpCodes.Brfalse, defaultLabel);
5097 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5098 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5100 get_item_int.EmitStatement (ec);
5101 get_item_object.Release (ec);
5104 EmitTableSwitch (ec, string_switch_variable);
5105 string_switch_variable.Release (ec);
5109 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5111 void EmitShortSwitch (EmitContext ec)
5113 MethodSpec equal_method = null;
5114 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5115 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5118 if (equal_method != null) {
5119 value.EmitBranchable (ec, nullLabel, false);
5122 for (int i = 0; i < case_labels.Count; ++i) {
5123 var label = case_labels [i];
5124 if (label == case_default || label == case_null)
5127 var constant = label.Converted;
5129 if (equal_method != null) {
5133 var call = new CallEmitter ();
5134 call.EmitPredefined (ec, equal_method, new Arguments (0));
5135 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5139 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5140 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5146 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5149 ec.Emit (OpCodes.Br, defaultLabel);
5152 void EmitDispatch (EmitContext ec)
5154 if (value == null) {
5156 // Constant switch, we already done the work
5161 if (string_dictionary != null) {
5162 DoEmitStringSwitch (ec);
5163 } else if (case_labels.Count < 4 || string_labels != null) {
5164 EmitShortSwitch (ec);
5166 EmitTableSwitch (ec, value);
5170 protected override void DoEmit (EmitContext ec)
5173 // Setup the codegen context
5175 Label old_end = ec.LoopEnd;
5176 Switch old_switch = ec.Switch;
5178 ec.LoopEnd = ec.DefineLabel ();
5181 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5182 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5184 if (value != null) {
5187 unwrap.EmitCheck (ec);
5188 ec.Emit (OpCodes.Brfalse, nullLabel);
5189 value.EmitAssign (ec, new_expr, false, false);
5190 } else if (new_expr != value) {
5191 value.EmitAssign (ec, new_expr, false, false);
5196 // Next statement is compiler generated we don't need extra
5197 // nop when we can use the statement for sequence point
5199 ec.Mark (block.StartLocation);
5200 block.IsCompilerGenerated = true;
5205 // Restore context state.
5206 ec.MarkLabel (ec.LoopEnd);
5209 // Restore the previous context
5211 ec.LoopEnd = old_end;
5212 ec.Switch = old_switch;
5215 protected override void CloneTo (CloneContext clonectx, Statement t)
5217 Switch target = (Switch) t;
5219 target.Expr = Expr.Clone (clonectx);
5220 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5223 public override object Accept (StructuralVisitor visitor)
5225 return visitor.Visit (this);
5228 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5230 if (case_default == null)
5233 if (end_reachable_das == null)
5234 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5236 end_reachable_das.Add (fc.DefiniteAssignment);
5239 public override void SetEndReachable ()
5241 end_reachable = true;
5245 // A place where execution can restart in a state machine
5246 public abstract class ResumableStatement : Statement
5249 protected Label resume_point;
5251 public Label PrepareForEmit (EmitContext ec)
5255 resume_point = ec.DefineLabel ();
5257 return resume_point;
5260 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5265 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5270 public abstract class TryFinallyBlock : ExceptionStatement
5272 protected Statement stmt;
5273 Label dispose_try_block;
5274 bool prepared_for_dispose, emitted_dispose;
5275 Method finally_host;
5277 protected TryFinallyBlock (Statement stmt, Location loc)
5285 public Statement Statement {
5293 protected abstract void EmitTryBody (EmitContext ec);
5294 public abstract void EmitFinallyBody (EmitContext ec);
5296 public override Label PrepareForDispose (EmitContext ec, Label end)
5298 if (!prepared_for_dispose) {
5299 prepared_for_dispose = true;
5300 dispose_try_block = ec.DefineLabel ();
5302 return dispose_try_block;
5305 protected sealed override void DoEmit (EmitContext ec)
5307 EmitTryBodyPrepare (ec);
5310 ec.BeginFinallyBlock ();
5312 Label start_finally = ec.DefineLabel ();
5313 if (resume_points != null) {
5314 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5316 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5317 ec.Emit (OpCodes.Brfalse_S, start_finally);
5318 ec.Emit (OpCodes.Endfinally);
5321 ec.MarkLabel (start_finally);
5323 if (finally_host != null) {
5324 finally_host.Define ();
5325 finally_host.PrepareEmit ();
5326 finally_host.Emit ();
5328 // Now it's safe to add, to close it properly and emit sequence points
5329 finally_host.Parent.AddMember (finally_host);
5331 var ce = new CallEmitter ();
5332 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5333 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5335 EmitFinallyBody (ec);
5338 ec.EndExceptionBlock ();
5341 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5343 if (emitted_dispose)
5346 emitted_dispose = true;
5348 Label end_of_try = ec.DefineLabel ();
5350 // Ensure that the only way we can get into this code is through a dispatcher
5351 if (have_dispatcher)
5352 ec.Emit (OpCodes.Br, end);
5354 ec.BeginExceptionBlock ();
5356 ec.MarkLabel (dispose_try_block);
5358 Label[] labels = null;
5359 for (int i = 0; i < resume_points.Count; ++i) {
5360 ResumableStatement s = resume_points[i];
5361 Label ret = s.PrepareForDispose (ec, end_of_try);
5362 if (ret.Equals (end_of_try) && labels == null)
5364 if (labels == null) {
5365 labels = new Label[resume_points.Count];
5366 for (int j = 0; j < i; ++j)
5367 labels[j] = end_of_try;
5372 if (labels != null) {
5374 for (j = 1; j < labels.Length; ++j)
5375 if (!labels[0].Equals (labels[j]))
5377 bool emit_dispatcher = j < labels.Length;
5379 if (emit_dispatcher) {
5380 ec.Emit (OpCodes.Ldloc, pc);
5381 ec.EmitInt (first_resume_pc);
5382 ec.Emit (OpCodes.Sub);
5383 ec.Emit (OpCodes.Switch, labels);
5386 foreach (ResumableStatement s in resume_points)
5387 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5390 ec.MarkLabel (end_of_try);
5392 ec.BeginFinallyBlock ();
5394 if (finally_host != null) {
5395 var ce = new CallEmitter ();
5396 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5397 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5399 EmitFinallyBody (ec);
5402 ec.EndExceptionBlock ();
5405 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5407 var res = stmt.FlowAnalysis (fc);
5412 public override Reachability MarkReachable (Reachability rc)
5414 base.MarkReachable (rc);
5415 return Statement.MarkReachable (rc);
5418 public override bool Resolve (BlockContext bc)
5422 parent = bc.CurrentTryBlock;
5423 bc.CurrentTryBlock = this;
5425 using (bc.Set (ResolveContext.Options.TryScope)) {
5426 ok = stmt.Resolve (bc);
5429 bc.CurrentTryBlock = parent;
5432 // Finally block inside iterator is called from MoveNext and
5433 // Dispose methods that means we need to lift the block into
5434 // newly created host method to emit the body only once. The
5435 // original block then simply calls the newly generated method.
5437 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5438 var b = stmt as Block;
5439 if (b != null && b.Explicit.HasYield) {
5440 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5444 return base.Resolve (bc) && ok;
5449 // Base class for blocks using exception handling
5451 public abstract class ExceptionStatement : ResumableStatement
5453 protected List<ResumableStatement> resume_points;
5454 protected int first_resume_pc;
5455 protected ExceptionStatement parent;
5457 protected ExceptionStatement (Location loc)
5462 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5464 StateMachineInitializer state_machine = null;
5465 if (resume_points != null) {
5466 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5468 ec.EmitInt ((int) IteratorStorey.State.Running);
5469 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5472 ec.BeginExceptionBlock ();
5474 if (resume_points != null) {
5475 ec.MarkLabel (resume_point);
5477 // For normal control flow, we want to fall-through the Switch
5478 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5479 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5480 ec.EmitInt (first_resume_pc);
5481 ec.Emit (OpCodes.Sub);
5483 Label[] labels = new Label[resume_points.Count];
5484 for (int i = 0; i < resume_points.Count; ++i)
5485 labels[i] = resume_points[i].PrepareForEmit (ec);
5486 ec.Emit (OpCodes.Switch, labels);
5490 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5492 if (parent != null) {
5493 // TODO: MOVE to virtual TryCatch
5494 var tc = this as TryCatch;
5495 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5497 pc = parent.AddResumePoint (s, pc, stateMachine);
5499 pc = stateMachine.AddResumePoint (this);
5502 if (resume_points == null) {
5503 resume_points = new List<ResumableStatement> ();
5504 first_resume_pc = pc;
5507 if (pc != first_resume_pc + resume_points.Count)
5508 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5510 resume_points.Add (stmt);
5515 public class Lock : TryFinallyBlock
5518 TemporaryVariableReference expr_copy;
5519 TemporaryVariableReference lock_taken;
5521 public Lock (Expression expr, Statement stmt, Location loc)
5527 public Expression Expr {
5533 public override bool Resolve (BlockContext ec)
5535 expr = expr.Resolve (ec);
5539 if (!TypeSpec.IsReferenceType (expr.Type)) {
5540 ec.Report.Error (185, loc,
5541 "`{0}' is not a reference type as required by the lock statement",
5542 expr.Type.GetSignatureForError ());
5545 if (expr.Type.IsGenericParameter) {
5546 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5549 VariableReference lv = expr as VariableReference;
5552 locked = lv.IsLockedByStatement;
5553 lv.IsLockedByStatement = true;
5560 // Have to keep original lock value around to unlock same location
5561 // in the case of original value has changed or is null
5563 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5564 expr_copy.Resolve (ec);
5567 // Ensure Monitor methods are available
5569 if (ResolvePredefinedMethods (ec) > 1) {
5570 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5571 lock_taken.Resolve (ec);
5574 using (ec.Set (ResolveContext.Options.LockScope)) {
5579 lv.IsLockedByStatement = locked;
5585 protected override void EmitTryBodyPrepare (EmitContext ec)
5587 expr_copy.EmitAssign (ec, expr);
5589 if (lock_taken != null) {
5591 // Initialize ref variable
5593 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5596 // Monitor.Enter (expr_copy)
5598 expr_copy.Emit (ec);
5599 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5602 base.EmitTryBodyPrepare (ec);
5605 protected override void EmitTryBody (EmitContext ec)
5608 // Monitor.Enter (expr_copy, ref lock_taken)
5610 if (lock_taken != null) {
5611 expr_copy.Emit (ec);
5612 lock_taken.LocalInfo.CreateBuilder (ec);
5613 lock_taken.AddressOf (ec, AddressOp.Load);
5614 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
5617 Statement.Emit (ec);
5620 public override void EmitFinallyBody (EmitContext ec)
5623 // if (lock_taken) Monitor.Exit (expr_copy)
5625 Label skip = ec.DefineLabel ();
5627 if (lock_taken != null) {
5628 lock_taken.Emit (ec);
5629 ec.Emit (OpCodes.Brfalse_S, skip);
5632 expr_copy.Emit (ec);
5633 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
5635 ec.Emit (OpCodes.Call, m);
5637 ec.MarkLabel (skip);
5640 int ResolvePredefinedMethods (ResolveContext rc)
5642 // Try 4.0 Monitor.Enter (object, ref bool) overload first
5643 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
5647 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
5651 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
5655 protected override void CloneTo (CloneContext clonectx, Statement t)
5657 Lock target = (Lock) t;
5659 target.expr = expr.Clone (clonectx);
5660 target.stmt = Statement.Clone (clonectx);
5663 public override object Accept (StructuralVisitor visitor)
5665 return visitor.Visit (this);
5670 public class Unchecked : Statement {
5673 public Unchecked (Block b, Location loc)
5680 public override bool Resolve (BlockContext ec)
5682 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
5683 return Block.Resolve (ec);
5686 protected override void DoEmit (EmitContext ec)
5688 using (ec.With (EmitContext.Options.CheckedScope, false))
5692 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5694 return Block.FlowAnalysis (fc);
5697 public override Reachability MarkReachable (Reachability rc)
5699 base.MarkReachable (rc);
5700 return Block.MarkReachable (rc);
5703 protected override void CloneTo (CloneContext clonectx, Statement t)
5705 Unchecked target = (Unchecked) t;
5707 target.Block = clonectx.LookupBlock (Block);
5710 public override object Accept (StructuralVisitor visitor)
5712 return visitor.Visit (this);
5716 public class Checked : Statement {
5719 public Checked (Block b, Location loc)
5722 b.Unchecked = false;
5726 public override bool Resolve (BlockContext ec)
5728 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
5729 return Block.Resolve (ec);
5732 protected override void DoEmit (EmitContext ec)
5734 using (ec.With (EmitContext.Options.CheckedScope, true))
5738 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5740 return Block.FlowAnalysis (fc);
5743 public override Reachability MarkReachable (Reachability rc)
5745 base.MarkReachable (rc);
5746 return Block.MarkReachable (rc);
5749 protected override void CloneTo (CloneContext clonectx, Statement t)
5751 Checked target = (Checked) t;
5753 target.Block = clonectx.LookupBlock (Block);
5756 public override object Accept (StructuralVisitor visitor)
5758 return visitor.Visit (this);
5762 public class Unsafe : Statement {
5765 public Unsafe (Block b, Location loc)
5768 Block.Unsafe = true;
5772 public override bool Resolve (BlockContext ec)
5774 if (ec.CurrentIterator != null)
5775 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5777 using (ec.Set (ResolveContext.Options.UnsafeScope))
5778 return Block.Resolve (ec);
5781 protected override void DoEmit (EmitContext ec)
5786 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5788 return Block.FlowAnalysis (fc);
5791 public override Reachability MarkReachable (Reachability rc)
5793 base.MarkReachable (rc);
5794 return Block.MarkReachable (rc);
5797 protected override void CloneTo (CloneContext clonectx, Statement t)
5799 Unsafe target = (Unsafe) t;
5801 target.Block = clonectx.LookupBlock (Block);
5804 public override object Accept (StructuralVisitor visitor)
5806 return visitor.Visit (this);
5813 public class Fixed : Statement
5815 abstract class Emitter : ShimExpression
5817 protected LocalVariable vi;
5819 protected Emitter (Expression expr, LocalVariable li)
5825 public abstract void EmitExit (EmitContext ec);
5827 public override void FlowAnalysis (FlowAnalysisContext fc)
5829 expr.FlowAnalysis (fc);
5833 class ExpressionEmitter : Emitter {
5834 public ExpressionEmitter (Expression converted, LocalVariable li) :
5835 base (converted, li)
5839 protected override Expression DoResolve (ResolveContext rc)
5841 throw new NotImplementedException ();
5844 public override void Emit (EmitContext ec) {
5846 // Store pointer in pinned location
5852 public override void EmitExit (EmitContext ec)
5855 ec.Emit (OpCodes.Conv_U);
5860 class StringEmitter : Emitter
5862 LocalVariable pinned_string;
5864 public StringEmitter (Expression expr, LocalVariable li)
5869 protected override Expression DoResolve (ResolveContext rc)
5871 pinned_string = new LocalVariable (vi.Block, "$pinned",
5872 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5874 pinned_string.Type = rc.BuiltinTypes.String;
5876 eclass = ExprClass.Variable;
5877 type = rc.BuiltinTypes.Int;
5881 public override void Emit (EmitContext ec)
5883 pinned_string.CreateBuilder (ec);
5886 pinned_string.EmitAssign (ec);
5888 // TODO: Should use Binary::Add
5889 pinned_string.Emit (ec);
5890 ec.Emit (OpCodes.Conv_I);
5892 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5896 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5897 //pe.InstanceExpression = pinned_string;
5898 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5900 ec.Emit (OpCodes.Add);
5904 public override void EmitExit (EmitContext ec)
5907 pinned_string.EmitAssign (ec);
5911 public class VariableDeclaration : BlockVariable
5913 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5918 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5920 if (!Variable.Type.IsPointer && li == Variable) {
5921 bc.Report.Error (209, TypeExpression.Location,
5922 "The type of locals declared in a fixed statement must be a pointer type");
5927 // The rules for the possible declarators are pretty wise,
5928 // but the production on the grammar is more concise.
5930 // So we have to enforce these rules here.
5932 // We do not resolve before doing the case 1 test,
5933 // because the grammar is explicit in that the token &
5934 // is present, so we need to test for this particular case.
5937 if (initializer is Cast) {
5938 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5942 initializer = initializer.Resolve (bc);
5944 if (initializer == null)
5950 if (initializer.Type.IsArray) {
5951 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5954 // Provided that array_type is unmanaged,
5956 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5960 // and T* is implicitly convertible to the
5961 // pointer type given in the fixed statement.
5963 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5965 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5966 if (converted == null)
5970 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5972 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5973 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5974 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5975 new NullLiteral (loc),
5978 converted = converted.Resolve (bc);
5980 return new ExpressionEmitter (converted, li);
5986 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5987 return new StringEmitter (initializer, li).Resolve (bc);
5990 // Case 3: fixed buffer
5991 if (initializer is FixedBufferPtr) {
5992 return new ExpressionEmitter (initializer, li);
5996 // Case 4: & object.
5998 bool already_fixed = true;
5999 Unary u = initializer as Unary;
6000 if (u != null && u.Oper == Unary.Operator.AddressOf) {
6001 IVariableReference vr = u.Expr as IVariableReference;
6002 if (vr == null || !vr.IsFixed) {
6003 already_fixed = false;
6007 if (already_fixed) {
6008 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6011 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
6012 return new ExpressionEmitter (initializer, li);
6017 VariableDeclaration decl;
6018 Statement statement;
6021 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6030 public Statement Statement {
6036 public BlockVariable Variables {
6044 public override bool Resolve (BlockContext bc)
6046 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6047 if (!decl.Resolve (bc))
6051 return statement.Resolve (bc);
6054 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6056 decl.FlowAnalysis (fc);
6057 return statement.FlowAnalysis (fc);
6060 protected override void DoEmit (EmitContext ec)
6062 decl.Variable.CreateBuilder (ec);
6063 decl.Initializer.Emit (ec);
6064 if (decl.Declarators != null) {
6065 foreach (var d in decl.Declarators) {
6066 d.Variable.CreateBuilder (ec);
6067 d.Initializer.Emit (ec);
6071 statement.Emit (ec);
6077 // Clear the pinned variable
6079 ((Emitter) decl.Initializer).EmitExit (ec);
6080 if (decl.Declarators != null) {
6081 foreach (var d in decl.Declarators) {
6082 ((Emitter)d.Initializer).EmitExit (ec);
6087 public override Reachability MarkReachable (Reachability rc)
6089 base.MarkReachable (rc);
6091 decl.MarkReachable (rc);
6093 rc = statement.MarkReachable (rc);
6095 // TODO: What if there is local exit?
6096 has_ret = rc.IsUnreachable;
6100 protected override void CloneTo (CloneContext clonectx, Statement t)
6102 Fixed target = (Fixed) t;
6104 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6105 target.statement = statement.Clone (clonectx);
6108 public override object Accept (StructuralVisitor visitor)
6110 return visitor.Visit (this);
6114 public class Catch : Statement
6116 ExplicitBlock block;
6118 FullNamedExpression type_expr;
6119 CompilerAssign assign;
6122 public Catch (ExplicitBlock block, Location loc)
6130 public ExplicitBlock Block {
6136 public TypeSpec CatchType {
6142 public bool IsGeneral {
6144 return type_expr == null;
6148 public FullNamedExpression TypeExpression {
6157 public LocalVariable Variable {
6168 protected override void DoEmit (EmitContext ec)
6171 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6173 ec.BeginCatchBlock (CatchType);
6176 li.CreateBuilder (ec);
6179 // Special case hoisted catch variable, we have to use a temporary variable
6180 // to pass via anonymous storey initialization with the value still on top
6183 if (li.HoistedVariant != null) {
6184 LocalTemporary lt = new LocalTemporary (li.Type);
6187 // switch to assigning from the temporary variable and not from top of the stack
6188 assign.UpdateSource (lt);
6191 ec.Emit (OpCodes.Pop);
6197 public override bool Resolve (BlockContext ec)
6199 using (ec.Set (ResolveContext.Options.CatchScope)) {
6200 if (type_expr != null) {
6201 type = type_expr.ResolveAsType (ec);
6205 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
6206 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6207 } else if (li != null) {
6209 li.PrepareAssignmentAnalysis (ec);
6211 // source variable is at the top of the stack
6212 Expression source = new EmptyExpression (li.Type);
6213 if (li.Type.IsGenericParameter)
6214 source = new UnboxCast (source, li.Type);
6217 // Uses Location.Null to hide from symbol file
6219 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6220 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6224 Block.SetCatchBlock ();
6225 return Block.Resolve (ec);
6229 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6232 fc.SetVariableAssigned (li.VariableInfo, true);
6235 return block.FlowAnalysis (fc);
6238 public override Reachability MarkReachable (Reachability rc)
6240 base.MarkReachable (rc);
6242 return block.MarkReachable (rc);
6245 protected override void CloneTo (CloneContext clonectx, Statement t)
6247 Catch target = (Catch) t;
6249 if (type_expr != null)
6250 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6252 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6256 public class TryFinally : TryFinallyBlock
6259 List<DefiniteAssignmentBitSet> try_exit_dat;
6261 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6267 public ExplicitBlock FinallyBlock {
6273 public void RegisterOutParametersCheck (DefiniteAssignmentBitSet vector)
6275 if (try_exit_dat == null)
6276 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6278 try_exit_dat.Add (vector);
6281 public override bool Resolve (BlockContext bc)
6283 bool ok = base.Resolve (bc);
6285 fini.SetFinallyBlock ();
6286 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6287 ok &= fini.Resolve (bc);
6293 protected override void EmitTryBody (EmitContext ec)
6298 public override void EmitFinallyBody (EmitContext ec)
6303 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6305 var da = fc.BranchDefiniteAssignment ();
6307 var tf = fc.TryFinally;
6308 fc.TryFinally = this;
6310 var res_stmt = Statement.FlowAnalysis (fc);
6314 var try_da = fc.DefiniteAssignment;
6315 fc.DefiniteAssignment = da;
6317 var res_fin = fini.FlowAnalysis (fc);
6319 if (try_exit_dat != null) {
6321 // try block has global exit but we need to run definite assignment check
6322 // for parameter block out parameter after finally block because it's always
6323 // executed before exit
6325 foreach (var try_da_part in try_exit_dat)
6326 fc.ParametersBlock.CheckOutParametersAssignment (fc, fc.DefiniteAssignment | try_da_part);
6328 try_exit_dat = null;
6331 fc.DefiniteAssignment |= try_da;
6332 return res_stmt | res_fin;
6335 public override Reachability MarkReachable (Reachability rc)
6338 // Mark finally block first for any exit statement in try block
6339 // to know whether the code which follows finally is reachable
6341 return fini.MarkReachable (rc) | base.MarkReachable (rc);
6344 protected override void CloneTo (CloneContext clonectx, Statement t)
6346 TryFinally target = (TryFinally) t;
6348 target.stmt = stmt.Clone (clonectx);
6350 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
6353 public override object Accept (StructuralVisitor visitor)
6355 return visitor.Visit (this);
6359 public class TryCatch : ExceptionStatement
6362 List<Catch> clauses;
6363 readonly bool inside_try_finally;
6365 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
6369 this.clauses = catch_clauses;
6370 this.inside_try_finally = inside_try_finally;
6373 public List<Catch> Clauses {
6379 public bool IsTryCatchFinally {
6381 return inside_try_finally;
6385 public override bool Resolve (BlockContext bc)
6389 using (bc.Set (ResolveContext.Options.TryScope)) {
6390 parent = bc.CurrentTryBlock;
6392 if (IsTryCatchFinally) {
6393 ok = Block.Resolve (bc);
6395 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
6396 bc.CurrentTryBlock = this;
6397 ok = Block.Resolve (bc);
6398 bc.CurrentTryBlock = parent;
6403 for (int i = 0; i < clauses.Count; ++i) {
6406 ok &= c.Resolve (bc);
6408 TypeSpec resolved_type = c.CatchType;
6409 for (int ii = 0; ii < clauses.Count; ++ii) {
6413 if (clauses[ii].IsGeneral) {
6414 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
6417 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
6420 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
6423 bc.Report.Warning (1058, 1, c.loc,
6424 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
6432 var ct = clauses[ii].CatchType;
6436 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
6437 bc.Report.Error (160, c.loc,
6438 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
6439 ct.GetSignatureForError ());
6445 return base.Resolve (bc) && ok;
6448 protected sealed override void DoEmit (EmitContext ec)
6450 if (!inside_try_finally)
6451 EmitTryBodyPrepare (ec);
6455 foreach (Catch c in clauses)
6458 if (!inside_try_finally)
6459 ec.EndExceptionBlock ();
6462 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6464 var start_fc = fc.BranchDefiniteAssignment ();
6465 var res = Block.FlowAnalysis (fc);
6467 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
6469 foreach (var c in clauses) {
6470 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
6471 if (!c.FlowAnalysis (fc)) {
6473 try_fc = fc.DefiniteAssignment;
6475 try_fc &= fc.DefiniteAssignment;
6481 fc.DefiniteAssignment = try_fc ?? start_fc;
6486 public override Reachability MarkReachable (Reachability rc)
6488 if (rc.IsUnreachable)
6491 base.MarkReachable (rc);
6493 var tc_rc = Block.MarkReachable (rc);
6495 foreach (var c in clauses)
6496 tc_rc &= c.MarkReachable (rc);
6501 protected override void CloneTo (CloneContext clonectx, Statement t)
6503 TryCatch target = (TryCatch) t;
6505 target.Block = clonectx.LookupBlock (Block);
6506 if (clauses != null){
6507 target.clauses = new List<Catch> ();
6508 foreach (Catch c in clauses)
6509 target.clauses.Add ((Catch) c.Clone (clonectx));
6513 public override object Accept (StructuralVisitor visitor)
6515 return visitor.Visit (this);
6519 public class Using : TryFinallyBlock
6521 public class VariableDeclaration : BlockVariable
6523 Statement dispose_call;
6525 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6530 public VariableDeclaration (LocalVariable li, Location loc)
6536 public VariableDeclaration (Expression expr)
6539 loc = expr.Location;
6545 public bool IsNested { get; private set; }
6549 public void EmitDispose (EmitContext ec)
6551 dispose_call.Emit (ec);
6554 public override bool Resolve (BlockContext bc)
6559 return base.Resolve (bc, false);
6562 public Expression ResolveExpression (BlockContext bc)
6564 var e = Initializer.Resolve (bc);
6568 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
6569 Initializer = ResolveInitializer (bc, Variable, e);
6573 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6575 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
6576 initializer = initializer.Resolve (bc);
6577 if (initializer == null)
6580 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
6581 Arguments args = new Arguments (1);
6582 args.Add (new Argument (initializer));
6583 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
6584 if (initializer == null)
6587 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
6588 dispose_call = CreateDisposeCall (bc, var);
6589 dispose_call.Resolve (bc);
6591 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
6594 if (li == Variable) {
6595 CheckIDiposableConversion (bc, li, initializer);
6596 dispose_call = CreateDisposeCall (bc, li);
6597 dispose_call.Resolve (bc);
6600 return base.ResolveInitializer (bc, li, initializer);
6603 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6607 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
6608 if (type.IsNullableType) {
6609 // it's handled in CreateDisposeCall
6613 bc.Report.SymbolRelatedToPreviousError (type);
6614 var loc = type_expr == null ? initializer.Location : type_expr.Location;
6615 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
6616 type.GetSignatureForError ());
6622 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6624 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
6626 var loc = lv.Location;
6628 var idt = bc.BuiltinTypes.IDisposable;
6629 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6631 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6632 dispose_mg.InstanceExpression = type.IsNullableType ?
6633 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
6637 // Hide it from symbol file via null location
6639 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
6641 // Add conditional call when disposing possible null variable
6642 if (!type.IsStruct || type.IsNullableType)
6643 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
6648 public void ResolveDeclaratorInitializer (BlockContext bc)
6650 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
6653 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
6655 for (int i = declarators.Count - 1; i >= 0; --i) {
6656 var d = declarators [i];
6657 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
6658 vd.Initializer = d.Initializer;
6660 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
6661 vd.dispose_call.Resolve (bc);
6663 stmt = new Using (vd, stmt, d.Variable.Location);
6670 public override object Accept (StructuralVisitor visitor)
6672 return visitor.Visit (this);
6676 VariableDeclaration decl;
6678 public Using (VariableDeclaration decl, Statement stmt, Location loc)
6684 public Using (Expression expr, Statement stmt, Location loc)
6687 this.decl = new VariableDeclaration (expr);
6692 public Expression Expr {
6694 return decl.Variable == null ? decl.Initializer : null;
6698 public BlockVariable Variables {
6706 public override void Emit (EmitContext ec)
6709 // Don't emit sequence point it will be set on variable declaration
6714 protected override void EmitTryBodyPrepare (EmitContext ec)
6717 base.EmitTryBodyPrepare (ec);
6720 protected override void EmitTryBody (EmitContext ec)
6725 public override void EmitFinallyBody (EmitContext ec)
6727 decl.EmitDispose (ec);
6730 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6732 decl.FlowAnalysis (fc);
6733 return stmt.FlowAnalysis (fc);
6736 public override Reachability MarkReachable (Reachability rc)
6738 decl.MarkReachable (rc);
6739 return base.MarkReachable (rc);
6742 public override bool Resolve (BlockContext ec)
6744 VariableReference vr;
6745 bool vr_locked = false;
6747 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
6748 if (decl.Variable == null) {
6749 vr = decl.ResolveExpression (ec) as VariableReference;
6751 vr_locked = vr.IsLockedByStatement;
6752 vr.IsLockedByStatement = true;
6755 if (decl.IsNested) {
6756 decl.ResolveDeclaratorInitializer (ec);
6758 if (!decl.Resolve (ec))
6761 if (decl.Declarators != null) {
6762 stmt = decl.RewriteUsingDeclarators (ec, stmt);
6773 vr.IsLockedByStatement = vr_locked;
6778 protected override void CloneTo (CloneContext clonectx, Statement t)
6780 Using target = (Using) t;
6782 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6783 target.stmt = stmt.Clone (clonectx);
6786 public override object Accept (StructuralVisitor visitor)
6788 return visitor.Visit (this);
6793 /// Implementation of the foreach C# statement
6795 public class Foreach : LoopStatement
6797 abstract class IteratorStatement : Statement
6799 protected readonly Foreach for_each;
6801 protected IteratorStatement (Foreach @foreach)
6803 this.for_each = @foreach;
6804 this.loc = @foreach.expr.Location;
6807 protected override void CloneTo (CloneContext clonectx, Statement target)
6809 throw new NotImplementedException ();
6812 public override void Emit (EmitContext ec)
6814 if (ec.EmitAccurateDebugInfo) {
6815 ec.Emit (OpCodes.Nop);
6821 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6823 throw new NotImplementedException ();
6827 sealed class ArrayForeach : IteratorStatement
6829 TemporaryVariableReference[] lengths;
6830 Expression [] length_exprs;
6831 StatementExpression[] counter;
6832 TemporaryVariableReference[] variables;
6834 TemporaryVariableReference copy;
6836 public ArrayForeach (Foreach @foreach, int rank)
6839 counter = new StatementExpression[rank];
6840 variables = new TemporaryVariableReference[rank];
6841 length_exprs = new Expression [rank];
6844 // Only use temporary length variables when dealing with
6845 // multi-dimensional arrays
6848 lengths = new TemporaryVariableReference [rank];
6851 public override bool Resolve (BlockContext ec)
6853 Block variables_block = for_each.variable.Block;
6854 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
6857 int rank = length_exprs.Length;
6858 Arguments list = new Arguments (rank);
6859 for (int i = 0; i < rank; i++) {
6860 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6862 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
6863 counter[i].Resolve (ec);
6866 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
6868 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
6869 lengths[i].Resolve (ec);
6871 Arguments args = new Arguments (1);
6872 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
6873 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
6876 list.Add (new Argument (v));
6879 var access = new ElementAccess (copy, list, loc).Resolve (ec);
6884 if (for_each.type is VarExpr) {
6885 // Infer implicitly typed local variable from foreach array type
6886 var_type = access.Type;
6888 var_type = for_each.type.ResolveAsType (ec);
6890 if (var_type == null)
6893 access = Convert.ExplicitConversion (ec, access, var_type, loc);
6898 for_each.variable.Type = var_type;
6900 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
6901 if (variable_ref == null)
6904 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
6906 return for_each.body.Resolve (ec);
6909 protected override void DoEmit (EmitContext ec)
6911 copy.EmitAssign (ec, for_each.expr);
6913 int rank = length_exprs.Length;
6914 Label[] test = new Label [rank];
6915 Label[] loop = new Label [rank];
6917 for (int i = 0; i < rank; i++) {
6918 test [i] = ec.DefineLabel ();
6919 loop [i] = ec.DefineLabel ();
6921 if (lengths != null)
6922 lengths [i].EmitAssign (ec, length_exprs [i]);
6925 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
6926 for (int i = 0; i < rank; i++) {
6927 variables [i].EmitAssign (ec, zero);
6929 ec.Emit (OpCodes.Br, test [i]);
6930 ec.MarkLabel (loop [i]);
6933 for_each.body.Emit (ec);
6935 ec.MarkLabel (ec.LoopBegin);
6936 ec.Mark (for_each.expr.Location);
6938 for (int i = rank - 1; i >= 0; i--){
6939 counter [i].Emit (ec);
6941 ec.MarkLabel (test [i]);
6942 variables [i].Emit (ec);
6944 if (lengths != null)
6945 lengths [i].Emit (ec);
6947 length_exprs [i].Emit (ec);
6949 ec.Emit (OpCodes.Blt, loop [i]);
6952 ec.MarkLabel (ec.LoopEnd);
6956 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6958 class RuntimeDispose : Using.VariableDeclaration
6960 public RuntimeDispose (LocalVariable lv, Location loc)
6965 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6967 // Defered to runtime check
6970 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6972 var idt = bc.BuiltinTypes.IDisposable;
6975 // Fabricates code like
6977 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6980 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6982 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6983 dispose_variable.CreateReferenceExpression (bc, loc),
6984 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6985 loc), new NullLiteral (loc));
6987 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6989 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6990 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6992 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6993 return new If (idisaposable_test, dispose, loc);
6997 LocalVariable variable;
6999 Statement statement;
7000 ExpressionStatement init;
7001 TemporaryVariableReference enumerator_variable;
7002 bool ambiguous_getenumerator_name;
7004 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7007 this.variable = var;
7011 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7013 rc.Report.SymbolRelatedToPreviousError (enumerator);
7014 rc.Report.Error (202, loc,
7015 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7016 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7019 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7022 // Option 1: Try to match by name GetEnumerator first
7024 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7025 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7027 var mg = mexpr as MethodGroupExpr;
7029 mg.InstanceExpression = expr;
7030 Arguments args = new Arguments (0);
7031 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
7033 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7034 if (ambiguous_getenumerator_name)
7037 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7043 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7046 PredefinedMember<MethodSpec> iface_candidate = null;
7047 var ptypes = rc.Module.PredefinedTypes;
7048 var gen_ienumerable = ptypes.IEnumerableGeneric;
7049 if (!gen_ienumerable.Define ())
7050 gen_ienumerable = null;
7052 var ifaces = t.Interfaces;
7053 if (ifaces != null) {
7054 foreach (var iface in ifaces) {
7055 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7056 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7057 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7058 rc.Report.Error (1640, loc,
7059 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7060 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7065 // TODO: Cache this somehow
7066 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7067 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7072 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7073 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7078 if (iface_candidate == null) {
7079 if (expr.Type != InternalType.ErrorType) {
7080 rc.Report.Error (1579, loc,
7081 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7082 expr.Type.GetSignatureForError (), "GetEnumerator");
7088 var method = iface_candidate.Resolve (loc);
7092 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7093 mg.InstanceExpression = expr;
7097 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7099 var ms = MemberCache.FindMember (enumerator.ReturnType,
7100 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7101 BindingRestriction.InstanceOnly) as MethodSpec;
7103 if (ms == null || !ms.IsPublic) {
7104 Error_WrongEnumerator (rc, enumerator);
7108 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7111 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7113 var ps = MemberCache.FindMember (enumerator.ReturnType,
7114 MemberFilter.Property ("Current", null),
7115 BindingRestriction.InstanceOnly) as PropertySpec;
7117 if (ps == null || !ps.IsPublic) {
7118 Error_WrongEnumerator (rc, enumerator);
7125 public override bool Resolve (BlockContext ec)
7127 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7130 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7131 } else if (expr.Type.IsNullableType) {
7132 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7135 var get_enumerator_mg = ResolveGetEnumerator (ec);
7136 if (get_enumerator_mg == null) {
7140 var get_enumerator = get_enumerator_mg.BestCandidate;
7141 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7142 enumerator_variable.Resolve (ec);
7144 // Prepare bool MoveNext ()
7145 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7146 if (move_next_mg == null) {
7150 move_next_mg.InstanceExpression = enumerator_variable;
7152 // Prepare ~T~ Current { get; }
7153 var current_prop = ResolveCurrent (ec, get_enumerator);
7154 if (current_prop == null) {
7158 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7159 if (current_pe == null)
7162 VarExpr ve = for_each.type as VarExpr;
7166 // Source type is dynamic, set element type to dynamic too
7167 variable.Type = ec.BuiltinTypes.Dynamic;
7169 // Infer implicitly typed local variable from foreach enumerable type
7170 variable.Type = current_pe.Type;
7174 // Explicit cast of dynamic collection elements has to be done at runtime
7175 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7178 variable.Type = for_each.type.ResolveAsType (ec);
7180 if (variable.Type == null)
7183 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7184 if (current_pe == null)
7188 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7189 if (variable_ref == null)
7192 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7194 var init = new Invocation (get_enumerator_mg, null);
7196 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7197 for_each.body, Location.Null);
7199 var enum_type = enumerator_variable.Type;
7202 // Add Dispose method call when enumerator can be IDisposable
7204 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7205 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7207 // Runtime Dispose check
7209 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7210 vd.Initializer = init;
7211 statement = new Using (vd, statement, Location.Null);
7214 // No Dispose call needed
7216 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7217 this.init.Resolve (ec);
7221 // Static Dispose check
7223 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
7224 vd.Initializer = init;
7225 statement = new Using (vd, statement, Location.Null);
7228 return statement.Resolve (ec);
7231 protected override void DoEmit (EmitContext ec)
7233 enumerator_variable.LocalInfo.CreateBuilder (ec);
7236 init.EmitStatement (ec);
7238 statement.Emit (ec);
7241 #region IErrorHandler Members
7243 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
7245 ec.Report.SymbolRelatedToPreviousError (best);
7246 ec.Report.Warning (278, 2, expr.Location,
7247 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
7248 expr.Type.GetSignatureForError (), "enumerable",
7249 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
7251 ambiguous_getenumerator_name = true;
7255 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
7260 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
7265 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
7274 LocalVariable variable;
7278 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
7282 this.variable = var;
7288 public Expression Expr {
7289 get { return expr; }
7292 public Expression TypeExpression {
7293 get { return type; }
7296 public LocalVariable Variable {
7297 get { return variable; }
7300 public override Reachability MarkReachable (Reachability rc)
7302 base.MarkReachable (rc);
7304 body.MarkReachable (rc);
7309 public override bool Resolve (BlockContext ec)
7311 expr = expr.Resolve (ec);
7316 ec.Report.Error (186, loc, "Use of null is not valid in this context");
7320 body.AddStatement (Statement);
7322 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
7323 Statement = new ArrayForeach (this, 1);
7324 } else if (expr.Type is ArrayContainer) {
7325 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
7327 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
7328 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
7329 expr.ExprClassName);
7333 Statement = new CollectionForeach (this, variable, expr);
7340 protected override void DoEmit (EmitContext ec)
7342 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
7343 ec.LoopBegin = ec.DefineLabel ();
7344 ec.LoopEnd = ec.DefineLabel ();
7346 if (!(Statement is Block))
7347 ec.BeginCompilerScope ();
7349 variable.CreateBuilder (ec);
7351 Statement.Emit (ec);
7353 if (!(Statement is Block))
7356 ec.LoopBegin = old_begin;
7357 ec.LoopEnd = old_end;
7360 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7362 expr.FlowAnalysis (fc);
7364 var da = fc.BranchDefiniteAssignment ();
7365 body.FlowAnalysis (fc);
7366 fc.DefiniteAssignment = da;
7370 protected override void CloneTo (CloneContext clonectx, Statement t)
7372 Foreach target = (Foreach) t;
7374 target.type = type.Clone (clonectx);
7375 target.expr = expr.Clone (clonectx);
7376 target.body = (Block) body.Clone (clonectx);
7377 target.Statement = Statement.Clone (clonectx);
7380 public override object Accept (StructuralVisitor visitor)
7382 return visitor.Visit (this);