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) {
693 Iterator.Resolve (bc);
698 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
700 Initializer.FlowAnalysis (fc);
702 DefiniteAssignmentBitSet da_false;
703 if (Condition != null) {
704 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = fc.DefiniteAssignment;
706 Condition.FlowAnalysis (fc);
707 fc.DefiniteAssignment = fc.DefiniteAssignmentOnTrue;
708 da_false = new DefiniteAssignmentBitSet (fc.DefiniteAssignmentOnFalse);
709 fc.DefiniteAssignmentOnTrue = fc.DefiniteAssignmentOnFalse = null;
711 da_false = fc.BranchDefiniteAssignment ();
714 Statement.FlowAnalysis (fc);
716 Iterator.FlowAnalysis (fc);
719 // Special case infinite for with breaks
721 if (end_reachable_das != null) {
722 da_false = DefiniteAssignmentBitSet.And (end_reachable_das);
723 end_reachable_das = null;
726 fc.DefiniteAssignment = da_false;
728 if (infinite && !end_reachable)
734 public override Reachability MarkReachable (Reachability rc)
736 base.MarkReachable (rc);
738 Initializer.MarkReachable (rc);
740 var body_rc = Statement.MarkReachable (rc);
741 if (!body_rc.IsUnreachable || iterator_reachable) {
742 Iterator.MarkReachable (rc);
746 // When infinite for end is unreachable via break anything what follows is unreachable too
748 if (infinite && !end_reachable) {
749 return Reachability.CreateUnreachable ();
755 protected override void DoEmit (EmitContext ec)
757 if (Initializer != null)
758 Initializer.Emit (ec);
761 Condition.EmitSideEffect (ec);
765 Label old_begin = ec.LoopBegin;
766 Label old_end = ec.LoopEnd;
767 Label loop = ec.DefineLabel ();
768 Label test = ec.DefineLabel ();
770 ec.LoopBegin = ec.DefineLabel ();
771 ec.LoopEnd = ec.DefineLabel ();
773 ec.Emit (OpCodes.Br, test);
777 ec.MarkLabel (ec.LoopBegin);
782 // If test is null, there is no test, and we are just
785 if (Condition != null) {
786 ec.Mark (Condition.Location);
789 // The Resolve code already catches the case for
790 // Test == Constant (false) so we know that
793 if (Condition is Constant) {
794 Condition.EmitSideEffect (ec);
795 ec.Emit (OpCodes.Br, loop);
797 Condition.EmitBranchable (ec, loop, true);
801 ec.Emit (OpCodes.Br, loop);
802 ec.MarkLabel (ec.LoopEnd);
804 ec.LoopBegin = old_begin;
805 ec.LoopEnd = old_end;
808 protected override void CloneTo (CloneContext clonectx, Statement t)
810 For target = (For) t;
812 if (Initializer != null)
813 target.Initializer = Initializer.Clone (clonectx);
814 if (Condition != null)
815 target.Condition = Condition.Clone (clonectx);
816 if (Iterator != null)
817 target.Iterator = Iterator.Clone (clonectx);
818 target.Statement = Statement.Clone (clonectx);
821 public override object Accept (StructuralVisitor visitor)
823 return visitor.Visit (this);
826 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
831 if (end_reachable_das == null)
832 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
834 end_reachable_das.Add (fc.DefiniteAssignment);
837 public override void SetEndReachable ()
839 end_reachable = true;
842 public override void SetIteratorReachable ()
844 iterator_reachable = true;
848 public abstract class LoopStatement : Statement
850 protected LoopStatement (Statement statement)
852 Statement = statement;
855 public Statement Statement { get; set; }
857 public override bool Resolve (BlockContext bc)
859 var prev_loop = bc.EnclosingLoop;
860 var prev_los = bc.EnclosingLoopOrSwitch;
861 bc.EnclosingLoopOrSwitch = bc.EnclosingLoop = this;
862 Statement.Resolve (bc);
863 bc.EnclosingLoopOrSwitch = prev_los;
864 bc.EnclosingLoop = prev_loop;
870 // Needed by possibly infinite loops statements (for, while) and switch statment
872 public virtual void AddEndDefiniteAssignment (FlowAnalysisContext fc)
876 public virtual void SetEndReachable ()
880 public virtual void SetIteratorReachable ()
885 public class StatementExpression : Statement
887 ExpressionStatement expr;
889 public StatementExpression (ExpressionStatement expr)
892 loc = expr.StartLocation;
895 public StatementExpression (ExpressionStatement expr, Location loc)
901 public ExpressionStatement Expr {
907 protected override void CloneTo (CloneContext clonectx, Statement t)
909 StatementExpression target = (StatementExpression) t;
910 target.expr = (ExpressionStatement) expr.Clone (clonectx);
913 protected override void DoEmit (EmitContext ec)
915 expr.EmitStatement (ec);
918 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
920 expr.FlowAnalysis (fc);
924 public override Reachability MarkReachable (Reachability rc)
926 base.MarkReachable (rc);
927 expr.MarkReachable (rc);
931 public override bool Resolve (BlockContext ec)
933 expr = expr.ResolveStatement (ec);
937 public override object Accept (StructuralVisitor visitor)
939 return visitor.Visit (this);
943 public class StatementErrorExpression : Statement
947 public StatementErrorExpression (Expression expr)
950 this.loc = expr.StartLocation;
953 public Expression Expr {
959 public override bool Resolve (BlockContext bc)
961 expr.Error_InvalidExpressionStatement (bc);
965 protected override void DoEmit (EmitContext ec)
967 throw new NotSupportedException ();
970 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
975 protected override void CloneTo (CloneContext clonectx, Statement target)
977 var t = (StatementErrorExpression) target;
979 t.expr = expr.Clone (clonectx);
982 public override object Accept (StructuralVisitor visitor)
984 return visitor.Visit (this);
989 // Simple version of statement list not requiring a block
991 public class StatementList : Statement
993 List<Statement> statements;
995 public StatementList (Statement first, Statement second)
997 statements = new List<Statement> { first, second };
1001 public IList<Statement> Statements {
1008 public void Add (Statement statement)
1010 statements.Add (statement);
1013 public override bool Resolve (BlockContext ec)
1015 foreach (var s in statements)
1021 protected override void DoEmit (EmitContext ec)
1023 foreach (var s in statements)
1027 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1029 foreach (var s in statements)
1030 s.FlowAnalysis (fc);
1035 public override Reachability MarkReachable (Reachability rc)
1037 base.MarkReachable (rc);
1039 Reachability res = rc;
1040 foreach (var s in statements)
1041 res = s.MarkReachable (rc);
1046 protected override void CloneTo (CloneContext clonectx, Statement target)
1048 StatementList t = (StatementList) target;
1050 t.statements = new List<Statement> (statements.Count);
1051 foreach (Statement s in statements)
1052 t.statements.Add (s.Clone (clonectx));
1055 public override object Accept (StructuralVisitor visitor)
1057 return visitor.Visit (this);
1062 // For statements which require special handling when inside try or catch block
1064 public abstract class ExitStatement : Statement
1066 protected bool unwind_protect;
1068 protected abstract bool DoResolve (BlockContext bc);
1069 protected abstract bool IsLocalExit { get; }
1071 public override bool Resolve (BlockContext bc)
1073 var res = DoResolve (bc);
1077 // We are inside finally scope but is it the scope we are exiting
1079 if (bc.HasSet (ResolveContext.Options.FinallyScope)) {
1081 for (var b = bc.CurrentBlock; b != null; b = b.Parent) {
1082 if (b.IsFinallyBlock) {
1083 Error_FinallyClauseExit (bc);
1087 if (b is ParametersBlock)
1093 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1097 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1102 if (fc.TryFinally != null) {
1103 fc.TryFinally.RegisterForControlExitCheck (new DefiniteAssignmentBitSet (fc.DefiniteAssignment));
1105 fc.ParametersBlock.CheckControlExit (fc);
1113 /// Implements the return statement
1115 public class Return : ExitStatement
1119 public Return (Expression expr, Location l)
1127 public Expression Expr {
1136 protected override bool IsLocalExit {
1144 protected override bool DoResolve (BlockContext ec)
1146 var block_return_type = ec.ReturnType;
1149 if (block_return_type.Kind == MemberKind.Void)
1153 // Return must not be followed by an expression when
1154 // the method return type is Task
1156 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
1157 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
1158 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
1160 // Extra trick not to emit ret/leave inside awaiter body
1162 expr = EmptyExpression.Null;
1166 if (storey.ReturnType.IsGenericTask)
1167 block_return_type = storey.ReturnType.TypeArguments[0];
1170 if (ec.CurrentIterator != null) {
1171 Error_ReturnFromIterator (ec);
1172 } else if (block_return_type != InternalType.ErrorType) {
1173 ec.Report.Error (126, loc,
1174 "An object of a type convertible to `{0}' is required for the return statement",
1175 block_return_type.GetSignatureForError ());
1181 expr = expr.Resolve (ec);
1183 AnonymousExpression am = ec.CurrentAnonymousMethod;
1185 if (block_return_type.Kind == MemberKind.Void) {
1186 ec.Report.Error (127, loc,
1187 "`{0}': A return keyword must not be followed by any expression when method returns void",
1188 ec.GetSignatureForError ());
1193 if (am.IsIterator) {
1194 Error_ReturnFromIterator (ec);
1198 var async_block = am as AsyncInitializer;
1199 if (async_block != null) {
1201 var storey = (AsyncTaskStorey) am.Storey;
1202 var async_type = storey.ReturnType;
1204 if (async_type == null && async_block.ReturnTypeInference != null) {
1205 async_block.ReturnTypeInference.AddCommonTypeBoundAsync (expr.Type);
1209 if (async_type.Kind == MemberKind.Void) {
1210 ec.Report.Error (127, loc,
1211 "`{0}': A return keyword must not be followed by any expression when method returns void",
1212 ec.GetSignatureForError ());
1217 if (!async_type.IsGenericTask) {
1218 if (this is ContextualReturn)
1221 // Same error code as .NET but better error message
1222 if (async_block.DelegateType != null) {
1223 ec.Report.Error (1997, loc,
1224 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
1225 async_block.DelegateType.GetSignatureForError ());
1227 ec.Report.Error (1997, loc,
1228 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
1229 ec.GetSignatureForError ());
1237 // The return type is actually Task<T> type argument
1239 if (expr.Type == async_type) {
1240 ec.Report.Error (4016, loc,
1241 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
1242 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
1244 block_return_type = async_type.TypeArguments[0];
1248 // Same error code as .NET but better error message
1249 if (block_return_type.Kind == MemberKind.Void) {
1250 ec.Report.Error (127, loc,
1251 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
1252 am.GetSignatureForError ());
1257 var l = am as AnonymousMethodBody;
1258 if (l != null && expr != null) {
1259 if (l.ReturnTypeInference != null) {
1260 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
1265 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
1266 // unexpected debugging experience
1268 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
1269 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
1278 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
1279 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
1282 if (am != null && block_return_type == ec.ReturnType) {
1283 ec.Report.Error (1662, loc,
1284 "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",
1285 am.ContainerType, am.GetSignatureForError ());
1294 protected override void DoEmit (EmitContext ec)
1299 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
1300 if (async_body != null) {
1301 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
1303 // It's null for await without async
1304 if (async_return != null) {
1305 async_return.EmitAssign (ec);
1310 ec.Emit (OpCodes.Leave, async_body.BodyEnd);
1316 if (unwind_protect || ec.EmitAccurateDebugInfo)
1317 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1320 if (unwind_protect) {
1321 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1322 } else if (ec.EmitAccurateDebugInfo) {
1323 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1325 ec.Emit (OpCodes.Ret);
1329 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1332 expr.FlowAnalysis (fc);
1334 base.DoFlowAnalysis (fc);
1338 void Error_ReturnFromIterator (ResolveContext rc)
1340 rc.Report.Error (1622, loc,
1341 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1344 public override Reachability MarkReachable (Reachability rc)
1346 base.MarkReachable (rc);
1347 return Reachability.CreateUnreachable ();
1350 protected override void CloneTo (CloneContext clonectx, Statement t)
1352 Return target = (Return) t;
1353 // It's null for simple return;
1355 target.expr = expr.Clone (clonectx);
1358 public override object Accept (StructuralVisitor visitor)
1360 return visitor.Visit (this);
1364 public class Goto : ExitStatement
1367 LabeledStatement label;
1368 TryFinally try_finally;
1370 public Goto (string label, Location l)
1376 public string Target {
1377 get { return target; }
1380 protected override bool IsLocalExit {
1386 protected override bool DoResolve (BlockContext bc)
1388 label = bc.CurrentBlock.LookupLabel (target);
1389 if (label == null) {
1390 Error_UnknownLabel (bc, target, loc);
1394 try_finally = bc.CurrentTryBlock as TryFinally;
1396 CheckExitBoundaries (bc, label.Block);
1401 public static void Error_UnknownLabel (BlockContext bc, string label, Location loc)
1403 bc.Report.Error (159, loc, "The label `{0}:' could not be found within the scope of the goto statement",
1407 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1409 if (fc.LabelStack == null) {
1410 fc.LabelStack = new List<LabeledStatement> ();
1411 } else if (fc.LabelStack.Contains (label)) {
1415 fc.LabelStack.Add (label);
1416 label.Block.ScanGotoJump (label, fc);
1417 fc.LabelStack.Remove (label);
1421 public override Reachability MarkReachable (Reachability rc)
1423 if (rc.IsUnreachable)
1426 base.MarkReachable (rc);
1428 if (try_finally != null) {
1429 if (try_finally.FinallyBlock.HasReachableClosingBrace) {
1430 label.AddGotoReference (rc, false);
1432 label.AddGotoReference (rc, true);
1437 label.AddGotoReference (rc, false);
1440 return Reachability.CreateUnreachable ();
1443 protected override void CloneTo (CloneContext clonectx, Statement target)
1448 protected override void DoEmit (EmitContext ec)
1451 throw new InternalErrorException ("goto emitted before target resolved");
1453 Label l = label.LabelTarget (ec);
1454 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1457 public override object Accept (StructuralVisitor visitor)
1459 return visitor.Visit (this);
1463 public class LabeledStatement : Statement {
1471 public LabeledStatement (string name, Block block, Location l)
1478 public Label LabelTarget (EmitContext ec)
1483 label = ec.DefineLabel ();
1488 public Block Block {
1494 public string Name {
1495 get { return name; }
1498 protected override void CloneTo (CloneContext clonectx, Statement target)
1500 var t = (LabeledStatement) target;
1502 t.block = clonectx.RemapBlockCopy (block);
1505 public override bool Resolve (BlockContext bc)
1510 protected override void DoEmit (EmitContext ec)
1513 ec.MarkLabel (label);
1516 ec.Emit (OpCodes.Br_S, label);
1519 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1522 fc.Report.Warning (164, 2, loc, "This label has not been referenced");
1528 public override Reachability MarkReachable (Reachability rc)
1530 base.MarkReachable (rc);
1533 rc = new Reachability ();
1538 public void AddGotoReference (Reachability rc, bool finalTarget)
1547 // Label is final target when goto jumps out of try block with
1548 // finally clause. In that case we need leave with target but in C#
1549 // terms the label is unreachable. Using finalTarget we emit
1550 // explicit label not just marker
1553 this.finalTarget = true;
1557 block.ScanGotoJump (this);
1560 public override object Accept (StructuralVisitor visitor)
1562 return visitor.Visit (this);
1568 /// `goto default' statement
1570 public class GotoDefault : SwitchGoto
1572 public GotoDefault (Location l)
1577 public override bool Resolve (BlockContext bc)
1579 if (bc.Switch == null) {
1580 Error_GotoCaseRequiresSwitchBlock (bc);
1584 bc.Switch.RegisterGotoCase (null, null);
1590 protected override void DoEmit (EmitContext ec)
1592 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1595 public override Reachability MarkReachable (Reachability rc)
1597 if (!rc.IsUnreachable) {
1598 var label = switch_statement.DefaultLabel;
1599 if (label.IsUnreachable) {
1600 label.MarkReachable (rc);
1601 switch_statement.Block.ScanGotoJump (label);
1605 return base.MarkReachable (rc);
1608 public override object Accept (StructuralVisitor visitor)
1610 return visitor.Visit (this);
1615 /// `goto case' statement
1617 public class GotoCase : SwitchGoto
1621 public GotoCase (Expression e, Location l)
1627 public Expression Expr {
1633 public SwitchLabel Label { get; set; }
1635 public override bool Resolve (BlockContext ec)
1637 if (ec.Switch == null) {
1638 Error_GotoCaseRequiresSwitchBlock (ec);
1642 Constant c = expr.ResolveLabelConstant (ec);
1648 if (ec.Switch.IsNullable && c is NullLiteral) {
1651 TypeSpec type = ec.Switch.SwitchType;
1652 res = c.Reduce (ec, type);
1654 c.Error_ValueCannotBeConverted (ec, type, true);
1658 if (!Convert.ImplicitStandardConversionExists (c, type))
1659 ec.Report.Warning (469, 2, loc,
1660 "The `goto case' value is not implicitly convertible to type `{0}'",
1661 type.GetSignatureForError ());
1665 ec.Switch.RegisterGotoCase (this, res);
1672 protected override void DoEmit (EmitContext ec)
1674 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, Label.GetILLabel (ec));
1677 protected override void CloneTo (CloneContext clonectx, Statement t)
1679 GotoCase target = (GotoCase) t;
1681 target.expr = expr.Clone (clonectx);
1684 public override Reachability MarkReachable (Reachability rc)
1686 if (!rc.IsUnreachable) {
1687 var label = switch_statement.FindLabel ((Constant) expr);
1688 if (label.IsUnreachable) {
1689 label.MarkReachable (rc);
1690 switch_statement.Block.ScanGotoJump (label);
1694 return base.MarkReachable (rc);
1697 public override object Accept (StructuralVisitor visitor)
1699 return visitor.Visit (this);
1703 public abstract class SwitchGoto : Statement
1705 protected bool unwind_protect;
1706 protected Switch switch_statement;
1708 protected SwitchGoto (Location loc)
1713 protected override void CloneTo (CloneContext clonectx, Statement target)
1718 public override bool Resolve (BlockContext bc)
1720 CheckExitBoundaries (bc, bc.Switch.Block);
1722 unwind_protect = bc.HasAny (ResolveContext.Options.TryScope | ResolveContext.Options.CatchScope);
1723 switch_statement = bc.Switch;
1728 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1733 public override Reachability MarkReachable (Reachability rc)
1735 base.MarkReachable (rc);
1736 return Reachability.CreateUnreachable ();
1739 protected void Error_GotoCaseRequiresSwitchBlock (BlockContext bc)
1741 bc.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1745 public class Throw : Statement {
1748 public Throw (Expression expr, Location l)
1754 public Expression Expr {
1760 public override bool Resolve (BlockContext ec)
1763 if (!ec.HasSet (ResolveContext.Options.CatchScope)) {
1764 ec.Report.Error (156, loc, "A throw statement with no arguments is not allowed outside of a catch clause");
1765 } else if (ec.HasSet (ResolveContext.Options.FinallyScope)) {
1766 for (var b = ec.CurrentBlock; b != null && !b.IsCatchBlock; b = b.Parent) {
1767 if (b.IsFinallyBlock) {
1768 ec.Report.Error (724, loc,
1769 "A throw statement with no arguments is not allowed inside of a finally clause nested inside of the innermost catch clause");
1778 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1783 var et = ec.BuiltinTypes.Exception;
1784 if (Convert.ImplicitConversionExists (ec, expr, et))
1785 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1787 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1792 protected override void DoEmit (EmitContext ec)
1795 ec.Emit (OpCodes.Rethrow);
1799 ec.Emit (OpCodes.Throw);
1803 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1806 expr.FlowAnalysis (fc);
1811 public override Reachability MarkReachable (Reachability rc)
1813 base.MarkReachable (rc);
1814 return Reachability.CreateUnreachable ();
1817 protected override void CloneTo (CloneContext clonectx, Statement t)
1819 Throw target = (Throw) t;
1822 target.expr = expr.Clone (clonectx);
1825 public override object Accept (StructuralVisitor visitor)
1827 return visitor.Visit (this);
1831 public class Break : LocalExitStatement
1833 public Break (Location l)
1838 public override object Accept (StructuralVisitor visitor)
1840 return visitor.Visit (this);
1843 protected override void DoEmit (EmitContext ec)
1845 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1848 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
1850 enclosing_loop.AddEndDefiniteAssignment (fc);
1854 protected override bool DoResolve (BlockContext bc)
1856 enclosing_loop = bc.EnclosingLoopOrSwitch;
1857 return base.DoResolve (bc);
1860 public override Reachability MarkReachable (Reachability rc)
1862 base.MarkReachable (rc);
1864 if (!rc.IsUnreachable)
1865 enclosing_loop.SetEndReachable ();
1867 return Reachability.CreateUnreachable ();
1871 public class Continue : LocalExitStatement
1873 public Continue (Location l)
1878 public override object Accept (StructuralVisitor visitor)
1880 return visitor.Visit (this);
1884 protected override void DoEmit (EmitContext ec)
1886 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1889 protected override bool DoResolve (BlockContext bc)
1891 enclosing_loop = bc.EnclosingLoop;
1892 return base.DoResolve (bc);
1895 public override Reachability MarkReachable (Reachability rc)
1897 base.MarkReachable (rc);
1899 if (!rc.IsUnreachable)
1900 enclosing_loop.SetIteratorReachable ();
1902 return Reachability.CreateUnreachable ();
1906 public abstract class LocalExitStatement : ExitStatement
1908 protected LoopStatement enclosing_loop;
1910 protected LocalExitStatement (Location loc)
1915 protected override bool IsLocalExit {
1921 protected override void CloneTo (CloneContext clonectx, Statement t)
1926 protected override bool DoResolve (BlockContext bc)
1928 if (enclosing_loop == null) {
1929 bc.Report.Error (139, loc, "No enclosing loop out of which to break or continue");
1933 var block = enclosing_loop.Statement as Block;
1935 // Don't need to do extra checks for simple statements loops
1936 if (block != null) {
1937 CheckExitBoundaries (bc, block);
1944 public interface ILocalVariable
1946 void Emit (EmitContext ec);
1947 void EmitAssign (EmitContext ec);
1948 void EmitAddressOf (EmitContext ec);
1951 public interface INamedBlockVariable
1953 Block Block { get; }
1954 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1955 bool IsDeclared { get; }
1956 bool IsParameter { get; }
1957 Location Location { get; }
1960 public class BlockVariableDeclarator
1963 Expression initializer;
1965 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1967 if (li.Type != null)
1968 throw new ArgumentException ("Expected null variable type");
1971 this.initializer = initializer;
1976 public LocalVariable Variable {
1982 public Expression Initializer {
1987 initializer = value;
1993 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1995 var t = (BlockVariableDeclarator) MemberwiseClone ();
1996 if (initializer != null)
1997 t.initializer = initializer.Clone (cloneCtx);
2003 public class BlockVariable : Statement
2005 Expression initializer;
2006 protected FullNamedExpression type_expr;
2007 protected LocalVariable li;
2008 protected List<BlockVariableDeclarator> declarators;
2011 public BlockVariable (FullNamedExpression type, LocalVariable li)
2013 this.type_expr = type;
2015 this.loc = type_expr.Location;
2018 protected BlockVariable (LocalVariable li)
2025 public List<BlockVariableDeclarator> Declarators {
2031 public Expression Initializer {
2036 initializer = value;
2040 public FullNamedExpression TypeExpression {
2046 public LocalVariable Variable {
2054 public void AddDeclarator (BlockVariableDeclarator decl)
2056 if (declarators == null)
2057 declarators = new List<BlockVariableDeclarator> ();
2059 declarators.Add (decl);
2062 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
2064 if (bc.Report.Errors != 0)
2067 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
2069 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
2070 new MemberName (li.Name, li.Location), null);
2072 container.AddField (f);
2075 li.HoistedVariant = new HoistedEvaluatorVariable (f);
2079 public override bool Resolve (BlockContext bc)
2081 return Resolve (bc, true);
2084 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
2086 if (type == null && !li.IsCompilerGenerated) {
2087 var vexpr = type_expr as VarExpr;
2090 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
2091 // same name exists or as a keyword when no type was found
2093 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
2094 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
2095 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
2098 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
2102 if (li.IsConstant) {
2103 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
2107 if (Initializer == null) {
2108 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
2112 if (declarators != null) {
2113 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
2117 Initializer = Initializer.Resolve (bc);
2118 if (Initializer != null) {
2119 ((VarExpr) type_expr).InferType (bc, Initializer);
2120 type = type_expr.Type;
2122 // Set error type to indicate the var was placed correctly but could
2125 // var a = missing ();
2127 type = InternalType.ErrorType;
2132 type = type_expr.ResolveAsType (bc);
2136 if (li.IsConstant && !type.IsConstantCompatible) {
2137 Const.Error_InvalidConstantType (type, loc, bc.Report);
2142 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
2147 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
2149 CreateEvaluatorVariable (bc, li);
2150 } else if (type != InternalType.ErrorType) {
2151 li.PrepareAssignmentAnalysis (bc);
2154 if (initializer != null) {
2155 initializer = ResolveInitializer (bc, li, initializer);
2156 // li.Variable.DefinitelyAssigned
2159 if (declarators != null) {
2160 foreach (var d in declarators) {
2161 d.Variable.Type = li.Type;
2163 CreateEvaluatorVariable (bc, d.Variable);
2164 } else if (type != InternalType.ErrorType) {
2165 d.Variable.PrepareAssignmentAnalysis (bc);
2168 if (d.Initializer != null && resolveDeclaratorInitializers) {
2169 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
2170 // d.Variable.DefinitelyAssigned
2178 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2180 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
2181 return a.ResolveStatement (bc);
2184 protected override void DoEmit (EmitContext ec)
2186 li.CreateBuilder (ec);
2188 if (Initializer != null)
2189 ((ExpressionStatement) Initializer).EmitStatement (ec);
2191 if (declarators != null) {
2192 foreach (var d in declarators) {
2193 d.Variable.CreateBuilder (ec);
2194 if (d.Initializer != null) {
2195 ec.Mark (d.Variable.Location);
2196 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
2202 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2204 if (Initializer != null)
2205 Initializer.FlowAnalysis (fc);
2207 if (declarators != null) {
2208 foreach (var d in declarators) {
2209 if (d.Initializer != null)
2210 d.Initializer.FlowAnalysis (fc);
2217 public override Reachability MarkReachable (Reachability rc)
2219 var init = initializer as ExpressionStatement;
2221 init.MarkReachable (rc);
2223 return base.MarkReachable (rc);
2226 protected override void CloneTo (CloneContext clonectx, Statement target)
2228 BlockVariable t = (BlockVariable) target;
2230 if (type_expr != null)
2231 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
2233 if (initializer != null)
2234 t.initializer = initializer.Clone (clonectx);
2236 if (declarators != null) {
2237 t.declarators = null;
2238 foreach (var d in declarators)
2239 t.AddDeclarator (d.Clone (clonectx));
2243 public override object Accept (StructuralVisitor visitor)
2245 return visitor.Visit (this);
2249 public class BlockConstant : BlockVariable
2251 public BlockConstant (FullNamedExpression type, LocalVariable li)
2256 public override void Emit (EmitContext ec)
2258 // Nothing to emit, not even sequence point
2261 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
2263 initializer = initializer.Resolve (bc);
2264 if (initializer == null)
2267 var c = initializer as Constant;
2269 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
2273 c = c.ConvertImplicitly (li.Type);
2275 if (TypeSpec.IsReferenceType (li.Type))
2276 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
2278 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
2283 li.ConstantValue = c;
2287 public override object Accept (StructuralVisitor visitor)
2289 return visitor.Visit (this);
2294 // The information about a user-perceived local variable
2296 public sealed class LocalVariable : INamedBlockVariable, ILocalVariable
2303 AddressTaken = 1 << 2,
2304 CompilerGenerated = 1 << 3,
2306 ForeachVariable = 1 << 5,
2307 FixedVariable = 1 << 6,
2308 UsingVariable = 1 << 7,
2311 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
2315 readonly string name;
2316 readonly Location loc;
2317 readonly Block block;
2319 Constant const_value;
2321 public VariableInfo VariableInfo;
2322 HoistedVariable hoisted_variant;
2324 LocalBuilder builder;
2326 public LocalVariable (Block block, string name, Location loc)
2333 public LocalVariable (Block block, string name, Flags flags, Location loc)
2334 : this (block, name, loc)
2340 // Used by variable declarators
2342 public LocalVariable (LocalVariable li, string name, Location loc)
2343 : this (li.block, name, li.flags, loc)
2349 public bool AddressTaken {
2351 return (flags & Flags.AddressTaken) != 0;
2355 public Block Block {
2361 public Constant ConstantValue {
2366 const_value = value;
2371 // Hoisted local variable variant
2373 public HoistedVariable HoistedVariant {
2375 return hoisted_variant;
2378 hoisted_variant = value;
2382 public bool IsDeclared {
2384 return type != null;
2388 public bool IsCompilerGenerated {
2390 return (flags & Flags.CompilerGenerated) != 0;
2394 public bool IsConstant {
2396 return (flags & Flags.Constant) != 0;
2400 public bool IsLocked {
2402 return (flags & Flags.IsLocked) != 0;
2405 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
2409 public bool IsThis {
2411 return (flags & Flags.IsThis) != 0;
2415 public bool IsFixed {
2417 return (flags & Flags.FixedVariable) != 0;
2421 bool INamedBlockVariable.IsParameter {
2427 public bool IsReadonly {
2429 return (flags & Flags.ReadonlyMask) != 0;
2433 public Location Location {
2439 public string Name {
2445 public TypeSpec Type {
2456 public void CreateBuilder (EmitContext ec)
2458 if ((flags & Flags.Used) == 0) {
2459 if (VariableInfo == null) {
2460 // Missing flow analysis or wrong variable flags
2461 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
2464 if (VariableInfo.IsEverAssigned)
2465 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
2467 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
2470 if (HoistedVariant != null)
2473 if (builder != null) {
2474 if ((flags & Flags.CompilerGenerated) != 0)
2477 // To avoid Used warning duplicates
2478 throw new InternalErrorException ("Already created variable `{0}'", name);
2482 // All fixed variabled are pinned, a slot has to be alocated
2484 builder = ec.DeclareLocal (Type, IsFixed);
2485 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
2486 ec.DefineLocalVariable (name, builder);
2489 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
2491 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
2496 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2498 if (IsConstant && const_value != null)
2499 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
2501 return new LocalVariableReference (this, loc);
2504 public void Emit (EmitContext ec)
2506 // TODO: Need something better for temporary variables
2507 if ((flags & Flags.CompilerGenerated) != 0)
2510 ec.Emit (OpCodes.Ldloc, builder);
2513 public void EmitAssign (EmitContext ec)
2515 // TODO: Need something better for temporary variables
2516 if ((flags & Flags.CompilerGenerated) != 0)
2519 ec.Emit (OpCodes.Stloc, builder);
2522 public void EmitAddressOf (EmitContext ec)
2524 ec.Emit (OpCodes.Ldloca, builder);
2527 public static string GetCompilerGeneratedName (Block block)
2529 // HACK: Debugger depends on the name semantics
2530 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
2533 public string GetReadOnlyContext ()
2535 switch (flags & Flags.ReadonlyMask) {
2536 case Flags.FixedVariable:
2537 return "fixed variable";
2538 case Flags.ForeachVariable:
2539 return "foreach iteration variable";
2540 case Flags.UsingVariable:
2541 return "using variable";
2544 throw new InternalErrorException ("Variable is not readonly");
2547 public bool IsThisAssigned (FlowAnalysisContext fc, Block block)
2549 if (VariableInfo == null)
2550 throw new Exception ();
2552 if (IsAssigned (fc))
2555 return VariableInfo.IsFullyInitialized (fc, block.StartLocation);
2558 public bool IsAssigned (FlowAnalysisContext fc)
2560 return fc.IsDefinitelyAssigned (VariableInfo);
2563 public void PrepareAssignmentAnalysis (BlockContext bc)
2566 // No need to run assignment analysis for these guys
2568 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2571 VariableInfo = VariableInfo.Create (bc, this);
2575 // Mark the variables as referenced in the user code
2577 public void SetIsUsed ()
2579 flags |= Flags.Used;
2582 public void SetHasAddressTaken ()
2584 flags |= (Flags.AddressTaken | Flags.Used);
2587 public override string ToString ()
2589 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2594 /// Block represents a C# block.
2598 /// This class is used in a number of places: either to represent
2599 /// explicit blocks that the programmer places or implicit blocks.
2601 /// Implicit blocks are used as labels or to introduce variable
2604 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2605 /// they contain extra information that is not necessary on normal blocks.
2607 public class Block : Statement {
2614 HasCapturedVariable = 64,
2615 HasCapturedThis = 1 << 7,
2616 IsExpressionTree = 1 << 8,
2617 CompilerGenerated = 1 << 9,
2618 HasAsyncModifier = 1 << 10,
2620 YieldBlock = 1 << 12,
2621 AwaitBlock = 1 << 13,
2622 FinallyBlock = 1 << 14,
2623 CatchBlock = 1 << 15,
2625 NoFlowAnalysis = 1 << 21
2628 public Block Parent;
2629 public Location StartLocation;
2630 public Location EndLocation;
2632 public ExplicitBlock Explicit;
2633 public ParametersBlock ParametersBlock;
2635 protected Flags flags;
2638 // The statements in this block
2640 protected List<Statement> statements;
2642 protected List<Statement> scope_initializers;
2644 int? resolving_init_idx;
2650 public int ID = id++;
2652 static int clone_id_counter;
2656 // int assignable_slots;
2658 public Block (Block parent, Location start, Location end)
2659 : this (parent, 0, start, end)
2663 public Block (Block parent, Flags flags, Location start, Location end)
2665 if (parent != null) {
2666 // the appropriate constructors will fixup these fields
2667 ParametersBlock = parent.ParametersBlock;
2668 Explicit = parent.Explicit;
2671 this.Parent = parent;
2673 this.StartLocation = start;
2674 this.EndLocation = end;
2676 statements = new List<Statement> (4);
2678 this.original = this;
2683 public Block Original {
2692 public bool IsCompilerGenerated {
2693 get { return (flags & Flags.CompilerGenerated) != 0; }
2694 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2698 public bool IsCatchBlock {
2700 return (flags & Flags.CatchBlock) != 0;
2704 public bool IsFinallyBlock {
2706 return (flags & Flags.FinallyBlock) != 0;
2710 public bool Unchecked {
2711 get { return (flags & Flags.Unchecked) != 0; }
2712 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2715 public bool Unsafe {
2716 get { return (flags & Flags.Unsafe) != 0; }
2717 set { flags |= Flags.Unsafe; }
2720 public List<Statement> Statements {
2721 get { return statements; }
2726 public void SetEndLocation (Location loc)
2731 public void AddLabel (LabeledStatement target)
2733 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2736 public void AddLocalName (LocalVariable li)
2738 AddLocalName (li.Name, li);
2741 public void AddLocalName (string name, INamedBlockVariable li)
2743 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2746 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2748 if (reason == null) {
2749 Error_AlreadyDeclared (name, variable);
2753 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2754 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2755 "to `{0}', which is already used in a `{1}' scope to denote something else",
2759 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2761 var pi = variable as ParametersBlock.ParameterInfo;
2763 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2765 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2766 "A local variable named `{0}' is already defined in this scope", name);
2770 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2772 ParametersBlock.TopBlock.Report.Error (412, loc,
2773 "The type parameter name `{0}' is the same as local variable or parameter name",
2778 // It should be used by expressions which require to
2779 // register a statement during resolve process.
2781 public void AddScopeStatement (Statement s)
2783 if (scope_initializers == null)
2784 scope_initializers = new List<Statement> ();
2787 // Simple recursive helper, when resolve scope initializer another
2788 // new scope initializer can be added, this ensures it's initialized
2789 // before existing one. For now this can happen with expression trees
2790 // in base ctor initializer only
2792 if (resolving_init_idx.HasValue) {
2793 scope_initializers.Insert (resolving_init_idx.Value, s);
2794 ++resolving_init_idx;
2796 scope_initializers.Add (s);
2800 public void InsertStatement (int index, Statement s)
2802 statements.Insert (index, s);
2805 public void AddStatement (Statement s)
2810 public LabeledStatement LookupLabel (string name)
2812 return ParametersBlock.GetLabel (name, this);
2815 public override Reachability MarkReachable (Reachability rc)
2817 if (rc.IsUnreachable)
2820 MarkReachableScope (rc);
2822 foreach (var s in statements) {
2823 rc = s.MarkReachable (rc);
2824 if (rc.IsUnreachable) {
2825 if ((flags & Flags.ReachableEnd) != 0)
2826 return new Reachability ();
2832 flags |= Flags.ReachableEnd;
2837 public void MarkReachableScope (Reachability rc)
2839 base.MarkReachable (rc);
2841 if (scope_initializers != null) {
2842 foreach (var si in scope_initializers)
2843 si.MarkReachable (rc);
2847 public override bool Resolve (BlockContext bc)
2849 if ((flags & Flags.Resolved) != 0)
2852 Block prev_block = bc.CurrentBlock;
2853 bc.CurrentBlock = this;
2856 // Compiler generated scope statements
2858 if (scope_initializers != null) {
2859 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2860 scope_initializers[resolving_init_idx.Value].Resolve (bc);
2863 resolving_init_idx = null;
2867 int statement_count = statements.Count;
2868 for (int ix = 0; ix < statement_count; ix++){
2869 Statement s = statements [ix];
2871 if (!s.Resolve (bc)) {
2873 if (!bc.IsInProbingMode)
2874 statements [ix] = new EmptyStatement (s.loc);
2880 bc.CurrentBlock = prev_block;
2882 flags |= Flags.Resolved;
2886 protected override void DoEmit (EmitContext ec)
2888 for (int ix = 0; ix < statements.Count; ix++){
2889 statements [ix].Emit (ec);
2893 public override void Emit (EmitContext ec)
2895 if (scope_initializers != null)
2896 EmitScopeInitializers (ec);
2901 protected void EmitScopeInitializers (EmitContext ec)
2903 foreach (Statement s in scope_initializers)
2907 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
2909 if (scope_initializers != null) {
2910 foreach (var si in scope_initializers)
2911 si.FlowAnalysis (fc);
2914 return DoFlowAnalysis (fc, 0);
2917 bool DoFlowAnalysis (FlowAnalysisContext fc, int startIndex)
2919 bool end_unreachable = !reachable;
2920 for (; startIndex < statements.Count; ++startIndex) {
2921 var s = statements[startIndex];
2923 end_unreachable = s.FlowAnalysis (fc);
2924 if (s.IsUnreachable) {
2925 statements[startIndex] = new EmptyStatement (s.loc);
2930 // Statement end reachability is needed mostly due to goto support. Consider
2939 // X label is reachable only via goto not as another statement after if. We need
2940 // this for flow-analysis only to carry variable info correctly.
2942 if (end_unreachable) {
2943 for (++startIndex; startIndex < statements.Count; ++startIndex) {
2944 s = statements[startIndex];
2945 if (s is SwitchLabel) {
2946 s.FlowAnalysis (fc);
2950 if (s.IsUnreachable) {
2951 s.FlowAnalysis (fc);
2952 statements[startIndex] = new EmptyStatement (s.loc);
2959 // The condition should be true unless there is forward jumping goto
2961 // if (this is ExplicitBlock && end_unreachable != Explicit.HasReachableClosingBrace)
2964 return !Explicit.HasReachableClosingBrace;
2967 public void ScanGotoJump (Statement label)
2970 for (i = 0; i < statements.Count; ++i) {
2971 if (statements[i] == label)
2975 var rc = new Reachability ();
2976 for (++i; i < statements.Count; ++i) {
2977 var s = statements[i];
2978 rc = s.MarkReachable (rc);
2979 if (rc.IsUnreachable)
2983 flags |= Flags.ReachableEnd;
2986 public void ScanGotoJump (Statement label, FlowAnalysisContext fc)
2989 for (i = 0; i < statements.Count; ++i) {
2990 if (statements[i] == label)
2994 DoFlowAnalysis (fc, ++i);
2998 public override string ToString ()
3000 return String.Format ("{0}: ID={1} Clone={2} Location={3}", GetType (), ID, clone_id != 0, StartLocation);
3004 protected override void CloneTo (CloneContext clonectx, Statement t)
3006 Block target = (Block) t;
3008 target.clone_id = ++clone_id_counter;
3011 clonectx.AddBlockMap (this, target);
3012 if (original != this)
3013 clonectx.AddBlockMap (original, target);
3015 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
3016 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
3019 target.Parent = clonectx.RemapBlockCopy (Parent);
3021 target.statements = new List<Statement> (statements.Count);
3022 foreach (Statement s in statements)
3023 target.statements.Add (s.Clone (clonectx));
3026 public override object Accept (StructuralVisitor visitor)
3028 return visitor.Visit (this);
3032 public class ExplicitBlock : Block
3034 protected AnonymousMethodStorey am_storey;
3036 public ExplicitBlock (Block parent, Location start, Location end)
3037 : this (parent, (Flags) 0, start, end)
3041 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
3042 : base (parent, flags, start, end)
3044 this.Explicit = this;
3049 public AnonymousMethodStorey AnonymousMethodStorey {
3055 public bool HasAwait {
3057 return (flags & Flags.AwaitBlock) != 0;
3061 public bool HasCapturedThis {
3063 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
3066 return (flags & Flags.HasCapturedThis) != 0;
3071 // Used to indicate that the block has reference to parent
3072 // block and cannot be made static when defining anonymous method
3074 public bool HasCapturedVariable {
3076 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
3079 return (flags & Flags.HasCapturedVariable) != 0;
3083 public bool HasReachableClosingBrace {
3085 return (flags & Flags.ReachableEnd) != 0;
3088 flags = value ? flags | Flags.ReachableEnd : flags & ~Flags.ReachableEnd;
3092 public bool HasYield {
3094 return (flags & Flags.YieldBlock) != 0;
3101 // Creates anonymous method storey in current block
3103 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
3106 // Return same story for iterator and async blocks unless we are
3107 // in nested anonymous method
3109 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
3110 return ec.CurrentAnonymousMethod.Storey;
3112 if (am_storey == null) {
3113 MemberBase mc = ec.MemberContext as MemberBase;
3116 // Creates anonymous method storey for this block
3118 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
3124 public override void Emit (EmitContext ec)
3126 if (am_storey != null) {
3127 DefineStoreyContainer (ec, am_storey);
3128 am_storey.EmitStoreyInstantiation (ec, this);
3131 if (scope_initializers != null)
3132 EmitScopeInitializers (ec);
3134 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
3135 ec.Emit (OpCodes.Nop);
3146 if (ec.EmitAccurateDebugInfo && HasReachableClosingBrace && !(this is ParametersBlock) &&
3147 !IsCompilerGenerated && ec.Mark (EndLocation)) {
3148 ec.Emit (OpCodes.Nop);
3152 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
3154 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
3155 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
3156 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
3160 // Creates anonymous method storey
3162 storey.CreateContainer ();
3163 storey.DefineContainer ();
3165 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
3168 // Only first storey in path will hold this reference. All children blocks will
3169 // reference it indirectly using $ref field
3171 for (Block b = Original.Explicit; b != null; b = b.Parent) {
3172 if (b.Parent != null) {
3173 var s = b.Parent.Explicit.AnonymousMethodStorey;
3175 storey.HoistedThis = s.HoistedThis;
3180 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
3181 if (storey.HoistedThis == null)
3182 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
3184 if (storey.HoistedThis != null)
3190 // We are the first storey on path and 'this' has to be hoisted
3192 if (storey.HoistedThis == null) {
3193 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
3195 // ThisReferencesFromChildrenBlock holds all reference even if they
3196 // are not on this path. It saves some memory otherwise it'd have to
3197 // be in every explicit block. We run this check to see if the reference
3198 // is valid for this storey
3200 Block block_on_path = ref_block;
3201 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
3203 if (block_on_path == null)
3206 if (storey.HoistedThis == null) {
3207 storey.AddCapturedThisField (ec, null);
3210 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3213 if (b.AnonymousMethodStorey != null) {
3215 // Don't add storey cross reference for `this' when the storey ends up not
3216 // beeing attached to any parent
3218 if (b.ParametersBlock.StateMachine == null) {
3219 AnonymousMethodStorey s = null;
3220 for (Block ab = b.AnonymousMethodStorey.OriginalSourceBlock.Parent; ab != null; ab = ab.Parent) {
3221 s = ab.Explicit.AnonymousMethodStorey;
3226 // Needs to be in sync with AnonymousMethodBody::DoCreateMethodHost
3228 var parent = storey == null || storey.Kind == MemberKind.Struct ? null : storey;
3229 b.AnonymousMethodStorey.AddCapturedThisField (ec, parent);
3234 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3235 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
3238 // Stop propagation inside same top block
3240 if (b.ParametersBlock == ParametersBlock.Original)
3243 b = b.ParametersBlock;
3246 pb = b as ParametersBlock;
3247 if (pb != null && pb.StateMachine != null) {
3248 if (pb.StateMachine == storey)
3252 // If we are state machine with no parent. We can hook into parent without additional
3253 // reference and capture this directly
3255 ExplicitBlock parent_storey_block = pb;
3256 while (parent_storey_block.Parent != null) {
3257 parent_storey_block = parent_storey_block.Parent.Explicit;
3258 if (parent_storey_block.AnonymousMethodStorey != null) {
3263 if (parent_storey_block.AnonymousMethodStorey == null) {
3264 pb.StateMachine.AddCapturedThisField (ec, null);
3265 b.HasCapturedThis = true;
3269 pb.StateMachine.AddParentStoreyReference (ec, storey);
3272 b.HasCapturedVariable = true;
3278 var ref_blocks = storey.ReferencesFromChildrenBlock;
3279 if (ref_blocks != null) {
3280 foreach (ExplicitBlock ref_block in ref_blocks) {
3281 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
3282 if (b.AnonymousMethodStorey != null) {
3283 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
3286 // Stop propagation inside same top block
3288 if (b.ParametersBlock == ParametersBlock.Original)
3291 b = b.ParametersBlock;
3294 var pb = b as ParametersBlock;
3295 if (pb != null && pb.StateMachine != null) {
3296 if (pb.StateMachine == storey)
3299 pb.StateMachine.AddParentStoreyReference (ec, storey);
3302 b.HasCapturedVariable = true;
3308 storey.PrepareEmit ();
3309 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
3312 public void RegisterAsyncAwait ()
3315 while ((block.flags & Flags.AwaitBlock) == 0) {
3316 block.flags |= Flags.AwaitBlock;
3318 if (block is ParametersBlock)
3321 block = block.Parent.Explicit;
3325 public void RegisterIteratorYield ()
3327 ParametersBlock.TopBlock.IsIterator = true;
3330 while ((block.flags & Flags.YieldBlock) == 0) {
3331 block.flags |= Flags.YieldBlock;
3333 if (block.Parent == null)
3336 block = block.Parent.Explicit;
3340 public void SetCatchBlock ()
3342 flags |= Flags.CatchBlock;
3345 public void SetFinallyBlock ()
3347 flags |= Flags.FinallyBlock;
3350 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
3352 tryBlock.statements = statements;
3353 statements = new List<Statement> (1);
3354 statements.Add (tf);
3359 // ParametersBlock was introduced to support anonymous methods
3360 // and lambda expressions
3362 public class ParametersBlock : ExplicitBlock
3364 public class ParameterInfo : INamedBlockVariable
3366 readonly ParametersBlock block;
3368 public VariableInfo VariableInfo;
3371 public ParameterInfo (ParametersBlock block, int index)
3379 public ParametersBlock Block {
3385 Block INamedBlockVariable.Block {
3391 public bool IsDeclared {
3397 public bool IsParameter {
3403 public bool IsLocked {
3412 public Location Location {
3414 return Parameter.Location;
3418 public Parameter Parameter {
3420 return block.Parameters [index];
3424 public TypeSpec ParameterType {
3426 return Parameter.Type;
3432 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
3434 return new ParameterReference (this, loc);
3439 // Block is converted into an expression
3441 sealed class BlockScopeExpression : Expression
3444 readonly ParametersBlock block;
3446 public BlockScopeExpression (Expression child, ParametersBlock block)
3452 public override bool ContainsEmitWithAwait ()
3454 return child.ContainsEmitWithAwait ();
3457 public override Expression CreateExpressionTree (ResolveContext ec)
3459 throw new NotSupportedException ();
3462 protected override Expression DoResolve (ResolveContext ec)
3467 child = child.Resolve (ec);
3471 eclass = child.eclass;
3476 public override void Emit (EmitContext ec)
3478 block.EmitScopeInitializers (ec);
3483 protected ParametersCompiled parameters;
3484 protected ParameterInfo[] parameter_info;
3485 protected bool resolved;
3486 protected ToplevelBlock top_block;
3487 protected StateMachine state_machine;
3488 protected Dictionary<string, object> labels;
3490 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start, Flags flags = 0)
3491 : base (parent, 0, start, start)
3493 if (parameters == null)
3494 throw new ArgumentNullException ("parameters");
3496 this.parameters = parameters;
3497 ParametersBlock = this;
3499 this.flags |= flags | (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
3501 this.top_block = parent.ParametersBlock.top_block;
3502 ProcessParameters ();
3505 protected ParametersBlock (ParametersCompiled parameters, Location start)
3506 : base (null, 0, start, start)
3508 if (parameters == null)
3509 throw new ArgumentNullException ("parameters");
3511 this.parameters = parameters;
3512 ParametersBlock = this;
3516 // It's supposed to be used by method body implementation of anonymous methods
3518 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
3519 : base (null, 0, source.StartLocation, source.EndLocation)
3521 this.parameters = parameters;
3522 this.statements = source.statements;
3523 this.scope_initializers = source.scope_initializers;
3525 this.resolved = true;
3526 this.reachable = source.reachable;
3527 this.am_storey = source.am_storey;
3528 this.state_machine = source.state_machine;
3529 this.flags = source.flags & Flags.ReachableEnd;
3531 ParametersBlock = this;
3534 // Overwrite original for comparison purposes when linking cross references
3535 // between anonymous methods
3537 Original = source.Original;
3542 public bool IsAsync {
3544 return (flags & Flags.HasAsyncModifier) != 0;
3547 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
3552 // Block has been converted to expression tree
3554 public bool IsExpressionTree {
3556 return (flags & Flags.IsExpressionTree) != 0;
3561 // The parameters for the block.
3563 public ParametersCompiled Parameters {
3569 public StateMachine StateMachine {
3571 return state_machine;
3575 public ToplevelBlock TopBlock {
3581 public bool Resolved {
3583 return (flags & Flags.Resolved) != 0;
3587 public int TemporaryLocalsCount { get; set; }
3592 // Checks whether all `out' parameters have been assigned.
3594 public void CheckControlExit (FlowAnalysisContext fc)
3596 CheckControlExit (fc, fc.DefiniteAssignment);
3599 public virtual void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
3601 if (parameter_info == null)
3604 foreach (var p in parameter_info) {
3605 if (p.VariableInfo == null)
3608 if (p.VariableInfo.IsAssigned (dat))
3611 fc.Report.Error (177, p.Location,
3612 "The out parameter `{0}' must be assigned to before control leaves the current method",
3617 protected override void CloneTo (CloneContext clonectx, Statement t)
3619 base.CloneTo (clonectx, t);
3621 var target = (ParametersBlock) t;
3624 // Clone label statements as well as they contain block reference
3628 if (pb.labels != null) {
3629 target.labels = new Dictionary<string, object> ();
3631 foreach (var entry in pb.labels) {
3632 var list = entry.Value as List<LabeledStatement>;
3635 var list_clone = new List<LabeledStatement> ();
3636 foreach (var lentry in list) {
3637 list_clone.Add (RemapLabeledStatement (lentry, lentry.Block, clonectx.RemapBlockCopy (lentry.Block)));
3640 target.labels.Add (entry.Key, list_clone);
3642 var labeled = (LabeledStatement) entry.Value;
3643 target.labels.Add (entry.Key, RemapLabeledStatement (labeled, labeled.Block, clonectx.RemapBlockCopy (labeled.Block)));
3650 if (pb.Parent == null)
3653 pb = pb.Parent.ParametersBlock;
3657 public override Expression CreateExpressionTree (ResolveContext ec)
3659 if (statements.Count == 1) {
3660 Expression expr = statements[0].CreateExpressionTree (ec);
3661 if (scope_initializers != null)
3662 expr = new BlockScopeExpression (expr, this);
3667 return base.CreateExpressionTree (ec);
3670 public override void Emit (EmitContext ec)
3672 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3673 DefineStoreyContainer (ec, state_machine);
3674 state_machine.EmitStoreyInstantiation (ec, this);
3680 public void EmitEmbedded (EmitContext ec)
3682 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3683 DefineStoreyContainer (ec, state_machine);
3684 state_machine.EmitStoreyInstantiation (ec, this);
3690 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
3692 var res = base.DoFlowAnalysis (fc);
3694 if (HasReachableClosingBrace)
3695 CheckControlExit (fc);
3700 public LabeledStatement GetLabel (string name, Block block)
3703 // Cloned parameters blocks can have their own cloned version of top-level labels
3705 if (labels == null) {
3707 return Parent.ParametersBlock.GetLabel (name, block);
3713 if (!labels.TryGetValue (name, out value)) {
3717 var label = value as LabeledStatement;
3719 if (label != null) {
3721 if (label.Block == b)
3724 } while (b != null);
3726 List<LabeledStatement> list = (List<LabeledStatement>) value;
3727 for (int i = 0; i < list.Count; ++i) {
3729 if (label.Block == b)
3737 public ParameterInfo GetParameterInfo (Parameter p)
3739 for (int i = 0; i < parameters.Count; ++i) {
3740 if (parameters[i] == p)
3741 return parameter_info[i];
3744 throw new ArgumentException ("Invalid parameter");
3747 public ParameterReference GetParameterReference (int index, Location loc)
3749 return new ParameterReference (parameter_info[index], loc);
3752 public Statement PerformClone ()
3754 CloneContext clonectx = new CloneContext ();
3755 return Clone (clonectx);
3758 protected void ProcessParameters ()
3760 if (parameters.Count == 0)
3763 parameter_info = new ParameterInfo[parameters.Count];
3764 for (int i = 0; i < parameter_info.Length; ++i) {
3765 var p = parameters.FixedParameters[i];
3769 // TODO: Should use Parameter only and more block there
3770 parameter_info[i] = new ParameterInfo (this, i);
3772 AddLocalName (p.Name, parameter_info[i]);
3776 static LabeledStatement RemapLabeledStatement (LabeledStatement stmt, Block src, Block dst)
3778 var src_stmts = src.Statements;
3779 for (int i = 0; i < src_stmts.Count; ++i) {
3780 if (src_stmts[i] == stmt)
3781 return (LabeledStatement) dst.Statements[i];
3784 throw new InternalErrorException ("Should never be reached");
3787 public override bool Resolve (BlockContext bc)
3789 // TODO: if ((flags & Flags.Resolved) != 0)
3796 if (bc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3797 flags |= Flags.IsExpressionTree;
3800 PrepareAssignmentAnalysis (bc);
3802 if (!base.Resolve (bc))
3805 } catch (Exception e) {
3806 if (e is CompletionResult || bc.Report.IsDisabled || e is FatalException || bc.Report.Printer is NullReportPrinter || bc.Module.Compiler.Settings.BreakOnInternalError)
3809 if (bc.CurrentBlock != null) {
3810 bc.Report.Error (584, bc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3812 bc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3817 // If an asynchronous body of F is either an expression classified as nothing, or a
3818 // statement block where no return statements have expressions, the inferred return type is Task
3821 var am = bc.CurrentAnonymousMethod as AnonymousMethodBody;
3822 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3823 am.ReturnTypeInference = null;
3824 am.ReturnType = bc.Module.PredefinedTypes.Task.TypeSpec;
3832 void PrepareAssignmentAnalysis (BlockContext bc)
3834 for (int i = 0; i < parameters.Count; ++i) {
3835 var par = parameters.FixedParameters[i];
3837 if ((par.ModFlags & Parameter.Modifier.OUT) == 0)
3840 parameter_info [i].VariableInfo = VariableInfo.Create (bc, (Parameter) par);
3844 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3846 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3847 var stateMachine = new IteratorStorey (iterator);
3849 state_machine = stateMachine;
3850 iterator.SetStateMachine (stateMachine);
3852 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null, Flags.CompilerGenerated);
3853 tlb.Original = this;
3854 tlb.state_machine = stateMachine;
3855 tlb.AddStatement (new Return (iterator, iterator.Location));
3859 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3861 for (int i = 0; i < parameters.Count; i++) {
3862 Parameter p = parameters[i];
3863 Parameter.Modifier mod = p.ModFlags;
3864 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3865 host.Compiler.Report.Error (1988, p.Location,
3866 "Async methods cannot have ref or out parameters");
3870 if (p is ArglistParameter) {
3871 host.Compiler.Report.Error (4006, p.Location,
3872 "__arglist is not allowed in parameter list of async methods");
3876 if (parameters.Types[i].IsPointer) {
3877 host.Compiler.Report.Error (4005, p.Location,
3878 "Async methods cannot have unsafe parameters");
3884 host.Compiler.Report.Warning (1998, 1, loc,
3885 "Async block lacks `await' operator and will run synchronously");
3888 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3889 var initializer = new AsyncInitializer (this, host, block_type);
3890 initializer.Type = block_type;
3891 initializer.DelegateType = delegateType;
3893 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3895 state_machine = stateMachine;
3896 initializer.SetStateMachine (stateMachine);
3898 const Flags flags = Flags.CompilerGenerated;
3900 var b = this is ToplevelBlock ?
3901 new ToplevelBlock (host.Compiler, Parameters, Location.Null, flags) :
3902 new ParametersBlock (Parent, parameters, Location.Null, flags | Flags.HasAsyncModifier);
3905 b.state_machine = stateMachine;
3906 b.AddStatement (new AsyncInitializerStatement (initializer));
3914 public class ToplevelBlock : ParametersBlock
3916 LocalVariable this_variable;
3917 CompilerContext compiler;
3918 Dictionary<string, object> names;
3920 List<ExplicitBlock> this_references;
3922 public ToplevelBlock (CompilerContext ctx, Location loc)
3923 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3927 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start, Flags flags = 0)
3928 : base (parameters, start)
3930 this.compiler = ctx;
3934 ProcessParameters ();
3938 // Recreates a top level block from parameters block. Used for
3939 // compiler generated methods where the original block comes from
3940 // explicit child block. This works for already resolved blocks
3941 // only to ensure we resolve them in the correct flow order
3943 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3944 : base (source, parameters)
3946 this.compiler = source.TopBlock.compiler;
3950 public bool IsIterator {
3952 return (flags & Flags.Iterator) != 0;
3955 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3959 public Report Report {
3961 return compiler.Report;
3966 // Used by anonymous blocks to track references of `this' variable
3968 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3970 return this_references;
3975 // Returns the "this" instance variable of this block.
3976 // See AddThisVariable() for more information.
3978 public LocalVariable ThisVariable {
3980 return this_variable;
3984 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3987 names = new Dictionary<string, object> ();
3990 if (!names.TryGetValue (name, out value)) {
3991 names.Add (name, li);
3995 INamedBlockVariable existing = value as INamedBlockVariable;
3996 List<INamedBlockVariable> existing_list;
3997 if (existing != null) {
3998 existing_list = new List<INamedBlockVariable> ();
3999 existing_list.Add (existing);
4000 names[name] = existing_list;
4002 existing_list = (List<INamedBlockVariable>) value;
4006 // A collision checking between local names
4008 var variable_block = li.Block.Explicit;
4009 for (int i = 0; i < existing_list.Count; ++i) {
4010 existing = existing_list[i];
4011 Block b = existing.Block.Explicit;
4013 // Collision at same level
4014 if (variable_block == b) {
4015 li.Block.Error_AlreadyDeclared (name, li);
4019 // Collision with parent
4020 Block parent = variable_block;
4021 while ((parent = parent.Parent) != null) {
4023 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
4024 i = existing_list.Count;
4029 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
4030 // Collision with children
4031 while ((b = b.Parent) != null) {
4032 if (variable_block == b) {
4033 li.Block.Error_AlreadyDeclared (name, li, "child");
4034 i = existing_list.Count;
4041 existing_list.Add (li);
4044 public void AddLabel (string name, LabeledStatement label)
4047 labels = new Dictionary<string, object> ();
4050 if (!labels.TryGetValue (name, out value)) {
4051 labels.Add (name, label);
4055 LabeledStatement existing = value as LabeledStatement;
4056 List<LabeledStatement> existing_list;
4057 if (existing != null) {
4058 existing_list = new List<LabeledStatement> ();
4059 existing_list.Add (existing);
4060 labels[name] = existing_list;
4062 existing_list = (List<LabeledStatement>) value;
4066 // A collision checking between labels
4068 for (int i = 0; i < existing_list.Count; ++i) {
4069 existing = existing_list[i];
4070 Block b = existing.Block;
4072 // Collision at same level
4073 if (label.Block == b) {
4074 Report.SymbolRelatedToPreviousError (existing.loc, name);
4075 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
4079 // Collision with parent
4081 while ((b = b.Parent) != null) {
4082 if (existing.Block == b) {
4083 Report.Error (158, label.loc,
4084 "The label `{0}' shadows another label by the same name in a contained scope", name);
4085 i = existing_list.Count;
4090 // Collision with with children
4092 while ((b = b.Parent) != null) {
4093 if (label.Block == b) {
4094 Report.Error (158, label.loc,
4095 "The label `{0}' shadows another label by the same name in a contained scope", name);
4096 i = existing_list.Count;
4102 existing_list.Add (label);
4105 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
4107 if (this_references == null)
4108 this_references = new List<ExplicitBlock> ();
4110 if (!this_references.Contains (block))
4111 this_references.Add (block);
4114 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
4116 this_references.Remove (block);
4120 // Creates an arguments set from all parameters, useful for method proxy calls
4122 public Arguments GetAllParametersArguments ()
4124 int count = parameters.Count;
4125 Arguments args = new Arguments (count);
4126 for (int i = 0; i < count; ++i) {
4127 var pi = parameter_info[i];
4128 var arg_expr = GetParameterReference (i, pi.Location);
4130 Argument.AType atype_modifier;
4131 switch (pi.Parameter.ParameterModifier & Parameter.Modifier.RefOutMask) {
4132 case Parameter.Modifier.REF:
4133 atype_modifier = Argument.AType.Ref;
4135 case Parameter.Modifier.OUT:
4136 atype_modifier = Argument.AType.Out;
4143 args.Add (new Argument (arg_expr, atype_modifier));
4150 // Lookup inside a block, the returned value can represent 3 states
4152 // true+variable: A local name was found and it's valid
4153 // false+variable: A local name was found in a child block only
4154 // false+null: No local name was found
4156 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
4162 if (!names.TryGetValue (name, out value))
4165 variable = value as INamedBlockVariable;
4167 if (variable != null) {
4169 if (variable.Block == b.Original)
4173 } while (b != null);
4181 } while (b != null);
4183 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
4184 for (int i = 0; i < list.Count; ++i) {
4187 if (variable.Block == b.Original)
4191 } while (b != null);
4199 } while (b != null);
4210 // This is used by non-static `struct' constructors which do not have an
4211 // initializer - in this case, the constructor must initialize all of the
4212 // struct's fields. To do this, we add a "this" variable and use the flow
4213 // analysis code to ensure that it's been fully initialized before control
4214 // leaves the constructor.
4216 public void AddThisVariable (BlockContext bc)
4218 if (this_variable != null)
4219 throw new InternalErrorException (StartLocation.ToString ());
4221 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
4222 this_variable.Type = bc.CurrentType;
4223 this_variable.PrepareAssignmentAnalysis (bc);
4226 public override void CheckControlExit (FlowAnalysisContext fc, DefiniteAssignmentBitSet dat)
4229 // If we're a non-static struct constructor which doesn't have an
4230 // initializer, then we must initialize all of the struct's fields.
4232 if (this_variable != null)
4233 this_variable.IsThisAssigned (fc, this);
4235 base.CheckControlExit (fc, dat);
4238 public override void Emit (EmitContext ec)
4240 if (Report.Errors > 0)
4244 if (IsCompilerGenerated) {
4245 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4253 // If `HasReturnLabel' is set, then we already emitted a
4254 // jump to the end of the method, so we must emit a `ret'
4257 // Unfortunately, System.Reflection.Emit automatically emits
4258 // a leave to the end of a finally block. This is a problem
4259 // if no code is following the try/finally block since we may
4260 // jump to a point after the end of the method.
4261 // As a workaround, we're always creating a return label in
4264 if (ec.HasReturnLabel || HasReachableClosingBrace) {
4265 if (ec.HasReturnLabel)
4266 ec.MarkLabel (ec.ReturnLabel);
4268 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
4269 ec.Mark (EndLocation);
4271 if (ec.ReturnType.Kind != MemberKind.Void)
4272 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
4274 ec.Emit (OpCodes.Ret);
4277 } catch (Exception e) {
4278 throw new InternalErrorException (e, StartLocation);
4282 public bool Resolve (BlockContext bc, IMethodData md)
4287 var errors = bc.Report.Errors;
4291 if (bc.Report.Errors > errors)
4294 MarkReachable (new Reachability ());
4296 if (HasReachableClosingBrace && bc.ReturnType.Kind != MemberKind.Void) {
4297 // TODO: var md = bc.CurrentMemberDefinition;
4298 bc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
4301 if ((flags & Flags.NoFlowAnalysis) != 0)
4304 var fc = new FlowAnalysisContext (bc.Module.Compiler, this, bc.AssignmentInfoOffset);
4307 } catch (Exception e) {
4308 throw new InternalErrorException (e, StartLocation);
4315 public class SwitchLabel : Statement
4323 // if expr == null, then it is the default case.
4325 public SwitchLabel (Expression expr, Location l)
4331 public bool IsDefault {
4333 return label == null;
4337 public Expression Label {
4343 public Location Location {
4349 public Constant Converted {
4358 public bool SectionStart { get; set; }
4360 public Label GetILLabel (EmitContext ec)
4362 if (il_label == null){
4363 il_label = ec.DefineLabel ();
4366 return il_label.Value;
4369 protected override void DoEmit (EmitContext ec)
4371 ec.MarkLabel (GetILLabel (ec));
4374 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4379 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (fc.SwitchInitialDefinitiveAssignment);
4383 public override bool Resolve (BlockContext bc)
4385 if (ResolveAndReduce (bc))
4386 bc.Switch.RegisterLabel (bc, this);
4392 // Resolves the expression, reduces it to a literal if possible
4393 // and then converts it to the requested type.
4395 bool ResolveAndReduce (BlockContext rc)
4400 var c = label.ResolveLabelConstant (rc);
4404 if (rc.Switch.IsNullable && c is NullLiteral) {
4409 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType);
4410 return converted != null;
4413 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
4415 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
4416 ec.Report.Error (152, loc, "The label `{0}' already occurs in this switch statement", GetSignatureForError ());
4419 protected override void CloneTo (CloneContext clonectx, Statement target)
4421 var t = (SwitchLabel) target;
4423 t.label = label.Clone (clonectx);
4426 public override object Accept (StructuralVisitor visitor)
4428 return visitor.Visit (this);
4431 public string GetSignatureForError ()
4434 if (converted == null)
4437 label = converted.GetValueAsLiteral ();
4439 return string.Format ("case {0}:", label);
4443 public class Switch : LoopStatement
4445 // structure used to hold blocks of keys while calculating table switch
4446 sealed class LabelsRange : IComparable<LabelsRange>
4448 public readonly long min;
4450 public readonly List<long> label_values;
4452 public LabelsRange (long value)
4455 label_values = new List<long> ();
4456 label_values.Add (value);
4459 public LabelsRange (long min, long max, ICollection<long> values)
4463 this.label_values = new List<long> (values);
4468 return max - min + 1;
4472 public bool AddValue (long value)
4474 var gap = value - min + 1;
4475 // Ensure the range has > 50% occupancy
4476 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
4480 label_values.Add (value);
4484 public int CompareTo (LabelsRange other)
4486 int nLength = label_values.Count;
4487 int nLengthOther = other.label_values.Count;
4488 if (nLengthOther == nLength)
4489 return (int) (other.min - min);
4491 return nLength - nLengthOther;
4495 sealed class DispatchStatement : Statement
4497 readonly Switch body;
4499 public DispatchStatement (Switch body)
4504 protected override void CloneTo (CloneContext clonectx, Statement target)
4506 throw new NotImplementedException ();
4509 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4514 protected override void DoEmit (EmitContext ec)
4516 body.EmitDispatch (ec);
4520 class MissingBreak : Statement
4524 public MissingBreak (SwitchLabel sl)
4530 protected override void DoEmit (EmitContext ec)
4534 protected override void CloneTo (CloneContext clonectx, Statement target)
4538 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4540 fc.Report.Error (163, loc, "Control cannot fall through from one case label `{0}' to another",
4541 label.GetSignatureForError ());
4547 public Expression Expr;
4550 // Mapping of all labels to their SwitchLabels
4552 Dictionary<long, SwitchLabel> labels;
4553 Dictionary<string, SwitchLabel> string_labels;
4554 List<SwitchLabel> case_labels;
4556 List<Tuple<GotoCase, Constant>> goto_cases;
4557 List<DefiniteAssignmentBitSet> end_reachable_das;
4560 /// The governing switch type
4562 public TypeSpec SwitchType;
4564 Expression new_expr;
4566 SwitchLabel case_null;
4567 SwitchLabel case_default;
4569 Label defaultLabel, nullLabel;
4570 VariableReference value;
4571 ExpressionStatement string_dictionary;
4572 FieldExpr switch_cache_field;
4573 ExplicitBlock block;
4577 // Nullable Types support
4579 Nullable.Unwrap unwrap;
4581 public Switch (Expression e, ExplicitBlock block, Location l)
4589 public SwitchLabel ActiveLabel { get; set; }
4591 public ExplicitBlock Block {
4597 public SwitchLabel DefaultLabel {
4599 return case_default;
4603 public bool IsNullable {
4605 return unwrap != null;
4609 public List<SwitchLabel> RegisteredLabels {
4616 // Determines the governing type for a switch. The returned
4617 // expression might be the expression from the switch, or an
4618 // expression that includes any potential conversions to
4620 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
4622 switch (expr.Type.BuiltinType) {
4623 case BuiltinTypeSpec.Type.Byte:
4624 case BuiltinTypeSpec.Type.SByte:
4625 case BuiltinTypeSpec.Type.UShort:
4626 case BuiltinTypeSpec.Type.Short:
4627 case BuiltinTypeSpec.Type.UInt:
4628 case BuiltinTypeSpec.Type.Int:
4629 case BuiltinTypeSpec.Type.ULong:
4630 case BuiltinTypeSpec.Type.Long:
4631 case BuiltinTypeSpec.Type.Char:
4632 case BuiltinTypeSpec.Type.String:
4633 case BuiltinTypeSpec.Type.Bool:
4637 if (expr.Type.IsEnum)
4641 // Try to find a *user* defined implicit conversion.
4643 // If there is no implicit conversion, or if there are multiple
4644 // conversions, we have to report an error
4646 Expression converted = null;
4647 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
4650 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
4655 // Ignore over-worked ImplicitUserConversions that do
4656 // an implicit conversion in addition to the user conversion.
4658 if (!(e is UserCast))
4661 if (converted != null){
4662 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
4671 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
4673 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
4688 public void RegisterLabel (BlockContext rc, SwitchLabel sl)
4690 case_labels.Add (sl);
4693 if (case_default != null) {
4694 sl.Error_AlreadyOccurs (rc, case_default);
4703 if (string_labels != null) {
4704 string string_value = sl.Converted.GetValue () as string;
4705 if (string_value == null)
4708 string_labels.Add (string_value, sl);
4710 if (sl.Converted is NullLiteral) {
4713 labels.Add (sl.Converted.GetValueAsLong (), sl);
4716 } catch (ArgumentException) {
4717 if (string_labels != null)
4718 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
4720 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
4725 // This method emits code for a lookup-based switch statement (non-string)
4726 // Basically it groups the cases into blocks that are at least half full,
4727 // and then spits out individual lookup opcodes for each block.
4728 // It emits the longest blocks first, and short blocks are just
4729 // handled with direct compares.
4731 void EmitTableSwitch (EmitContext ec, Expression val)
4733 if (labels != null && labels.Count > 0) {
4734 List<LabelsRange> ranges;
4735 if (string_labels != null) {
4736 // We have done all hard work for string already
4737 // setup single range only
4738 ranges = new List<LabelsRange> (1);
4739 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
4741 var element_keys = new long[labels.Count];
4742 labels.Keys.CopyTo (element_keys, 0);
4743 Array.Sort (element_keys);
4746 // Build possible ranges of switch labes to reduce number
4749 ranges = new List<LabelsRange> (element_keys.Length);
4750 var range = new LabelsRange (element_keys[0]);
4752 for (int i = 1; i < element_keys.Length; ++i) {
4753 var l = element_keys[i];
4754 if (range.AddValue (l))
4757 range = new LabelsRange (l);
4761 // sort the blocks so we can tackle the largest ones first
4765 Label lbl_default = defaultLabel;
4766 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4768 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4769 LabelsRange kb = ranges[range_index];
4770 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4772 // Optimize small ranges using simple equality check
4773 if (kb.Range <= 2) {
4774 foreach (var key in kb.label_values) {
4775 SwitchLabel sl = labels[key];
4776 if (sl == case_default || sl == case_null)
4779 if (sl.Converted.IsZeroInteger) {
4780 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4783 sl.Converted.Emit (ec);
4784 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4788 // TODO: if all the keys in the block are the same and there are
4789 // no gaps/defaults then just use a range-check.
4790 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4791 // TODO: optimize constant/I4 cases
4793 // check block range (could be > 2^31)
4795 ec.EmitLong (kb.min);
4796 ec.Emit (OpCodes.Blt, lbl_default);
4799 ec.EmitLong (kb.max);
4800 ec.Emit (OpCodes.Bgt, lbl_default);
4805 ec.EmitLong (kb.min);
4806 ec.Emit (OpCodes.Sub);
4809 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4813 int first = (int) kb.min;
4816 ec.Emit (OpCodes.Sub);
4817 } else if (first < 0) {
4818 ec.EmitInt (-first);
4819 ec.Emit (OpCodes.Add);
4823 // first, build the list of labels for the switch
4825 long cJumps = kb.Range;
4826 Label[] switch_labels = new Label[cJumps];
4827 for (int iJump = 0; iJump < cJumps; iJump++) {
4828 var key = kb.label_values[iKey];
4829 if (key == kb.min + iJump) {
4830 switch_labels[iJump] = labels[key].GetILLabel (ec);
4833 switch_labels[iJump] = lbl_default;
4837 // emit the switch opcode
4838 ec.Emit (OpCodes.Switch, switch_labels);
4841 // mark the default for this block
4842 if (range_index != 0)
4843 ec.MarkLabel (lbl_default);
4846 // the last default just goes to the end
4847 if (ranges.Count > 0)
4848 ec.Emit (OpCodes.Br, lbl_default);
4852 public SwitchLabel FindLabel (Constant value)
4854 SwitchLabel sl = null;
4856 if (string_labels != null) {
4857 string s = value.GetValue () as string;
4859 if (case_null != null)
4861 else if (case_default != null)
4864 string_labels.TryGetValue (s, out sl);
4867 if (value is NullLiteral) {
4870 labels.TryGetValue (value.GetValueAsLong (), out sl);
4877 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
4879 Expr.FlowAnalysis (fc);
4881 var prev_switch = fc.SwitchInitialDefinitiveAssignment;
4882 var InitialDefinitiveAssignment = fc.DefiniteAssignment;
4883 fc.SwitchInitialDefinitiveAssignment = InitialDefinitiveAssignment;
4885 block.FlowAnalysis (fc);
4887 fc.SwitchInitialDefinitiveAssignment = prev_switch;
4889 if (end_reachable_das != null) {
4890 var sections_das = DefiniteAssignmentBitSet.And (end_reachable_das);
4891 InitialDefinitiveAssignment |= sections_das;
4892 end_reachable_das = null;
4895 fc.DefiniteAssignment = InitialDefinitiveAssignment;
4897 return case_default != null && !end_reachable;
4900 public override bool Resolve (BlockContext ec)
4902 Expr = Expr.Resolve (ec);
4906 new_expr = SwitchGoverningType (ec, Expr);
4908 if (new_expr == null && Expr.Type.IsNullableType) {
4909 unwrap = Nullable.Unwrap.Create (Expr, false);
4913 new_expr = SwitchGoverningType (ec, unwrap);
4916 if (new_expr == null) {
4917 if (Expr.Type != InternalType.ErrorType) {
4918 ec.Report.Error (151, loc,
4919 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4920 Expr.Type.GetSignatureForError ());
4927 SwitchType = new_expr.Type;
4929 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4930 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4934 if (block.Statements.Count == 0)
4937 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4938 string_labels = new Dictionary<string, SwitchLabel> ();
4940 labels = new Dictionary<long, SwitchLabel> ();
4943 case_labels = new List<SwitchLabel> ();
4945 var constant = new_expr as Constant;
4948 // Don't need extra variable for constant switch or switch with
4949 // only default case
4951 if (constant == null) {
4953 // Store switch expression for comparison purposes
4955 value = new_expr as VariableReference;
4956 if (value == null && !HasOnlyDefaultSection ()) {
4957 var current_block = ec.CurrentBlock;
4958 ec.CurrentBlock = Block;
4959 // Create temporary variable inside switch scope
4960 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4962 ec.CurrentBlock = current_block;
4966 Switch old_switch = ec.Switch;
4968 var parent_los = ec.EnclosingLoopOrSwitch;
4969 ec.EnclosingLoopOrSwitch = this;
4971 var ok = Statement.Resolve (ec);
4973 ec.EnclosingLoopOrSwitch = parent_los;
4974 ec.Switch = old_switch;
4977 // Check if all goto cases are valid. Needs to be done after switch
4978 // is resolved because goto can jump forward in the scope.
4980 if (goto_cases != null) {
4981 foreach (var gc in goto_cases) {
4982 if (gc.Item1 == null) {
4983 if (DefaultLabel == null) {
4984 Goto.Error_UnknownLabel (ec, "default", loc);
4990 var sl = FindLabel (gc.Item2);
4992 Goto.Error_UnknownLabel (ec, "case " + gc.Item2.GetValueAsLiteral (), loc);
4994 gc.Item1.Label = sl;
5002 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
5003 ResolveStringSwitchMap (ec);
5007 // Anonymous storey initialization has to happen before
5008 // any generated switch dispatch
5010 block.InsertStatement (0, new DispatchStatement (this));
5015 bool HasOnlyDefaultSection ()
5017 for (int i = 0; i < block.Statements.Count; ++i) {
5018 var s = block.Statements[i] as SwitchLabel;
5020 if (s == null || s.IsDefault)
5029 public override Reachability MarkReachable (Reachability rc)
5031 if (rc.IsUnreachable)
5034 base.MarkReachable (rc);
5036 block.MarkReachableScope (rc);
5038 if (block.Statements.Count == 0)
5041 SwitchLabel constant_label = null;
5042 var constant = new_expr as Constant;
5044 if (constant != null) {
5045 constant_label = FindLabel (constant) ?? case_default;
5046 if (constant_label == null) {
5047 block.Statements.RemoveAt (0);
5052 var section_rc = new Reachability ();
5053 SwitchLabel prev_label = null;
5055 for (int i = 0; i < block.Statements.Count; ++i) {
5056 var s = block.Statements[i];
5057 var sl = s as SwitchLabel;
5059 if (sl != null && sl.SectionStart) {
5061 // Section is marked already via constant switch or goto case
5063 if (!sl.IsUnreachable) {
5064 section_rc = new Reachability ();
5068 if (section_rc.IsUnreachable) {
5069 section_rc = new Reachability ();
5071 if (prev_label != null) {
5072 sl.SectionStart = false;
5073 s = new MissingBreak (prev_label);
5074 s.MarkReachable (rc);
5075 block.Statements.Insert (i - 1, s);
5082 if (constant_label != null && constant_label != sl)
5083 section_rc = Reachability.CreateUnreachable ();
5086 section_rc = s.MarkReachable (section_rc);
5089 if (!section_rc.IsUnreachable && prev_label != null) {
5090 prev_label.SectionStart = false;
5091 var s = new MissingBreak (prev_label);
5092 s.MarkReachable (rc);
5093 block.Statements.Add (s);
5097 // Reachability can affect parent only when all possible paths are handled but
5098 // we still need to run reachability check on switch body to check for fall-through
5100 if (case_default == null && constant_label == null)
5104 // We have at least one local exit from the switch
5109 return Reachability.CreateUnreachable ();
5112 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
5114 if (goto_cases == null)
5115 goto_cases = new List<Tuple<GotoCase, Constant>> ();
5117 goto_cases.Add (Tuple.Create (gotoCase, value));
5121 // Converts string switch into string hashtable
5123 void ResolveStringSwitchMap (ResolveContext ec)
5125 FullNamedExpression string_dictionary_type;
5126 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
5127 string_dictionary_type = new TypeExpression (
5128 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
5129 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
5131 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
5132 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
5134 ec.Module.PredefinedTypes.Dictionary.Resolve ();
5138 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
5139 Field field = new Field (ctype, string_dictionary_type,
5140 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
5141 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
5142 if (!field.Define ())
5144 ctype.AddField (field);
5146 var init = new List<Expression> ();
5148 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
5149 string value = null;
5151 foreach (SwitchLabel sl in case_labels) {
5153 if (sl.SectionStart)
5154 labels.Add (++counter, sl);
5156 if (sl == case_default || sl == case_null)
5159 value = (string) sl.Converted.GetValue ();
5160 var init_args = new List<Expression> (2);
5161 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
5163 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
5164 init_args.Add (sl.Converted);
5166 init.Add (new CollectionElementInitializer (init_args, loc));
5169 Arguments args = new Arguments (1);
5170 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
5171 Expression initializer = new NewInitialize (string_dictionary_type, args,
5172 new CollectionOrObjectInitializers (init, loc), loc);
5174 switch_cache_field = new FieldExpr (field, loc);
5175 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
5178 void DoEmitStringSwitch (EmitContext ec)
5180 Label l_initialized = ec.DefineLabel ();
5183 // Skip initialization when value is null
5185 value.EmitBranchable (ec, nullLabel, false);
5188 // Check if string dictionary is initialized and initialize
5190 switch_cache_field.EmitBranchable (ec, l_initialized, true);
5191 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
5192 string_dictionary.EmitStatement (ec);
5194 ec.MarkLabel (l_initialized);
5196 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
5198 ResolveContext rc = new ResolveContext (ec.MemberContext);
5200 if (switch_cache_field.Type.IsGeneric) {
5201 Arguments get_value_args = new Arguments (2);
5202 get_value_args.Add (new Argument (value));
5203 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
5204 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
5205 if (get_item == null)
5209 // A value was not found, go to default case
5211 get_item.EmitBranchable (ec, defaultLabel, false);
5213 Arguments get_value_args = new Arguments (1);
5214 get_value_args.Add (new Argument (value));
5216 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
5217 if (get_item == null)
5220 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
5221 get_item_object.EmitAssign (ec, get_item, true, false);
5222 ec.Emit (OpCodes.Brfalse, defaultLabel);
5224 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
5225 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
5227 get_item_int.EmitStatement (ec);
5228 get_item_object.Release (ec);
5231 EmitTableSwitch (ec, string_switch_variable);
5232 string_switch_variable.Release (ec);
5236 // Emits switch using simple if/else comparison for small label count (4 + optional default)
5238 void EmitShortSwitch (EmitContext ec)
5240 MethodSpec equal_method = null;
5241 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
5242 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
5245 if (equal_method != null) {
5246 value.EmitBranchable (ec, nullLabel, false);
5249 for (int i = 0; i < case_labels.Count; ++i) {
5250 var label = case_labels [i];
5251 if (label == case_default || label == case_null)
5254 var constant = label.Converted;
5256 if (equal_method != null) {
5260 var call = new CallEmitter ();
5261 call.EmitPredefined (ec, equal_method, new Arguments (0));
5262 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
5266 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
5267 value.EmitBranchable (ec, label.GetILLabel (ec), false);
5273 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
5276 ec.Emit (OpCodes.Br, defaultLabel);
5279 void EmitDispatch (EmitContext ec)
5281 if (value == null) {
5283 // Constant switch, we've already done the work if there is only 1 label
5287 foreach (var sl in case_labels) {
5288 if (sl.IsUnreachable)
5291 if (reachable++ > 0) {
5292 var constant = (Constant) new_expr;
5293 var constant_label = FindLabel (constant) ?? case_default;
5295 ec.Emit (OpCodes.Br, constant_label.GetILLabel (ec));
5303 if (string_dictionary != null) {
5304 DoEmitStringSwitch (ec);
5305 } else if (case_labels.Count < 4 || string_labels != null) {
5306 EmitShortSwitch (ec);
5308 EmitTableSwitch (ec, value);
5312 protected override void DoEmit (EmitContext ec)
5315 // Setup the codegen context
5317 Label old_end = ec.LoopEnd;
5318 Switch old_switch = ec.Switch;
5320 ec.LoopEnd = ec.DefineLabel ();
5323 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
5324 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
5326 if (value != null) {
5329 unwrap.EmitCheck (ec);
5330 ec.Emit (OpCodes.Brfalse, nullLabel);
5331 value.EmitAssign (ec, new_expr, false, false);
5332 } else if (new_expr != value) {
5333 value.EmitAssign (ec, new_expr, false, false);
5338 // Next statement is compiler generated we don't need extra
5339 // nop when we can use the statement for sequence point
5341 ec.Mark (block.StartLocation);
5342 block.IsCompilerGenerated = true;
5347 // Restore context state.
5348 ec.MarkLabel (ec.LoopEnd);
5351 // Restore the previous context
5353 ec.LoopEnd = old_end;
5354 ec.Switch = old_switch;
5357 protected override void CloneTo (CloneContext clonectx, Statement t)
5359 Switch target = (Switch) t;
5361 target.Expr = Expr.Clone (clonectx);
5362 target.Statement = target.block = (ExplicitBlock) block.Clone (clonectx);
5365 public override object Accept (StructuralVisitor visitor)
5367 return visitor.Visit (this);
5370 public override void AddEndDefiniteAssignment (FlowAnalysisContext fc)
5372 if (case_default == null)
5375 if (end_reachable_das == null)
5376 end_reachable_das = new List<DefiniteAssignmentBitSet> ();
5378 end_reachable_das.Add (fc.DefiniteAssignment);
5381 public override void SetEndReachable ()
5383 end_reachable = true;
5387 // A place where execution can restart in a state machine
5388 public abstract class ResumableStatement : Statement
5391 protected Label resume_point;
5393 public Label PrepareForEmit (EmitContext ec)
5397 resume_point = ec.DefineLabel ();
5399 return resume_point;
5402 public virtual Label PrepareForDispose (EmitContext ec, Label end)
5407 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5412 public abstract class TryFinallyBlock : ExceptionStatement
5414 protected Statement stmt;
5415 Label dispose_try_block;
5416 bool prepared_for_dispose, emitted_dispose;
5417 Method finally_host;
5419 protected TryFinallyBlock (Statement stmt, Location loc)
5427 public Statement Statement {
5435 protected abstract void EmitTryBody (EmitContext ec);
5436 public abstract void EmitFinallyBody (EmitContext ec);
5438 public override Label PrepareForDispose (EmitContext ec, Label end)
5440 if (!prepared_for_dispose) {
5441 prepared_for_dispose = true;
5442 dispose_try_block = ec.DefineLabel ();
5444 return dispose_try_block;
5447 protected sealed override void DoEmit (EmitContext ec)
5449 EmitTryBodyPrepare (ec);
5452 ec.BeginFinallyBlock ();
5454 Label start_finally = ec.DefineLabel ();
5455 if (resume_points != null) {
5456 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5458 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
5459 ec.Emit (OpCodes.Brfalse_S, start_finally);
5460 ec.Emit (OpCodes.Endfinally);
5463 ec.MarkLabel (start_finally);
5465 if (finally_host != null) {
5466 finally_host.Define ();
5467 finally_host.PrepareEmit ();
5468 finally_host.Emit ();
5470 // Now it's safe to add, to close it properly and emit sequence points
5471 finally_host.Parent.AddMember (finally_host);
5473 var ce = new CallEmitter ();
5474 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5475 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5477 EmitFinallyBody (ec);
5480 ec.EndExceptionBlock ();
5483 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
5485 if (emitted_dispose)
5488 emitted_dispose = true;
5490 Label end_of_try = ec.DefineLabel ();
5492 // Ensure that the only way we can get into this code is through a dispatcher
5493 if (have_dispatcher)
5494 ec.Emit (OpCodes.Br, end);
5496 ec.BeginExceptionBlock ();
5498 ec.MarkLabel (dispose_try_block);
5500 Label[] labels = null;
5501 for (int i = 0; i < resume_points.Count; ++i) {
5502 ResumableStatement s = resume_points[i];
5503 Label ret = s.PrepareForDispose (ec, end_of_try);
5504 if (ret.Equals (end_of_try) && labels == null)
5506 if (labels == null) {
5507 labels = new Label[resume_points.Count];
5508 for (int j = 0; j < i; ++j)
5509 labels[j] = end_of_try;
5514 if (labels != null) {
5516 for (j = 1; j < labels.Length; ++j)
5517 if (!labels[0].Equals (labels[j]))
5519 bool emit_dispatcher = j < labels.Length;
5521 if (emit_dispatcher) {
5522 ec.Emit (OpCodes.Ldloc, pc);
5523 ec.EmitInt (first_resume_pc);
5524 ec.Emit (OpCodes.Sub);
5525 ec.Emit (OpCodes.Switch, labels);
5528 foreach (ResumableStatement s in resume_points)
5529 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
5532 ec.MarkLabel (end_of_try);
5534 ec.BeginFinallyBlock ();
5536 if (finally_host != null) {
5537 var ce = new CallEmitter ();
5538 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
5539 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
5541 EmitFinallyBody (ec);
5544 ec.EndExceptionBlock ();
5547 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5549 var res = stmt.FlowAnalysis (fc);
5554 public override Reachability MarkReachable (Reachability rc)
5556 base.MarkReachable (rc);
5557 return Statement.MarkReachable (rc);
5560 public override bool Resolve (BlockContext bc)
5564 parent = bc.CurrentTryBlock;
5565 bc.CurrentTryBlock = this;
5567 using (bc.Set (ResolveContext.Options.TryScope)) {
5568 ok = stmt.Resolve (bc);
5571 bc.CurrentTryBlock = parent;
5574 // Finally block inside iterator is called from MoveNext and
5575 // Dispose methods that means we need to lift the block into
5576 // newly created host method to emit the body only once. The
5577 // original block then simply calls the newly generated method.
5579 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
5580 var b = stmt as Block;
5581 if (b != null && b.Explicit.HasYield) {
5582 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
5586 return base.Resolve (bc) && ok;
5591 // Base class for blocks using exception handling
5593 public abstract class ExceptionStatement : ResumableStatement
5595 protected List<ResumableStatement> resume_points;
5596 protected int first_resume_pc;
5597 protected ExceptionStatement parent;
5599 protected ExceptionStatement (Location loc)
5604 protected virtual void EmitTryBodyPrepare (EmitContext ec)
5606 StateMachineInitializer state_machine = null;
5607 if (resume_points != null) {
5608 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
5610 ec.EmitInt ((int) IteratorStorey.State.Running);
5611 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
5614 ec.BeginExceptionBlock ();
5616 if (resume_points != null) {
5617 ec.MarkLabel (resume_point);
5619 // For normal control flow, we want to fall-through the Switch
5620 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
5621 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
5622 ec.EmitInt (first_resume_pc);
5623 ec.Emit (OpCodes.Sub);
5625 Label[] labels = new Label[resume_points.Count];
5626 for (int i = 0; i < resume_points.Count; ++i)
5627 labels[i] = resume_points[i].PrepareForEmit (ec);
5628 ec.Emit (OpCodes.Switch, labels);
5632 public virtual int AddResumePoint (ResumableStatement stmt, int pc, StateMachineInitializer stateMachine)
5634 if (parent != null) {
5635 // TODO: MOVE to virtual TryCatch
5636 var tc = this as TryCatch;
5637 var s = tc != null && tc.IsTryCatchFinally ? stmt : this;
5639 pc = parent.AddResumePoint (s, pc, stateMachine);
5641 pc = stateMachine.AddResumePoint (this);
5644 if (resume_points == null) {
5645 resume_points = new List<ResumableStatement> ();
5646 first_resume_pc = pc;
5649 if (pc != first_resume_pc + resume_points.Count)
5650 throw new InternalErrorException ("missed an intervening AddResumePoint?");
5652 resume_points.Add (stmt);
5657 public class Lock : TryFinallyBlock
5660 TemporaryVariableReference expr_copy;
5661 TemporaryVariableReference lock_taken;
5663 public Lock (Expression expr, Statement stmt, Location loc)
5669 public Expression Expr {
5675 public override bool Resolve (BlockContext ec)
5677 expr = expr.Resolve (ec);
5681 if (!TypeSpec.IsReferenceType (expr.Type)) {
5682 ec.Report.Error (185, loc,
5683 "`{0}' is not a reference type as required by the lock statement",
5684 expr.Type.GetSignatureForError ());
5687 if (expr.Type.IsGenericParameter) {
5688 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
5691 VariableReference lv = expr as VariableReference;
5694 locked = lv.IsLockedByStatement;
5695 lv.IsLockedByStatement = true;
5702 // Have to keep original lock value around to unlock same location
5703 // in the case of original value has changed or is null
5705 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
5706 expr_copy.Resolve (ec);
5709 // Ensure Monitor methods are available
5711 if (ResolvePredefinedMethods (ec) > 1) {
5712 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
5713 lock_taken.Resolve (ec);
5716 using (ec.Set (ResolveContext.Options.LockScope)) {
5721 lv.IsLockedByStatement = locked;
5727 protected override void EmitTryBodyPrepare (EmitContext ec)
5729 expr_copy.EmitAssign (ec, expr);
5731 if (lock_taken != null) {
5733 // Initialize ref variable
5735 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
5738 // Monitor.Enter (expr_copy)
5740 expr_copy.Emit (ec);
5741 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
5744 base.EmitTryBodyPrepare (ec);
5747 protected override void EmitTryBody (EmitContext ec)
5750 // Monitor.Enter (expr_copy, ref lock_taken)
5752 if (lock_taken != null) {
5753 expr_copy.Emit (ec);
5754 lock_taken.LocalInfo.CreateBuilder (ec);
5755 lock_taken.AddressOf (ec, AddressOp.Load);
5756 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
5759 Statement.Emit (ec);
5762 public override void EmitFinallyBody (EmitContext ec)
5765 // if (lock_taken) Monitor.Exit (expr_copy)
5767 Label skip = ec.DefineLabel ();
5769 if (lock_taken != null) {
5770 lock_taken.Emit (ec);
5771 ec.Emit (OpCodes.Brfalse_S, skip);
5774 expr_copy.Emit (ec);
5775 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
5777 ec.Emit (OpCodes.Call, m);
5779 ec.MarkLabel (skip);
5782 int ResolvePredefinedMethods (ResolveContext rc)
5784 // Try 4.0 Monitor.Enter (object, ref bool) overload first
5785 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
5789 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
5793 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
5797 protected override void CloneTo (CloneContext clonectx, Statement t)
5799 Lock target = (Lock) t;
5801 target.expr = expr.Clone (clonectx);
5802 target.stmt = Statement.Clone (clonectx);
5805 public override object Accept (StructuralVisitor visitor)
5807 return visitor.Visit (this);
5812 public class Unchecked : Statement {
5815 public Unchecked (Block b, Location loc)
5822 public override bool Resolve (BlockContext ec)
5824 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
5825 return Block.Resolve (ec);
5828 protected override void DoEmit (EmitContext ec)
5830 using (ec.With (EmitContext.Options.CheckedScope, false))
5834 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5836 return Block.FlowAnalysis (fc);
5839 public override Reachability MarkReachable (Reachability rc)
5841 base.MarkReachable (rc);
5842 return Block.MarkReachable (rc);
5845 protected override void CloneTo (CloneContext clonectx, Statement t)
5847 Unchecked target = (Unchecked) t;
5849 target.Block = clonectx.LookupBlock (Block);
5852 public override object Accept (StructuralVisitor visitor)
5854 return visitor.Visit (this);
5858 public class Checked : Statement {
5861 public Checked (Block b, Location loc)
5864 b.Unchecked = false;
5868 public override bool Resolve (BlockContext ec)
5870 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
5871 return Block.Resolve (ec);
5874 protected override void DoEmit (EmitContext ec)
5876 using (ec.With (EmitContext.Options.CheckedScope, true))
5880 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5882 return Block.FlowAnalysis (fc);
5885 public override Reachability MarkReachable (Reachability rc)
5887 base.MarkReachable (rc);
5888 return Block.MarkReachable (rc);
5891 protected override void CloneTo (CloneContext clonectx, Statement t)
5893 Checked target = (Checked) t;
5895 target.Block = clonectx.LookupBlock (Block);
5898 public override object Accept (StructuralVisitor visitor)
5900 return visitor.Visit (this);
5904 public class Unsafe : Statement {
5907 public Unsafe (Block b, Location loc)
5910 Block.Unsafe = true;
5914 public override bool Resolve (BlockContext ec)
5916 if (ec.CurrentIterator != null)
5917 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5919 using (ec.Set (ResolveContext.Options.UnsafeScope))
5920 return Block.Resolve (ec);
5923 protected override void DoEmit (EmitContext ec)
5928 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
5930 return Block.FlowAnalysis (fc);
5933 public override Reachability MarkReachable (Reachability rc)
5935 base.MarkReachable (rc);
5936 return Block.MarkReachable (rc);
5939 protected override void CloneTo (CloneContext clonectx, Statement t)
5941 Unsafe target = (Unsafe) t;
5943 target.Block = clonectx.LookupBlock (Block);
5946 public override object Accept (StructuralVisitor visitor)
5948 return visitor.Visit (this);
5955 public class Fixed : Statement
5957 abstract class Emitter : ShimExpression
5959 protected LocalVariable vi;
5961 protected Emitter (Expression expr, LocalVariable li)
5967 public abstract void EmitExit (EmitContext ec);
5969 public override void FlowAnalysis (FlowAnalysisContext fc)
5971 expr.FlowAnalysis (fc);
5975 class ExpressionEmitter : Emitter {
5976 public ExpressionEmitter (Expression converted, LocalVariable li) :
5977 base (converted, li)
5981 protected override Expression DoResolve (ResolveContext rc)
5983 throw new NotImplementedException ();
5986 public override void Emit (EmitContext ec) {
5988 // Store pointer in pinned location
5994 public override void EmitExit (EmitContext ec)
5997 ec.Emit (OpCodes.Conv_U);
6002 class StringEmitter : Emitter
6004 LocalVariable pinned_string;
6006 public StringEmitter (Expression expr, LocalVariable li)
6011 protected override Expression DoResolve (ResolveContext rc)
6013 pinned_string = new LocalVariable (vi.Block, "$pinned",
6014 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
6016 pinned_string.Type = rc.BuiltinTypes.String;
6018 eclass = ExprClass.Variable;
6019 type = rc.BuiltinTypes.Int;
6023 public override void Emit (EmitContext ec)
6025 pinned_string.CreateBuilder (ec);
6028 pinned_string.EmitAssign (ec);
6030 // TODO: Should use Binary::Add
6031 pinned_string.Emit (ec);
6032 ec.Emit (OpCodes.Conv_I);
6034 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
6038 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
6039 //pe.InstanceExpression = pinned_string;
6040 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
6042 ec.Emit (OpCodes.Add);
6046 public override void EmitExit (EmitContext ec)
6049 pinned_string.EmitAssign (ec);
6053 public class VariableDeclaration : BlockVariable
6055 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6060 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6062 if (!Variable.Type.IsPointer && li == Variable) {
6063 bc.Report.Error (209, TypeExpression.Location,
6064 "The type of locals declared in a fixed statement must be a pointer type");
6069 // The rules for the possible declarators are pretty wise,
6070 // but the production on the grammar is more concise.
6072 // So we have to enforce these rules here.
6074 // We do not resolve before doing the case 1 test,
6075 // because the grammar is explicit in that the token &
6076 // is present, so we need to test for this particular case.
6079 if (initializer is Cast) {
6080 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
6084 initializer = initializer.Resolve (bc);
6086 if (initializer == null)
6092 if (initializer.Type.IsArray) {
6093 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
6096 // Provided that array_type is unmanaged,
6098 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
6102 // and T* is implicitly convertible to the
6103 // pointer type given in the fixed statement.
6105 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
6107 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
6108 if (converted == null)
6112 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
6114 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
6115 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
6116 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
6117 new NullLiteral (loc),
6120 converted = converted.Resolve (bc);
6122 return new ExpressionEmitter (converted, li);
6128 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6129 return new StringEmitter (initializer, li).Resolve (bc);
6132 // Case 3: fixed buffer
6133 if (initializer is FixedBufferPtr) {
6134 return new ExpressionEmitter (initializer, li);
6138 // Case 4: & object.
6140 bool already_fixed = true;
6141 Unary u = initializer as Unary;
6142 if (u != null && u.Oper == Unary.Operator.AddressOf) {
6143 IVariableReference vr = u.Expr as IVariableReference;
6144 if (vr == null || !vr.IsFixed) {
6145 already_fixed = false;
6149 if (already_fixed) {
6150 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
6153 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
6154 return new ExpressionEmitter (initializer, li);
6159 VariableDeclaration decl;
6160 Statement statement;
6163 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
6172 public Statement Statement {
6178 public BlockVariable Variables {
6186 public override bool Resolve (BlockContext bc)
6188 using (bc.Set (ResolveContext.Options.FixedInitializerScope)) {
6189 if (!decl.Resolve (bc))
6193 return statement.Resolve (bc);
6196 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6198 decl.FlowAnalysis (fc);
6199 return statement.FlowAnalysis (fc);
6202 protected override void DoEmit (EmitContext ec)
6204 decl.Variable.CreateBuilder (ec);
6205 decl.Initializer.Emit (ec);
6206 if (decl.Declarators != null) {
6207 foreach (var d in decl.Declarators) {
6208 d.Variable.CreateBuilder (ec);
6209 d.Initializer.Emit (ec);
6213 statement.Emit (ec);
6219 // Clear the pinned variable
6221 ((Emitter) decl.Initializer).EmitExit (ec);
6222 if (decl.Declarators != null) {
6223 foreach (var d in decl.Declarators) {
6224 ((Emitter)d.Initializer).EmitExit (ec);
6229 public override Reachability MarkReachable (Reachability rc)
6231 base.MarkReachable (rc);
6233 decl.MarkReachable (rc);
6235 rc = statement.MarkReachable (rc);
6237 // TODO: What if there is local exit?
6238 has_ret = rc.IsUnreachable;
6242 protected override void CloneTo (CloneContext clonectx, Statement t)
6244 Fixed target = (Fixed) t;
6246 target.decl = (VariableDeclaration) decl.Clone (clonectx);
6247 target.statement = statement.Clone (clonectx);
6250 public override object Accept (StructuralVisitor visitor)
6252 return visitor.Visit (this);
6256 public class Catch : Statement
6258 class FilterStatement : Statement
6260 readonly Catch ctch;
6262 public FilterStatement (Catch ctch)
6267 protected override void CloneTo (CloneContext clonectx, Statement target)
6271 protected override void DoEmit (EmitContext ec)
6273 if (ctch.li != null) {
6274 if (ctch.hoisted_temp != null)
6275 ctch.hoisted_temp.Emit (ec);
6280 var expr_start = ec.DefineLabel ();
6281 var end = ec.DefineLabel ();
6283 ec.Emit (OpCodes.Brtrue_S, expr_start);
6285 ec.Emit (OpCodes.Br, end);
6286 ec.MarkLabel (expr_start);
6288 ctch.Filter.Emit (ec);
6291 ec.Emit (OpCodes.Endfilter);
6292 ec.BeginFilterHandler ();
6293 ec.Emit (OpCodes.Pop);
6296 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6298 ctch.Filter.FlowAnalysis (fc);
6302 public override bool Resolve (BlockContext bc)
6304 ctch.Filter = ctch.Filter.Resolve (bc);
6305 var c = ctch.Filter as Constant;
6306 if (c != null && !c.IsDefaultValue) {
6307 bc.Report.Warning (7095, 1, ctch.Filter.Location, "Exception filter expression is a constant");
6314 ExplicitBlock block;
6316 FullNamedExpression type_expr;
6317 CompilerAssign assign;
6319 LocalTemporary hoisted_temp;
6321 public Catch (ExplicitBlock block, Location loc)
6329 public ExplicitBlock Block {
6335 public TypeSpec CatchType {
6341 public Expression Filter {
6345 public bool IsGeneral {
6347 return type_expr == null;
6351 public FullNamedExpression TypeExpression {
6360 public LocalVariable Variable {
6371 protected override void DoEmit (EmitContext ec)
6373 if (Filter != null) {
6374 ec.BeginExceptionFilterBlock ();
6375 ec.Emit (OpCodes.Isinst, IsGeneral ? ec.BuiltinTypes.Object : CatchType);
6378 EmitCatchVariableStore (ec);
6381 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
6383 ec.BeginCatchBlock (CatchType);
6386 EmitCatchVariableStore (ec);
6388 ec.Emit (OpCodes.Pop);
6395 void EmitCatchVariableStore (EmitContext ec)
6397 li.CreateBuilder (ec);
6400 // Special case hoisted catch variable, we have to use a temporary variable
6401 // to pass via anonymous storey initialization with the value still on top
6404 if (li.HoistedVariant != null) {
6405 hoisted_temp = new LocalTemporary (li.Type);
6406 hoisted_temp.Store (ec);
6408 // switch to assigning from the temporary variable and not from top of the stack
6409 assign.UpdateSource (hoisted_temp);
6413 public override bool Resolve (BlockContext ec)
6415 using (ec.Set (ResolveContext.Options.CatchScope)) {
6416 if (type_expr != null) {
6417 type = type_expr.ResolveAsType (ec);
6421 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
6422 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
6423 } else if (li != null) {
6425 li.PrepareAssignmentAnalysis (ec);
6427 // source variable is at the top of the stack
6428 Expression source = new EmptyExpression (li.Type);
6429 if (li.Type.IsGenericParameter)
6430 source = new UnboxCast (source, li.Type);
6433 // Uses Location.Null to hide from symbol file
6435 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
6436 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
6440 if (Filter != null) {
6441 Block.AddScopeStatement (new FilterStatement (this));
6444 Block.SetCatchBlock ();
6445 return Block.Resolve (ec);
6449 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6452 fc.SetVariableAssigned (li.VariableInfo, true);
6455 return block.FlowAnalysis (fc);
6458 public override Reachability MarkReachable (Reachability rc)
6460 base.MarkReachable (rc);
6462 var c = Filter as Constant;
6463 if (c != null && c.IsDefaultValue)
6464 return Reachability.CreateUnreachable ();
6466 return block.MarkReachable (rc);
6469 protected override void CloneTo (CloneContext clonectx, Statement t)
6471 Catch target = (Catch) t;
6473 if (type_expr != null)
6474 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
6477 target.Filter = Filter.Clone (clonectx);
6479 target.block = (ExplicitBlock) clonectx.LookupBlock (block);
6483 public class TryFinally : TryFinallyBlock
6486 List<DefiniteAssignmentBitSet> try_exit_dat;
6488 public TryFinally (Statement stmt, ExplicitBlock fini, Location loc)
6494 public ExplicitBlock FinallyBlock {
6500 public void RegisterForControlExitCheck (DefiniteAssignmentBitSet vector)
6502 if (try_exit_dat == null)
6503 try_exit_dat = new List<DefiniteAssignmentBitSet> ();
6505 try_exit_dat.Add (vector);
6508 public override bool Resolve (BlockContext bc)
6510 bool ok = base.Resolve (bc);
6512 fini.SetFinallyBlock ();
6513 using (bc.Set (ResolveContext.Options.FinallyScope)) {
6514 ok &= fini.Resolve (bc);
6520 protected override void EmitTryBody (EmitContext ec)
6525 public override void EmitFinallyBody (EmitContext ec)
6530 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6532 var da = fc.BranchDefiniteAssignment ();
6534 var tf = fc.TryFinally;
6535 fc.TryFinally = this;
6537 var res_stmt = Statement.FlowAnalysis (fc);
6541 var try_da = fc.DefiniteAssignment;
6542 fc.DefiniteAssignment = da;
6544 var res_fin = fini.FlowAnalysis (fc);
6546 if (try_exit_dat != null) {
6548 // try block has global exit but we need to run definite assignment check
6549 // for parameter block out parameter after finally block because it's always
6550 // executed before exit
6552 foreach (var try_da_part in try_exit_dat)
6553 fc.ParametersBlock.CheckControlExit (fc, fc.DefiniteAssignment | try_da_part);
6555 try_exit_dat = null;
6558 fc.DefiniteAssignment |= try_da;
6559 return res_stmt | res_fin;
6562 public override Reachability MarkReachable (Reachability rc)
6565 // Mark finally block first for any exit statement in try block
6566 // to know whether the code which follows finally is reachable
6568 return fini.MarkReachable (rc) | base.MarkReachable (rc);
6571 protected override void CloneTo (CloneContext clonectx, Statement t)
6573 TryFinally target = (TryFinally) t;
6575 target.stmt = stmt.Clone (clonectx);
6577 target.fini = (ExplicitBlock) clonectx.LookupBlock (fini);
6580 public override object Accept (StructuralVisitor visitor)
6582 return visitor.Visit (this);
6586 public class TryCatch : ExceptionStatement
6589 List<Catch> clauses;
6590 readonly bool inside_try_finally;
6592 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
6596 this.clauses = catch_clauses;
6597 this.inside_try_finally = inside_try_finally;
6600 public List<Catch> Clauses {
6606 public bool IsTryCatchFinally {
6608 return inside_try_finally;
6612 public override bool Resolve (BlockContext bc)
6616 using (bc.Set (ResolveContext.Options.TryScope)) {
6617 parent = bc.CurrentTryBlock;
6619 if (IsTryCatchFinally) {
6620 ok = Block.Resolve (bc);
6622 using (bc.Set (ResolveContext.Options.TryWithCatchScope)) {
6623 bc.CurrentTryBlock = this;
6624 ok = Block.Resolve (bc);
6625 bc.CurrentTryBlock = parent;
6630 for (int i = 0; i < clauses.Count; ++i) {
6633 ok &= c.Resolve (bc);
6635 if (c.Filter != null)
6638 TypeSpec resolved_type = c.CatchType;
6639 if (resolved_type == null)
6642 for (int ii = 0; ii < clauses.Count; ++ii) {
6646 if (clauses[ii].Filter != null)
6649 if (clauses[ii].IsGeneral) {
6650 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
6653 if (!bc.Module.DeclaringAssembly.WrapNonExceptionThrows)
6656 if (!bc.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
6659 bc.Report.Warning (1058, 1, c.loc,
6660 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
6668 var ct = clauses[ii].CatchType;
6672 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
6673 bc.Report.Error (160, c.loc,
6674 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
6675 ct.GetSignatureForError ());
6681 return base.Resolve (bc) && ok;
6684 protected sealed override void DoEmit (EmitContext ec)
6686 if (!inside_try_finally)
6687 EmitTryBodyPrepare (ec);
6691 foreach (Catch c in clauses)
6694 if (!inside_try_finally)
6695 ec.EndExceptionBlock ();
6698 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6700 var start_fc = fc.BranchDefiniteAssignment ();
6701 var res = Block.FlowAnalysis (fc);
6703 DefiniteAssignmentBitSet try_fc = res ? null : fc.DefiniteAssignment;
6705 foreach (var c in clauses) {
6706 fc.DefiniteAssignment = new DefiniteAssignmentBitSet (start_fc);
6707 if (!c.FlowAnalysis (fc)) {
6709 try_fc = fc.DefiniteAssignment;
6711 try_fc &= fc.DefiniteAssignment;
6717 fc.DefiniteAssignment = try_fc ?? start_fc;
6722 public override Reachability MarkReachable (Reachability rc)
6724 if (rc.IsUnreachable)
6727 base.MarkReachable (rc);
6729 var tc_rc = Block.MarkReachable (rc);
6731 foreach (var c in clauses)
6732 tc_rc &= c.MarkReachable (rc);
6737 protected override void CloneTo (CloneContext clonectx, Statement t)
6739 TryCatch target = (TryCatch) t;
6741 target.Block = clonectx.LookupBlock (Block);
6742 if (clauses != null){
6743 target.clauses = new List<Catch> ();
6744 foreach (Catch c in clauses)
6745 target.clauses.Add ((Catch) c.Clone (clonectx));
6749 public override object Accept (StructuralVisitor visitor)
6751 return visitor.Visit (this);
6755 public class Using : TryFinallyBlock
6757 public class VariableDeclaration : BlockVariable
6759 Statement dispose_call;
6761 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
6766 public VariableDeclaration (LocalVariable li, Location loc)
6772 public VariableDeclaration (Expression expr)
6775 loc = expr.Location;
6781 public bool IsNested { get; private set; }
6785 public void EmitDispose (EmitContext ec)
6787 dispose_call.Emit (ec);
6790 public override bool Resolve (BlockContext bc)
6795 return base.Resolve (bc, false);
6798 public Expression ResolveExpression (BlockContext bc)
6800 var e = Initializer.Resolve (bc);
6804 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
6805 Initializer = ResolveInitializer (bc, Variable, e);
6809 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
6811 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
6812 initializer = initializer.Resolve (bc);
6813 if (initializer == null)
6816 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
6817 Arguments args = new Arguments (1);
6818 args.Add (new Argument (initializer));
6819 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
6820 if (initializer == null)
6823 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
6824 dispose_call = CreateDisposeCall (bc, var);
6825 dispose_call.Resolve (bc);
6827 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
6830 if (li == Variable) {
6831 CheckIDiposableConversion (bc, li, initializer);
6832 dispose_call = CreateDisposeCall (bc, li);
6833 dispose_call.Resolve (bc);
6836 return base.ResolveInitializer (bc, li, initializer);
6839 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6843 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
6844 if (type.IsNullableType) {
6845 // it's handled in CreateDisposeCall
6849 if (type != InternalType.ErrorType) {
6850 bc.Report.SymbolRelatedToPreviousError (type);
6851 var loc = type_expr == null ? initializer.Location : type_expr.Location;
6852 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
6853 type.GetSignatureForError ());
6860 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6862 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
6864 var loc = lv.Location;
6866 var idt = bc.BuiltinTypes.IDisposable;
6867 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6869 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6870 dispose_mg.InstanceExpression = type.IsNullableType ?
6871 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
6875 // Hide it from symbol file via null location
6877 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
6879 // Add conditional call when disposing possible null variable
6880 if (!type.IsStruct || type.IsNullableType)
6881 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
6886 public void ResolveDeclaratorInitializer (BlockContext bc)
6888 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
6891 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
6893 for (int i = declarators.Count - 1; i >= 0; --i) {
6894 var d = declarators [i];
6895 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
6896 vd.Initializer = d.Initializer;
6898 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
6899 vd.dispose_call.Resolve (bc);
6901 stmt = new Using (vd, stmt, d.Variable.Location);
6908 public override object Accept (StructuralVisitor visitor)
6910 return visitor.Visit (this);
6914 VariableDeclaration decl;
6916 public Using (VariableDeclaration decl, Statement stmt, Location loc)
6922 public Using (Expression expr, Statement stmt, Location loc)
6925 this.decl = new VariableDeclaration (expr);
6930 public Expression Expr {
6932 return decl.Variable == null ? decl.Initializer : null;
6936 public BlockVariable Variables {
6944 public override void Emit (EmitContext ec)
6947 // Don't emit sequence point it will be set on variable declaration
6952 protected override void EmitTryBodyPrepare (EmitContext ec)
6955 base.EmitTryBodyPrepare (ec);
6958 protected override void EmitTryBody (EmitContext ec)
6963 public override void EmitFinallyBody (EmitContext ec)
6965 decl.EmitDispose (ec);
6968 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
6970 decl.FlowAnalysis (fc);
6971 return stmt.FlowAnalysis (fc);
6974 public override Reachability MarkReachable (Reachability rc)
6976 decl.MarkReachable (rc);
6977 return base.MarkReachable (rc);
6980 public override bool Resolve (BlockContext ec)
6982 VariableReference vr;
6983 bool vr_locked = false;
6985 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
6986 if (decl.Variable == null) {
6987 vr = decl.ResolveExpression (ec) as VariableReference;
6989 vr_locked = vr.IsLockedByStatement;
6990 vr.IsLockedByStatement = true;
6993 if (decl.IsNested) {
6994 decl.ResolveDeclaratorInitializer (ec);
6996 if (!decl.Resolve (ec))
6999 if (decl.Declarators != null) {
7000 stmt = decl.RewriteUsingDeclarators (ec, stmt);
7011 vr.IsLockedByStatement = vr_locked;
7016 protected override void CloneTo (CloneContext clonectx, Statement t)
7018 Using target = (Using) t;
7020 target.decl = (VariableDeclaration) decl.Clone (clonectx);
7021 target.stmt = stmt.Clone (clonectx);
7024 public override object Accept (StructuralVisitor visitor)
7026 return visitor.Visit (this);
7031 /// Implementation of the foreach C# statement
7033 public class Foreach : LoopStatement
7035 abstract class IteratorStatement : Statement
7037 protected readonly Foreach for_each;
7039 protected IteratorStatement (Foreach @foreach)
7041 this.for_each = @foreach;
7042 this.loc = @foreach.expr.Location;
7045 protected override void CloneTo (CloneContext clonectx, Statement target)
7047 throw new NotImplementedException ();
7050 public override void Emit (EmitContext ec)
7052 if (ec.EmitAccurateDebugInfo) {
7053 ec.Emit (OpCodes.Nop);
7059 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7061 throw new NotImplementedException ();
7065 sealed class ArrayForeach : IteratorStatement
7067 TemporaryVariableReference[] lengths;
7068 Expression [] length_exprs;
7069 StatementExpression[] counter;
7070 TemporaryVariableReference[] variables;
7072 TemporaryVariableReference copy;
7074 public ArrayForeach (Foreach @foreach, int rank)
7077 counter = new StatementExpression[rank];
7078 variables = new TemporaryVariableReference[rank];
7079 length_exprs = new Expression [rank];
7082 // Only use temporary length variables when dealing with
7083 // multi-dimensional arrays
7086 lengths = new TemporaryVariableReference [rank];
7089 public override bool Resolve (BlockContext ec)
7091 Block variables_block = for_each.variable.Block;
7092 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
7095 int rank = length_exprs.Length;
7096 Arguments list = new Arguments (rank);
7097 for (int i = 0; i < rank; i++) {
7098 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7100 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
7101 counter[i].Resolve (ec);
7104 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
7106 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
7107 lengths[i].Resolve (ec);
7109 Arguments args = new Arguments (1);
7110 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
7111 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
7114 list.Add (new Argument (v));
7117 var access = new ElementAccess (copy, list, loc).Resolve (ec);
7122 if (for_each.type is VarExpr) {
7123 // Infer implicitly typed local variable from foreach array type
7124 var_type = access.Type;
7126 var_type = for_each.type.ResolveAsType (ec);
7128 if (var_type == null)
7131 access = Convert.ExplicitConversion (ec, access, var_type, loc);
7136 for_each.variable.Type = var_type;
7138 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
7139 if (variable_ref == null)
7142 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
7144 return for_each.body.Resolve (ec);
7147 protected override void DoEmit (EmitContext ec)
7149 copy.EmitAssign (ec, for_each.expr);
7151 int rank = length_exprs.Length;
7152 Label[] test = new Label [rank];
7153 Label[] loop = new Label [rank];
7155 for (int i = 0; i < rank; i++) {
7156 test [i] = ec.DefineLabel ();
7157 loop [i] = ec.DefineLabel ();
7159 if (lengths != null)
7160 lengths [i].EmitAssign (ec, length_exprs [i]);
7163 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
7164 for (int i = 0; i < rank; i++) {
7165 variables [i].EmitAssign (ec, zero);
7167 ec.Emit (OpCodes.Br, test [i]);
7168 ec.MarkLabel (loop [i]);
7171 for_each.body.Emit (ec);
7173 ec.MarkLabel (ec.LoopBegin);
7174 ec.Mark (for_each.expr.Location);
7176 for (int i = rank - 1; i >= 0; i--){
7177 counter [i].Emit (ec);
7179 ec.MarkLabel (test [i]);
7180 variables [i].Emit (ec);
7182 if (lengths != null)
7183 lengths [i].Emit (ec);
7185 length_exprs [i].Emit (ec);
7187 ec.Emit (OpCodes.Blt, loop [i]);
7190 ec.MarkLabel (ec.LoopEnd);
7194 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
7196 class RuntimeDispose : Using.VariableDeclaration
7198 public RuntimeDispose (LocalVariable lv, Location loc)
7203 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
7205 // Defered to runtime check
7208 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
7210 var idt = bc.BuiltinTypes.IDisposable;
7213 // Fabricates code like
7215 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
7218 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
7220 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
7221 dispose_variable.CreateReferenceExpression (bc, loc),
7222 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
7223 loc), new NullLiteral (loc));
7225 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
7227 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
7228 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
7230 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
7231 return new If (idisaposable_test, dispose, loc);
7235 LocalVariable variable;
7237 Statement statement;
7238 ExpressionStatement init;
7239 TemporaryVariableReference enumerator_variable;
7240 bool ambiguous_getenumerator_name;
7242 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
7245 this.variable = var;
7249 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
7251 rc.Report.SymbolRelatedToPreviousError (enumerator);
7252 rc.Report.Error (202, loc,
7253 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
7254 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
7257 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
7260 // Option 1: Try to match by name GetEnumerator first
7262 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
7263 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
7265 var mg = mexpr as MethodGroupExpr;
7267 mg.InstanceExpression = expr;
7268 Arguments args = new Arguments (0);
7269 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
7271 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
7272 if (ambiguous_getenumerator_name)
7275 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
7281 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
7284 PredefinedMember<MethodSpec> iface_candidate = null;
7285 var ptypes = rc.Module.PredefinedTypes;
7286 var gen_ienumerable = ptypes.IEnumerableGeneric;
7287 if (!gen_ienumerable.Define ())
7288 gen_ienumerable = null;
7290 var ifaces = t.Interfaces;
7291 if (ifaces != null) {
7292 foreach (var iface in ifaces) {
7293 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
7294 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
7295 rc.Report.SymbolRelatedToPreviousError (expr.Type);
7296 rc.Report.Error (1640, loc,
7297 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
7298 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
7303 // TODO: Cache this somehow
7304 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
7305 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
7310 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
7311 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
7316 if (iface_candidate == null) {
7317 if (expr.Type != InternalType.ErrorType) {
7318 rc.Report.Error (1579, loc,
7319 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
7320 expr.Type.GetSignatureForError (), "GetEnumerator");
7326 var method = iface_candidate.Resolve (loc);
7330 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
7331 mg.InstanceExpression = expr;
7335 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
7337 var ms = MemberCache.FindMember (enumerator.ReturnType,
7338 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
7339 BindingRestriction.InstanceOnly) as MethodSpec;
7341 if (ms == null || !ms.IsPublic) {
7342 Error_WrongEnumerator (rc, enumerator);
7346 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
7349 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
7351 var ps = MemberCache.FindMember (enumerator.ReturnType,
7352 MemberFilter.Property ("Current", null),
7353 BindingRestriction.InstanceOnly) as PropertySpec;
7355 if (ps == null || !ps.IsPublic) {
7356 Error_WrongEnumerator (rc, enumerator);
7363 public override bool Resolve (BlockContext ec)
7365 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
7368 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
7369 } else if (expr.Type.IsNullableType) {
7370 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
7373 var get_enumerator_mg = ResolveGetEnumerator (ec);
7374 if (get_enumerator_mg == null) {
7378 var get_enumerator = get_enumerator_mg.BestCandidate;
7379 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
7380 enumerator_variable.Resolve (ec);
7382 // Prepare bool MoveNext ()
7383 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
7384 if (move_next_mg == null) {
7388 move_next_mg.InstanceExpression = enumerator_variable;
7390 // Prepare ~T~ Current { get; }
7391 var current_prop = ResolveCurrent (ec, get_enumerator);
7392 if (current_prop == null) {
7396 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
7397 if (current_pe == null)
7400 VarExpr ve = for_each.type as VarExpr;
7404 // Source type is dynamic, set element type to dynamic too
7405 variable.Type = ec.BuiltinTypes.Dynamic;
7407 // Infer implicitly typed local variable from foreach enumerable type
7408 variable.Type = current_pe.Type;
7412 // Explicit cast of dynamic collection elements has to be done at runtime
7413 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
7416 variable.Type = for_each.type.ResolveAsType (ec);
7418 if (variable.Type == null)
7421 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
7422 if (current_pe == null)
7426 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
7427 if (variable_ref == null)
7430 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
7432 var init = new Invocation (get_enumerator_mg, null);
7434 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
7435 for_each.body, Location.Null);
7437 var enum_type = enumerator_variable.Type;
7440 // Add Dispose method call when enumerator can be IDisposable
7442 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
7443 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
7445 // Runtime Dispose check
7447 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
7448 vd.Initializer = init;
7449 statement = new Using (vd, statement, Location.Null);
7452 // No Dispose call needed
7454 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
7455 this.init.Resolve (ec);
7459 // Static Dispose check
7461 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
7462 vd.Initializer = init;
7463 statement = new Using (vd, statement, Location.Null);
7466 return statement.Resolve (ec);
7469 protected override void DoEmit (EmitContext ec)
7471 enumerator_variable.LocalInfo.CreateBuilder (ec);
7474 init.EmitStatement (ec);
7476 statement.Emit (ec);
7479 #region IErrorHandler Members
7481 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
7483 ec.Report.SymbolRelatedToPreviousError (best);
7484 ec.Report.Warning (278, 2, expr.Location,
7485 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
7486 expr.Type.GetSignatureForError (), "enumerable",
7487 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
7489 ambiguous_getenumerator_name = true;
7493 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
7498 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
7503 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
7512 LocalVariable variable;
7516 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
7520 this.variable = var;
7526 public Expression Expr {
7527 get { return expr; }
7530 public Expression TypeExpression {
7531 get { return type; }
7534 public LocalVariable Variable {
7535 get { return variable; }
7538 public override Reachability MarkReachable (Reachability rc)
7540 base.MarkReachable (rc);
7542 body.MarkReachable (rc);
7547 public override bool Resolve (BlockContext ec)
7549 expr = expr.Resolve (ec);
7554 ec.Report.Error (186, loc, "Use of null is not valid in this context");
7558 body.AddStatement (Statement);
7560 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
7561 Statement = new ArrayForeach (this, 1);
7562 } else if (expr.Type is ArrayContainer) {
7563 Statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
7565 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
7566 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
7567 expr.ExprClassName);
7571 Statement = new CollectionForeach (this, variable, expr);
7578 protected override void DoEmit (EmitContext ec)
7580 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
7581 ec.LoopBegin = ec.DefineLabel ();
7582 ec.LoopEnd = ec.DefineLabel ();
7584 if (!(Statement is Block))
7585 ec.BeginCompilerScope ();
7587 variable.CreateBuilder (ec);
7589 Statement.Emit (ec);
7591 if (!(Statement is Block))
7594 ec.LoopBegin = old_begin;
7595 ec.LoopEnd = old_end;
7598 protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
7600 expr.FlowAnalysis (fc);
7602 var da = fc.BranchDefiniteAssignment ();
7603 body.FlowAnalysis (fc);
7604 fc.DefiniteAssignment = da;
7608 protected override void CloneTo (CloneContext clonectx, Statement t)
7610 Foreach target = (Foreach) t;
7612 target.type = type.Clone (clonectx);
7613 target.expr = expr.Clone (clonectx);
7614 target.body = (Block) body.Clone (clonectx);
7615 target.Statement = Statement.Clone (clonectx);
7618 public override object Accept (StructuralVisitor visitor)
7620 return visitor.Visit (this);