2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
11 // Copyright 2011 Xamarin Inc.
15 using System.Collections.Generic;
18 using IKVM.Reflection.Emit;
20 using System.Reflection.Emit;
23 namespace Mono.CSharp {
25 public abstract class Statement {
27 protected bool reachable;
29 public bool IsUnreachable {
36 /// Resolves the statement, true means that all sub-statements
39 public virtual bool Resolve (BlockContext bc)
45 /// Return value indicates whether all code paths emitted return.
47 protected abstract void DoEmit (EmitContext ec);
49 public virtual void Emit (EmitContext ec)
54 if (ec.StatementEpilogue != null) {
60 // This routine must be overrided in derived classes and make copies
61 // of all the data that might be modified if resolved
63 protected abstract void CloneTo (CloneContext clonectx, Statement target);
65 public Statement Clone (CloneContext clonectx)
67 Statement s = (Statement) this.MemberwiseClone ();
68 CloneTo (clonectx, s);
72 public virtual Expression CreateExpressionTree (ResolveContext ec)
74 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
78 public virtual object Accept (StructuralVisitor visitor)
80 return visitor.Visit (this);
84 // Return value indicates whether statement has unreachable end
86 protected abstract bool DoFlowAnalysis (FlowAnalysisContext fc);
88 public bool FlowAnalysis (FlowAnalysisContext fc)
91 fc.UnreachableReported = false;
92 var res = DoFlowAnalysis (fc);
93 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
98 // Special handling cases
101 return DoFlowAnalysis (fc);
104 if (this is EmptyStatement || loc.IsNull)
107 if (fc.UnreachableReported)
110 fc.Report.Warning (162, 2, loc, "Unreachable code detected");
111 fc.UnreachableReported = true;
115 public virtual Reachability MarkReachable (Reachability rc)
117 if (!rc.IsUnreachable)
123 protected void CheckExitBoundaries (BlockContext bc, Block scope)
125 if (bc.CurrentBlock.ParametersBlock.Original != scope.ParametersBlock.Original) {
126 bc.Report.Error (1632, loc, "Control cannot leave the body of an anonymous method");
130 for (var b = bc.CurrentBlock; b != null && b != scope; b = b.Parent) {
131 if (b.IsFinallyBlock) {
132 Error_FinallyClauseExit (bc);
138 protected void Error_FinallyClauseExit (BlockContext bc)
140 bc.Report.Error (157, loc, "Control cannot leave the body of a finally clause");
144 public sealed class EmptyStatement : Statement
146 public EmptyStatement (Location loc)
151 public override bool Resolve (BlockContext ec)
156 public override void Emit (EmitContext ec)
160 protected override void DoEmit (EmitContext ec)
162 throw new NotSupportedException ();
165 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
170 protected override void CloneTo (CloneContext clonectx, Statement target)
175 public override object Accept (StructuralVisitor visitor)
177 return visitor.Visit (this);
181 public class If : Statement {
183 public Statement TrueStatement;
184 public Statement FalseStatement;
186 bool true_returns, false_returns;
188 public If (Expression bool_expr, Statement true_statement, Location l)
189 : this (bool_expr, true_statement, null, l)
193 public If (Expression bool_expr,
194 Statement true_statement,
195 Statement false_statement,
198 this.expr = bool_expr;
199 TrueStatement = true_statement;
200 FalseStatement = false_statement;
204 public Expression Expr {
210 public override bool Resolve (BlockContext ec)
212 expr = expr.Resolve (ec);
214 var ok = TrueStatement.Resolve (ec);
216 if (FalseStatement != null) {
217 ok &= FalseStatement.Resolve (ec);
223 protected override void DoEmit (EmitContext ec)
225 Label false_target = ec.DefineLabel ();
229 // If we're a boolean constant, Resolve() already
230 // eliminated dead code for us.
232 Constant c = expr as Constant;
234 c.EmitSideEffect (ec);
236 if (!c.IsDefaultValue)
237 TrueStatement.Emit (ec);
238 else if (FalseStatement != null)
239 FalseStatement.Emit (ec);
244 expr.EmitBranchable (ec, false_target, false);
246 TrueStatement.Emit (ec);
248 if (FalseStatement != null){
249 bool branch_emitted = false;
251 end = ec.DefineLabel ();
253 ec.Emit (OpCodes.Br, end);
254 branch_emitted = true;
257 ec.MarkLabel (false_target);
258 FalseStatement.Emit (ec);
263 ec.MarkLabel (false_target);
267 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
269 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
271 expr.FlowAnalysis (fc);
273 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
275 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
276 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
278 var res = TrueStatement.FlowAnalysis (fc);
280 if (FalseStatement == null) {
282 fc.DefiniteAssignment = da_false;
284 fc.DefiniteAssignment &= da_false;
290 fc.DefiniteAssignment = da_false;
291 return FalseStatement.FlowAnalysis (fc);
294 var da_true = fc.DefiniteAssignment;
296 fc.DefiniteAssignment = da_false;
297 res &= FalseStatement.FlowAnalysis (fc);
299 if (!TrueStatement.IsUnreachable) {
300 if (false_returns || FalseStatement.IsUnreachable)
301 fc.DefiniteAssignment = da_true;
303 fc.DefiniteAssignment &= da_true;
309 public override Reachability MarkReachable (Reachability rc)
311 if (rc.IsUnreachable)
314 base.MarkReachable (rc);
316 var c = expr as Constant;
318 bool take = !c.IsDefaultValue;
320 rc = TrueStatement.MarkReachable (rc);
322 if (FalseStatement != null)
323 rc = FalseStatement.MarkReachable (rc);
329 var true_rc = TrueStatement.MarkReachable (rc);
330 true_returns = true_rc.IsUnreachable;
332 if (FalseStatement == null)
335 var false_rc = FalseStatement.MarkReachable (rc);
336 false_returns = false_rc.IsUnreachable;
338 return true_rc & false_rc;
341 protected override void CloneTo (CloneContext clonectx, Statement t)
345 target.expr = expr.Clone (clonectx);
346 target.TrueStatement = TrueStatement.Clone (clonectx);
347 if (FalseStatement != null)
348 target.FalseStatement = FalseStatement.Clone (clonectx);
351 public override object Accept (StructuralVisitor visitor)
353 return visitor.Visit (this);
357 public class Do : LoopStatement
359 public Expression expr;
360 bool iterator_reachable, end_reachable;
362 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
367 WhileLocation = whileLocation;
370 public Location WhileLocation {
374 public override bool Resolve (BlockContext bc)
376 var ok = base.Resolve (bc);
378 expr = expr.Resolve (bc);
383 protected override void DoEmit (EmitContext ec)
385 Label loop = ec.DefineLabel ();
386 Label old_begin = ec.LoopBegin;
387 Label old_end = ec.LoopEnd;
389 ec.LoopBegin = ec.DefineLabel ();
390 ec.LoopEnd = ec.DefineLabel ();
394 ec.MarkLabel (ec.LoopBegin);
396 // Mark start of while condition
397 ec.Mark (WhileLocation);
400 // Dead code elimination
402 if (expr is Constant) {
403 bool res = !((Constant) expr).IsDefaultValue;
405 expr.EmitSideEffect (ec);
407 ec.Emit (OpCodes.Br, loop);
409 expr.EmitBranchable (ec, loop, true);
412 ec.MarkLabel (ec.LoopEnd);
414 ec.LoopBegin = old_begin;
415 ec.LoopEnd = old_end;
418 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
420 var res = Statement.FlowAnalysis (fc);
422 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
423 expr.FlowAnalysis (fc);
425 fc.DefiniteAssignment = fc.DefiniteAssignmentOnFalse;
427 if (res && !iterator_reachable)
428 return !end_reachable;
430 if (!end_reachable) {
431 var c = expr as Constant;
432 if (c != null && !c.IsDefaultValue)
439 public override Reachability MarkReachable (Reachability rc)
441 base.MarkReachable (rc);
443 var body_rc = Statement.MarkReachable (rc);
445 if (body_rc.IsUnreachable && !iterator_reachable) {
446 expr = new UnreachableExpression (expr);
447 return end_reachable ? rc : Reachability.CreateUnreachable ();
450 if (!end_reachable) {
451 var c = expr as Constant;
452 if (c != null && !c.IsDefaultValue)
453 return Reachability.CreateUnreachable ();
459 protected override void CloneTo (CloneContext clonectx, Statement t)
463 target.Statement = Statement.Clone (clonectx);
464 target.expr = expr.Clone (clonectx);
467 public override object Accept (StructuralVisitor visitor)
469 return visitor.Visit (this);
472 public override void SetEndReachable ()
474 end_reachable = true;
477 public override void SetIteratorReachable ()
479 iterator_reachable = true;
483 public class While : LoopStatement
485 public Expression expr;
486 bool empty, infinite, end_reachable;
487 List<DefiniteAssignmentBitSet> end_reachable_das;
489 public While (BooleanExpression bool_expr, Statement statement, Location l)
492 this.expr = bool_expr;
496 public override bool Resolve (BlockContext bc)
500 expr = expr.Resolve (bc);
504 var c = expr as Constant;
506 empty = c.IsDefaultValue;
510 ok &= base.Resolve (bc);
514 protected override void DoEmit (EmitContext ec)
517 expr.EmitSideEffect (ec);
521 Label old_begin = ec.LoopBegin;
522 Label old_end = ec.LoopEnd;
524 ec.LoopBegin = ec.DefineLabel ();
525 ec.LoopEnd = ec.DefineLabel ();
528 // Inform whether we are infinite or not
530 if (expr is Constant) {
531 // expr is 'true', since the 'empty' case above handles the 'false' case
532 ec.MarkLabel (ec.LoopBegin);
534 if (ec.EmitAccurateDebugInfo)
535 ec.Emit (OpCodes.Nop);
537 expr.EmitSideEffect (ec);
539 ec.Emit (OpCodes.Br, ec.LoopBegin);
542 // Inform that we are infinite (ie, `we return'), only
543 // if we do not `break' inside the code.
545 ec.MarkLabel (ec.LoopEnd);
547 Label while_loop = ec.DefineLabel ();
549 ec.Emit (OpCodes.Br, ec.LoopBegin);
550 ec.MarkLabel (while_loop);
554 ec.MarkLabel (ec.LoopBegin);
557 expr.EmitBranchable (ec, while_loop, true);
559 ec.MarkLabel (ec.LoopEnd);
562 ec.LoopBegin = old_begin;
563 ec.LoopEnd = old_end;
566 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
568 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
570 expr.FlowAnalysis (fc);
572 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
573 var da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
574 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
576 Statement.FlowAnalysis (fc);
579 // Special case infinite while with breaks
581 if (end_reachable_das != null) {
582 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
583 end_reachable_das = null;
586 fc.DefiniteAssignment = da_false;
588 if (infinite && !end_reachable)
594 public override Reachability MarkReachable (Reachability rc)
596 if (rc.IsUnreachable)
599 base.MarkReachable (rc);
602 // Special case unreachable while body
605 Statement.MarkReachable (Reachability.CreateUnreachable ());
609 Statement.MarkReachable (rc);
612 // When infinite while end is unreachable via break anything what follows is unreachable too
614 if (infinite && !end_reachable)
615 return Reachability.CreateUnreachable ();
620 protected override void CloneTo (CloneContext clonectx, Statement t)
622 While target = (While) t;
624 target.expr = expr.Clone (clonectx);
625 target.Statement = Statement.Clone (clonectx);
628 public override object Accept (StructuralVisitor visitor)
630 return visitor.Visit (this);
633 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
638 if (end_reachable_das == null)
639 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
641 end_reachable_das.Add (fc.DefiniteAssignment);
644 public override void SetEndReachable ()
646 end_reachable = true;
650 public class For : LoopStatement
652 bool infinite, empty, iterator_reachable, end_reachable;
653 List<DefiniteAssignmentBitSet> end_reachable_das;
655 public For (Location l)
661 public Statement Initializer {
665 public Expression Condition {
669 public Statement Iterator {
673 public override bool Resolve (BlockContext bc)
675 Initializer.Resolve (bc);
677 if (Condition != null) {
678 Condition = Condition.Resolve (bc);
679 var condition_constant = Condition as Constant;
680 if (condition_constant != null) {
681 if (condition_constant.IsDefaultValue) {
691 return base.Resolve (bc) && Iterator.Resolve (bc);
694 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
696 Initializer.FlowAnalysis (fc);
698 DefiniteAssignmentBitSet da_false;
699 if (Condition != null) {
700 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
702 Condition.FlowAnalysis (fc);
703 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
704 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
705 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
707 da_false = fc.BranchDefiniteAssignment ();
710 Statement.FlowAnalysis (fc);
712 Iterator.FlowAnalysis (fc);
715 // Special case infinite for with breaks
717 if (end_reachable_das != null) {
718 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
719 end_reachable_das = null;
722 fc.DefiniteAssignment = da_false;
724 if (infinite && !end_reachable)
730 public override Reachability MarkReachable (Reachability rc)
732 base.MarkReachable (rc);
734 Initializer.MarkReachable (rc);
736 var body_rc = Statement.MarkReachable (rc);
737 if (!body_rc.IsUnreachable || iterator_reachable) {
738 Iterator.MarkReachable (rc);
742 // When infinite for end is unreachable via break anything what follows is unreachable too
744 if (infinite && !end_reachable) {
745 return Reachability.CreateUnreachable ();
751 protected override void DoEmit (EmitContext ec)
753 if (Initializer != null)
754 Initializer.Emit (ec);
757 Condition.EmitSideEffect (ec);
761 Label old_begin = ec.LoopBegin;
762 Label old_end = ec.LoopEnd;
763 Label loop = ec.DefineLabel ();
764 Label test = ec.DefineLabel ();
766 ec.LoopBegin = ec.DefineLabel ();
767 ec.LoopEnd = ec.DefineLabel ();
769 ec.Emit (OpCodes.Br, test);
773 ec.MarkLabel (ec.LoopBegin);
778 // If test is null, there is no test, and we are just
781 if (Condition != null) {
782 ec.Mark (Condition.Location);
785 // The Resolve code already catches the case for
786 // Test == Constant (false) so we know that
789 if (Condition is Constant) {
790 Condition.EmitSideEffect (ec);
791 ec.Emit (OpCodes.Br, loop);
793 Condition.EmitBranchable (ec, loop, true);
797 ec.Emit (OpCodes.Br, loop);
798 ec.MarkLabel (ec.LoopEnd);
800 ec.LoopBegin = old_begin;
801 ec.LoopEnd = old_end;
804 protected override void CloneTo (CloneContext clonectx, Statement t)
806 For target = (For) t;
808 if (Initializer != null)
809 target.Initializer = Initializer.Clone (clonectx);
810 if (Condition != null)
811 target.Condition = Condition.Clone (clonectx);
812 if (Iterator != null)
813 target.Iterator = Iterator.Clone (clonectx);
814 target.Statement = Statement.Clone (clonectx);
817 public override object Accept (StructuralVisitor visitor)
819 return visitor.Visit (this);
822 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
827 if (end_reachable_das == null)
828 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
830 end_reachable_das.Add (fc.DefiniteAssignment);
833 public override void SetEndReachable ()
835 end_reachable = true;
838 public override void SetIteratorReachable ()
840 iterator_reachable = true;
844 public abstract class LoopStatement : Statement
846 protected LoopStatement (Statement statement)
848 Statement = statement;
851 public Statement Statement { get; set; }
853 public override bool Resolve (BlockContext bc)
855 var prev_loop = bc.EnclosingLoop;
856 var prev_los = bc.EnclosingLoopOrSwitch;
857 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
858 var ok = Statement.Resolve (bc);
859 bc.EnclosingLoopOrSwitch = prev_los;
860 bc.EnclosingLoop = prev_loop;
866 // Needed by possibly infinite loops statements (for, while) and switch statment
868 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
872 public virtual void SetEndReachable ()
876 public virtual void SetIteratorReachable ()
881 public class StatementExpression : Statement
883 ExpressionStatement expr;
885 public StatementExpression (ExpressionStatement expr)
888 loc = expr.StartLocation;
891 public StatementExpression (ExpressionStatement expr, Location loc)
897 public ExpressionStatement Expr {
903 protected override void CloneTo (CloneContext clonectx, Statement t)
905 StatementExpression target = (StatementExpression) t;
906 target.expr = (ExpressionStatement) expr.Clone (clonectx);
909 protected override void DoEmit (EmitContext ec)
911 expr.EmitStatement (ec);
914 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
916 expr.FlowAnalysis (fc);
920 public override Reachability MarkReachable (Reachability rc)
922 base.MarkReachable (rc);
923 expr.MarkReachable (rc);
927 public override bool Resolve (BlockContext ec)
929 expr = expr.ResolveStatement (ec);
933 public override object Accept (StructuralVisitor visitor)
935 return visitor.Visit (this);
939 public class StatementErrorExpression : Statement
943 public StatementErrorExpression (Expression expr)
946 this.loc = expr.StartLocation;
949 public Expression Expr {
955 public override bool Resolve (BlockContext bc)
957 expr.Error_InvalidExpressionStatement (bc);
961 protected override void DoEmit (EmitContext ec)
963 throw new NotSupportedException ();
966 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
971 protected override void CloneTo (CloneContext clonectx, Statement target)
973 var t = (StatementErrorExpression) target;
975 t.expr = expr.Clone (clonectx);
978 public override object Accept (StructuralVisitor visitor)
980 return visitor.Visit (this);
985 // Simple version of statement list not requiring a block
987 public class StatementList : Statement
989 List<Statement> statements;
991 public StatementList (Statement first, Statement second)
993 statements = new List<Statement> { first, second };
997 public IList<Statement> Statements {
1004 public void Add (Statement statement)
1006 statements.Add (statement);
1009 public override bool Resolve (BlockContext ec)
1011 foreach (var s in statements)
1017 protected override void DoEmit (EmitContext ec)
1019 foreach (var s in statements)
1023 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1025 foreach (var s in statements)
1026 s.FlowAnalysis (fc);
1031 public override Reachability MarkReachable (Reachability rc)
1033 base.MarkReachable (rc);
1035 Reachability res = rc;
1036 foreach (var s in statements)
1037 res = s.MarkReachable (rc);
1042 protected override void CloneTo (CloneContext clonectx, Statement target)
1044 StatementList t = (StatementList) target;
1046 t.statements = new List<Statement> (statements.Count);
1047 foreach (Statement s in statements)
1048 t.statements.Add (s.Clone (clonectx));
1051 public override object Accept (StructuralVisitor visitor)
1053 return visitor.Visit (this);
1058 // For statements which require special handling when inside try or catch block
1060 public abstract class ExitStatement : Statement
1062 protected bool unwind_protect;
1064 protected abstract bool DoResolve (BlockContext bc);
1065 protected abstract bool IsLocalExit { get; }
1067 public override bool Resolve (BlockContext bc)
1069 var res = DoResolve (bc);
1073 // We are inside finally scope but is it the scope we are exiting
1075 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1077 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1078 if (b.IsFinallyBlock) {
1079 Error_FinallyClauseExit (bc);
1083 if (b is ParametersBlock)
1089 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1093 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1098 if (fc.TryFinally != null) {
1099 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1101 fc.ParametersBlock.CheckControlExit (fc);
1109 /// Implements the return statement
1111 public class Return : ExitStatement
1115 public Return (Expression expr, Location l)
1123 public Expression Expr {
1132 protected override bool IsLocalExit {
1140 protected override bool DoResolve (BlockContext ec)
1142 var block_return_type = ec.ReturnType;
1145 if (block_return_type.Kind == MemberKind.Void)
1149 // Return must not be followed by an expression when
1150 // the method return type is Task
1152 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1153 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1154 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1156 // Extra trick not to emit ret/leave inside awaiter body
1158 expr = EmptyExpression.Null;
1162 if (storey.ReturnType.IsGenericTask)
1163 block_return_type = storey.ReturnType.TypeArguments[0];
1166 if (ec.CurrentIterator != null) {
1167 Error_ReturnFromIterator (ec);
1168 } else if (block_return_type != InternalType.ErrorType) {
1169 ec.Report.Error (126, loc,
1170 "An object of a type convertible to `{0}' is required for the return statement",
1171 block_return_type.GetSignatureForError ());
1177 expr = expr.Resolve (ec);
1179 AnonymousExpression am = ec.CurrentAnonymousMethod;
1181 if (block_return_type.Kind == MemberKind.Void) {
1182 ec.Report.Error (127, loc,
1183 "`{0}': A return keyword must not be followed by any expression when method returns void",
1184 ec.GetSignatureForError ());
1189 if (am.IsIterator) {
1190 Error_ReturnFromIterator (ec);
1194 var async_block = am as AsyncInitializer;
1195 if (async_block != null) {
1197 var storey = (AsyncTaskStorey) am.Storey;
1198 var async_type = storey.ReturnType;
1200 if (async_type == null && async_block.ReturnTypeInference != null) {
1201 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1205 if (async_type.Kind == MemberKind.Void) {
1206 ec.Report.Error (127, loc,
1207 "`{0}': A return keyword must not be followed by any expression when method returns void",
1208 ec.GetSignatureForError ());
1213 if (!async_type.IsGenericTask) {
1214 if (this is ContextualReturn)
1217 // Same error code as .NET but better error message
1218 if (async_block.DelegateType != null) {
1219 ec.Report.Error (1997, loc,
1220 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
1221 async_block.DelegateType.GetSignatureForError ());
1223 ec.Report.Error (1997, loc,
1224 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1225 ec.GetSignatureForError ());
1233 // The return type is actually Task<T> type argument
1235 if (expr.Type == async_type) {
1236 ec.Report.Error (4016, loc,
1237 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1238 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1240 block_return_type = async_type.TypeArguments[0];
1244 // Same error code as .NET but better error message
1245 if (block_return_type.Kind == MemberKind.Void) {
1246 ec.Report.Error (127, loc,
1247 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
1248 am.GetSignatureForError ());
1253 var l = am as AnonymousMethodBody;
1254 if (l != null && expr != null) {
1255 if (l.ReturnTypeInference != null) {
1256 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1261 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1262 // unexpected debugging experience
1264 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1265 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1274 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1275 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1278 if (am != null && block_return_type == ec.ReturnType) {
1279 ec.Report.Error (1662, loc,
1280 "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",
1281 am.ContainerType, am.GetSignatureForError ());
1290 protected override void DoEmit (EmitContext ec)
1295 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1296 if (async_body != null) {
1297 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
1299 // It's null for await without async
1300 if (async_return != null) {
1301 async_return.EmitAssign (ec);
1306 ec.Emit (OpCodes.Leave, async_body.BodyEnd);
1312 if (unwind_protect || ec.EmitAccurateDebugInfo)
1313 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1316 if (unwind_protect) {
1317 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1318 } else if (ec.EmitAccurateDebugInfo) {
1319 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1321 ec.Emit (OpCodes.Ret);
1325 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1328 expr.FlowAnalysis (fc);
1330 base.DoFlowAnalysis (fc);
1334 void Error_ReturnFromIterator (ResolveContext rc)
1336 rc.Report.Error (1622, loc,
1337 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1340 public override Reachability MarkReachable (Reachability rc)
1342 base.MarkReachable (rc);
1343 return Reachability.CreateUnreachable ();
1346 protected override void CloneTo (CloneContext clonectx, Statement t)
1348 Return target = (Return) t;
1349 // It's null for simple return;
1351 target.expr = expr.Clone (clonectx);
1354 public override object Accept (StructuralVisitor visitor)
1356 return visitor.Visit (this);
1360 public class Goto : ExitStatement
1363 LabeledStatement label;
1364 TryFinally try_finally;
1366 public Goto (string label, Location l)
1372 public string Target {
1373 get { return target; }
1376 protected override bool IsLocalExit {
1382 protected override bool DoResolve (BlockContext bc)
1384 label = bc.CurrentBlock.LookupLabel (target);
1385 if (label == null) {
1386 Error_UnknownLabel (bc, target, loc);
1390 try_finally = bc.CurrentTryBlock as TryFinally;
1392 CheckExitBoundaries (bc, label.Block);
1397 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1399 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1403 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1405 if (fc.LabelStack == null) {
1406 fc.LabelStack = new List<LabeledStatement> ();
1407 } else if (fc.LabelStack.Contains (label)) {
1411 fc.LabelStack.Add (label);
1412 label.Block.ScanGotoJump (label, fc);
1413 fc.LabelStack.Remove (label);
1417 public override Reachability MarkReachable (Reachability rc)
1419 if (rc.IsUnreachable)
1422 base.MarkReachable (rc);
1424 if (try_finally != null) {
1425 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1426 label.AddGotoReference (rc, false);
1428 label.AddGotoReference (rc, true);
1433 label.AddGotoReference (rc, false);
1436 return Reachability.CreateUnreachable ();
1439 protected override void CloneTo (CloneContext clonectx, Statement target)
1444 protected override void DoEmit (EmitContext ec)
1447 throw new InternalErrorException ("goto emitted before target resolved");
1449 Label l = label.LabelTarget (ec);
1450 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1453 public override object Accept (StructuralVisitor visitor)
1455 return visitor.Visit (this);
1459 public class LabeledStatement : Statement {
1467 public LabeledStatement (string name, Block block, Location l)
1474 public Label LabelTarget (EmitContext ec)
1479 label = ec.DefineLabel ();
1484 public Block Block {
1490 public string Name {
1491 get { return name; }
1494 protected override void CloneTo (CloneContext clonectx, Statement target)
1496 var t = (LabeledStatement) target;
1498 t.block = clonectx.RemapBlockCopy (block);
1501 public override bool Resolve (BlockContext bc)
1506 protected override void DoEmit (EmitContext ec)
1509 ec.MarkLabel (label);
1512 ec.Emit (OpCodes.Br_S, label);
1515 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1518 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1524 public override Reachability MarkReachable (Reachability rc)
1526 base.MarkReachable (rc);
1529 rc = new Reachability ();
1534 public void AddGotoReference (Reachability rc, bool finalTarget)
1543 // Label is final target when goto jumps out of try block with
1544 // finally clause. In that case we need leave with target but in C#
1545 // terms the label is unreachable. Using finalTarget we emit
1546 // explicit label not just marker
1549 this.finalTarget = true;
1553 block.ScanGotoJump (this);
1556 public override object Accept (StructuralVisitor visitor)
1558 return visitor.Visit (this);
1564 /// `goto default' statement
1566 public class GotoDefault : SwitchGoto
1568 public GotoDefault (Location l)
1573 public override bool Resolve (BlockContext bc)
1575 if (bc.Switch == null) {
1576 Error_GotoCaseRequiresSwitchBlock (bc);
1580 bc.Switch.RegisterGotoCase (null, null);
1586 protected override void DoEmit (EmitContext ec)
1588 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1591 public override Reachability MarkReachable (Reachability rc)
1593 if (!rc.IsUnreachable) {
1594 var label = switch_statement.DefaultLabel;
1595 if (label.IsUnreachable) {
1596 label.MarkReachable (rc);
1597 switch_statement.Block.ScanGotoJump (label);
1601 return base.MarkReachable (rc);
1604 public override object Accept (StructuralVisitor visitor)
1606 return visitor.Visit (this);
1611 /// `goto case' statement
1613 public class GotoCase : SwitchGoto
1617 public GotoCase (Expression e, Location l)
1623 public Expression Expr {
1629 public SwitchLabel Label { get; set; }
1631 public override bool Resolve (BlockContext ec)
1633 if (ec.Switch == null) {
1634 Error_GotoCaseRequiresSwitchBlock (ec);
1638 Constant c = expr.ResolveLabelConstant (ec);
1644 if (ec.Switch.IsNullable && c is NullLiteral) {
1647 TypeSpec type = ec.Switch.SwitchType;
1648 res = c.Reduce (ec, type);
1650 c.Error_ValueCannotBeConverted (ec, type, true);
1654 if (!Convert.ImplicitStandardConversionExists (c, type))
1655 ec.Report.Warning (469, 2, loc,
1656 "The `goto case' value is not implicitly convertible to type `{0}'",
1657 type.GetSignatureForError ());
1661 ec.Switch.RegisterGotoCase (this, res);
1668 protected override void DoEmit (EmitContext ec)
1670 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1673 protected override void CloneTo (CloneContext clonectx, Statement t)
1675 GotoCase target = (GotoCase) t;
1677 target.expr = expr.Clone (clonectx);
1680 public override Reachability MarkReachable (Reachability rc)
1682 if (!rc.IsUnreachable) {
1683 var label = switch_statement.FindLabel ((Constant) expr);
1684 if (label.IsUnreachable) {
1685 label.MarkReachable (rc);
1686 switch_statement.Block.ScanGotoJump (label);
1690 return base.MarkReachable (rc);
1693 public override object Accept (StructuralVisitor visitor)
1695 return visitor.Visit (this);
1699 public abstract class SwitchGoto : Statement
1701 protected bool unwind_protect;
1702 protected Switch switch_statement;
1704 protected SwitchGoto (Location loc)
1709 protected override void CloneTo (CloneContext clonectx, Statement target)
1714 public override bool Resolve (BlockContext bc)
1716 CheckExitBoundaries (bc, bc.Switch.Block);
1718 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1719 switch_statement = bc.Switch;
1724 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1729 public override Reachability MarkReachable (Reachability rc)
1731 base.MarkReachable (rc);
1732 return Reachability.CreateUnreachable ();
1735 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1737 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1741 public class Throw : Statement {
1744 public Throw (Expression expr, Location l)
1750 public Expression Expr {
1756 public override bool Resolve (BlockContext ec)
1759 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1760 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1761 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1762 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1763 if (b.IsFinallyBlock) {
1764 ec.Report.Error (724, loc,
1765 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1774 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1779 var et = ec.BuiltinTypes.Exception;
1780 if (Convert.ImplicitConversionExists (ec, expr, et))
1781 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1783 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1788 protected override void DoEmit (EmitContext ec)
1791 ec.Emit (OpCodes.Rethrow);
1795 ec.Emit (OpCodes.Throw);
1799 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1802 expr.FlowAnalysis (fc);
1807 public override Reachability MarkReachable (Reachability rc)
1809 base.MarkReachable (rc);
1810 return Reachability.CreateUnreachable ();
1813 protected override void CloneTo (CloneContext clonectx, Statement t)
1815 Throw target = (Throw) t;
1818 target.expr = expr.Clone (clonectx);
1821 public override object Accept (StructuralVisitor visitor)
1823 return visitor.Visit (this);
1827 public class Break : LocalExitStatement
1829 public Break (Location l)
1834 public override object Accept (StructuralVisitor visitor)
1836 return visitor.Visit (this);
1839 protected override void DoEmit (EmitContext ec)
1841 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1844 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1846 enclosing_loop.AddEndDefiniteAssignment (fc);
1850 protected override bool DoResolve (BlockContext bc)
1852 enclosing_loop = bc.EnclosingLoopOrSwitch;
1853 return base.DoResolve (bc);
1856 public override Reachability MarkReachable (Reachability rc)
1858 base.MarkReachable (rc);
1860 if (!rc.IsUnreachable)
1861 enclosing_loop.SetEndReachable ();
1863 return Reachability.CreateUnreachable ();
1867 public class Continue : LocalExitStatement
1869 public Continue (Location l)
1874 public override object Accept (StructuralVisitor visitor)
1876 return visitor.Visit (this);
1880 protected override void DoEmit (EmitContext ec)
1882 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1885 protected override bool DoResolve (BlockContext bc)
1887 enclosing_loop = bc.EnclosingLoop;
1888 return base.DoResolve (bc);
1891 public override Reachability MarkReachable (Reachability rc)
1893 base.MarkReachable (rc);
1895 if (!rc.IsUnreachable)
1896 enclosing_loop.SetIteratorReachable ();
1898 return Reachability.CreateUnreachable ();
1902 public abstract class LocalExitStatement : ExitStatement
1904 protected LoopStatement enclosing_loop;
1906 protected LocalExitStatement (Location loc)
1911 protected override bool IsLocalExit {
1917 protected override void CloneTo (CloneContext clonectx, Statement t)
1922 protected override bool DoResolve (BlockContext bc)
1924 if (enclosing_loop == null) {
1925 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1929 var block = enclosing_loop.Statement as Block;
1931 // Don't need to do extra checks for simple statements loops
1932 if (block != null) {
1933 CheckExitBoundaries (bc, block);
1940 public interface ILocalVariable
1942 void Emit (EmitContext ec);
1943 void EmitAssign (EmitContext ec);
1944 void EmitAddressOf (EmitContext ec);
1947 public interface INamedBlockVariable
1949 Block Block { get; }
1950 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1951 bool IsDeclared { get; }
1952 bool IsParameter { get; }
1953 Location Location { get; }
1956 public class BlockVariableDeclarator
1959 Expression initializer;
1961 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1963 if (li.Type != null)
1964 throw new ArgumentException ("Expected null variable type");
1967 this.initializer = initializer;
1972 public LocalVariable Variable {
1978 public Expression Initializer {
1983 initializer = value;
1989 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1991 var t = (BlockVariableDeclarator) MemberwiseClone ();
1992 if (initializer != null)
1993 t.initializer = initializer.Clone (cloneCtx);
1999 public class BlockVariable : Statement
2001 Expression initializer;
2002 protected FullNamedExpression type_expr;
2003 protected LocalVariable li;
2004 protected List<BlockVariableDeclarator> declarators;
2007 public BlockVariable (FullNamedExpression type, LocalVariable li)
2009 this.type_expr = type;
2011 this.loc = type_expr.Location;
2014 protected BlockVariable (LocalVariable li)
2021 public List<BlockVariableDeclarator> Declarators {
2027 public Expression Initializer {
2032 initializer = value;
2036 public FullNamedExpression TypeExpression {
2042 public LocalVariable Variable {
2050 public void AddDeclarator (BlockVariableDeclarator decl)
2052 if (declarators == null)
2053 declarators = new List<BlockVariableDeclarator> ();
2055 declarators.Add (decl);
2058 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2060 if (bc.Report.Errors != 0)
2063 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2065 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2066 new MemberName (li.Name, li.Location), null);
2068 container.AddField (f);
2071 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2075 public override bool Resolve (BlockContext bc)
2077 return Resolve (bc, true);
2080 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2082 if (type == null && !li.IsCompilerGenerated) {
2083 var vexpr = type_expr as VarExpr;
2086 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2087 // same name exists or as a keyword when no type was found
2089 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2090 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2091 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2094 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2098 if (li.IsConstant) {
2099 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2103 if (Initializer == null) {
2104 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2108 if (declarators != null) {
2109 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2113 Initializer = Initializer.Resolve (bc);
2114 if (Initializer != null) {
2115 ((VarExpr) type_expr).InferType (bc, Initializer);
2116 type = type_expr.Type;
2118 // Set error type to indicate the var was placed correctly but could
2121 // var a = missing ();
2123 type = InternalType.ErrorType;
2128 type = type_expr.ResolveAsType (bc);
2132 if (li.IsConstant && !type.IsConstantCompatible) {
2133 Const.Error_InvalidConstantType (type, loc, bc.Report);
2138 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2143 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2145 CreateEvaluatorVariable (bc, li);
2146 } else if (type != InternalType.ErrorType) {
2147 li.PrepareAssignmentAnalysis (bc);
2150 if (initializer != null) {
2151 initializer = ResolveInitializer (bc, li, initializer);
2152 // li.Variable.DefinitelyAssigned
2155 if (declarators != null) {
2156 foreach (var d in declarators) {
2157 d.Variable.Type = li.Type;
2159 CreateEvaluatorVariable (bc, d.Variable);
2160 } else if (type != InternalType.ErrorType) {
2161 d.Variable.PrepareAssignmentAnalysis (bc);
2164 if (d.Initializer != null && resolveDeclaratorInitializers) {
2165 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2166 // d.Variable.DefinitelyAssigned
2174 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2176 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2177 return a.ResolveStatement (bc);
2180 protected override void DoEmit (EmitContext ec)
2182 li.CreateBuilder (ec);
2184 if (Initializer != null)
2185 ((ExpressionStatement) Initializer).EmitStatement (ec);
2187 if (declarators != null) {
2188 foreach (var d in declarators) {
2189 d.Variable.CreateBuilder (ec);
2190 if (d.Initializer != null) {
2191 ec.Mark (d.Variable.Location);
2192 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2198 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2200 if (Initializer != null)
2201 Initializer.FlowAnalysis (fc);
2203 if (declarators != null) {
2204 foreach (var d in declarators) {
2205 if (d.Initializer != null)
2206 d.Initializer.FlowAnalysis (fc);
2213 public override Reachability MarkReachable (Reachability rc)
2215 var init = initializer as ExpressionStatement;
2217 init.MarkReachable (rc);
2219 return base.MarkReachable (rc);
2222 protected override void CloneTo (CloneContext clonectx, Statement target)
2224 BlockVariable t = (BlockVariable) target;
2226 if (type_expr != null)
2227 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2229 if (initializer != null)
2230 t.initializer = initializer.Clone (clonectx);
2232 if (declarators != null) {
2233 t.declarators = null;
2234 foreach (var d in declarators)
2235 t.AddDeclarator (d.Clone (clonectx));
2239 public override object Accept (StructuralVisitor visitor)
2241 return visitor.Visit (this);
2245 public class BlockConstant : BlockVariable
2247 public BlockConstant (FullNamedExpression type, LocalVariable li)
2252 public override void Emit (EmitContext ec)
2254 // Nothing to emit, not even sequence point
2257 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2259 initializer = initializer.Resolve (bc);
2260 if (initializer == null)
2263 var c = initializer as Constant;
2265 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2269 c = c.ConvertImplicitly (li.Type);
2271 if (TypeSpec.IsReferenceType (li.Type))
2272 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2274 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2279 li.ConstantValue = c;
2283 public override object Accept (StructuralVisitor visitor)
2285 return visitor.Visit (this);
2290 // The information about a user-perceived local variable
2292 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2299 AddressTaken = 1 << 2,
2300 CompilerGenerated = 1 << 3,
2302 ForeachVariable = 1 << 5,
2303 FixedVariable = 1 << 6,
2304 UsingVariable = 1 << 7,
2307 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2311 readonly string name;
2312 readonly Location loc;
2313 readonly Block block;
2315 Constant const_value;
2317 public VariableInfo VariableInfo;
2318 HoistedVariable hoisted_variant;
2320 LocalBuilder builder;
2322 public LocalVariable (Block block, string name, Location loc)
2329 public LocalVariable (Block block, string name, Flags flags, Location loc)
2330 : this (block, name, loc)
2336 // Used by variable declarators
2338 public LocalVariable (LocalVariable li, string name, Location loc)
2339 : this (li.block, name, li.flags, loc)
2345 public bool AddressTaken {
2347 return (flags & Flags.AddressTaken) != 0;
2351 public Block Block {
2357 public Constant ConstantValue {
2362 const_value = value;
2367 // Hoisted local variable variant
2369 public HoistedVariable HoistedVariant {
2371 return hoisted_variant;
2374 hoisted_variant = value;
2378 public bool IsDeclared {
2380 return type != null;
2384 public bool IsCompilerGenerated {
2386 return (flags & Flags.CompilerGenerated) != 0;
2390 public bool IsConstant {
2392 return (flags & Flags.Constant) != 0;
2396 public bool IsLocked {
2398 return (flags & Flags.IsLocked) != 0;
2401 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2405 public bool IsThis {
2407 return (flags & Flags.IsThis) != 0;
2411 public bool IsFixed {
2413 return (flags & Flags.FixedVariable) != 0;
2417 bool INamedBlockVariable.IsParameter {
2423 public bool IsReadonly {
2425 return (flags & Flags.ReadonlyMask) != 0;
2429 public Location Location {
2435 public string Name {
2441 public TypeSpec Type {
2452 public void CreateBuilder (EmitContext ec)
2454 if ((flags & Flags.Used) == 0) {
2455 if (VariableInfo == null) {
2456 // Missing flow analysis or wrong variable flags
2457 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2460 if (VariableInfo.IsEverAssigned)
2461 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2463 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2466 if (HoistedVariant != null)
2469 if (builder != null) {
2470 if ((flags & Flags.CompilerGenerated) != 0)
2473 // To avoid Used warning duplicates
2474 throw new InternalErrorException ("Already created variable `{0}'", name);
2478 // All fixed variabled are pinned, a slot has to be alocated
2480 builder = ec.DeclareLocal (Type, IsFixed);
2481 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2482 ec.DefineLocalVariable (name, builder);
2485 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2487 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2492 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2494 if (IsConstant && const_value != null)
2495 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2497 return new LocalVariableReference (this, loc);
2500 public void Emit (EmitContext ec)
2502 // TODO: Need something better for temporary variables
2503 if ((flags & Flags.CompilerGenerated) != 0)
2506 ec.Emit (OpCodes.Ldloc, builder);
2509 public void EmitAssign (EmitContext ec)
2511 // TODO: Need something better for temporary variables
2512 if ((flags & Flags.CompilerGenerated) != 0)
2515 ec.Emit (OpCodes.Stloc, builder);
2518 public void EmitAddressOf (EmitContext ec)
2520 ec.Emit (OpCodes.Ldloca, builder);
2523 public static string GetCompilerGeneratedName (Block block)
2525 // HACK: Debugger depends on the name semantics
2526 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2529 public string GetReadOnlyContext ()
2531 switch (flags & Flags.ReadonlyMask) {
2532 case Flags.FixedVariable:
2533 return "fixed variable";
2534 case Flags.ForeachVariable:
2535 return "foreach iteration variable";
2536 case Flags.UsingVariable:
2537 return "using variable";
2540 throw new InternalErrorException ("Variable is not readonly");
2543 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2545 if (VariableInfo == null)
2546 throw new Exception ();
2548 if (IsAssigned (fc))
2551 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2554 public bool IsAssigned (FlowAnalysisContext fc)
2556 return fc.IsDefinitelyAssigned (VariableInfo);
2559 public void PrepareAssignmentAnalysis (BlockContext bc)
2562 // No need to run assignment analysis for these guys
2564 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2567 VariableInfo = VariableInfo.Create (bc, this);
2571 // Mark the variables as referenced in the user code
2573 public void SetIsUsed ()
2575 flags |= Flags.Used;
2578 public void SetHasAddressTaken ()
2580 flags |= (Flags.AddressTaken | Flags.Used);
2583 public override string ToString ()
2585 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2590 /// Block represents a C# block.
2594 /// This class is used in a number of places: either to represent
2595 /// explicit blocks that the programmer places or implicit blocks.
2597 /// Implicit blocks are used as labels or to introduce variable
2600 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2601 /// they contain extra information that is not necessary on normal blocks.
2603 public class Block : Statement {
2610 HasCapturedVariable = 64,
2611 HasCapturedThis = 1 << 7,
2612 IsExpressionTree = 1 << 8,
2613 CompilerGenerated = 1 << 9,
2614 HasAsyncModifier = 1 << 10,
2616 YieldBlock = 1 << 12,
2617 AwaitBlock = 1 << 13,
2618 FinallyBlock = 1 << 14,
2619 CatchBlock = 1 << 15,
2621 NoFlowAnalysis = 1 << 21
2624 public Block Parent;
2625 public Location StartLocation;
2626 public Location EndLocation;
2628 public ExplicitBlock Explicit;
2629 public ParametersBlock ParametersBlock;
2631 protected Flags flags;
2634 // The statements in this block
2636 protected List<Statement> statements;
2638 protected List<Statement> scope_initializers;
2640 int? resolving_init_idx;
2646 public int ID = id++;
2648 static int clone_id_counter;
2652 // int assignable_slots;
2654 public Block (Block parent, Location start, Location end)
2655 : this (parent, 0, start, end)
2659 public Block (Block parent, Flags flags, Location start, Location end)
2661 if (parent != null) {
2662 // the appropriate constructors will fixup these fields
2663 ParametersBlock = parent.ParametersBlock;
2664 Explicit = parent.Explicit;
2667 this.Parent = parent;
2669 this.StartLocation = start;
2670 this.EndLocation = end;
2672 statements = new List<Statement> (4);
2674 this.original = this;
2679 public Block Original {
2688 public bool IsCompilerGenerated {
2689 get { return (flags & Flags.CompilerGenerated) != 0; }
2690 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2694 public bool IsCatchBlock {
2696 return (flags & Flags.CatchBlock) != 0;
2700 public bool IsFinallyBlock {
2702 return (flags & Flags.FinallyBlock) != 0;
2706 public bool Unchecked {
2707 get { return (flags & Flags.Unchecked) != 0; }
2708 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2711 public bool Unsafe {
2712 get { return (flags & Flags.Unsafe) != 0; }
2713 set { flags |= Flags.Unsafe; }
2716 public List<Statement> Statements {
2717 get { return statements; }
2722 public void SetEndLocation (Location loc)
2727 public void AddLabel (LabeledStatement target)
2729 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2732 public void AddLocalName (LocalVariable li)
2734 AddLocalName (li.Name, li);
2737 public void AddLocalName (string name, INamedBlockVariable li)
2739 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2742 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2744 if (reason == null) {
2745 Error_AlreadyDeclared (name, variable);
2749 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2750 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2751 "to `{0}', which is already used in a `{1}' scope to denote something else",
2755 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2757 var pi = variable as ParametersBlock.ParameterInfo;
2759 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2761 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2762 "A local variable named `{0}' is already defined in this scope", name);
2766 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2768 ParametersBlock.TopBlock.Report.Error (412, loc,
2769 "The type parameter name `{0}' is the same as local variable or parameter name",
2774 // It should be used by expressions which require to
2775 // register a statement during resolve process.
2777 public void AddScopeStatement (Statement s)
2779 if (scope_initializers == null)
2780 scope_initializers = new List<Statement> ();
2783 // Simple recursive helper, when resolve scope initializer another
2784 // new scope initializer can be added, this ensures it's initialized
2785 // before existing one. For now this can happen with expression trees
2786 // in base ctor initializer only
2788 if (resolving_init_idx.HasValue) {
2789 scope_initializers.Insert (resolving_init_idx.Value, s);
2790 ++resolving_init_idx;
2792 scope_initializers.Add (s);
2796 public void InsertStatement (int index, Statement s)
2798 statements.Insert (index, s);
2801 public void AddStatement (Statement s)
2806 public LabeledStatement LookupLabel (string name)
2808 return ParametersBlock.GetLabel (name, this);
2811 public override Reachability MarkReachable (Reachability rc)
2813 if (rc.IsUnreachable)
2816 MarkReachableScope (rc);
2818 foreach (var s in statements) {
2819 rc = s.MarkReachable (rc);
2820 if (rc.IsUnreachable) {
2821 if ((flags & Flags.ReachableEnd) != 0)
2822 return new Reachability ();
2828 flags |= Flags.ReachableEnd;
2833 public void MarkReachableScope (Reachability rc)
2835 base.MarkReachable (rc);
2837 if (scope_initializers != null) {
2838 foreach (var si in scope_initializers)
2839 si.MarkReachable (rc);
2843 public override bool Resolve (BlockContext bc)
2845 if ((flags & Flags.Resolved) != 0)
2848 Block prev_block = bc.CurrentBlock;
2849 bc.CurrentBlock = this;
2852 // Compiler generated scope statements
2854 if (scope_initializers != null) {
2855 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2856 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2859 resolving_init_idx = null;
2863 int statement_count = statements.Count;
2864 for (int ix = 0; ix < statement_count; ix++){
2865 Statement s = statements [ix];
2867 if (!s.Resolve (bc)) {
2869 if (!bc.IsInProbingMode)
2870 statements [ix] = new EmptyStatement (s.loc);
2876 bc.CurrentBlock = prev_block;
2878 flags |= Flags.Resolved;
2882 protected override void DoEmit (EmitContext ec)
2884 for (int ix = 0; ix < statements.Count; ix++){
2885 statements [ix].Emit (ec);
2889 public override void Emit (EmitContext ec)
2891 if (scope_initializers != null)
2892 EmitScopeInitializers (ec);
2897 protected void EmitScopeInitializers (EmitContext ec)
2899 foreach (Statement s in scope_initializers)
2903 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2905 if (scope_initializers != null) {
2906 foreach (var si in scope_initializers)
2907 si.FlowAnalysis (fc);
2910 return DoFlowAnalysis (fc, 0);
2913 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2915 bool end_unreachable = !reachable;
2916 for (; startIndex < statements.Count; ++startIndex) {
2917 var s = statements[startIndex];
2919 end_unreachable = s.FlowAnalysis (fc);
2920 if (s.IsUnreachable) {
2921 statements[startIndex] = new EmptyStatement (s.loc);
2926 // Statement end reachability is needed mostly due to goto support. Consider
2935 // X label is reachable only via goto not as another statement after if. We need
2936 // this for flow-analysis only to carry variable info correctly.
2938 if (end_unreachable) {
2939 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2940 s = statements[startIndex];
2941 if (s is SwitchLabel) {
2942 s.FlowAnalysis (fc);
2946 if (s.IsUnreachable) {
2947 s.FlowAnalysis (fc);
2948 statements[startIndex] = new EmptyStatement (s.loc);
2955 // The condition should be true unless there is forward jumping goto
2957 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
2960 return !Explicit.HasReachableClosingBrace;
2963 public void ScanGotoJump (Statement label)
2966 for (i = 0; i < statements.Count; ++i) {
2967 if (statements[i] == label)
2971 var rc = new Reachability ();
2972 for (++i; i < statements.Count; ++i) {
2973 var s = statements[i];
2974 rc = s.MarkReachable (rc);
2975 if (rc.IsUnreachable)
2979 flags |= Flags.ReachableEnd;
2982 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
2985 for (i = 0; i < statements.Count; ++i) {
2986 if (statements[i] == label)
2990 DoFlowAnalysis (fc, ++i);
2994 public override string ToString ()
2996 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3000 protected override void CloneTo (CloneContext clonectx, Statement t)
3002 Block target = (Block) t;
3004 target.clone_id = ++clone_id_counter;
3007 clonectx.AddBlockMap (this, target);
3008 if (original != this)
3009 clonectx.AddBlockMap (original, target);
3011 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3012 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3015 target.Parent = clonectx.RemapBlockCopy (Parent);
3017 target.statements = new List<Statement> (statements.Count);
3018 foreach (Statement s in statements)
3019 target.statements.Add (s.Clone (clonectx));
3022 public override object Accept (StructuralVisitor visitor)
3024 return visitor.Visit (this);
3028 public class ExplicitBlock : Block
3030 protected AnonymousMethodStorey am_storey;
3032 public ExplicitBlock (Block parent, Location start, Location end)
3033 : this (parent, (Flags) 0, start, end)
3037 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3038 : base (parent, flags, start, end)
3040 this.Explicit = this;
3045 public AnonymousMethodStorey AnonymousMethodStorey {
3051 public bool HasAwait {
3053 return (flags & Flags.AwaitBlock) != 0;
3057 public bool HasCapturedThis {
3059 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3062 return (flags & Flags.HasCapturedThis) != 0;
3067 // Used to indicate that the block has reference to parent
3068 // block and cannot be made static when defining anonymous method
3070 public bool HasCapturedVariable {
3072 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3075 return (flags & Flags.HasCapturedVariable) != 0;
3079 public bool HasReachableClosingBrace {
3081 return (flags & Flags.ReachableEnd) != 0;
3084 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3088 public bool HasYield {
3090 return (flags & Flags.YieldBlock) != 0;
3097 // Creates anonymous method storey in current block
3099 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3102 // Return same story for iterator and async blocks unless we are
3103 // in nested anonymous method
3105 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3106 return ec.CurrentAnonymousMethod.Storey;
3108 if (am_storey == null) {
3109 MemberBase mc = ec.MemberContext as MemberBase;
3112 // Creates anonymous method storey for this block
3114 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3120 public override void Emit (EmitContext ec)
3122 if (am_storey != null) {
3123 DefineStoreyContainer (ec, am_storey);
3124 am_storey.EmitStoreyInstantiation (ec, this);
3127 if (scope_initializers != null)
3128 EmitScopeInitializers (ec);
3130 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3131 ec.Emit (OpCodes.Nop);
3142 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3143 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3144 ec.Emit (OpCodes.Nop);
3148 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3150 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3151 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3152 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3156 // Creates anonymous method storey
3158 storey.CreateContainer ();
3159 storey.DefineContainer ();
3161 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3164 // Only first storey in path will hold this reference. All children blocks will
3165 // reference it indirectly using $ref field
3167 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3168 if (b.Parent != null) {
3169 var s = b.Parent.Explicit.AnonymousMethodStorey;
3171 storey.HoistedThis = s.HoistedThis;
3176 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3177 if (storey.HoistedThis == null)
3178 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3180 if (storey.HoistedThis != null)
3186 // We are the first storey on path and 'this' has to be hoisted
3188 if (storey.HoistedThis == null) {
3189 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3191 // ThisReferencesFromChildrenBlock holds all reference even if they
3192 // are not on this path. It saves some memory otherwise it'd have to
3193 // be in every explicit block. We run this check to see if the reference
3194 // is valid for this storey
3196 Block block_on_path = ref_block;
3197 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3199 if (block_on_path == null)
3202 if (storey.HoistedThis == null) {
3203 storey.AddCapturedThisField (ec, null);
3206 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3208 AnonymousMethodStorey b_storey = b.AnonymousMethodStorey;
3210 if (b_storey != null) {
3212 // Don't add storey cross reference for `this' when the storey ends up not
3213 // beeing attached to any parent
3215 if (b.ParametersBlock.StateMachine == null) {
3216 AnonymousMethodStorey s = null;
3217 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3218 s = ab.Explicit.AnonymousMethodStorey;
3223 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3225 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3226 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3233 // Stop propagation inside same top block
3235 if (b.ParametersBlock == ParametersBlock.Original) {
3236 b_storey.AddParentStoreyReference (ec, storey);
3237 // b_storey.HoistedThis = storey.HoistedThis;
3241 b = pb = b.ParametersBlock;
3243 pb = b as ParametersBlock;
3246 if (pb != null && pb.StateMachine != null) {
3247 if (pb.StateMachine == storey)
3251 // If we are state machine with no parent. We can hook into parent without additional
3252 // reference and capture this directly
3254 ExplicitBlock parent_storey_block = pb;
3255 while (parent_storey_block.Parent != null) {
3256 parent_storey_block = parent_storey_block.Parent.Explicit;
3257 if (parent_storey_block.AnonymousMethodStorey != null) {
3262 if (parent_storey_block.AnonymousMethodStorey == null) {
3263 pb.StateMachine.AddCapturedThisField (ec, null);
3264 b.HasCapturedThis = true;
3268 pb.StateMachine.AddParentStoreyReference (ec, storey);
3272 // Add parent storey reference only when this is not captured directly
3274 if (b_storey != null) {
3275 b_storey.AddParentStoreyReference (ec, storey);
3276 b_storey.HoistedThis = storey.HoistedThis;
3283 var ref_blocks = storey.ReferencesFromChildrenBlock;
3284 if (ref_blocks != null) {
3285 foreach (ExplicitBlock ref_block in ref_blocks) {
3286 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3287 if (b.AnonymousMethodStorey != null) {
3288 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3291 // Stop propagation inside same top block
3293 if (b.ParametersBlock == ParametersBlock.Original)
3296 b = b.ParametersBlock;
3299 var pb = b as ParametersBlock;
3300 if (pb != null && pb.StateMachine != null) {
3301 if (pb.StateMachine == storey)
3304 pb.StateMachine.AddParentStoreyReference (ec, storey);
3307 b.HasCapturedVariable = true;
3313 storey.PrepareEmit ();
3314 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3317 public void RegisterAsyncAwait ()
3320 while ((block.flags & Flags.AwaitBlock) == 0) {
3321 block.flags |= Flags.AwaitBlock;
3323 if (block is ParametersBlock)
3326 block = block.Parent.Explicit;
3330 public void RegisterIteratorYield ()
3332 ParametersBlock.TopBlock.IsIterator = true;
3335 while ((block.flags & Flags.YieldBlock) == 0) {
3336 block.flags |= Flags.YieldBlock;
3338 if (block.Parent == null)
3341 block = block.Parent.Explicit;
3345 public void SetCatchBlock ()
3347 flags |= Flags.CatchBlock;
3350 public void SetFinallyBlock ()
3352 flags |= Flags.FinallyBlock;
3355 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3357 tryBlock.statements = statements;
3358 statements = new List<Statement> (1);
3359 statements.Add (tf);
3364 // ParametersBlock was introduced to support anonymous methods
3365 // and lambda expressions
3367 public class ParametersBlock : ExplicitBlock
3369 public class ParameterInfo : INamedBlockVariable
3371 readonly ParametersBlock block;
3373 public VariableInfo VariableInfo;
3376 public ParameterInfo (ParametersBlock block, int index)
3384 public ParametersBlock Block {
3390 Block INamedBlockVariable.Block {
3396 public bool IsDeclared {
3402 public bool IsParameter {
3408 public bool IsLocked {
3417 public Location Location {
3419 return Parameter.Location;
3423 public Parameter Parameter {
3425 return block.Parameters [index];
3429 public TypeSpec ParameterType {
3431 return Parameter.Type;
3437 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3439 return new ParameterReference (this, loc);
3444 // Block is converted into an expression
3446 sealed class BlockScopeExpression : Expression
3449 readonly ParametersBlock block;
3451 public BlockScopeExpression (Expression child, ParametersBlock block)
3457 public override bool ContainsEmitWithAwait ()
3459 return child.ContainsEmitWithAwait ();
3462 public override Expression CreateExpressionTree (ResolveContext ec)
3464 throw new NotSupportedException ();
3467 protected override Expression DoResolve (ResolveContext ec)
3472 child = child.Resolve (ec);
3476 eclass = child.eclass;
3481 public override void Emit (EmitContext ec)
3483 block.EmitScopeInitializers (ec);
3488 protected ParametersCompiled parameters;
3489 protected ParameterInfo[] parameter_info;
3490 protected bool resolved;
3491 protected ToplevelBlock top_block;
3492 protected StateMachine state_machine;
3493 protected Dictionary<string, object> labels;
3495 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3496 : base (parent, 0, start, start)
3498 if (parameters == null)
3499 throw new ArgumentNullException ("parameters");
3501 this.parameters = parameters;
3502 ParametersBlock = this;
3504 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3506 this.top_block = parent.ParametersBlock.top_block;
3507 ProcessParameters ();
3510 protected ParametersBlock (ParametersCompiled parameters, Location start)
3511 : base (null, 0, start, start)
3513 if (parameters == null)
3514 throw new ArgumentNullException ("parameters");
3516 this.parameters = parameters;
3517 ParametersBlock = this;
3521 // It's supposed to be used by method body implementation of anonymous methods
3523 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3524 : base (null, 0, source.StartLocation, source.EndLocation)
3526 this.parameters = parameters;
3527 this.statements = source.statements;
3528 this.scope_initializers = source.scope_initializers;
3530 this.resolved = true;
3531 this.reachable = source.reachable;
3532 this.am_storey = source.am_storey;
3533 this.state_machine = source.state_machine;
3534 this.flags = source.flags & Flags.ReachableEnd;
3536 ParametersBlock = this;
3539 // Overwrite original for comparison purposes when linking cross references
3540 // between anonymous methods
3542 Original = source.Original;
3547 public bool IsAsync {
3549 return (flags & Flags.HasAsyncModifier) != 0;
3552 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3557 // Block has been converted to expression tree
3559 public bool IsExpressionTree {
3561 return (flags & Flags.IsExpressionTree) != 0;
3566 // The parameters for the block.
3568 public ParametersCompiled Parameters {
3574 public StateMachine StateMachine {
3576 return state_machine;
3580 public ToplevelBlock TopBlock {
3586 public bool Resolved {
3588 return (flags & Flags.Resolved) != 0;
3592 public int TemporaryLocalsCount { get; set; }
3597 // Checks whether all `out' parameters have been assigned.
3599 public void CheckControlExit (FlowAnalysisContext fc)
3601 CheckControlExit (fc, fc.DefiniteAssignment);
3604 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3606 if (parameter_info == null)
3609 foreach (var p in parameter_info) {
3610 if (p.VariableInfo == null)
3613 if (p.VariableInfo.IsAssigned (dat))
3616 fc.Report.Error (177, p.Location,
3617 "The out parameter `{0}' must be assigned to before control leaves the current method",
3622 protected override void CloneTo (CloneContext clonectx, Statement t)
3624 base.CloneTo (clonectx, t);
3626 var target = (ParametersBlock) t;
3629 // Clone label statements as well as they contain block reference
3633 if (pb.labels != null) {
3634 target.labels = new Dictionary<string, object> ();
3636 foreach (var entry in pb.labels) {
3637 var list = entry.Value as List<LabeledStatement>;
3640 var list_clone = new List<LabeledStatement> ();
3641 foreach (var lentry in list) {
3642 list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block)));
3645 target.labels.Add (entry.Key, list_clone);
3647 var labeled = (LabeledStatement) entry.Value;
3648 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block)));
3655 if (pb.Parent == null)
3658 pb = pb.Parent.ParametersBlock;
3662 public override Expression CreateExpressionTree (ResolveContext ec)
3664 if (statements.Count == 1) {
3665 Expression expr = statements[0].CreateExpressionTree (ec);
3666 if (scope_initializers != null)
3667 expr = new BlockScopeExpression (expr, this);
3672 return base.CreateExpressionTree (ec);
3675 public override void Emit (EmitContext ec)
3677 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3678 DefineStoreyContainer (ec, state_machine);
3679 state_machine.EmitStoreyInstantiation (ec, this);
3685 public void EmitEmbedded (EmitContext ec)
3687 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3688 DefineStoreyContainer (ec, state_machine);
3689 state_machine.EmitStoreyInstantiation (ec, this);
3695 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3697 var res = base.DoFlowAnalysis (fc);
3699 if (HasReachableClosingBrace)
3700 CheckControlExit (fc);
3705 public LabeledStatement GetLabel (string name, Block block)
3708 // Cloned parameters blocks can have their own cloned version of top-level labels
3710 if (labels == null) {
3712 return Parent.ParametersBlock.GetLabel (name, block);
3718 if (!labels.TryGetValue (name, out value)) {
3722 var label = value as LabeledStatement;
3724 if (label != null) {
3725 if (IsLabelVisible (label, b))
3729 List<LabeledStatement> list = (List<LabeledStatement>) value;
3730 for (int i = 0; i < list.Count; ++i) {
3732 if (IsLabelVisible (label, b))
3740 static bool IsLabelVisible (LabeledStatement label, Block b)
3743 if (label.Block == b)
3746 } while (b != null);
3751 public ParameterInfo GetParameterInfo (Parameter p)
3753 for (int i = 0; i < parameters.Count; ++i) {
3754 if (parameters[i] == p)
3755 return parameter_info[i];
3758 throw new ArgumentException ("Invalid parameter");
3761 public ParameterReference GetParameterReference (int index, Location loc)
3763 return new ParameterReference (parameter_info[index], loc);
3766 public Statement PerformClone ()
3768 CloneContext clonectx = new CloneContext ();
3769 return Clone (clonectx);
3772 protected void ProcessParameters ()
3774 if (parameters.Count == 0)
3777 parameter_info = new ParameterInfo[parameters.Count];
3778 for (int i = 0; i < parameter_info.Length; ++i) {
3779 var p = parameters.FixedParameters[i];
3783 // TODO: Should use Parameter only and more block there
3784 parameter_info[i] = new ParameterInfo (this, i);
3786 AddLocalName (p.Name, parameter_info[i]);
3790 static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst)
3792 var src_stmts = src.Statements;
3793 for (int i = 0; i < src_stmts.Count; ++i) {
3794 if (src_stmts[i] == stmt)
3795 return (LabeledStatement) dst.Statements[i];
3798 throw new InternalErrorException ("Should never be reached");
3801 public override bool Resolve (BlockContext bc)
3803 // TODO: if ((flags & Flags.Resolved) != 0)
3810 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3811 flags |= Flags.IsExpressionTree;
3814 PrepareAssignmentAnalysis (bc);
3816 if (!base.Resolve (bc))
3819 } catch (Exception e) {
3820 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3823 if (bc.CurrentBlock != null) {
3824 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3826 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3831 // If an asynchronous body of F is either an expression classified as nothing, or a
3832 // statement block where no return statements have expressions, the inferred return type is Task
3835 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3836 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3837 am.ReturnTypeInference = null;
3838 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3846 void PrepareAssignmentAnalysis (BlockContext bc)
3848 for (int i = 0; i < parameters.Count; ++i) {
3849 var par = parameters.FixedParameters[i];
3851 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3854 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3858 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3860 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3861 var stateMachine = new IteratorStorey (iterator);
3863 state_machine = stateMachine;
3864 iterator.SetStateMachine (stateMachine);
3866 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3867 tlb.Original = this;
3868 tlb.state_machine = stateMachine;
3869 tlb.AddStatement (new Return (iterator, iterator.Location));
3873 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3875 for (int i = 0; i < parameters.Count; i++) {
3876 Parameter p = parameters[i];
3877 Parameter.Modifier mod = p.ModFlags;
3878 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3879 host.Compiler.Report.Error (1988, p.Location,
3880 "Async methods cannot have ref or out parameters");
3884 if (p is ArglistParameter) {
3885 host.Compiler.Report.Error (4006, p.Location,
3886 "__arglist is not allowed in parameter list of async methods");
3890 if (parameters.Types[i].IsPointer) {
3891 host.Compiler.Report.Error (4005, p.Location,
3892 "Async methods cannot have unsafe parameters");
3898 host.Compiler.Report.Warning (1998, 1, loc,
3899 "Async block lacks `await' operator and will run synchronously");
3902 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3903 var initializer = new AsyncInitializer (this, host, block_type);
3904 initializer.Type = block_type;
3905 initializer.DelegateType = delegateType;
3907 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3909 state_machine = stateMachine;
3910 initializer.SetStateMachine (stateMachine);
3912 const Flags flags = Flags.CompilerGenerated;
3914 var b = this is ToplevelBlock ?
3915 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
3916 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
3919 b.state_machine = stateMachine;
3920 b.AddStatement (new AsyncInitializerStatement (initializer));
3928 public class ToplevelBlock : ParametersBlock
3930 LocalVariable this_variable;
3931 CompilerContext compiler;
3932 Dictionary<string, object> names;
3934 List<ExplicitBlock> this_references;
3936 public ToplevelBlock (CompilerContext ctx, Location loc)
3937 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3941 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
3942 : base (parameters, start)
3944 this.compiler = ctx;
3948 ProcessParameters ();
3952 // Recreates a top level block from parameters block. Used for
3953 // compiler generated methods where the original block comes from
3954 // explicit child block. This works for already resolved blocks
3955 // only to ensure we resolve them in the correct flow order
3957 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3958 : base (source, parameters)
3960 this.compiler = source.TopBlock.compiler;
3964 public bool IsIterator {
3966 return (flags & Flags.Iterator) != 0;
3969 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3973 public Report Report {
3975 return compiler.Report;
3980 // Used by anonymous blocks to track references of `this' variable
3982 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3984 return this_references;
3989 // Returns the "this" instance variable of this block.
3990 // See AddThisVariable() for more information.
3992 public LocalVariable ThisVariable {
3994 return this_variable;
3998 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
4001 names = new Dictionary<string, object> ();
4004 if (!names.TryGetValue (name, out value)) {
4005 names.Add (name, li);
4009 INamedBlockVariable existing = value as INamedBlockVariable;
4010 List<INamedBlockVariable> existing_list;
4011 if (existing != null) {
4012 existing_list = new List<INamedBlockVariable> ();
4013 existing_list.Add (existing);
4014 names[name] = existing_list;
4016 existing_list = (List<INamedBlockVariable>) value;
4020 // A collision checking between local names
4022 var variable_block = li.Block.Explicit;
4023 for (int i = 0; i < existing_list.Count; ++i) {
4024 existing = existing_list[i];
4025 Block b = existing.Block.Explicit;
4027 // Collision at same level
4028 if (variable_block == b) {
4029 li.Block.Error_AlreadyDeclared (name, li);
4033 // Collision with parent
4034 Block parent = variable_block;
4035 while ((parent = parent.Parent) != null) {
4037 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4038 i = existing_list.Count;
4043 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4044 // Collision with children
4045 while ((b = b.Parent) != null) {
4046 if (variable_block == b) {
4047 li.Block.Error_AlreadyDeclared (name, li, "child");
4048 i = existing_list.Count;
4055 existing_list.Add (li);
4058 public void AddLabel (string name, LabeledStatement label)
4061 labels = new Dictionary<string, object> ();
4064 if (!labels.TryGetValue (name, out value)) {
4065 labels.Add (name, label);
4069 LabeledStatement existing = value as LabeledStatement;
4070 List<LabeledStatement> existing_list;
4071 if (existing != null) {
4072 existing_list = new List<LabeledStatement> ();
4073 existing_list.Add (existing);
4074 labels[name] = existing_list;
4076 existing_list = (List<LabeledStatement>) value;
4080 // A collision checking between labels
4082 for (int i = 0; i < existing_list.Count; ++i) {
4083 existing = existing_list[i];
4084 Block b = existing.Block;
4086 // Collision at same level
4087 if (label.Block == b) {
4088 Report.SymbolRelatedToPreviousError (existing.loc, name);
4089 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4093 // Collision with parent
4095 while ((b = b.Parent) != null) {
4096 if (existing.Block == b) {
4097 Report.Error (158, label.loc,
4098 "The label `{0}' shadows another label by the same name in a contained scope", name);
4099 i = existing_list.Count;
4104 // Collision with with children
4106 while ((b = b.Parent) != null) {
4107 if (label.Block == b) {
4108 Report.Error (158, label.loc,
4109 "The label `{0}' shadows another label by the same name in a contained scope", name);
4110 i = existing_list.Count;
4116 existing_list.Add (label);
4119 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4121 if (this_references == null)
4122 this_references = new List<ExplicitBlock> ();
4124 if (!this_references.Contains (block))
4125 this_references.Add (block);
4128 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4130 this_references.Remove (block);
4134 // Creates an arguments set from all parameters, useful for method proxy calls
4136 public Arguments GetAllParametersArguments ()
4138 int count = parameters.Count;
4139 Arguments args = new Arguments (count);
4140 for (int i = 0; i < count; ++i) {
4141 var pi = parameter_info[i];
4142 var arg_expr = GetParameterReference (i, pi.Location);
4144 Argument.AType atype_modifier;
4145 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4146 case Parameter.Modifier.REF:
4147 atype_modifier = Argument.AType.Ref;
4149 case Parameter.Modifier.OUT:
4150 atype_modifier = Argument.AType.Out;
4157 args.Add (new Argument (arg_expr, atype_modifier));
4164 // Lookup inside a block, the returned value can represent 3 states
4166 // true+variable: A local name was found and it's valid
4167 // false+variable: A local name was found in a child block only
4168 // false+null: No local name was found
4170 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4176 if (!names.TryGetValue (name, out value))
4179 variable = value as INamedBlockVariable;
4181 if (variable != null) {
4183 if (variable.Block == b.Original)
4187 } while (b != null);
4195 } while (b != null);
4197 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4198 for (int i = 0; i < list.Count; ++i) {
4201 if (variable.Block == b.Original)
4205 } while (b != null);
4213 } while (b != null);
4224 // This is used by non-static `struct' constructors which do not have an
4225 // initializer - in this case, the constructor must initialize all of the
4226 // struct's fields. To do this, we add a "this" variable and use the flow
4227 // analysis code to ensure that it's been fully initialized before control
4228 // leaves the constructor.
4230 public void AddThisVariable (BlockContext bc)
4232 if (this_variable != null)
4233 throw new InternalErrorException (StartLocation.ToString ());
4235 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4236 this_variable.Type = bc.CurrentType;
4237 this_variable.PrepareAssignmentAnalysis (bc);
4240 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4243 // If we're a non-static struct constructor which doesn't have an
4244 // initializer, then we must initialize all of the struct's fields.
4246 if (this_variable != null)
4247 this_variable.IsThisAssigned (fc, this);
4249 base.CheckControlExit (fc, dat);
4252 public override void Emit (EmitContext ec)
4254 if (Report.Errors > 0)
4258 if (IsCompilerGenerated) {
4259 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4267 // If `HasReturnLabel' is set, then we already emitted a
4268 // jump to the end of the method, so we must emit a `ret'
4271 // Unfortunately, System.Reflection.Emit automatically emits
4272 // a leave to the end of a finally block. This is a problem
4273 // if no code is following the try/finally block since we may
4274 // jump to a point after the end of the method.
4275 // As a workaround, we're always creating a return label in
4278 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4279 if (ec.HasReturnLabel)
4280 ec.MarkLabel (ec.ReturnLabel);
4282 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4283 ec.Mark (EndLocation);
4285 if (ec.ReturnType.Kind != MemberKind.Void)
4286 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4288 ec.Emit (OpCodes.Ret);
4291 } catch (Exception e) {
4292 throw new InternalErrorException (e, StartLocation);
4296 public bool Resolve (BlockContext bc, IMethodData md)
4301 var errors = bc.Report.Errors;
4305 if (bc.Report.Errors > errors)
4308 MarkReachable (new Reachability ());
4310 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4311 // TODO: var md = bc.CurrentMemberDefinition;
4312 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4315 if ((flags & Flags.NoFlowAnalysis) != 0)
4318 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4321 } catch (Exception e) {
4322 throw new InternalErrorException (e, StartLocation);
4329 public class SwitchLabel : Statement
4337 // if expr == null, then it is the default case.
4339 public SwitchLabel (Expression expr, Location l)
4345 public bool IsDefault {
4347 return label == null;
4351 public Expression Label {
4357 public Location Location {
4363 public Constant Converted {
4372 public bool SectionStart { get; set; }
4374 public Label GetILLabel (EmitContext ec)
4376 if (il_label == null){
4377 il_label = ec.DefineLabel ();
4380 return il_label.Value;
4383 protected override void DoEmit (EmitContext ec)
4385 ec.MarkLabel (GetILLabel (ec));
4388 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4393 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
4397 public override bool Resolve (BlockContext bc)
4399 if (ResolveAndReduce (bc))
4400 bc.Switch.RegisterLabel (bc, this);
4406 // Resolves the expression, reduces it to a literal if possible
4407 // and then converts it to the requested type.
4409 bool ResolveAndReduce (BlockContext rc)
4414 var c = label.ResolveLabelConstant (rc);
4418 if (rc.Switch.IsNullable && c is NullLiteral) {
4423 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType);
4424 return converted != null;
4427 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4429 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4430 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4433 protected override void CloneTo (CloneContext clonectx, Statement target)
4435 var t = (SwitchLabel) target;
4437 t.label = label.Clone (clonectx);
4440 public override object Accept (StructuralVisitor visitor)
4442 return visitor.Visit (this);
4445 public string GetSignatureForError ()
4448 if (converted == null)
4451 label = converted.GetValueAsLiteral ();
4453 return string.Format ("case {0}:", label);
4457 public class Switch : LoopStatement
4459 // structure used to hold blocks of keys while calculating table switch
4460 sealed class LabelsRange : IComparable<LabelsRange>
4462 public readonly long min;
4464 public readonly List<long> label_values;
4466 public LabelsRange (long value)
4469 label_values = new List<long> ();
4470 label_values.Add (value);
4473 public LabelsRange (long min, long max, ICollection<long> values)
4477 this.label_values = new List<long> (values);
4482 return max - min + 1;
4486 public bool AddValue (long value)
4488 var gap = value - min + 1;
4489 // Ensure the range has > 50% occupancy
4490 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4494 label_values.Add (value);
4498 public int CompareTo (LabelsRange other)
4500 int nLength = label_values.Count;
4501 int nLengthOther = other.label_values.Count;
4502 if (nLengthOther == nLength)
4503 return (int) (other.min - min);
4505 return nLength - nLengthOther;
4509 sealed class DispatchStatement : Statement
4511 readonly Switch body;
4513 public DispatchStatement (Switch body)
4518 protected override void CloneTo (CloneContext clonectx, Statement target)
4520 throw new NotImplementedException ();
4523 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4528 protected override void DoEmit (EmitContext ec)
4530 body.EmitDispatch (ec);
4534 class MissingBreak : Statement
4538 public MissingBreak (SwitchLabel sl)
4544 protected override void DoEmit (EmitContext ec)
4548 protected override void CloneTo (CloneContext clonectx, Statement target)
4552 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4554 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4555 label.GetSignatureForError ());
4561 public Expression Expr;
4564 // Mapping of all labels to their SwitchLabels
4566 Dictionary<long, SwitchLabel> labels;
4567 Dictionary<string, SwitchLabel> string_labels;
4568 List<SwitchLabel> case_labels;
4570 List<Tuple<GotoCase, Constant>> goto_cases;
4571 List<DefiniteAssignmentBitSet> end_reachable_das;
4574 /// The governing switch type
4576 public TypeSpec SwitchType;
4578 Expression new_expr;
4580 SwitchLabel case_null;
4581 SwitchLabel case_default;
4583 Label defaultLabel, nullLabel;
4584 VariableReference value;
4585 ExpressionStatement string_dictionary;
4586 FieldExpr switch_cache_field;
4587 ExplicitBlock block;
4591 // Nullable Types support
4593 Nullable.Unwrap unwrap;
4595 public Switch (Expression e, ExplicitBlock block, Location l)
4603 public SwitchLabel ActiveLabel { get; set; }
4605 public ExplicitBlock Block {
4611 public SwitchLabel DefaultLabel {
4613 return case_default;
4617 public bool IsNullable {
4619 return unwrap != null;
4623 public List<SwitchLabel> RegisteredLabels {
4630 // Determines the governing type for a switch. The returned
4631 // expression might be the expression from the switch, or an
4632 // expression that includes any potential conversions to
4634 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
4636 switch (expr.Type.BuiltinType) {
4637 case BuiltinTypeSpec.Type.Byte:
4638 case BuiltinTypeSpec.Type.SByte:
4639 case BuiltinTypeSpec.Type.UShort:
4640 case BuiltinTypeSpec.Type.Short:
4641 case BuiltinTypeSpec.Type.UInt:
4642 case BuiltinTypeSpec.Type.Int:
4643 case BuiltinTypeSpec.Type.ULong:
4644 case BuiltinTypeSpec.Type.Long:
4645 case BuiltinTypeSpec.Type.Char:
4646 case BuiltinTypeSpec.Type.String:
4647 case BuiltinTypeSpec.Type.Bool:
4651 if (expr.Type.IsEnum)
4655 // Try to find a *user* defined implicit conversion.
4657 // If there is no implicit conversion, or if there are multiple
4658 // conversions, we have to report an error
4660 Expression converted = null;
4661 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
4664 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
4669 // Ignore over-worked ImplicitUserConversions that do
4670 // an implicit conversion in addition to the user conversion.
4672 if (!(e is UserCast))
4675 if (converted != null){
4676 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4685 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
4687 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4702 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4704 case_labels.Add (sl);
4707 if (case_default != null) {
4708 sl.Error_AlreadyOccurs (rc, case_default);
4717 if (string_labels != null) {
4718 string string_value = sl.Converted.GetValue () as string;
4719 if (string_value == null)
4722 string_labels.Add (string_value, sl);
4724 if (sl.Converted is NullLiteral) {
4727 labels.Add (sl.Converted.GetValueAsLong (), sl);
4730 } catch (ArgumentException) {
4731 if (string_labels != null)
4732 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4734 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4739 // This method emits code for a lookup-based switch statement (non-string)
4740 // Basically it groups the cases into blocks that are at least half full,
4741 // and then spits out individual lookup opcodes for each block.
4742 // It emits the longest blocks first, and short blocks are just
4743 // handled with direct compares.
4745 void EmitTableSwitch (EmitContext ec, Expression val)
4747 if (labels != null && labels.Count > 0) {
4748 List<LabelsRange> ranges;
4749 if (string_labels != null) {
4750 // We have done all hard work for string already
4751 // setup single range only
4752 ranges = new List<LabelsRange> (1);
4753 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4755 var element_keys = new long[labels.Count];
4756 labels.Keys.CopyTo (element_keys, 0);
4757 Array.Sort (element_keys);
4760 // Build possible ranges of switch labes to reduce number
4763 ranges = new List<LabelsRange> (element_keys.Length);
4764 var range = new LabelsRange (element_keys[0]);
4766 for (int i = 1; i < element_keys.Length; ++i) {
4767 var l = element_keys[i];
4768 if (range.AddValue (l))
4771 range = new LabelsRange (l);
4775 // sort the blocks so we can tackle the largest ones first
4779 Label lbl_default = defaultLabel;
4780 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4782 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4783 LabelsRange kb = ranges[range_index];
4784 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4786 // Optimize small ranges using simple equality check
4787 if (kb.Range <= 2) {
4788 foreach (var key in kb.label_values) {
4789 SwitchLabel sl = labels[key];
4790 if (sl == case_default || sl == case_null)
4793 if (sl.Converted.IsZeroInteger) {
4794 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4797 sl.Converted.Emit (ec);
4798 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4802 // TODO: if all the keys in the block are the same and there are
4803 // no gaps/defaults then just use a range-check.
4804 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4805 // TODO: optimize constant/I4 cases
4807 // check block range (could be > 2^31)
4809 ec.EmitLong (kb.min);
4810 ec.Emit (OpCodes.Blt, lbl_default);
4813 ec.EmitLong (kb.max);
4814 ec.Emit (OpCodes.Bgt, lbl_default);
4819 ec.EmitLong (kb.min);
4820 ec.Emit (OpCodes.Sub);
4823 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4827 int first = (int) kb.min;
4830 ec.Emit (OpCodes.Sub);
4831 } else if (first < 0) {
4832 ec.EmitInt (-first);
4833 ec.Emit (OpCodes.Add);
4837 // first, build the list of labels for the switch
4839 long cJumps = kb.Range;
4840 Label[] switch_labels = new Label[cJumps];
4841 for (int iJump = 0; iJump < cJumps; iJump++) {
4842 var key = kb.label_values[iKey];
4843 if (key == kb.min + iJump) {
4844 switch_labels[iJump] = labels[key].GetILLabel (ec);
4847 switch_labels[iJump] = lbl_default;
4851 // emit the switch opcode
4852 ec.Emit (OpCodes.Switch, switch_labels);
4855 // mark the default for this block
4856 if (range_index != 0)
4857 ec.MarkLabel (lbl_default);
4860 // the last default just goes to the end
4861 if (ranges.Count > 0)
4862 ec.Emit (OpCodes.Br, lbl_default);
4866 public SwitchLabel FindLabel (Constant value)
4868 SwitchLabel sl = null;
4870 if (string_labels != null) {
4871 string s = value.GetValue () as string;
4873 if (case_null != null)
4875 else if (case_default != null)
4878 string_labels.TryGetValue (s, out sl);
4881 if (value is NullLiteral) {
4884 labels.TryGetValue (value.GetValueAsLong (), out sl);
4891 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4893 Expr.FlowAnalysis (fc);
4895 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
4896 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
4897 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
4899 block.FlowAnalysis (fc);
4901 fc.SwitchInitialDefinitiveAssignment = prev_switch;
4903 if (end_reachable_das != null) {
4904 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
4905 InitialDefinitiveAssignment |= sections_das;
4906 end_reachable_das = null;
4909 fc.DefiniteAssignment = InitialDefinitiveAssignment;
4911 return case_default != null && !end_reachable;
4914 public override bool Resolve (BlockContext ec)
4916 Expr = Expr.Resolve (ec);
4920 new_expr = SwitchGoverningType (ec, Expr);
4922 if (new_expr == null && Expr.Type.IsNullableType) {
4923 unwrap = Nullable.Unwrap.Create (Expr, false);
4927 new_expr = SwitchGoverningType (ec, unwrap);
4930 if (new_expr == null) {
4931 if (Expr.Type != InternalType.ErrorType) {
4932 ec.Report.Error (151, loc,
4933 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4934 Expr.Type.GetSignatureForError ());
4941 SwitchType = new_expr.Type;
4943 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4944 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4948 if (block.Statements.Count == 0)
4951 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4952 string_labels = new Dictionary<string, SwitchLabel> ();
4954 labels = new Dictionary<long, SwitchLabel> ();
4957 case_labels = new List<SwitchLabel> ();
4959 var constant = new_expr as Constant;
4962 // Don't need extra variable for constant switch or switch with
4963 // only default case
4965 if (constant == null) {
4967 // Store switch expression for comparison purposes
4969 value = new_expr as VariableReference;
4970 if (value == null && !HasOnlyDefaultSection ()) {
4971 var current_block = ec.CurrentBlock;
4972 ec.CurrentBlock = Block;
4973 // Create temporary variable inside switch scope
4974 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4976 ec.CurrentBlock = current_block;
4980 Switch old_switch = ec.Switch;
4982 var parent_los = ec.EnclosingLoopOrSwitch;
4983 ec.EnclosingLoopOrSwitch = this;
4985 var ok = Statement.Resolve (ec);
4987 ec.EnclosingLoopOrSwitch = parent_los;
4988 ec.Switch = old_switch;
4991 // Check if all goto cases are valid. Needs to be done after switch
4992 // is resolved because goto can jump forward in the scope.
4994 if (goto_cases != null) {
4995 foreach (var gc in goto_cases) {
4996 if (gc.Item1 == null) {
4997 if (DefaultLabel == null) {
4998 Goto.Error_UnknownLabel (ec, "default", loc);
5004 var sl = FindLabel (gc.Item2);
5006 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
5008 gc.Item1.Label = sl;
5016 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5017 ResolveStringSwitchMap (ec);
5021 // Anonymous storey initialization has to happen before
5022 // any generated switch dispatch
5024 block.InsertStatement (0, new DispatchStatement (this));
5029 bool HasOnlyDefaultSection ()
5031 for (int i = 0; i < block.Statements.Count; ++i) {
5032 var s = block.Statements[i] as SwitchLabel;
5034 if (s == null || s.IsDefault)
5043 public override Reachability MarkReachable (Reachability rc)
5045 if (rc.IsUnreachable)
5048 base.MarkReachable (rc);
5050 block.MarkReachableScope (rc);
5052 if (block.Statements.Count == 0)
5055 SwitchLabel constant_label = null;
5056 var constant = new_expr as Constant;
5058 if (constant != null) {
5059 constant_label = FindLabel (constant) ?? case_default;
5060 if (constant_label == null) {
5061 block.Statements.RemoveAt (0);
5066 var section_rc = new Reachability ();
5067 SwitchLabel prev_label = null;
5069 for (int i = 0; i < block.Statements.Count; ++i) {
5070 var s = block.Statements[i];
5071 var sl = s as SwitchLabel;
5073 if (sl != null && sl.SectionStart) {
5075 // Section is marked already via constant switch or goto case
5077 if (!sl.IsUnreachable) {
5078 section_rc = new Reachability ();
5082 if (section_rc.IsUnreachable) {
5083 section_rc = new Reachability ();
5085 if (prev_label != null) {
5086 sl.SectionStart = false;
5087 s = new MissingBreak (prev_label);
5088 s.MarkReachable (rc);
5089 block.Statements.Insert (i - 1, s);
5096 if (constant_label != null && constant_label != sl)
5097 section_rc = Reachability.CreateUnreachable ();
5100 section_rc = s.MarkReachable (section_rc);
5103 if (!section_rc.IsUnreachable && prev_label != null) {
5104 prev_label.SectionStart = false;
5105 var s = new MissingBreak (prev_label);
5106 s.MarkReachable (rc);
5107 block.Statements.Add (s);
5111 // Reachability can affect parent only when all possible paths are handled but
5112 // we still need to run reachability check on switch body to check for fall-through
5114 if (case_default == null && constant_label == null)
5118 // We have at least one local exit from the switch
5123 return Reachability.CreateUnreachable ();
5126 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5128 if (goto_cases == null)
5129 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5131 goto_cases.Add (Tuple.Create (gotoCase, value));
5135 // Converts string switch into string hashtable
5137 void ResolveStringSwitchMap (ResolveContext ec)
5139 FullNamedExpression string_dictionary_type;
5140 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5141 string_dictionary_type = new TypeExpression (
5142 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5143 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5145 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5146 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5148 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5152 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5153 Field field = new Field (ctype, string_dictionary_type,
5154 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5155 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5156 if (!field.Define ())
5158 ctype.AddField (field);
5160 var init = new List<Expression> ();
5162 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5163 string value = null;
5165 foreach (SwitchLabel sl in case_labels) {
5167 if (sl.SectionStart)
5168 labels.Add (++counter, sl);
5170 if (sl == case_default || sl == case_null)
5173 value = (string) sl.Converted.GetValue ();
5174 var init_args = new List<Expression> (2);
5175 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5177 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5178 init_args.Add (sl.Converted);
5180 init.Add (new CollectionElementInitializer (init_args, loc));
5183 Arguments args = new Arguments (1);
5184 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5185 Expression initializer = new NewInitialize (string_dictionary_type, args,
5186 new CollectionOrObjectInitializers (init, loc), loc);
5188 switch_cache_field = new FieldExpr (field, loc);
5189 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5192 void DoEmitStringSwitch (EmitContext ec)
5194 Label l_initialized = ec.DefineLabel ();
5197 // Skip initialization when value is null
5199 value.EmitBranchable (ec, nullLabel, false);
5202 // Check if string dictionary is initialized and initialize
5204 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5205 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5206 string_dictionary.EmitStatement (ec);
5208 ec.MarkLabel (l_initialized);
5210 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5212 ResolveContext rc = new ResolveContext (ec.MemberContext);
5214 if (switch_cache_field.Type.IsGeneric) {
5215 Arguments get_value_args = new Arguments (2);
5216 get_value_args.Add (new Argument (value));
5217 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5218 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5219 if (get_item == null)
5223 // A value was not found, go to default case
5225 get_item.EmitBranchable (ec, defaultLabel, false);
5227 Arguments get_value_args = new Arguments (1);
5228 get_value_args.Add (new Argument (value));
5230 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5231 if (get_item == null)
5234 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5235 get_item_object.EmitAssign (ec, get_item, true, false);
5236 ec.Emit (OpCodes.Brfalse, defaultLabel);
5238 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5239 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5241 get_item_int.EmitStatement (ec);
5242 get_item_object.Release (ec);
5245 EmitTableSwitch (ec, string_switch_variable);
5246 string_switch_variable.Release (ec);
5250 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5252 void EmitShortSwitch (EmitContext ec)
5254 MethodSpec equal_method = null;
5255 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5256 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5259 if (equal_method != null) {
5260 value.EmitBranchable (ec, nullLabel, false);
5263 for (int i = 0; i < case_labels.Count; ++i) {
5264 var label = case_labels [i];
5265 if (label == case_default || label == case_null)
5268 var constant = label.Converted;
5270 if (equal_method != null) {
5274 var call = new CallEmitter ();
5275 call.EmitPredefined (ec, equal_method, new Arguments (0));
5276 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5280 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5281 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5287 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5290 ec.Emit (OpCodes.Br, defaultLabel);
5293 void EmitDispatch (EmitContext ec)
5295 if (value == null) {
5297 // Constant switch, we've already done the work if there is only 1 label
5301 foreach (var sl in case_labels) {
5302 if (sl.IsUnreachable)
5305 if (reachable++ > 0) {
5306 var constant = (Constant) new_expr;
5307 var constant_label = FindLabel (constant) ?? case_default;
5309 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5317 if (string_dictionary != null) {
5318 DoEmitStringSwitch (ec);
5319 } else if (case_labels.Count < 4 || string_labels != null) {
5320 EmitShortSwitch (ec);
5322 EmitTableSwitch (ec, value);
5326 protected override void DoEmit (EmitContext ec)
5329 // Setup the codegen context
5331 Label old_end = ec.LoopEnd;
5332 Switch old_switch = ec.Switch;
5334 ec.LoopEnd = ec.DefineLabel ();
5337 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5338 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5340 if (value != null) {
5343 unwrap.EmitCheck (ec);
5344 ec.Emit (OpCodes.Brfalse, nullLabel);
5345 value.EmitAssign (ec, new_expr, false, false);
5346 } else if (new_expr != value) {
5347 value.EmitAssign (ec, new_expr, false, false);
5352 // Next statement is compiler generated we don't need extra
5353 // nop when we can use the statement for sequence point
5355 ec.Mark (block.StartLocation);
5356 block.IsCompilerGenerated = true;
5361 // Restore context state.
5362 ec.MarkLabel (ec.LoopEnd);
5365 // Restore the previous context
5367 ec.LoopEnd = old_end;
5368 ec.Switch = old_switch;
5371 protected override void CloneTo (CloneContext clonectx, Statement t)
5373 Switch target = (Switch) t;
5375 target.Expr = Expr.Clone (clonectx);
5376 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5379 public override object Accept (StructuralVisitor visitor)
5381 return visitor.Visit (this);
5384 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5386 if (case_default == null)
5389 if (end_reachable_das == null)
5390 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5392 end_reachable_das.Add (fc.DefiniteAssignment);
5395 public override void SetEndReachable ()
5397 end_reachable = true;
5401 // A place where execution can restart in a state machine
5402 public abstract class ResumableStatement : Statement
5405 protected Label resume_point;
5407 public Label PrepareForEmit (EmitContext ec)
5411 resume_point = ec.DefineLabel ();
5413 return resume_point;
5416 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5421 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5426 public abstract class TryFinallyBlock : ExceptionStatement
5428 protected Statement stmt;
5429 Label dispose_try_block;
5430 bool prepared_for_dispose, emitted_dispose;
5431 Method finally_host;
5433 protected TryFinallyBlock (Statement stmt, Location loc)
5441 public Statement Statement {
5449 protected abstract void EmitTryBody (EmitContext ec);
5450 public abstract void EmitFinallyBody (EmitContext ec);
5452 public override Label PrepareForDispose (EmitContext ec, Label end)
5454 if (!prepared_for_dispose) {
5455 prepared_for_dispose = true;
5456 dispose_try_block = ec.DefineLabel ();
5458 return dispose_try_block;
5461 protected sealed override void DoEmit (EmitContext ec)
5463 EmitTryBodyPrepare (ec);
5466 ec.BeginFinallyBlock ();
5468 Label start_finally = ec.DefineLabel ();
5469 if (resume_points != null) {
5470 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5472 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5473 ec.Emit (OpCodes.Brfalse_S, start_finally);
5474 ec.Emit (OpCodes.Endfinally);
5477 ec.MarkLabel (start_finally);
5479 if (finally_host != null) {
5480 finally_host.Define ();
5481 finally_host.PrepareEmit ();
5482 finally_host.Emit ();
5484 // Now it's safe to add, to close it properly and emit sequence points
5485 finally_host.Parent.AddMember (finally_host);
5487 var ce = new CallEmitter ();
5488 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5489 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5491 EmitFinallyBody (ec);
5494 ec.EndExceptionBlock ();
5497 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5499 if (emitted_dispose)
5502 emitted_dispose = true;
5504 Label end_of_try = ec.DefineLabel ();
5506 // Ensure that the only way we can get into this code is through a dispatcher
5507 if (have_dispatcher)
5508 ec.Emit (OpCodes.Br, end);
5510 ec.BeginExceptionBlock ();
5512 ec.MarkLabel (dispose_try_block);
5514 Label[] labels = null;
5515 for (int i = 0; i < resume_points.Count; ++i) {
5516 ResumableStatement s = resume_points[i];
5517 Label ret = s.PrepareForDispose (ec, end_of_try);
5518 if (ret.Equals (end_of_try) && labels == null)
5520 if (labels == null) {
5521 labels = new Label[resume_points.Count];
5522 for (int j = 0; j < i; ++j)
5523 labels[j] = end_of_try;
5528 if (labels != null) {
5530 for (j = 1; j < labels.Length; ++j)
5531 if (!labels[0].Equals (labels[j]))
5533 bool emit_dispatcher = j < labels.Length;
5535 if (emit_dispatcher) {
5536 ec.Emit (OpCodes.Ldloc, pc);
5537 ec.EmitInt (first_resume_pc);
5538 ec.Emit (OpCodes.Sub);
5539 ec.Emit (OpCodes.Switch, labels);
5542 foreach (ResumableStatement s in resume_points)
5543 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5546 ec.MarkLabel (end_of_try);
5548 ec.BeginFinallyBlock ();
5550 if (finally_host != null) {
5551 var ce = new CallEmitter ();
5552 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5553 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5555 EmitFinallyBody (ec);
5558 ec.EndExceptionBlock ();
5561 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5563 var res = stmt.FlowAnalysis (fc);
5568 public override Reachability MarkReachable (Reachability rc)
5570 base.MarkReachable (rc);
5571 return Statement.MarkReachable (rc);
5574 public override bool Resolve (BlockContext bc)
5578 parent = bc.CurrentTryBlock;
5579 bc.CurrentTryBlock = this;
5581 using (bc.Set (ResolveContext.Options.TryScope)) {
5582 ok = stmt.Resolve (bc);
5585 bc.CurrentTryBlock = parent;
5588 // Finally block inside iterator is called from MoveNext and
5589 // Dispose methods that means we need to lift the block into
5590 // newly created host method to emit the body only once. The
5591 // original block then simply calls the newly generated method.
5593 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5594 var b = stmt as Block;
5595 if (b != null && b.Explicit.HasYield) {
5596 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5600 return base.Resolve (bc) && ok;
5605 // Base class for blocks using exception handling
5607 public abstract class ExceptionStatement : ResumableStatement
5609 protected List<ResumableStatement> resume_points;
5610 protected int first_resume_pc;
5611 protected ExceptionStatement parent;
5613 protected ExceptionStatement (Location loc)
5618 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5620 StateMachineInitializer state_machine = null;
5621 if (resume_points != null) {
5622 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5624 ec.EmitInt ((int) IteratorStorey.State.Running);
5625 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5628 ec.BeginExceptionBlock ();
5630 if (resume_points != null) {
5631 ec.MarkLabel (resume_point);
5633 // For normal control flow, we want to fall-through the Switch
5634 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5635 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5636 ec.EmitInt (first_resume_pc);
5637 ec.Emit (OpCodes.Sub);
5639 Label[] labels = new Label[resume_points.Count];
5640 for (int i = 0; i < resume_points.Count; ++i)
5641 labels[i] = resume_points[i].PrepareForEmit (ec);
5642 ec.Emit (OpCodes.Switch, labels);
5646 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5648 if (parent != null) {
5649 // TODO: MOVE to virtual TryCatch
5650 var tc = this as TryCatch;
5651 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5653 pc = parent.AddResumePoint (s, pc, stateMachine);
5655 pc = stateMachine.AddResumePoint (this);
5658 if (resume_points == null) {
5659 resume_points = new List<ResumableStatement> ();
5660 first_resume_pc = pc;
5663 if (pc != first_resume_pc + resume_points.Count)
5664 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5666 resume_points.Add (stmt);
5671 public class Lock : TryFinallyBlock
5674 TemporaryVariableReference expr_copy;
5675 TemporaryVariableReference lock_taken;
5677 public Lock (Expression expr, Statement stmt, Location loc)
5683 public Expression Expr {
5689 public override bool Resolve (BlockContext ec)
5691 expr = expr.Resolve (ec);
5695 if (!TypeSpec.IsReferenceType (expr.Type)) {
5696 ec.Report.Error (185, loc,
5697 "`{0}' is not a reference type as required by the lock statement",
5698 expr.Type.GetSignatureForError ());
5701 if (expr.Type.IsGenericParameter) {
5702 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5705 VariableReference lv = expr as VariableReference;
5708 locked = lv.IsLockedByStatement;
5709 lv.IsLockedByStatement = true;
5716 // Have to keep original lock value around to unlock same location
5717 // in the case of original value has changed or is null
5719 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5720 expr_copy.Resolve (ec);
5723 // Ensure Monitor methods are available
5725 if (ResolvePredefinedMethods (ec) > 1) {
5726 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5727 lock_taken.Resolve (ec);
5730 using (ec.Set (ResolveContext.Options.LockScope)) {
5735 lv.IsLockedByStatement = locked;
5741 protected override void EmitTryBodyPrepare (EmitContext ec)
5743 expr_copy.EmitAssign (ec, expr);
5745 if (lock_taken != null) {
5747 // Initialize ref variable
5749 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5752 // Monitor.Enter (expr_copy)
5754 expr_copy.Emit (ec);
5755 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5758 base.EmitTryBodyPrepare (ec);
5761 protected override void EmitTryBody (EmitContext ec)
5764 // Monitor.Enter (expr_copy, ref lock_taken)
5766 if (lock_taken != null) {
5767 expr_copy.Emit (ec);
5768 lock_taken.LocalInfo.CreateBuilder (ec);
5769 lock_taken.AddressOf (ec, AddressOp.Load);
5770 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
5773 Statement.Emit (ec);
5776 public override void EmitFinallyBody (EmitContext ec)
5779 // if (lock_taken) Monitor.Exit (expr_copy)
5781 Label skip = ec.DefineLabel ();
5783 if (lock_taken != null) {
5784 lock_taken.Emit (ec);
5785 ec.Emit (OpCodes.Brfalse_S, skip);
5788 expr_copy.Emit (ec);
5789 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
5791 ec.Emit (OpCodes.Call, m);
5793 ec.MarkLabel (skip);
5796 int ResolvePredefinedMethods (ResolveContext rc)
5798 // Try 4.0 Monitor.Enter (object, ref bool) overload first
5799 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
5803 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
5807 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
5811 protected override void CloneTo (CloneContext clonectx, Statement t)
5813 Lock target = (Lock) t;
5815 target.expr = expr.Clone (clonectx);
5816 target.stmt = Statement.Clone (clonectx);
5819 public override object Accept (StructuralVisitor visitor)
5821 return visitor.Visit (this);
5826 public class Unchecked : Statement {
5829 public Unchecked (Block b, Location loc)
5836 public override bool Resolve (BlockContext ec)
5838 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
5839 return Block.Resolve (ec);
5842 protected override void DoEmit (EmitContext ec)
5844 using (ec.With (EmitContext.Options.CheckedScope, false))
5848 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5850 return Block.FlowAnalysis (fc);
5853 public override Reachability MarkReachable (Reachability rc)
5855 base.MarkReachable (rc);
5856 return Block.MarkReachable (rc);
5859 protected override void CloneTo (CloneContext clonectx, Statement t)
5861 Unchecked target = (Unchecked) t;
5863 target.Block = clonectx.LookupBlock (Block);
5866 public override object Accept (StructuralVisitor visitor)
5868 return visitor.Visit (this);
5872 public class Checked : Statement {
5875 public Checked (Block b, Location loc)
5878 b.Unchecked = false;
5882 public override bool Resolve (BlockContext ec)
5884 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
5885 return Block.Resolve (ec);
5888 protected override void DoEmit (EmitContext ec)
5890 using (ec.With (EmitContext.Options.CheckedScope, true))
5894 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5896 return Block.FlowAnalysis (fc);
5899 public override Reachability MarkReachable (Reachability rc)
5901 base.MarkReachable (rc);
5902 return Block.MarkReachable (rc);
5905 protected override void CloneTo (CloneContext clonectx, Statement t)
5907 Checked target = (Checked) t;
5909 target.Block = clonectx.LookupBlock (Block);
5912 public override object Accept (StructuralVisitor visitor)
5914 return visitor.Visit (this);
5918 public class Unsafe : Statement {
5921 public Unsafe (Block b, Location loc)
5924 Block.Unsafe = true;
5928 public override bool Resolve (BlockContext ec)
5930 if (ec.CurrentIterator != null)
5931 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5933 using (ec.Set (ResolveContext.Options.UnsafeScope))
5934 return Block.Resolve (ec);
5937 protected override void DoEmit (EmitContext ec)
5942 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5944 return Block.FlowAnalysis (fc);
5947 public override Reachability MarkReachable (Reachability rc)
5949 base.MarkReachable (rc);
5950 return Block.MarkReachable (rc);
5953 protected override void CloneTo (CloneContext clonectx, Statement t)
5955 Unsafe target = (Unsafe) t;
5957 target.Block = clonectx.LookupBlock (Block);
5960 public override object Accept (StructuralVisitor visitor)
5962 return visitor.Visit (this);
5969 public class Fixed : Statement
5971 abstract class Emitter : ShimExpression
5973 protected LocalVariable vi;
5975 protected Emitter (Expression expr, LocalVariable li)
5981 public abstract void EmitExit (EmitContext ec);
5983 public override void FlowAnalysis (FlowAnalysisContext fc)
5985 expr.FlowAnalysis (fc);
5989 class ExpressionEmitter : Emitter {
5990 public ExpressionEmitter (Expression converted, LocalVariable li) :
5991 base (converted, li)
5995 protected override Expression DoResolve (ResolveContext rc)
5997 throw new NotImplementedException ();
6000 public override void Emit (EmitContext ec) {
6002 // Store pointer in pinned location
6008 public override void EmitExit (EmitContext ec)
6011 ec.Emit (OpCodes.Conv_U);
6016 class StringEmitter : Emitter
6018 LocalVariable pinned_string;
6020 public StringEmitter (Expression expr, LocalVariable li)
6025 protected override Expression DoResolve (ResolveContext rc)
6027 pinned_string = new LocalVariable (vi.Block, "$pinned",
6028 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6030 pinned_string.Type = rc.BuiltinTypes.String;
6032 eclass = ExprClass.Variable;
6033 type = rc.BuiltinTypes.Int;
6037 public override void Emit (EmitContext ec)
6039 pinned_string.CreateBuilder (ec);
6042 pinned_string.EmitAssign (ec);
6044 // TODO: Should use Binary::Add
6045 pinned_string.Emit (ec);
6046 ec.Emit (OpCodes.Conv_I);
6048 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6052 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6053 //pe.InstanceExpression = pinned_string;
6054 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6056 ec.Emit (OpCodes.Add);
6060 public override void EmitExit (EmitContext ec)
6063 pinned_string.EmitAssign (ec);
6067 public class VariableDeclaration : BlockVariable
6069 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6074 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6076 if (!Variable.Type.IsPointer && li == Variable) {
6077 bc.Report.Error (209, TypeExpression.Location,
6078 "The type of locals declared in a fixed statement must be a pointer type");
6083 // The rules for the possible declarators are pretty wise,
6084 // but the production on the grammar is more concise.
6086 // So we have to enforce these rules here.
6088 // We do not resolve before doing the case 1 test,
6089 // because the grammar is explicit in that the token &
6090 // is present, so we need to test for this particular case.
6093 if (initializer is Cast) {
6094 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6098 initializer = initializer.Resolve (bc);
6100 if (initializer == null)
6106 if (initializer.Type.IsArray) {
6107 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
6110 // Provided that array_type is unmanaged,
6112 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6116 // and T* is implicitly convertible to the
6117 // pointer type given in the fixed statement.
6119 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
6121 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6122 if (converted == null)
6126 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6128 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6129 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
6130 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6131 new NullLiteral (loc),
6134 converted = converted.Resolve (bc);
6136 return new ExpressionEmitter (converted, li);
6142 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6143 return new StringEmitter (initializer, li).Resolve (bc);
6146 // Case 3: fixed buffer
6147 if (initializer is FixedBufferPtr) {
6148 return new ExpressionEmitter (initializer, li);
6152 // Case 4: & object.
6154 bool already_fixed = true;
6155 Unary u = initializer as Unary;
6156 if (u != null && u.Oper == Unary.Operator.AddressOf) {
6157 IVariableReference vr = u.Expr as IVariableReference;
6158 if (vr == null || !vr.IsFixed) {
6159 already_fixed = false;
6163 if (already_fixed) {
6164 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6167 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
6168 return new ExpressionEmitter (initializer, li);
6173 VariableDeclaration decl;
6174 Statement statement;
6177 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6186 public Statement Statement {
6192 public BlockVariable Variables {
6200 public override bool Resolve (BlockContext bc)
6202 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6203 if (!decl.Resolve (bc))
6207 return statement.Resolve (bc);
6210 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6212 decl.FlowAnalysis (fc);
6213 return statement.FlowAnalysis (fc);
6216 protected override void DoEmit (EmitContext ec)
6218 decl.Variable.CreateBuilder (ec);
6219 decl.Initializer.Emit (ec);
6220 if (decl.Declarators != null) {
6221 foreach (var d in decl.Declarators) {
6222 d.Variable.CreateBuilder (ec);
6223 d.Initializer.Emit (ec);
6227 statement.Emit (ec);
6233 // Clear the pinned variable
6235 ((Emitter) decl.Initializer).EmitExit (ec);
6236 if (decl.Declarators != null) {
6237 foreach (var d in decl.Declarators) {
6238 ((Emitter)d.Initializer).EmitExit (ec);
6243 public override Reachability MarkReachable (Reachability rc)
6245 base.MarkReachable (rc);
6247 decl.MarkReachable (rc);
6249 rc = statement.MarkReachable (rc);
6251 // TODO: What if there is local exit?
6252 has_ret = rc.IsUnreachable;
6256 protected override void CloneTo (CloneContext clonectx, Statement t)
6258 Fixed target = (Fixed) t;
6260 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6261 target.statement = statement.Clone (clonectx);
6264 public override object Accept (StructuralVisitor visitor)
6266 return visitor.Visit (this);
6270 public class Catch : Statement
6272 class FilterStatement : Statement
6274 readonly Catch ctch;
6276 public FilterStatement (Catch ctch)
6281 protected override void CloneTo (CloneContext clonectx, Statement target)
6285 protected override void DoEmit (EmitContext ec)
6287 if (ctch.li != null) {
6288 if (ctch.hoisted_temp != null)
6289 ctch.hoisted_temp.Emit (ec);
6294 var expr_start = ec.DefineLabel ();
6295 var end = ec.DefineLabel ();
6297 ec.Emit (OpCodes.Brtrue_S, expr_start);
6299 ec.Emit (OpCodes.Br, end);
6300 ec.MarkLabel (expr_start);
6302 ctch.Filter.Emit (ec);
6305 ec.Emit (OpCodes.Endfilter);
6306 ec.BeginFilterHandler ();
6307 ec.Emit (OpCodes.Pop);
6310 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6312 ctch.Filter.FlowAnalysis (fc);
6316 public override bool Resolve (BlockContext bc)
6318 ctch.Filter = ctch.Filter.Resolve (bc);
6319 var c = ctch.Filter as Constant;
6320 if (c != null && !c.IsDefaultValue) {
6321 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6328 ExplicitBlock block;
6330 FullNamedExpression type_expr;
6331 CompilerAssign assign;
6333 LocalTemporary hoisted_temp;
6335 public Catch (ExplicitBlock block, Location loc)
6343 public ExplicitBlock Block {
6349 public TypeSpec CatchType {
6355 public Expression Filter {
6359 public bool IsGeneral {
6361 return type_expr == null;
6365 public FullNamedExpression TypeExpression {
6374 public LocalVariable Variable {
6385 protected override void DoEmit (EmitContext ec)
6387 if (Filter != null) {
6388 ec.BeginExceptionFilterBlock ();
6389 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6392 EmitCatchVariableStore (ec);
6395 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6397 ec.BeginCatchBlock (CatchType);
6400 EmitCatchVariableStore (ec);
6402 ec.Emit (OpCodes.Pop);
6409 void EmitCatchVariableStore (EmitContext ec)
6411 li.CreateBuilder (ec);
6414 // Special case hoisted catch variable, we have to use a temporary variable
6415 // to pass via anonymous storey initialization with the value still on top
6418 if (li.HoistedVariant != null) {
6419 hoisted_temp = new LocalTemporary (li.Type);
6420 hoisted_temp.Store (ec);
6422 // switch to assigning from the temporary variable and not from top of the stack
6423 assign.UpdateSource (hoisted_temp);
6427 public override bool Resolve (BlockContext ec)
6429 using (ec.Set (ResolveContext.Options.CatchScope)) {
6430 if (type_expr != null) {
6431 type = type_expr.ResolveAsType (ec);
6435 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
6436 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6437 } else if (li != null) {
6439 li.PrepareAssignmentAnalysis (ec);
6441 // source variable is at the top of the stack
6442 Expression source = new EmptyExpression (li.Type);
6443 if (li.Type.IsGenericParameter)
6444 source = new UnboxCast (source, li.Type);
6447 // Uses Location.Null to hide from symbol file
6449 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6450 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6454 if (Filter != null) {
6455 Block.AddScopeStatement (new FilterStatement (this));
6458 Block.SetCatchBlock ();
6459 return Block.Resolve (ec);
6463 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6466 fc.SetVariableAssigned (li.VariableInfo, true);
6469 return block.FlowAnalysis (fc);
6472 public override Reachability MarkReachable (Reachability rc)
6474 base.MarkReachable (rc);
6476 var c = Filter as Constant;
6477 if (c != null && c.IsDefaultValue)
6478 return Reachability.CreateUnreachable ();
6480 return block.MarkReachable (rc);
6483 protected override void CloneTo (CloneContext clonectx, Statement t)
6485 Catch target = (Catch) t;
6487 if (type_expr != null)
6488 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6491 target.Filter = Filter.Clone (clonectx);
6493 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6497 public class TryFinally : TryFinallyBlock
6500 List<DefiniteAssignmentBitSet> try_exit_dat;
6502 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6508 public ExplicitBlock FinallyBlock {
6514 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6516 if (try_exit_dat == null)
6517 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6519 try_exit_dat.Add (vector);
6522 public override bool Resolve (BlockContext bc)
6524 bool ok = base.Resolve (bc);
6526 fini.SetFinallyBlock ();
6527 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6528 ok &= fini.Resolve (bc);
6534 protected override void EmitTryBody (EmitContext ec)
6539 public override void EmitFinallyBody (EmitContext ec)
6544 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6546 var da = fc.BranchDefiniteAssignment ();
6548 var tf = fc.TryFinally;
6549 fc.TryFinally = this;
6551 var res_stmt = Statement.FlowAnalysis (fc);
6555 var try_da = fc.DefiniteAssignment;
6556 fc.DefiniteAssignment = da;
6558 var res_fin = fini.FlowAnalysis (fc);
6560 if (try_exit_dat != null) {
6562 // try block has global exit but we need to run definite assignment check
6563 // for parameter block out parameter after finally block because it's always
6564 // executed before exit
6566 foreach (var try_da_part in try_exit_dat)
6567 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
6569 try_exit_dat = null;
6572 fc.DefiniteAssignment |= try_da;
6573 return res_stmt | res_fin;
6576 public override Reachability MarkReachable (Reachability rc)
6579 // Mark finally block first for any exit statement in try block
6580 // to know whether the code which follows finally is reachable
6582 return fini.MarkReachable (rc) | base.MarkReachable (rc);
6585 protected override void CloneTo (CloneContext clonectx, Statement t)
6587 TryFinally target = (TryFinally) t;
6589 target.stmt = stmt.Clone (clonectx);
6591 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
6594 public override object Accept (StructuralVisitor visitor)
6596 return visitor.Visit (this);
6600 public class TryCatch : ExceptionStatement
6603 List<Catch> clauses;
6604 readonly bool inside_try_finally;
6606 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
6610 this.clauses = catch_clauses;
6611 this.inside_try_finally = inside_try_finally;
6614 public List<Catch> Clauses {
6620 public bool IsTryCatchFinally {
6622 return inside_try_finally;
6626 public override bool Resolve (BlockContext bc)
6630 using (bc.Set (ResolveContext.Options.TryScope)) {
6631 parent = bc.CurrentTryBlock;
6633 if (IsTryCatchFinally) {
6634 ok = Block.Resolve (bc);
6636 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
6637 bc.CurrentTryBlock = this;
6638 ok = Block.Resolve (bc);
6639 bc.CurrentTryBlock = parent;
6644 for (int i = 0; i < clauses.Count; ++i) {
6647 ok &= c.Resolve (bc);
6649 if (c.Filter != null)
6652 TypeSpec resolved_type = c.CatchType;
6653 if (resolved_type == null)
6656 for (int ii = 0; ii < clauses.Count; ++ii) {
6660 if (clauses[ii].Filter != null)
6663 if (clauses[ii].IsGeneral) {
6664 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
6667 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
6670 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
6673 bc.Report.Warning (1058, 1, c.loc,
6674 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
6682 var ct = clauses[ii].CatchType;
6686 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
6687 bc.Report.Error (160, c.loc,
6688 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
6689 ct.GetSignatureForError ());
6695 return base.Resolve (bc) && ok;
6698 protected sealed override void DoEmit (EmitContext ec)
6700 if (!inside_try_finally)
6701 EmitTryBodyPrepare (ec);
6705 foreach (Catch c in clauses)
6708 if (!inside_try_finally)
6709 ec.EndExceptionBlock ();
6712 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6714 var start_fc = fc.BranchDefiniteAssignment ();
6715 var res = Block.FlowAnalysis (fc);
6717 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
6719 foreach (var c in clauses) {
6720 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
6721 if (!c.FlowAnalysis (fc)) {
6723 try_fc = fc.DefiniteAssignment;
6725 try_fc &= fc.DefiniteAssignment;
6731 fc.DefiniteAssignment = try_fc ?? start_fc;
6736 public override Reachability MarkReachable (Reachability rc)
6738 if (rc.IsUnreachable)
6741 base.MarkReachable (rc);
6743 var tc_rc = Block.MarkReachable (rc);
6745 foreach (var c in clauses)
6746 tc_rc &= c.MarkReachable (rc);
6751 protected override void CloneTo (CloneContext clonectx, Statement t)
6753 TryCatch target = (TryCatch) t;
6755 target.Block = clonectx.LookupBlock (Block);
6756 if (clauses != null){
6757 target.clauses = new List<Catch> ();
6758 foreach (Catch c in clauses)
6759 target.clauses.Add ((Catch) c.Clone (clonectx));
6763 public override object Accept (StructuralVisitor visitor)
6765 return visitor.Visit (this);
6769 public class Using : TryFinallyBlock
6771 public class VariableDeclaration : BlockVariable
6773 Statement dispose_call;
6775 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6780 public VariableDeclaration (LocalVariable li, Location loc)
6786 public VariableDeclaration (Expression expr)
6789 loc = expr.Location;
6795 public bool IsNested { get; private set; }
6799 public void EmitDispose (EmitContext ec)
6801 dispose_call.Emit (ec);
6804 public override bool Resolve (BlockContext bc)
6809 return base.Resolve (bc, false);
6812 public Expression ResolveExpression (BlockContext bc)
6814 var e = Initializer.Resolve (bc);
6818 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
6819 Initializer = ResolveInitializer (bc, Variable, e);
6823 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6825 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
6826 initializer = initializer.Resolve (bc);
6827 if (initializer == null)
6830 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
6831 Arguments args = new Arguments (1);
6832 args.Add (new Argument (initializer));
6833 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
6834 if (initializer == null)
6837 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
6838 dispose_call = CreateDisposeCall (bc, var);
6839 dispose_call.Resolve (bc);
6841 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
6844 if (li == Variable) {
6845 CheckIDiposableConversion (bc, li, initializer);
6846 dispose_call = CreateDisposeCall (bc, li);
6847 dispose_call.Resolve (bc);
6850 return base.ResolveInitializer (bc, li, initializer);
6853 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6857 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !CanConvertToIDisposable (bc, type)) {
6858 if (type.IsNullableType) {
6859 // it's handled in CreateDisposeCall
6863 if (type != InternalType.ErrorType) {
6864 bc.Report.SymbolRelatedToPreviousError (type);
6865 var loc = type_expr == null ? initializer.Location : type_expr.Location;
6866 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
6867 type.GetSignatureForError ());
6874 static bool CanConvertToIDisposable (BlockContext bc, TypeSpec type)
6876 var target = bc.BuiltinTypes.IDisposable;
6877 var tp = type as TypeParameterSpec;
6879 return Convert.ImplicitTypeParameterConversion (null, tp, target) != null;
6881 return type.ImplementsInterface (target, false);
6884 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6886 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
6888 var loc = lv.Location;
6890 var idt = bc.BuiltinTypes.IDisposable;
6891 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6893 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6894 dispose_mg.InstanceExpression = type.IsNullableType ?
6895 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
6899 // Hide it from symbol file via null location
6901 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
6903 // Add conditional call when disposing possible null variable
6904 if (!TypeSpec.IsValueType (type) || type.IsNullableType)
6905 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
6910 public void ResolveDeclaratorInitializer (BlockContext bc)
6912 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
6915 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
6917 for (int i = declarators.Count - 1; i >= 0; --i) {
6918 var d = declarators [i];
6919 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
6920 vd.Initializer = d.Initializer;
6922 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
6923 vd.dispose_call.Resolve (bc);
6925 stmt = new Using (vd, stmt, d.Variable.Location);
6932 public override object Accept (StructuralVisitor visitor)
6934 return visitor.Visit (this);
6938 VariableDeclaration decl;
6940 public Using (VariableDeclaration decl, Statement stmt, Location loc)
6946 public Using (Expression expr, Statement stmt, Location loc)
6949 this.decl = new VariableDeclaration (expr);
6954 public Expression Expr {
6956 return decl.Variable == null ? decl.Initializer : null;
6960 public BlockVariable Variables {
6968 public override void Emit (EmitContext ec)
6971 // Don't emit sequence point it will be set on variable declaration
6976 protected override void EmitTryBodyPrepare (EmitContext ec)
6979 base.EmitTryBodyPrepare (ec);
6982 protected override void EmitTryBody (EmitContext ec)
6987 public override void EmitFinallyBody (EmitContext ec)
6989 decl.EmitDispose (ec);
6992 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6994 decl.FlowAnalysis (fc);
6995 return stmt.FlowAnalysis (fc);
6998 public override Reachability MarkReachable (Reachability rc)
7000 decl.MarkReachable (rc);
7001 return base.MarkReachable (rc);
7004 public override bool Resolve (BlockContext ec)
7006 VariableReference vr;
7007 bool vr_locked = false;
7009 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
7010 if (decl.Variable == null) {
7011 vr = decl.ResolveExpression (ec) as VariableReference;
7013 vr_locked = vr.IsLockedByStatement;
7014 vr.IsLockedByStatement = true;
7017 if (decl.IsNested) {
7018 decl.ResolveDeclaratorInitializer (ec);
7020 if (!decl.Resolve (ec))
7023 if (decl.Declarators != null) {
7024 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7032 var ok = base.Resolve (ec);
7035 vr.IsLockedByStatement = vr_locked;
7040 protected override void CloneTo (CloneContext clonectx, Statement t)
7042 Using target = (Using) t;
7044 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7045 target.stmt = stmt.Clone (clonectx);
7048 public override object Accept (StructuralVisitor visitor)
7050 return visitor.Visit (this);
7055 /// Implementation of the foreach C# statement
7057 public class Foreach : LoopStatement
7059 abstract class IteratorStatement : Statement
7061 protected readonly Foreach for_each;
7063 protected IteratorStatement (Foreach @foreach)
7065 this.for_each = @foreach;
7066 this.loc = @foreach.expr.Location;
7069 protected override void CloneTo (CloneContext clonectx, Statement target)
7071 throw new NotImplementedException ();
7074 public override void Emit (EmitContext ec)
7076 if (ec.EmitAccurateDebugInfo) {
7077 ec.Emit (OpCodes.Nop);
7083 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7085 throw new NotImplementedException ();
7089 sealed class ArrayForeach : IteratorStatement
7091 TemporaryVariableReference[] lengths;
7092 Expression [] length_exprs;
7093 StatementExpression[] counter;
7094 TemporaryVariableReference[] variables;
7096 TemporaryVariableReference copy;
7098 public ArrayForeach (Foreach @foreach, int rank)
7101 counter = new StatementExpression[rank];
7102 variables = new TemporaryVariableReference[rank];
7103 length_exprs = new Expression [rank];
7106 // Only use temporary length variables when dealing with
7107 // multi-dimensional arrays
7110 lengths = new TemporaryVariableReference [rank];
7113 public override bool Resolve (BlockContext ec)
7115 Block variables_block = for_each.variable.Block;
7116 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7119 int rank = length_exprs.Length;
7120 Arguments list = new Arguments (rank);
7121 for (int i = 0; i < rank; i++) {
7122 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7124 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7125 counter[i].Resolve (ec);
7128 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7130 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7131 lengths[i].Resolve (ec);
7133 Arguments args = new Arguments (1);
7134 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7135 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7138 list.Add (new Argument (v));
7141 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7146 if (for_each.type is VarExpr) {
7147 // Infer implicitly typed local variable from foreach array type
7148 var_type = access.Type;
7150 var_type = for_each.type.ResolveAsType (ec);
7152 if (var_type == null)
7155 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7160 for_each.variable.Type = var_type;
7162 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7163 if (variable_ref == null)
7166 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7168 return for_each.body.Resolve (ec);
7171 protected override void DoEmit (EmitContext ec)
7173 copy.EmitAssign (ec, for_each.expr);
7175 int rank = length_exprs.Length;
7176 Label[] test = new Label [rank];
7177 Label[] loop = new Label [rank];
7179 for (int i = 0; i < rank; i++) {
7180 test [i] = ec.DefineLabel ();
7181 loop [i] = ec.DefineLabel ();
7183 if (lengths != null)
7184 lengths [i].EmitAssign (ec, length_exprs [i]);
7187 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7188 for (int i = 0; i < rank; i++) {
7189 variables [i].EmitAssign (ec, zero);
7191 ec.Emit (OpCodes.Br, test [i]);
7192 ec.MarkLabel (loop [i]);
7195 for_each.body.Emit (ec);
7197 ec.MarkLabel (ec.LoopBegin);
7198 ec.Mark (for_each.expr.Location);
7200 for (int i = rank - 1; i >= 0; i--){
7201 counter [i].Emit (ec);
7203 ec.MarkLabel (test [i]);
7204 variables [i].Emit (ec);
7206 if (lengths != null)
7207 lengths [i].Emit (ec);
7209 length_exprs [i].Emit (ec);
7211 ec.Emit (OpCodes.Blt, loop [i]);
7214 ec.MarkLabel (ec.LoopEnd);
7218 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7220 class RuntimeDispose : Using.VariableDeclaration
7222 public RuntimeDispose (LocalVariable lv, Location loc)
7227 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7229 // Defered to runtime check
7232 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7234 var idt = bc.BuiltinTypes.IDisposable;
7237 // Fabricates code like
7239 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7242 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7244 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7245 dispose_variable.CreateReferenceExpression (bc, loc),
7246 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7247 loc), new NullLiteral (loc));
7249 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7251 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7252 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7254 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7255 return new If (idisaposable_test, dispose, loc);
7259 LocalVariable variable;
7261 Statement statement;
7262 ExpressionStatement init;
7263 TemporaryVariableReference enumerator_variable;
7264 bool ambiguous_getenumerator_name;
7266 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7269 this.variable = var;
7273 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7275 rc.Report.SymbolRelatedToPreviousError (enumerator);
7276 rc.Report.Error (202, loc,
7277 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7278 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7281 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7284 // Option 1: Try to match by name GetEnumerator first
7286 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7287 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7289 var mg = mexpr as MethodGroupExpr;
7291 mg.InstanceExpression = expr;
7292 Arguments args = new Arguments (0);
7293 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
7295 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7296 if (ambiguous_getenumerator_name)
7299 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7305 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7308 PredefinedMember<MethodSpec> iface_candidate = null;
7309 var ptypes = rc.Module.PredefinedTypes;
7310 var gen_ienumerable = ptypes.IEnumerableGeneric;
7311 if (!gen_ienumerable.Define ())
7312 gen_ienumerable = null;
7314 var ifaces = t.Interfaces;
7315 if (ifaces != null) {
7316 foreach (var iface in ifaces) {
7317 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7318 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7319 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7320 rc.Report.Error (1640, loc,
7321 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7322 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7327 // TODO: Cache this somehow
7328 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7329 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7334 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7335 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7340 if (iface_candidate == null) {
7341 if (expr.Type != InternalType.ErrorType) {
7342 rc.Report.Error (1579, loc,
7343 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7344 expr.Type.GetSignatureForError (), "GetEnumerator");
7350 var method = iface_candidate.Resolve (loc);
7354 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7355 mg.InstanceExpression = expr;
7359 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7361 var ms = MemberCache.FindMember (enumerator.ReturnType,
7362 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7363 BindingRestriction.InstanceOnly) as MethodSpec;
7365 if (ms == null || !ms.IsPublic) {
7366 Error_WrongEnumerator (rc, enumerator);
7370 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7373 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7375 var ps = MemberCache.FindMember (enumerator.ReturnType,
7376 MemberFilter.Property ("Current", null),
7377 BindingRestriction.InstanceOnly) as PropertySpec;
7379 if (ps == null || !ps.IsPublic) {
7380 Error_WrongEnumerator (rc, enumerator);
7387 public override bool Resolve (BlockContext ec)
7389 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7392 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7393 } else if (expr.Type.IsNullableType) {
7394 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7397 var get_enumerator_mg = ResolveGetEnumerator (ec);
7398 if (get_enumerator_mg == null) {
7402 var get_enumerator = get_enumerator_mg.BestCandidate;
7403 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7404 enumerator_variable.Resolve (ec);
7406 // Prepare bool MoveNext ()
7407 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7408 if (move_next_mg == null) {
7412 move_next_mg.InstanceExpression = enumerator_variable;
7414 // Prepare ~T~ Current { get; }
7415 var current_prop = ResolveCurrent (ec, get_enumerator);
7416 if (current_prop == null) {
7420 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7421 if (current_pe == null)
7424 VarExpr ve = for_each.type as VarExpr;
7428 // Source type is dynamic, set element type to dynamic too
7429 variable.Type = ec.BuiltinTypes.Dynamic;
7431 // Infer implicitly typed local variable from foreach enumerable type
7432 variable.Type = current_pe.Type;
7436 // Explicit cast of dynamic collection elements has to be done at runtime
7437 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7440 variable.Type = for_each.type.ResolveAsType (ec);
7442 if (variable.Type == null)
7445 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7446 if (current_pe == null)
7450 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7451 if (variable_ref == null)
7454 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7456 var init = new Invocation (get_enumerator_mg, null);
7458 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7459 for_each.body, Location.Null);
7461 var enum_type = enumerator_variable.Type;
7464 // Add Dispose method call when enumerator can be IDisposable
7466 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7467 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7469 // Runtime Dispose check
7471 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7472 vd.Initializer = init;
7473 statement = new Using (vd, statement, Location.Null);
7476 // No Dispose call needed
7478 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7479 this.init.Resolve (ec);
7483 // Static Dispose check
7485 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
7486 vd.Initializer = init;
7487 statement = new Using (vd, statement, Location.Null);
7490 return statement.Resolve (ec);
7493 protected override void DoEmit (EmitContext ec)
7495 enumerator_variable.LocalInfo.CreateBuilder (ec);
7498 init.EmitStatement (ec);
7500 statement.Emit (ec);
7503 #region IErrorHandler Members
7505 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
7507 ec.Report.SymbolRelatedToPreviousError (best);
7508 ec.Report.Warning (278, 2, expr.Location,
7509 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
7510 expr.Type.GetSignatureForError (), "enumerable",
7511 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
7513 ambiguous_getenumerator_name = true;
7517 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
7522 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
7527 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
7536 LocalVariable variable;
7540 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
7544 this.variable = var;
7550 public Expression Expr {
7551 get { return expr; }
7554 public Expression TypeExpression {
7555 get { return type; }
7558 public LocalVariable Variable {
7559 get { return variable; }
7562 public override Reachability MarkReachable (Reachability rc)
7564 base.MarkReachable (rc);
7566 body.MarkReachable (rc);
7571 public override bool Resolve (BlockContext ec)
7573 expr = expr.Resolve (ec);
7578 ec.Report.Error (186, loc, "Use of null is not valid in this context");
7582 body.AddStatement (Statement);
7584 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
7585 Statement = new ArrayForeach (this, 1);
7586 } else if (expr.Type is ArrayContainer) {
7587 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
7589 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
7590 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
7591 expr.ExprClassName);
7595 Statement = new CollectionForeach (this, variable, expr);
7598 return base.Resolve (ec);
7601 protected override void DoEmit (EmitContext ec)
7603 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
7604 ec.LoopBegin = ec.DefineLabel ();
7605 ec.LoopEnd = ec.DefineLabel ();
7607 if (!(Statement is Block))
7608 ec.BeginCompilerScope ();
7610 variable.CreateBuilder (ec);
7612 Statement.Emit (ec);
7614 if (!(Statement is Block))
7617 ec.LoopBegin = old_begin;
7618 ec.LoopEnd = old_end;
7621 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7623 expr.FlowAnalysis (fc);
7625 var da = fc.BranchDefiniteAssignment ();
7626 body.FlowAnalysis (fc);
7627 fc.DefiniteAssignment = da;
7631 protected override void CloneTo (CloneContext clonectx, Statement t)
7633 Foreach target = (Foreach) t;
7635 target.type = type.Clone (clonectx);
7636 target.expr = expr.Clone (clonectx);
7637 target.body = (Block) body.Clone (clonectx);
7638 target.Statement = Statement.Clone (clonectx);
7641 public override object Accept (StructuralVisitor visitor)
7643 return visitor.Visit (this);