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 {
29 /// Resolves the statement, true means that all sub-statements
32 public virtual bool Resolve (BlockContext bc)
38 /// We already know that the statement is unreachable, but we still
39 /// need to resolve it to catch errors.
41 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
44 // This conflicts with csc's way of doing this, but IMHO it's
45 // the right thing to do.
47 // If something is unreachable, we still check whether it's
48 // correct. This means that you cannot use unassigned variables
49 // in unreachable code, for instance.
52 bool unreachable = false;
53 if (warn && !ec.UnreachableReported) {
54 ec.UnreachableReported = true;
56 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
59 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
60 ec.CurrentBranching.CurrentUsageVector.Goto ();
61 bool ok = Resolve (ec);
62 ec.KillFlowBranching ();
65 ec.UnreachableReported = false;
72 /// Return value indicates whether all code paths emitted return.
74 protected abstract void DoEmit (EmitContext ec);
76 public virtual void Emit (EmitContext ec)
81 if (ec.StatementEpilogue != null) {
87 // This routine must be overrided in derived classes and make copies
88 // of all the data that might be modified if resolved
90 protected abstract void CloneTo (CloneContext clonectx, Statement target);
92 public Statement Clone (CloneContext clonectx)
94 Statement s = (Statement) this.MemberwiseClone ();
95 CloneTo (clonectx, s);
99 public virtual Expression CreateExpressionTree (ResolveContext ec)
101 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
105 public virtual object Accept (StructuralVisitor visitor)
107 return visitor.Visit (this);
111 public sealed class EmptyStatement : Statement
113 public EmptyStatement (Location loc)
118 public override bool Resolve (BlockContext ec)
123 public override bool ResolveUnreachable (BlockContext ec, bool warn)
128 public override void Emit (EmitContext ec)
132 protected override void DoEmit (EmitContext ec)
134 throw new NotSupportedException ();
137 protected override void CloneTo (CloneContext clonectx, Statement target)
142 public override object Accept (StructuralVisitor visitor)
144 return visitor.Visit (this);
148 public class If : Statement {
150 public Statement TrueStatement;
151 public Statement FalseStatement;
155 public If (Expression bool_expr, Statement true_statement, Location l)
156 : this (bool_expr, true_statement, null, l)
160 public If (Expression bool_expr,
161 Statement true_statement,
162 Statement false_statement,
165 this.expr = bool_expr;
166 TrueStatement = true_statement;
167 FalseStatement = false_statement;
171 public Expression Expr {
177 public override bool Resolve (BlockContext ec)
181 expr = expr.Resolve (ec);
186 // Dead code elimination
188 if (expr is Constant) {
189 bool take = !((Constant) expr).IsDefaultValue;
192 if (!TrueStatement.Resolve (ec))
195 if ((FalseStatement != null) &&
196 !FalseStatement.ResolveUnreachable (ec, true))
198 FalseStatement = null;
200 if (!TrueStatement.ResolveUnreachable (ec, true))
202 TrueStatement = null;
204 if ((FalseStatement != null) &&
205 !FalseStatement.Resolve (ec))
213 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
215 ok &= TrueStatement.Resolve (ec);
217 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
219 ec.CurrentBranching.CreateSibling ();
221 if (FalseStatement != null)
222 ok &= FalseStatement.Resolve (ec);
224 ec.EndFlowBranching ();
229 protected override void DoEmit (EmitContext ec)
231 Label false_target = ec.DefineLabel ();
235 // If we're a boolean constant, Resolve() already
236 // eliminated dead code for us.
238 Constant c = expr as Constant;
240 c.EmitSideEffect (ec);
242 if (!c.IsDefaultValue)
243 TrueStatement.Emit (ec);
244 else if (FalseStatement != null)
245 FalseStatement.Emit (ec);
250 expr.EmitBranchable (ec, false_target, false);
252 TrueStatement.Emit (ec);
254 if (FalseStatement != null){
255 bool branch_emitted = false;
257 end = ec.DefineLabel ();
259 ec.Emit (OpCodes.Br, end);
260 branch_emitted = true;
263 ec.MarkLabel (false_target);
264 FalseStatement.Emit (ec);
269 ec.MarkLabel (false_target);
273 protected override void CloneTo (CloneContext clonectx, Statement t)
277 target.expr = expr.Clone (clonectx);
278 target.TrueStatement = TrueStatement.Clone (clonectx);
279 if (FalseStatement != null)
280 target.FalseStatement = FalseStatement.Clone (clonectx);
283 public override object Accept (StructuralVisitor visitor)
285 return visitor.Visit (this);
289 public class Do : Statement {
290 public Expression expr;
291 public Statement EmbeddedStatement;
293 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
296 EmbeddedStatement = statement;
298 WhileLocation = whileLocation;
301 public Location WhileLocation {
305 public override bool Resolve (BlockContext ec)
309 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
311 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
313 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
314 if (!EmbeddedStatement.Resolve (ec))
316 ec.EndFlowBranching ();
318 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
319 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
321 expr = expr.Resolve (ec);
324 else if (expr is Constant){
325 bool infinite = !((Constant) expr).IsDefaultValue;
327 ec.CurrentBranching.CurrentUsageVector.Goto ();
330 ec.EndFlowBranching ();
335 protected override void DoEmit (EmitContext ec)
337 Label loop = ec.DefineLabel ();
338 Label old_begin = ec.LoopBegin;
339 Label old_end = ec.LoopEnd;
341 ec.LoopBegin = ec.DefineLabel ();
342 ec.LoopEnd = ec.DefineLabel ();
345 EmbeddedStatement.Emit (ec);
346 ec.MarkLabel (ec.LoopBegin);
348 // Mark start of while condition
349 ec.Mark (WhileLocation);
352 // Dead code elimination
354 if (expr is Constant) {
355 bool res = !((Constant) expr).IsDefaultValue;
357 expr.EmitSideEffect (ec);
359 ec.Emit (OpCodes.Br, loop);
361 expr.EmitBranchable (ec, loop, true);
364 ec.MarkLabel (ec.LoopEnd);
366 ec.LoopBegin = old_begin;
367 ec.LoopEnd = old_end;
370 protected override void CloneTo (CloneContext clonectx, Statement t)
374 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
375 target.expr = expr.Clone (clonectx);
378 public override object Accept (StructuralVisitor visitor)
380 return visitor.Visit (this);
384 public class While : Statement {
385 public Expression expr;
386 public Statement Statement;
387 bool infinite, empty;
389 public While (BooleanExpression bool_expr, Statement statement, Location l)
391 this.expr = bool_expr;
392 Statement = statement;
396 public override bool Resolve (BlockContext ec)
400 expr = expr.Resolve (ec);
405 // Inform whether we are infinite or not
407 if (expr is Constant){
408 bool value = !((Constant) expr).IsDefaultValue;
411 if (!Statement.ResolveUnreachable (ec, true))
419 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
421 ec.CurrentBranching.CreateSibling ();
423 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
424 if (!Statement.Resolve (ec))
426 ec.EndFlowBranching ();
428 // There's no direct control flow from the end of the embedded statement to the end of the loop
429 ec.CurrentBranching.CurrentUsageVector.Goto ();
431 ec.EndFlowBranching ();
436 protected override void DoEmit (EmitContext ec)
439 expr.EmitSideEffect (ec);
443 Label old_begin = ec.LoopBegin;
444 Label old_end = ec.LoopEnd;
446 ec.LoopBegin = ec.DefineLabel ();
447 ec.LoopEnd = ec.DefineLabel ();
450 // Inform whether we are infinite or not
452 if (expr is Constant) {
453 // expr is 'true', since the 'empty' case above handles the 'false' case
454 ec.MarkLabel (ec.LoopBegin);
456 if (ec.EmitAccurateDebugInfo)
457 ec.Emit (OpCodes.Nop);
459 expr.EmitSideEffect (ec);
461 ec.Emit (OpCodes.Br, ec.LoopBegin);
464 // Inform that we are infinite (ie, `we return'), only
465 // if we do not `break' inside the code.
467 ec.MarkLabel (ec.LoopEnd);
469 Label while_loop = ec.DefineLabel ();
471 ec.Emit (OpCodes.Br, ec.LoopBegin);
472 ec.MarkLabel (while_loop);
476 ec.MarkLabel (ec.LoopBegin);
479 expr.EmitBranchable (ec, while_loop, true);
481 ec.MarkLabel (ec.LoopEnd);
484 ec.LoopBegin = old_begin;
485 ec.LoopEnd = old_end;
488 protected override void CloneTo (CloneContext clonectx, Statement t)
490 While target = (While) t;
492 target.expr = expr.Clone (clonectx);
493 target.Statement = Statement.Clone (clonectx);
496 public override object Accept (StructuralVisitor visitor)
498 return visitor.Visit (this);
502 public class For : Statement
504 bool infinite, empty;
506 public For (Location l)
511 public Statement Initializer {
515 public Expression Condition {
519 public Statement Iterator {
523 public Statement Statement {
527 public override bool Resolve (BlockContext ec)
531 if (Initializer != null) {
532 if (!Initializer.Resolve (ec))
536 if (Condition != null) {
537 Condition = Condition.Resolve (ec);
538 if (Condition == null)
540 else if (Condition is Constant) {
541 bool value = !((Constant) Condition).IsDefaultValue;
544 if (!Statement.ResolveUnreachable (ec, true))
546 if ((Iterator != null) &&
547 !Iterator.ResolveUnreachable (ec, false))
557 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
559 ec.CurrentBranching.CreateSibling ();
561 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
563 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
564 if (!Statement.Resolve (ec))
566 ec.EndFlowBranching ();
568 if (Iterator != null){
569 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
570 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
573 if (!Iterator.Resolve (ec))
578 // There's no direct control flow from the end of the embedded statement to the end of the loop
579 ec.CurrentBranching.CurrentUsageVector.Goto ();
581 ec.EndFlowBranching ();
586 protected override void DoEmit (EmitContext ec)
588 if (Initializer != null)
589 Initializer.Emit (ec);
592 Condition.EmitSideEffect (ec);
596 Label old_begin = ec.LoopBegin;
597 Label old_end = ec.LoopEnd;
598 Label loop = ec.DefineLabel ();
599 Label test = ec.DefineLabel ();
601 ec.LoopBegin = ec.DefineLabel ();
602 ec.LoopEnd = ec.DefineLabel ();
604 ec.Emit (OpCodes.Br, test);
608 ec.MarkLabel (ec.LoopBegin);
613 // If test is null, there is no test, and we are just
616 if (Condition != null) {
617 ec.Mark (Condition.Location);
620 // The Resolve code already catches the case for
621 // Test == Constant (false) so we know that
624 if (Condition is Constant) {
625 Condition.EmitSideEffect (ec);
626 ec.Emit (OpCodes.Br, loop);
628 Condition.EmitBranchable (ec, loop, true);
632 ec.Emit (OpCodes.Br, loop);
633 ec.MarkLabel (ec.LoopEnd);
635 ec.LoopBegin = old_begin;
636 ec.LoopEnd = old_end;
639 protected override void CloneTo (CloneContext clonectx, Statement t)
641 For target = (For) t;
643 if (Initializer != null)
644 target.Initializer = Initializer.Clone (clonectx);
645 if (Condition != null)
646 target.Condition = Condition.Clone (clonectx);
647 if (Iterator != null)
648 target.Iterator = Iterator.Clone (clonectx);
649 target.Statement = Statement.Clone (clonectx);
652 public override object Accept (StructuralVisitor visitor)
654 return visitor.Visit (this);
658 public class StatementExpression : Statement
660 ExpressionStatement expr;
662 public StatementExpression (ExpressionStatement expr)
665 loc = expr.StartLocation;
668 public StatementExpression (ExpressionStatement expr, Location loc)
674 public ExpressionStatement Expr {
680 protected override void CloneTo (CloneContext clonectx, Statement t)
682 StatementExpression target = (StatementExpression) t;
683 target.expr = (ExpressionStatement) expr.Clone (clonectx);
686 protected override void DoEmit (EmitContext ec)
688 expr.EmitStatement (ec);
691 public override bool Resolve (BlockContext ec)
693 expr = expr.ResolveStatement (ec);
697 public override object Accept (StructuralVisitor visitor)
699 return visitor.Visit (this);
703 public class StatementErrorExpression : Statement
707 public StatementErrorExpression (Expression expr)
710 this.loc = expr.StartLocation;
713 public Expression Expr {
719 public override bool Resolve (BlockContext bc)
721 expr.Error_InvalidExpressionStatement (bc);
725 protected override void DoEmit (EmitContext ec)
727 throw new NotSupportedException ();
730 protected override void CloneTo (CloneContext clonectx, Statement target)
732 var t = (StatementErrorExpression) target;
734 t.expr = expr.Clone (clonectx);
737 public override object Accept (StructuralVisitor visitor)
739 return visitor.Visit (this);
744 // Simple version of statement list not requiring a block
746 public class StatementList : Statement
748 List<Statement> statements;
750 public StatementList (Statement first, Statement second)
752 statements = new List<Statement> () { first, second };
756 public IList<Statement> Statements {
763 public void Add (Statement statement)
765 statements.Add (statement);
768 public override bool Resolve (BlockContext ec)
770 foreach (var s in statements)
776 protected override void DoEmit (EmitContext ec)
778 foreach (var s in statements)
782 protected override void CloneTo (CloneContext clonectx, Statement target)
784 StatementList t = (StatementList) target;
786 t.statements = new List<Statement> (statements.Count);
787 foreach (Statement s in statements)
788 t.statements.Add (s.Clone (clonectx));
791 public override object Accept (StructuralVisitor visitor)
793 return visitor.Visit (this);
797 // A 'return' or a 'yield break'
798 public abstract class ExitStatement : Statement
800 protected bool unwind_protect;
801 protected abstract bool DoResolve (BlockContext ec);
803 public virtual void Error_FinallyClause (Report Report)
805 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
808 public sealed override bool Resolve (BlockContext ec)
810 var res = DoResolve (ec);
811 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
812 ec.CurrentBranching.CurrentUsageVector.Goto ();
818 /// Implements the return statement
820 public class Return : ExitStatement
824 public Return (Expression expr, Location l)
832 public Expression Expr {
843 protected override bool DoResolve (BlockContext ec)
846 if (ec.ReturnType.Kind == MemberKind.Void)
850 // Return must not be followed by an expression when
851 // the method return type is Task
853 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
854 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
855 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
857 // Extra trick not to emit ret/leave inside awaiter body
859 expr = EmptyExpression.Null;
864 if (ec.CurrentIterator != null) {
865 Error_ReturnFromIterator (ec);
866 } else if (ec.ReturnType != InternalType.ErrorType) {
867 ec.Report.Error (126, loc,
868 "An object of a type convertible to `{0}' is required for the return statement",
869 ec.ReturnType.GetSignatureForError ());
875 expr = expr.Resolve (ec);
876 TypeSpec block_return_type = ec.ReturnType;
878 AnonymousExpression am = ec.CurrentAnonymousMethod;
880 if (block_return_type.Kind == MemberKind.Void) {
881 ec.Report.Error (127, loc,
882 "`{0}': A return keyword must not be followed by any expression when method returns void",
883 ec.GetSignatureForError ());
889 Error_ReturnFromIterator (ec);
893 var async_block = am as AsyncInitializer;
894 if (async_block != null) {
896 var storey = (AsyncTaskStorey) am.Storey;
897 var async_type = storey.ReturnType;
899 if (async_type == null && async_block.ReturnTypeInference != null) {
900 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
904 if (async_type.Kind == MemberKind.Void) {
905 ec.Report.Error (127, loc,
906 "`{0}': A return keyword must not be followed by any expression when method returns void",
907 ec.GetSignatureForError ());
912 if (!async_type.IsGenericTask) {
913 if (this is ContextualReturn)
916 // Same error code as .NET but better error message
917 if (async_block.DelegateType != null) {
918 ec.Report.Error (1997, loc,
919 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
920 async_block.DelegateType.GetSignatureForError ());
922 ec.Report.Error (1997, loc,
923 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
924 ec.GetSignatureForError ());
932 // The return type is actually Task<T> type argument
934 if (expr.Type == async_type) {
935 ec.Report.Error (4016, loc,
936 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
937 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
939 block_return_type = async_type.TypeArguments[0];
943 // Same error code as .NET but better error message
944 if (block_return_type.Kind == MemberKind.Void) {
945 ec.Report.Error (127, loc,
946 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
947 am.GetSignatureForError ());
952 var l = am as AnonymousMethodBody;
953 if (l != null && expr != null) {
954 if (l.ReturnTypeInference != null) {
955 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
960 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
961 // unexpected debugging experience
963 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
964 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
973 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
974 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
977 if (am != null && block_return_type == ec.ReturnType) {
978 ec.Report.Error (1662, loc,
979 "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",
980 am.ContainerType, am.GetSignatureForError ());
989 protected override void DoEmit (EmitContext ec)
994 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
995 if (async_body != null) {
996 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
998 // It's null for await without async
999 if (async_return != null) {
1000 async_return.EmitAssign (ec);
1005 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
1011 if (unwind_protect || ec.EmitAccurateDebugInfo)
1012 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1015 if (unwind_protect) {
1016 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1017 } else if (ec.EmitAccurateDebugInfo) {
1018 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1020 ec.Emit (OpCodes.Ret);
1024 void Error_ReturnFromIterator (ResolveContext rc)
1026 rc.Report.Error (1622, loc,
1027 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1030 protected override void CloneTo (CloneContext clonectx, Statement t)
1032 Return target = (Return) t;
1033 // It's null for simple return;
1035 target.expr = expr.Clone (clonectx);
1038 public override object Accept (StructuralVisitor visitor)
1040 return visitor.Visit (this);
1044 public class Goto : Statement {
1046 LabeledStatement label;
1047 bool unwind_protect;
1049 public override bool Resolve (BlockContext ec)
1051 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1052 ec.CurrentBranching.CurrentUsageVector.Goto ();
1056 public Goto (string label, Location l)
1062 public string Target {
1063 get { return target; }
1066 public void SetResolvedTarget (LabeledStatement label)
1069 label.AddReference ();
1072 protected override void CloneTo (CloneContext clonectx, Statement target)
1077 protected override void DoEmit (EmitContext ec)
1080 throw new InternalErrorException ("goto emitted before target resolved");
1081 Label l = label.LabelTarget (ec);
1082 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1085 public override object Accept (StructuralVisitor visitor)
1087 return visitor.Visit (this);
1091 public class LabeledStatement : Statement {
1098 FlowBranching.UsageVector vectors;
1100 public LabeledStatement (string name, Block block, Location l)
1107 public Label LabelTarget (EmitContext ec)
1112 label = ec.DefineLabel ();
1117 public Block Block {
1123 public string Name {
1124 get { return name; }
1127 public bool IsDefined {
1128 get { return defined; }
1131 public bool HasBeenReferenced {
1132 get { return referenced; }
1135 public FlowBranching.UsageVector JumpOrigins {
1136 get { return vectors; }
1139 public void AddUsageVector (FlowBranching.UsageVector vector)
1141 vector = vector.Clone ();
1142 vector.Next = vectors;
1146 protected override void CloneTo (CloneContext clonectx, Statement target)
1151 public override bool Resolve (BlockContext ec)
1153 // this flow-branching will be terminated when the surrounding block ends
1154 ec.StartFlowBranching (this);
1158 protected override void DoEmit (EmitContext ec)
1160 if (!HasBeenReferenced)
1161 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1164 ec.MarkLabel (label);
1167 public void AddReference ()
1172 public override object Accept (StructuralVisitor visitor)
1174 return visitor.Visit (this);
1180 /// `goto default' statement
1182 public class GotoDefault : Statement {
1184 public GotoDefault (Location l)
1189 protected override void CloneTo (CloneContext clonectx, Statement target)
1194 public override bool Resolve (BlockContext ec)
1196 ec.CurrentBranching.CurrentUsageVector.Goto ();
1198 if (ec.Switch == null) {
1199 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1203 ec.Switch.RegisterGotoCase (null, null);
1208 protected override void DoEmit (EmitContext ec)
1210 ec.Emit (OpCodes.Br, ec.Switch.DefaultLabel.GetILLabel (ec));
1213 public override object Accept (StructuralVisitor visitor)
1215 return visitor.Visit (this);
1220 /// `goto case' statement
1222 public class GotoCase : Statement {
1225 public GotoCase (Expression e, Location l)
1231 public Expression Expr {
1237 public SwitchLabel Label { get; set; }
1239 public override bool Resolve (BlockContext ec)
1241 if (ec.Switch == null){
1242 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1246 ec.CurrentBranching.CurrentUsageVector.Goto ();
1248 Constant c = expr.ResolveLabelConstant (ec);
1254 if (ec.Switch.IsNullable && c is NullLiteral) {
1257 TypeSpec type = ec.Switch.SwitchType;
1258 res = c.Reduce (ec, type);
1260 c.Error_ValueCannotBeConverted (ec, type, true);
1264 if (!Convert.ImplicitStandardConversionExists (c, type))
1265 ec.Report.Warning (469, 2, loc,
1266 "The `goto case' value is not implicitly convertible to type `{0}'",
1267 type.GetSignatureForError ());
1271 ec.Switch.RegisterGotoCase (this, res);
1275 protected override void DoEmit (EmitContext ec)
1277 ec.Emit (OpCodes.Br, Label.GetILLabel (ec));
1280 protected override void CloneTo (CloneContext clonectx, Statement t)
1282 GotoCase target = (GotoCase) t;
1284 target.expr = expr.Clone (clonectx);
1287 public override object Accept (StructuralVisitor visitor)
1289 return visitor.Visit (this);
1293 public class Throw : Statement {
1296 public Throw (Expression expr, Location l)
1302 public Expression Expr {
1308 public override bool Resolve (BlockContext ec)
1311 ec.CurrentBranching.CurrentUsageVector.Goto ();
1312 return ec.CurrentBranching.CheckRethrow (loc);
1315 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1316 ec.CurrentBranching.CurrentUsageVector.Goto ();
1321 var et = ec.BuiltinTypes.Exception;
1322 if (Convert.ImplicitConversionExists (ec, expr, et))
1323 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1325 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1330 protected override void DoEmit (EmitContext ec)
1333 ec.Emit (OpCodes.Rethrow);
1337 ec.Emit (OpCodes.Throw);
1341 protected override void CloneTo (CloneContext clonectx, Statement t)
1343 Throw target = (Throw) t;
1346 target.expr = expr.Clone (clonectx);
1349 public override object Accept (StructuralVisitor visitor)
1351 return visitor.Visit (this);
1355 public class Break : Statement {
1357 public Break (Location l)
1362 bool unwind_protect;
1364 public override bool Resolve (BlockContext ec)
1366 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1367 ec.CurrentBranching.CurrentUsageVector.Goto ();
1371 protected override void DoEmit (EmitContext ec)
1373 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1376 protected override void CloneTo (CloneContext clonectx, Statement t)
1381 public override object Accept (StructuralVisitor visitor)
1383 return visitor.Visit (this);
1387 public class Continue : Statement {
1389 public Continue (Location l)
1394 bool unwind_protect;
1396 public override bool Resolve (BlockContext ec)
1398 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1399 ec.CurrentBranching.CurrentUsageVector.Goto ();
1403 protected override void DoEmit (EmitContext ec)
1405 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1408 protected override void CloneTo (CloneContext clonectx, Statement t)
1413 public override object Accept (StructuralVisitor visitor)
1415 return visitor.Visit (this);
1419 public interface ILocalVariable
1421 void Emit (EmitContext ec);
1422 void EmitAssign (EmitContext ec);
1423 void EmitAddressOf (EmitContext ec);
1426 public interface INamedBlockVariable
1428 Block Block { get; }
1429 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1430 bool IsDeclared { get; }
1431 bool IsParameter { get; }
1432 Location Location { get; }
1435 public class BlockVariableDeclarator
1438 Expression initializer;
1440 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1442 if (li.Type != null)
1443 throw new ArgumentException ("Expected null variable type");
1446 this.initializer = initializer;
1451 public LocalVariable Variable {
1457 public Expression Initializer {
1462 initializer = value;
1468 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1470 var t = (BlockVariableDeclarator) MemberwiseClone ();
1471 if (initializer != null)
1472 t.initializer = initializer.Clone (cloneCtx);
1478 public class BlockVariable : Statement
1480 Expression initializer;
1481 protected FullNamedExpression type_expr;
1482 protected LocalVariable li;
1483 protected List<BlockVariableDeclarator> declarators;
1486 public BlockVariable (FullNamedExpression type, LocalVariable li)
1488 this.type_expr = type;
1490 this.loc = type_expr.Location;
1493 protected BlockVariable (LocalVariable li)
1500 public List<BlockVariableDeclarator> Declarators {
1506 public Expression Initializer {
1511 initializer = value;
1515 public FullNamedExpression TypeExpression {
1521 public LocalVariable Variable {
1529 public void AddDeclarator (BlockVariableDeclarator decl)
1531 if (declarators == null)
1532 declarators = new List<BlockVariableDeclarator> ();
1534 declarators.Add (decl);
1537 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1539 if (bc.Report.Errors != 0)
1542 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1544 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1545 new MemberName (li.Name, li.Location), null);
1547 container.AddField (f);
1550 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1554 public override bool Resolve (BlockContext bc)
1556 return Resolve (bc, true);
1559 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1561 if (type == null && !li.IsCompilerGenerated) {
1562 var vexpr = type_expr as VarExpr;
1565 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1566 // same name exists or as a keyword when no type was found
1568 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1569 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1570 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1573 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1577 if (li.IsConstant) {
1578 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1582 if (Initializer == null) {
1583 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1587 if (declarators != null) {
1588 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1592 Initializer = Initializer.Resolve (bc);
1593 if (Initializer != null) {
1594 ((VarExpr) type_expr).InferType (bc, Initializer);
1595 type = type_expr.Type;
1597 // Set error type to indicate the var was placed correctly but could
1600 // var a = missing ();
1602 type = InternalType.ErrorType;
1607 type = type_expr.ResolveAsType (bc);
1611 if (li.IsConstant && !type.IsConstantCompatible) {
1612 Const.Error_InvalidConstantType (type, loc, bc.Report);
1617 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1622 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1624 CreateEvaluatorVariable (bc, li);
1625 } else if (type != InternalType.ErrorType) {
1626 li.PrepareForFlowAnalysis (bc);
1629 if (initializer != null) {
1630 initializer = ResolveInitializer (bc, li, initializer);
1631 // li.Variable.DefinitelyAssigned
1634 if (declarators != null) {
1635 foreach (var d in declarators) {
1636 d.Variable.Type = li.Type;
1638 CreateEvaluatorVariable (bc, d.Variable);
1639 } else if (type != InternalType.ErrorType) {
1640 d.Variable.PrepareForFlowAnalysis (bc);
1643 if (d.Initializer != null && resolveDeclaratorInitializers) {
1644 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1645 // d.Variable.DefinitelyAssigned
1653 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1655 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1656 return a.ResolveStatement (bc);
1659 protected override void DoEmit (EmitContext ec)
1661 li.CreateBuilder (ec);
1663 if (Initializer != null)
1664 ((ExpressionStatement) Initializer).EmitStatement (ec);
1666 if (declarators != null) {
1667 foreach (var d in declarators) {
1668 d.Variable.CreateBuilder (ec);
1669 if (d.Initializer != null) {
1670 ec.Mark (d.Variable.Location);
1671 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1677 protected override void CloneTo (CloneContext clonectx, Statement target)
1679 BlockVariable t = (BlockVariable) target;
1681 if (type_expr != null)
1682 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1684 if (initializer != null)
1685 t.initializer = initializer.Clone (clonectx);
1687 if (declarators != null) {
1688 t.declarators = null;
1689 foreach (var d in declarators)
1690 t.AddDeclarator (d.Clone (clonectx));
1694 public override object Accept (StructuralVisitor visitor)
1696 return visitor.Visit (this);
1700 public class BlockConstant : BlockVariable
1702 public BlockConstant (FullNamedExpression type, LocalVariable li)
1707 public override void Emit (EmitContext ec)
1709 // Nothing to emit, not even sequence point
1712 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1714 initializer = initializer.Resolve (bc);
1715 if (initializer == null)
1718 var c = initializer as Constant;
1720 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1724 c = c.ConvertImplicitly (li.Type);
1726 if (TypeSpec.IsReferenceType (li.Type))
1727 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1729 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1734 li.ConstantValue = c;
1738 public override object Accept (StructuralVisitor visitor)
1740 return visitor.Visit (this);
1745 // The information about a user-perceived local variable
1747 public class LocalVariable : INamedBlockVariable, ILocalVariable
1754 AddressTaken = 1 << 2,
1755 CompilerGenerated = 1 << 3,
1757 ForeachVariable = 1 << 5,
1758 FixedVariable = 1 << 6,
1759 UsingVariable = 1 << 7,
1760 // DefinitelyAssigned = 1 << 8,
1763 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1767 readonly string name;
1768 readonly Location loc;
1769 readonly Block block;
1771 Constant const_value;
1773 public VariableInfo VariableInfo;
1774 HoistedVariable hoisted_variant;
1776 LocalBuilder builder;
1778 public LocalVariable (Block block, string name, Location loc)
1785 public LocalVariable (Block block, string name, Flags flags, Location loc)
1786 : this (block, name, loc)
1792 // Used by variable declarators
1794 public LocalVariable (LocalVariable li, string name, Location loc)
1795 : this (li.block, name, li.flags, loc)
1801 public bool AddressTaken {
1803 return (flags & Flags.AddressTaken) != 0;
1807 public Block Block {
1813 public Constant ConstantValue {
1818 const_value = value;
1823 // Hoisted local variable variant
1825 public HoistedVariable HoistedVariant {
1827 return hoisted_variant;
1830 hoisted_variant = value;
1834 public bool IsDeclared {
1836 return type != null;
1840 public bool IsCompilerGenerated {
1842 return (flags & Flags.CompilerGenerated) != 0;
1846 public bool IsConstant {
1848 return (flags & Flags.Constant) != 0;
1852 public bool IsLocked {
1854 return (flags & Flags.IsLocked) != 0;
1857 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1861 public bool IsThis {
1863 return (flags & Flags.IsThis) != 0;
1867 public bool IsFixed {
1869 return (flags & Flags.FixedVariable) != 0;
1873 bool INamedBlockVariable.IsParameter {
1879 public bool IsReadonly {
1881 return (flags & Flags.ReadonlyMask) != 0;
1885 public Location Location {
1891 public string Name {
1897 public TypeSpec Type {
1908 public void CreateBuilder (EmitContext ec)
1910 if ((flags & Flags.Used) == 0) {
1911 if (VariableInfo == null) {
1912 // Missing flow analysis or wrong variable flags
1913 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1916 if (VariableInfo.IsEverAssigned)
1917 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1919 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1922 if (HoistedVariant != null)
1925 if (builder != null) {
1926 if ((flags & Flags.CompilerGenerated) != 0)
1929 // To avoid Used warning duplicates
1930 throw new InternalErrorException ("Already created variable `{0}'", name);
1934 // All fixed variabled are pinned, a slot has to be alocated
1936 builder = ec.DeclareLocal (Type, IsFixed);
1937 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1938 ec.DefineLocalVariable (name, builder);
1941 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1943 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1948 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1950 if (IsConstant && const_value != null)
1951 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1953 return new LocalVariableReference (this, loc);
1956 public void Emit (EmitContext ec)
1958 // TODO: Need something better for temporary variables
1959 if ((flags & Flags.CompilerGenerated) != 0)
1962 ec.Emit (OpCodes.Ldloc, builder);
1965 public void EmitAssign (EmitContext ec)
1967 // TODO: Need something better for temporary variables
1968 if ((flags & Flags.CompilerGenerated) != 0)
1971 ec.Emit (OpCodes.Stloc, builder);
1974 public void EmitAddressOf (EmitContext ec)
1976 ec.Emit (OpCodes.Ldloca, builder);
1979 public static string GetCompilerGeneratedName (Block block)
1981 // HACK: Debugger depends on the name semantics
1982 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1985 public string GetReadOnlyContext ()
1987 switch (flags & Flags.ReadonlyMask) {
1988 case Flags.FixedVariable:
1989 return "fixed variable";
1990 case Flags.ForeachVariable:
1991 return "foreach iteration variable";
1992 case Flags.UsingVariable:
1993 return "using variable";
1996 throw new InternalErrorException ("Variable is not readonly");
1999 public bool IsThisAssigned (BlockContext ec, Block block)
2001 if (VariableInfo == null)
2002 throw new Exception ();
2004 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
2007 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
2010 public bool IsAssigned (BlockContext ec)
2012 if (VariableInfo == null)
2013 throw new Exception ();
2015 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
2018 public void PrepareForFlowAnalysis (BlockContext bc)
2021 // No need for definitely assigned check for these guys
2023 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2026 VariableInfo = new VariableInfo (this, bc.FlowOffset);
2027 bc.FlowOffset += VariableInfo.Length;
2031 // Mark the variables as referenced in the user code
2033 public void SetIsUsed ()
2035 flags |= Flags.Used;
2038 public void SetHasAddressTaken ()
2040 flags |= (Flags.AddressTaken | Flags.Used);
2043 public override string ToString ()
2045 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2050 /// Block represents a C# block.
2054 /// This class is used in a number of places: either to represent
2055 /// explicit blocks that the programmer places or implicit blocks.
2057 /// Implicit blocks are used as labels or to introduce variable
2060 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2061 /// they contain extra information that is not necessary on normal blocks.
2063 public class Block : Statement {
2070 HasCapturedVariable = 64,
2071 HasCapturedThis = 1 << 7,
2072 IsExpressionTree = 1 << 8,
2073 CompilerGenerated = 1 << 9,
2074 HasAsyncModifier = 1 << 10,
2076 YieldBlock = 1 << 12,
2077 AwaitBlock = 1 << 13,
2081 public Block Parent;
2082 public Location StartLocation;
2083 public Location EndLocation;
2085 public ExplicitBlock Explicit;
2086 public ParametersBlock ParametersBlock;
2088 protected Flags flags;
2091 // The statements in this block
2093 protected List<Statement> statements;
2095 protected List<Statement> scope_initializers;
2097 int? resolving_init_idx;
2103 public int ID = id++;
2105 static int clone_id_counter;
2109 // int assignable_slots;
2111 public Block (Block parent, Location start, Location end)
2112 : this (parent, 0, start, end)
2116 public Block (Block parent, Flags flags, Location start, Location end)
2118 if (parent != null) {
2119 // the appropriate constructors will fixup these fields
2120 ParametersBlock = parent.ParametersBlock;
2121 Explicit = parent.Explicit;
2124 this.Parent = parent;
2126 this.StartLocation = start;
2127 this.EndLocation = end;
2129 statements = new List<Statement> (4);
2131 this.original = this;
2136 public bool HasUnreachableClosingBrace {
2138 return (flags & Flags.HasRet) != 0;
2141 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2145 public Block Original {
2154 public bool IsCompilerGenerated {
2155 get { return (flags & Flags.CompilerGenerated) != 0; }
2156 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2159 public bool Unchecked {
2160 get { return (flags & Flags.Unchecked) != 0; }
2161 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2164 public bool Unsafe {
2165 get { return (flags & Flags.Unsafe) != 0; }
2166 set { flags |= Flags.Unsafe; }
2169 public List<Statement> Statements {
2170 get { return statements; }
2175 public void SetEndLocation (Location loc)
2180 public void AddLabel (LabeledStatement target)
2182 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2185 public void AddLocalName (LocalVariable li)
2187 AddLocalName (li.Name, li);
2190 public void AddLocalName (string name, INamedBlockVariable li)
2192 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2195 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2197 if (reason == null) {
2198 Error_AlreadyDeclared (name, variable);
2202 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2203 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2204 "to `{0}', which is already used in a `{1}' scope to denote something else",
2208 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2210 var pi = variable as ParametersBlock.ParameterInfo;
2212 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2214 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2215 "A local variable named `{0}' is already defined in this scope", name);
2219 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2221 ParametersBlock.TopBlock.Report.Error (412, loc,
2222 "The type parameter name `{0}' is the same as local variable or parameter name",
2227 // It should be used by expressions which require to
2228 // register a statement during resolve process.
2230 public void AddScopeStatement (Statement s)
2232 if (scope_initializers == null)
2233 scope_initializers = new List<Statement> ();
2236 // Simple recursive helper, when resolve scope initializer another
2237 // new scope initializer can be added, this ensures it's initialized
2238 // before existing one. For now this can happen with expression trees
2239 // in base ctor initializer only
2241 if (resolving_init_idx.HasValue) {
2242 scope_initializers.Insert (resolving_init_idx.Value, s);
2243 ++resolving_init_idx;
2245 scope_initializers.Add (s);
2249 public void InsertStatement (int index, Statement s)
2251 statements.Insert (index, s);
2254 public void AddStatement (Statement s)
2259 public int AssignableSlots {
2261 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2263 // return assignable_slots;
2267 public LabeledStatement LookupLabel (string name)
2269 return ParametersBlock.TopBlock.GetLabel (name, this);
2272 public override bool Resolve (BlockContext ec)
2274 if ((flags & Flags.Resolved) != 0)
2277 Block prev_block = ec.CurrentBlock;
2279 bool unreachable = ec.IsUnreachable;
2280 bool prev_unreachable = unreachable;
2282 ec.CurrentBlock = this;
2283 ec.StartFlowBranching (this);
2286 // Compiler generated scope statements
2288 if (scope_initializers != null) {
2289 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2290 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2293 resolving_init_idx = null;
2297 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2298 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2299 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2300 // responsible for handling the situation.
2302 int statement_count = statements.Count;
2303 for (int ix = 0; ix < statement_count; ix++){
2304 Statement s = statements [ix];
2307 // Warn if we detect unreachable code.
2310 if (s is EmptyStatement)
2313 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2314 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2315 ec.UnreachableReported = true;
2320 // Note that we're not using ResolveUnreachable() for unreachable
2321 // statements here. ResolveUnreachable() creates a temporary
2322 // flow branching and kills it afterwards. This leads to problems
2323 // if you have two unreachable statements where the first one
2324 // assigns a variable and the second one tries to access it.
2327 if (!s.Resolve (ec)) {
2329 if (!ec.IsInProbingMode)
2330 statements [ix] = new EmptyStatement (s.loc);
2335 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2336 statements [ix] = new EmptyStatement (s.loc);
2338 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2340 ec.IsUnreachable = true;
2341 } else if (ec.IsUnreachable)
2342 ec.IsUnreachable = false;
2345 if (unreachable != prev_unreachable) {
2346 ec.IsUnreachable = prev_unreachable;
2347 ec.UnreachableReported = false;
2350 while (ec.CurrentBranching is FlowBranchingLabeled)
2351 ec.EndFlowBranching ();
2353 bool flow_unreachable = ec.EndFlowBranching ();
2355 ec.CurrentBlock = prev_block;
2357 if (flow_unreachable)
2358 flags |= Flags.HasRet;
2360 // If we're a non-static `struct' constructor which doesn't have an
2361 // initializer, then we must initialize all of the struct's fields.
2362 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2365 flags |= Flags.Resolved;
2369 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2371 bool unreachable = false;
2372 if (warn && !ec.UnreachableReported) {
2373 ec.UnreachableReported = true;
2375 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2378 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2379 fb.CurrentUsageVector.IsUnreachable = true;
2380 bool ok = Resolve (ec);
2381 ec.KillFlowBranching ();
2384 ec.UnreachableReported = false;
2389 protected override void DoEmit (EmitContext ec)
2391 for (int ix = 0; ix < statements.Count; ix++){
2392 statements [ix].Emit (ec);
2396 public override void Emit (EmitContext ec)
2398 if (scope_initializers != null)
2399 EmitScopeInitializers (ec);
2404 protected void EmitScopeInitializers (EmitContext ec)
2406 foreach (Statement s in scope_initializers)
2411 public override string ToString ()
2413 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2417 protected override void CloneTo (CloneContext clonectx, Statement t)
2419 Block target = (Block) t;
2421 target.clone_id = clone_id_counter++;
2424 clonectx.AddBlockMap (this, target);
2425 if (original != this)
2426 clonectx.AddBlockMap (original, target);
2428 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2429 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2432 target.Parent = clonectx.RemapBlockCopy (Parent);
2434 target.statements = new List<Statement> (statements.Count);
2435 foreach (Statement s in statements)
2436 target.statements.Add (s.Clone (clonectx));
2439 public override object Accept (StructuralVisitor visitor)
2441 return visitor.Visit (this);
2445 public class ExplicitBlock : Block
2447 protected AnonymousMethodStorey am_storey;
2449 public ExplicitBlock (Block parent, Location start, Location end)
2450 : this (parent, (Flags) 0, start, end)
2454 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2455 : base (parent, flags, start, end)
2457 this.Explicit = this;
2462 public AnonymousMethodStorey AnonymousMethodStorey {
2468 public bool HasAwait {
2470 return (flags & Flags.AwaitBlock) != 0;
2474 public bool HasCapturedThis {
2476 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2479 return (flags & Flags.HasCapturedThis) != 0;
2484 // Used to indicate that the block has reference to parent
2485 // block and cannot be made static when defining anonymous method
2487 public bool HasCapturedVariable {
2489 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2492 return (flags & Flags.HasCapturedVariable) != 0;
2496 public bool HasYield {
2498 return (flags & Flags.YieldBlock) != 0;
2505 // Creates anonymous method storey in current block
2507 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2510 // Return same story for iterator and async blocks unless we are
2511 // in nested anonymous method
2513 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2514 return ec.CurrentAnonymousMethod.Storey;
2516 if (am_storey == null) {
2517 MemberBase mc = ec.MemberContext as MemberBase;
2520 // Creates anonymous method storey for this block
2522 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2528 public override void Emit (EmitContext ec)
2530 if (am_storey != null) {
2531 DefineStoreyContainer (ec, am_storey);
2532 am_storey.EmitStoreyInstantiation (ec, this);
2535 if (scope_initializers != null)
2536 EmitScopeInitializers (ec);
2538 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2539 ec.Emit (OpCodes.Nop);
2550 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2551 ec.Emit (OpCodes.Nop);
2555 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2557 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2558 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2559 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2563 // Creates anonymous method storey
2565 storey.CreateContainer ();
2566 storey.DefineContainer ();
2568 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2571 // Only first storey in path will hold this reference. All children blocks will
2572 // reference it indirectly using $ref field
2574 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2575 if (b.Parent != null) {
2576 var s = b.Parent.Explicit.AnonymousMethodStorey;
2578 storey.HoistedThis = s.HoistedThis;
2583 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2584 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2586 if (storey.HoistedThis != null)
2592 // We are the first storey on path and 'this' has to be hoisted
2594 if (storey.HoistedThis == null) {
2595 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2597 // ThisReferencesFromChildrenBlock holds all reference even if they
2598 // are not on this path. It saves some memory otherwise it'd have to
2599 // be in every explicit block. We run this check to see if the reference
2600 // is valid for this storey
2602 Block block_on_path = ref_block;
2603 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2605 if (block_on_path == null)
2608 if (storey.HoistedThis == null) {
2609 storey.AddCapturedThisField (ec);
2612 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2613 if (b.AnonymousMethodStorey != null) {
2614 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2615 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2618 // Stop propagation inside same top block
2620 if (b.ParametersBlock == ParametersBlock.Original)
2623 b = b.ParametersBlock;
2626 var pb = b as ParametersBlock;
2627 if (pb != null && pb.StateMachine != null) {
2628 if (pb.StateMachine == storey)
2632 // If we are state machine with no parent we can hook into we don't
2633 // add reference but capture this directly
2635 ExplicitBlock parent_storey_block = pb;
2636 while (parent_storey_block.Parent != null) {
2637 parent_storey_block = parent_storey_block.Parent.Explicit;
2638 if (parent_storey_block.AnonymousMethodStorey != null) {
2643 if (parent_storey_block.AnonymousMethodStorey == null) {
2644 pb.StateMachine.AddCapturedThisField (ec);
2645 b.HasCapturedThis = true;
2649 pb.StateMachine.AddParentStoreyReference (ec, storey);
2652 b.HasCapturedVariable = true;
2658 var ref_blocks = storey.ReferencesFromChildrenBlock;
2659 if (ref_blocks != null) {
2660 foreach (ExplicitBlock ref_block in ref_blocks) {
2661 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2662 if (b.AnonymousMethodStorey != null) {
2663 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2666 // Stop propagation inside same top block
2668 if (b.ParametersBlock == ParametersBlock.Original)
2671 b = b.ParametersBlock;
2674 var pb = b as ParametersBlock;
2675 if (pb != null && pb.StateMachine != null) {
2676 if (pb.StateMachine == storey)
2679 pb.StateMachine.AddParentStoreyReference (ec, storey);
2682 b.HasCapturedVariable = true;
2688 storey.PrepareEmit ();
2689 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2692 public void RegisterAsyncAwait ()
2695 while ((block.flags & Flags.AwaitBlock) == 0) {
2696 block.flags |= Flags.AwaitBlock;
2698 if (block is ParametersBlock)
2701 block = block.Parent.Explicit;
2705 public void RegisterIteratorYield ()
2707 ParametersBlock.TopBlock.IsIterator = true;
2710 while ((block.flags & Flags.YieldBlock) == 0) {
2711 block.flags |= Flags.YieldBlock;
2713 if (block.Parent == null)
2716 block = block.Parent.Explicit;
2720 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2722 tryBlock.statements = statements;
2723 statements = new List<Statement> (1);
2724 statements.Add (tf);
2729 // ParametersBlock was introduced to support anonymous methods
2730 // and lambda expressions
2732 public class ParametersBlock : ExplicitBlock
2734 public class ParameterInfo : INamedBlockVariable
2736 readonly ParametersBlock block;
2738 public VariableInfo VariableInfo;
2741 public ParameterInfo (ParametersBlock block, int index)
2749 public ParametersBlock Block {
2755 Block INamedBlockVariable.Block {
2761 public bool IsDeclared {
2767 public bool IsParameter {
2773 public bool IsLocked {
2782 public Location Location {
2784 return Parameter.Location;
2788 public Parameter Parameter {
2790 return block.Parameters [index];
2794 public TypeSpec ParameterType {
2796 return Parameter.Type;
2802 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2804 return new ParameterReference (this, loc);
2809 // Block is converted into an expression
2811 sealed class BlockScopeExpression : Expression
2814 readonly ParametersBlock block;
2816 public BlockScopeExpression (Expression child, ParametersBlock block)
2822 public override bool ContainsEmitWithAwait ()
2824 return child.ContainsEmitWithAwait ();
2827 public override Expression CreateExpressionTree (ResolveContext ec)
2829 throw new NotSupportedException ();
2832 protected override Expression DoResolve (ResolveContext ec)
2837 child = child.Resolve (ec);
2841 eclass = child.eclass;
2846 public override void Emit (EmitContext ec)
2848 block.EmitScopeInitializers (ec);
2853 protected ParametersCompiled parameters;
2854 protected ParameterInfo[] parameter_info;
2856 protected bool unreachable;
2857 protected ToplevelBlock top_block;
2858 protected StateMachine state_machine;
2860 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2861 : base (parent, 0, start, start)
2863 if (parameters == null)
2864 throw new ArgumentNullException ("parameters");
2866 this.parameters = parameters;
2867 ParametersBlock = this;
2869 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2871 this.top_block = parent.ParametersBlock.top_block;
2872 ProcessParameters ();
2875 protected ParametersBlock (ParametersCompiled parameters, Location start)
2876 : base (null, 0, start, start)
2878 if (parameters == null)
2879 throw new ArgumentNullException ("parameters");
2881 this.parameters = parameters;
2882 ParametersBlock = this;
2886 // It's supposed to be used by method body implementation of anonymous methods
2888 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2889 : base (null, 0, source.StartLocation, source.EndLocation)
2891 this.parameters = parameters;
2892 this.statements = source.statements;
2893 this.scope_initializers = source.scope_initializers;
2895 this.resolved = true;
2896 this.unreachable = source.unreachable;
2897 this.am_storey = source.am_storey;
2898 this.state_machine = source.state_machine;
2900 ParametersBlock = this;
2903 // Overwrite original for comparison purposes when linking cross references
2904 // between anonymous methods
2906 Original = source.Original;
2911 public bool IsAsync {
2913 return (flags & Flags.HasAsyncModifier) != 0;
2916 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2921 // Block has been converted to expression tree
2923 public bool IsExpressionTree {
2925 return (flags & Flags.IsExpressionTree) != 0;
2930 // The parameters for the block.
2932 public ParametersCompiled Parameters {
2938 public StateMachine StateMachine {
2940 return state_machine;
2944 public ToplevelBlock TopBlock {
2950 public bool Resolved {
2952 return (flags & Flags.Resolved) != 0;
2956 public int TemporaryLocalsCount { get; set; }
2961 // Check whether all `out' parameters have been assigned.
2963 public void CheckOutParameters (FlowBranching.UsageVector vector)
2965 if (vector.IsUnreachable)
2968 int n = parameter_info == null ? 0 : parameter_info.Length;
2970 for (int i = 0; i < n; i++) {
2971 VariableInfo var = parameter_info[i].VariableInfo;
2976 if (vector.IsAssigned (var, false))
2979 var p = parameter_info[i].Parameter;
2980 TopBlock.Report.Error (177, p.Location,
2981 "The out parameter `{0}' must be assigned to before control leaves the current method",
2986 public override Expression CreateExpressionTree (ResolveContext ec)
2988 if (statements.Count == 1) {
2989 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2990 if (scope_initializers != null)
2991 expr = new BlockScopeExpression (expr, this);
2996 return base.CreateExpressionTree (ec);
2999 public override void Emit (EmitContext ec)
3001 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3002 DefineStoreyContainer (ec, state_machine);
3003 state_machine.EmitStoreyInstantiation (ec, this);
3009 public void EmitEmbedded (EmitContext ec)
3011 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
3012 DefineStoreyContainer (ec, state_machine);
3013 state_machine.EmitStoreyInstantiation (ec, this);
3019 public ParameterInfo GetParameterInfo (Parameter p)
3021 for (int i = 0; i < parameters.Count; ++i) {
3022 if (parameters[i] == p)
3023 return parameter_info[i];
3026 throw new ArgumentException ("Invalid parameter");
3029 public ParameterReference GetParameterReference (int index, Location loc)
3031 return new ParameterReference (parameter_info[index], loc);
3034 public Statement PerformClone ()
3036 CloneContext clonectx = new CloneContext ();
3037 return Clone (clonectx);
3040 protected void ProcessParameters ()
3042 if (parameters.Count == 0)
3045 parameter_info = new ParameterInfo[parameters.Count];
3046 for (int i = 0; i < parameter_info.Length; ++i) {
3047 var p = parameters.FixedParameters[i];
3051 // TODO: Should use Parameter only and more block there
3052 parameter_info[i] = new ParameterInfo (this, i);
3054 AddLocalName (p.Name, parameter_info[i]);
3058 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3065 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3066 flags |= Flags.IsExpressionTree;
3071 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3072 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3077 unreachable = top_level.End ();
3079 } catch (Exception e) {
3080 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3083 if (rc.CurrentBlock != null) {
3084 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3086 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3089 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3093 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3094 if (rc.CurrentAnonymousMethod == null) {
3095 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3096 if (md is StateMachineMethod) {
3099 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3104 // If an asynchronous body of F is either an expression classified as nothing, or a
3105 // statement block where no return statements have expressions, the inferred return type is Task
3108 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3109 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3110 am.ReturnTypeInference = null;
3111 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3116 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3117 rc.CurrentAnonymousMethod.GetSignatureForError ());
3125 void ResolveMeta (BlockContext ec)
3127 int orig_count = parameters.Count;
3129 for (int i = 0; i < orig_count; ++i) {
3130 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3132 if ((mod & Parameter.Modifier.OUT) == 0)
3135 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3136 parameter_info[i].VariableInfo = vi;
3137 ec.FlowOffset += vi.Length;
3141 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3143 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3144 var stateMachine = new IteratorStorey (iterator);
3146 state_machine = stateMachine;
3147 iterator.SetStateMachine (stateMachine);
3149 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3150 tlb.Original = this;
3151 tlb.IsCompilerGenerated = true;
3152 tlb.state_machine = stateMachine;
3153 tlb.AddStatement (new Return (iterator, iterator.Location));
3157 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3159 for (int i = 0; i < parameters.Count; i++) {
3160 Parameter p = parameters[i];
3161 Parameter.Modifier mod = p.ModFlags;
3162 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3163 host.Compiler.Report.Error (1988, p.Location,
3164 "Async methods cannot have ref or out parameters");
3168 if (p is ArglistParameter) {
3169 host.Compiler.Report.Error (4006, p.Location,
3170 "__arglist is not allowed in parameter list of async methods");
3174 if (parameters.Types[i].IsPointer) {
3175 host.Compiler.Report.Error (4005, p.Location,
3176 "Async methods cannot have unsafe parameters");
3182 host.Compiler.Report.Warning (1998, 1, loc,
3183 "Async block lacks `await' operator and will run synchronously");
3186 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3187 var initializer = new AsyncInitializer (this, host, block_type);
3188 initializer.Type = block_type;
3189 initializer.DelegateType = delegateType;
3191 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3193 state_machine = stateMachine;
3194 initializer.SetStateMachine (stateMachine);
3196 var b = this is ToplevelBlock ?
3197 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3198 new ParametersBlock (Parent, parameters, Location.Null) {
3203 b.IsCompilerGenerated = true;
3204 b.state_machine = stateMachine;
3205 b.AddStatement (new StatementExpression (initializer));
3213 public class ToplevelBlock : ParametersBlock
3215 LocalVariable this_variable;
3216 CompilerContext compiler;
3217 Dictionary<string, object> names;
3218 Dictionary<string, object> labels;
3220 List<ExplicitBlock> this_references;
3222 public ToplevelBlock (CompilerContext ctx, Location loc)
3223 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3227 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3228 : base (parameters, start)
3230 this.compiler = ctx;
3232 flags |= Flags.HasRet;
3234 ProcessParameters ();
3238 // Recreates a top level block from parameters block. Used for
3239 // compiler generated methods where the original block comes from
3240 // explicit child block. This works for already resolved blocks
3241 // only to ensure we resolve them in the correct flow order
3243 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3244 : base (source, parameters)
3246 this.compiler = source.TopBlock.compiler;
3248 flags |= Flags.HasRet;
3251 public bool IsIterator {
3253 return (flags & Flags.Iterator) != 0;
3256 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3260 public Report Report {
3262 return compiler.Report;
3267 // Used by anonymous blocks to track references of `this' variable
3269 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3271 return this_references;
3276 // Returns the "this" instance variable of this block.
3277 // See AddThisVariable() for more information.
3279 public LocalVariable ThisVariable {
3281 return this_variable;
3285 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3288 names = new Dictionary<string, object> ();
3291 if (!names.TryGetValue (name, out value)) {
3292 names.Add (name, li);
3296 INamedBlockVariable existing = value as INamedBlockVariable;
3297 List<INamedBlockVariable> existing_list;
3298 if (existing != null) {
3299 existing_list = new List<INamedBlockVariable> ();
3300 existing_list.Add (existing);
3301 names[name] = existing_list;
3303 existing_list = (List<INamedBlockVariable>) value;
3307 // A collision checking between local names
3309 var variable_block = li.Block.Explicit;
3310 for (int i = 0; i < existing_list.Count; ++i) {
3311 existing = existing_list[i];
3312 Block b = existing.Block.Explicit;
3314 // Collision at same level
3315 if (variable_block == b) {
3316 li.Block.Error_AlreadyDeclared (name, li);
3320 // Collision with parent
3321 Block parent = variable_block;
3322 while ((parent = parent.Parent) != null) {
3324 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3325 i = existing_list.Count;
3330 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3331 // Collision with children
3332 while ((b = b.Parent) != null) {
3333 if (variable_block == b) {
3334 li.Block.Error_AlreadyDeclared (name, li, "child");
3335 i = existing_list.Count;
3342 existing_list.Add (li);
3345 public void AddLabel (string name, LabeledStatement label)
3348 labels = new Dictionary<string, object> ();
3351 if (!labels.TryGetValue (name, out value)) {
3352 labels.Add (name, label);
3356 LabeledStatement existing = value as LabeledStatement;
3357 List<LabeledStatement> existing_list;
3358 if (existing != null) {
3359 existing_list = new List<LabeledStatement> ();
3360 existing_list.Add (existing);
3361 labels[name] = existing_list;
3363 existing_list = (List<LabeledStatement>) value;
3367 // A collision checking between labels
3369 for (int i = 0; i < existing_list.Count; ++i) {
3370 existing = existing_list[i];
3371 Block b = existing.Block;
3373 // Collision at same level
3374 if (label.Block == b) {
3375 Report.SymbolRelatedToPreviousError (existing.loc, name);
3376 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3380 // Collision with parent
3382 while ((b = b.Parent) != null) {
3383 if (existing.Block == b) {
3384 Report.Error (158, label.loc,
3385 "The label `{0}' shadows another label by the same name in a contained scope", name);
3386 i = existing_list.Count;
3391 // Collision with with children
3393 while ((b = b.Parent) != null) {
3394 if (label.Block == b) {
3395 Report.Error (158, label.loc,
3396 "The label `{0}' shadows another label by the same name in a contained scope", name);
3397 i = existing_list.Count;
3403 existing_list.Add (label);
3406 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3408 if (this_references == null)
3409 this_references = new List<ExplicitBlock> ();
3411 if (!this_references.Contains (block))
3412 this_references.Add (block);
3415 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3417 this_references.Remove (block);
3421 // Creates an arguments set from all parameters, useful for method proxy calls
3423 public Arguments GetAllParametersArguments ()
3425 int count = parameters.Count;
3426 Arguments args = new Arguments (count);
3427 for (int i = 0; i < count; ++i) {
3428 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3429 args.Add (new Argument (arg_expr));
3436 // Lookup inside a block, the returned value can represent 3 states
3438 // true+variable: A local name was found and it's valid
3439 // false+variable: A local name was found in a child block only
3440 // false+null: No local name was found
3442 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3448 if (!names.TryGetValue (name, out value))
3451 variable = value as INamedBlockVariable;
3453 if (variable != null) {
3455 if (variable.Block == b.Original)
3459 } while (b != null);
3467 } while (b != null);
3469 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3470 for (int i = 0; i < list.Count; ++i) {
3473 if (variable.Block == b.Original)
3477 } while (b != null);
3485 } while (b != null);
3495 public LabeledStatement GetLabel (string name, Block block)
3501 if (!labels.TryGetValue (name, out value)) {
3505 var label = value as LabeledStatement;
3507 if (label != null) {
3508 if (label.Block == b.Original)
3511 List<LabeledStatement> list = (List<LabeledStatement>) value;
3512 for (int i = 0; i < list.Count; ++i) {
3514 if (label.Block == b.Original)
3523 // This is used by non-static `struct' constructors which do not have an
3524 // initializer - in this case, the constructor must initialize all of the
3525 // struct's fields. To do this, we add a "this" variable and use the flow
3526 // analysis code to ensure that it's been fully initialized before control
3527 // leaves the constructor.
3529 public void AddThisVariable (BlockContext bc)
3531 if (this_variable != null)
3532 throw new InternalErrorException (StartLocation.ToString ());
3534 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3535 this_variable.Type = bc.CurrentType;
3536 this_variable.PrepareForFlowAnalysis (bc);
3539 public bool IsThisAssigned (BlockContext ec)
3541 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3544 public override void Emit (EmitContext ec)
3546 if (Report.Errors > 0)
3550 if (IsCompilerGenerated) {
3551 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3559 // If `HasReturnLabel' is set, then we already emitted a
3560 // jump to the end of the method, so we must emit a `ret'
3563 // Unfortunately, System.Reflection.Emit automatically emits
3564 // a leave to the end of a finally block. This is a problem
3565 // if no code is following the try/finally block since we may
3566 // jump to a point after the end of the method.
3567 // As a workaround, we're always creating a return label in
3570 if (ec.HasReturnLabel || !unreachable) {
3571 if (ec.HasReturnLabel)
3572 ec.MarkLabel (ec.ReturnLabel);
3574 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3575 ec.Mark (EndLocation);
3577 if (ec.ReturnType.Kind != MemberKind.Void)
3578 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3580 ec.Emit (OpCodes.Ret);
3583 } catch (Exception e) {
3584 throw new InternalErrorException (e, StartLocation);
3589 public class SwitchLabel : Statement
3597 // if expr == null, then it is the default case.
3599 public SwitchLabel (Expression expr, Location l)
3605 public bool IsDefault {
3607 return label == null;
3611 public Expression Label {
3617 public Location Location {
3623 public Constant Converted {
3632 public bool SectionStart { get; set; }
3634 public Label GetILLabel (EmitContext ec)
3636 if (il_label == null){
3637 il_label = ec.DefineLabel ();
3640 return il_label.Value;
3643 protected override void DoEmit (EmitContext ec)
3645 ec.MarkLabel (GetILLabel (ec));
3648 public override bool Resolve (BlockContext bc)
3650 if (ResolveAndReduce (bc))
3651 bc.Switch.RegisterLabel (bc, this);
3653 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3655 return base.Resolve (bc);
3659 // Resolves the expression, reduces it to a literal if possible
3660 // and then converts it to the requested type.
3662 bool ResolveAndReduce (ResolveContext rc)
3667 var c = label.ResolveLabelConstant (rc);
3671 if (rc.Switch.IsNullable && c is NullLiteral) {
3676 converted = c.ImplicitConversionRequired (rc, rc.Switch.SwitchType, loc);
3677 return converted != null;
3680 public void Error_AlreadyOccurs (ResolveContext ec, SwitchLabel collision_with)
3683 if (converted == null)
3686 label = converted.GetValueAsLiteral ();
3688 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3689 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3692 protected override void CloneTo (CloneContext clonectx, Statement target)
3694 var t = (SwitchLabel) target;
3696 t.label = label.Clone (clonectx);
3699 public override object Accept (StructuralVisitor visitor)
3701 return visitor.Visit (this);
3705 public class Switch : Statement
3707 // structure used to hold blocks of keys while calculating table switch
3708 sealed class LabelsRange : IComparable<LabelsRange>
3710 public readonly long min;
3712 public readonly List<long> label_values;
3714 public LabelsRange (long value)
3717 label_values = new List<long> ();
3718 label_values.Add (value);
3721 public LabelsRange (long min, long max, ICollection<long> values)
3725 this.label_values = new List<long> (values);
3730 return max - min + 1;
3734 public bool AddValue (long value)
3736 var gap = value - min + 1;
3737 // Ensure the range has > 50% occupancy
3738 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3742 label_values.Add (value);
3746 public int CompareTo (LabelsRange other)
3748 int nLength = label_values.Count;
3749 int nLengthOther = other.label_values.Count;
3750 if (nLengthOther == nLength)
3751 return (int) (other.min - min);
3753 return nLength - nLengthOther;
3757 sealed class DispatchStatement : Statement
3759 readonly Switch body;
3761 public DispatchStatement (Switch body)
3766 protected override void CloneTo (CloneContext clonectx, Statement target)
3768 throw new NotImplementedException ();
3771 protected override void DoEmit (EmitContext ec)
3773 body.EmitDispatch (ec);
3777 public Expression Expr;
3780 // Mapping of all labels to their SwitchLabels
3782 Dictionary<long, SwitchLabel> labels;
3783 Dictionary<string, SwitchLabel> string_labels;
3784 List<SwitchLabel> case_labels;
3786 List<Tuple<GotoCase, Constant>> goto_cases;
3789 /// The governing switch type
3791 public TypeSpec SwitchType;
3793 Expression new_expr;
3795 SwitchLabel case_null;
3796 SwitchLabel case_default;
3798 Label defaultLabel, nullLabel;
3799 VariableReference value;
3800 ExpressionStatement string_dictionary;
3801 FieldExpr switch_cache_field;
3802 ExplicitBlock block;
3805 // Nullable Types support
3807 Nullable.Unwrap unwrap;
3809 public Switch (Expression e, ExplicitBlock block, Location l)
3816 public ExplicitBlock Block {
3822 public SwitchLabel DefaultLabel {
3824 return case_default;
3828 public bool IsNullable {
3830 return unwrap != null;
3835 // Determines the governing type for a switch. The returned
3836 // expression might be the expression from the switch, or an
3837 // expression that includes any potential conversions to
3839 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3841 switch (expr.Type.BuiltinType) {
3842 case BuiltinTypeSpec.Type.Byte:
3843 case BuiltinTypeSpec.Type.SByte:
3844 case BuiltinTypeSpec.Type.UShort:
3845 case BuiltinTypeSpec.Type.Short:
3846 case BuiltinTypeSpec.Type.UInt:
3847 case BuiltinTypeSpec.Type.Int:
3848 case BuiltinTypeSpec.Type.ULong:
3849 case BuiltinTypeSpec.Type.Long:
3850 case BuiltinTypeSpec.Type.Char:
3851 case BuiltinTypeSpec.Type.String:
3852 case BuiltinTypeSpec.Type.Bool:
3856 if (expr.Type.IsEnum)
3860 // Try to find a *user* defined implicit conversion.
3862 // If there is no implicit conversion, or if there are multiple
3863 // conversions, we have to report an error
3865 Expression converted = null;
3866 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3869 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3874 // Ignore over-worked ImplicitUserConversions that do
3875 // an implicit conversion in addition to the user conversion.
3877 if (!(e is UserCast))
3880 if (converted != null){
3881 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3890 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3892 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3907 public void RegisterLabel (ResolveContext rc, SwitchLabel sl)
3909 case_labels.Add (sl);
3912 if (case_default != null) {
3913 sl.Error_AlreadyOccurs (rc, case_default);
3922 if (string_labels != null) {
3923 string string_value = sl.Converted.GetValue () as string;
3924 if (string_value == null)
3927 string_labels.Add (string_value, sl);
3929 if (sl.Converted is NullLiteral) {
3932 labels.Add (sl.Converted.GetValueAsLong (), sl);
3935 } catch (ArgumentException) {
3936 if (string_labels != null)
3937 sl.Error_AlreadyOccurs (rc, string_labels[(string) sl.Converted.GetValue ()]);
3939 sl.Error_AlreadyOccurs (rc, labels[sl.Converted.GetValueAsLong ()]);
3944 // This method emits code for a lookup-based switch statement (non-string)
3945 // Basically it groups the cases into blocks that are at least half full,
3946 // and then spits out individual lookup opcodes for each block.
3947 // It emits the longest blocks first, and short blocks are just
3948 // handled with direct compares.
3950 void EmitTableSwitch (EmitContext ec, Expression val)
3952 if (labels != null && labels.Count > 0) {
3953 List<LabelsRange> ranges;
3954 if (string_labels != null) {
3955 // We have done all hard work for string already
3956 // setup single range only
3957 ranges = new List<LabelsRange> (1);
3958 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3960 var element_keys = new long[labels.Count];
3961 labels.Keys.CopyTo (element_keys, 0);
3962 Array.Sort (element_keys);
3965 // Build possible ranges of switch labes to reduce number
3968 ranges = new List<LabelsRange> (element_keys.Length);
3969 var range = new LabelsRange (element_keys[0]);
3971 for (int i = 1; i < element_keys.Length; ++i) {
3972 var l = element_keys[i];
3973 if (range.AddValue (l))
3976 range = new LabelsRange (l);
3980 // sort the blocks so we can tackle the largest ones first
3984 Label lbl_default = defaultLabel;
3985 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
3987 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
3988 LabelsRange kb = ranges[range_index];
3989 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
3991 // Optimize small ranges using simple equality check
3992 if (kb.Range <= 2) {
3993 foreach (var key in kb.label_values) {
3994 SwitchLabel sl = labels[key];
3995 if (sl == case_default || sl == case_null)
3998 if (sl.Converted.IsZeroInteger) {
3999 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4002 sl.Converted.Emit (ec);
4003 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4007 // TODO: if all the keys in the block are the same and there are
4008 // no gaps/defaults then just use a range-check.
4009 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4010 // TODO: optimize constant/I4 cases
4012 // check block range (could be > 2^31)
4014 ec.EmitLong (kb.min);
4015 ec.Emit (OpCodes.Blt, lbl_default);
4018 ec.EmitLong (kb.max);
4019 ec.Emit (OpCodes.Bgt, lbl_default);
4024 ec.EmitLong (kb.min);
4025 ec.Emit (OpCodes.Sub);
4028 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4032 int first = (int) kb.min;
4035 ec.Emit (OpCodes.Sub);
4036 } else if (first < 0) {
4037 ec.EmitInt (-first);
4038 ec.Emit (OpCodes.Add);
4042 // first, build the list of labels for the switch
4044 long cJumps = kb.Range;
4045 Label[] switch_labels = new Label[cJumps];
4046 for (int iJump = 0; iJump < cJumps; iJump++) {
4047 var key = kb.label_values[iKey];
4048 if (key == kb.min + iJump) {
4049 switch_labels[iJump] = labels[key].GetILLabel (ec);
4052 switch_labels[iJump] = lbl_default;
4056 // emit the switch opcode
4057 ec.Emit (OpCodes.Switch, switch_labels);
4060 // mark the default for this block
4061 if (range_index != 0)
4062 ec.MarkLabel (lbl_default);
4065 // the last default just goes to the end
4066 if (ranges.Count > 0)
4067 ec.Emit (OpCodes.Br, lbl_default);
4071 SwitchLabel FindLabel (Constant value)
4073 SwitchLabel sl = null;
4075 if (string_labels != null) {
4076 string s = value.GetValue () as string;
4078 if (case_null != null)
4080 else if (case_default != null)
4083 string_labels.TryGetValue (s, out sl);
4086 if (value is NullLiteral) {
4089 labels.TryGetValue (value.GetValueAsLong (), out sl);
4096 public override bool Resolve (BlockContext ec)
4098 Expr = Expr.Resolve (ec);
4102 new_expr = SwitchGoverningType (ec, Expr);
4104 if (new_expr == null && Expr.Type.IsNullableType) {
4105 unwrap = Nullable.Unwrap.Create (Expr, false);
4109 new_expr = SwitchGoverningType (ec, unwrap);
4112 if (new_expr == null) {
4113 if (Expr.Type != InternalType.ErrorType) {
4114 ec.Report.Error (151, loc,
4115 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4116 Expr.Type.GetSignatureForError ());
4123 SwitchType = new_expr.Type;
4125 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4126 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4130 if (block.Statements.Count == 0)
4133 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4134 string_labels = new Dictionary<string, SwitchLabel> ();
4136 labels = new Dictionary<long, SwitchLabel> ();
4139 case_labels = new List<SwitchLabel> ();
4141 var constant = new_expr as Constant;
4144 // Don't need extra variable for constant switch or switch with
4145 // only default case
4147 if (constant == null) {
4149 // Store switch expression for comparison purposes
4151 value = new_expr as VariableReference;
4152 if (value == null && !HasOnlyDefaultSection ()) {
4153 var current_block = ec.CurrentBlock;
4154 ec.CurrentBlock = Block;
4155 // Create temporary variable inside switch scope
4156 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4158 ec.CurrentBlock = current_block;
4162 Switch old_switch = ec.Switch;
4164 ec.Switch.SwitchType = SwitchType;
4166 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4168 ec.CurrentBranching.CurrentUsageVector.Goto ();
4170 var ok = block.Resolve (ec);
4172 if (case_default == null)
4173 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4175 ec.EndFlowBranching ();
4176 ec.Switch = old_switch;
4179 // Check if all goto cases are valid. Needs to be done after switch
4180 // is resolved becuase goto can jump forward in the scope.
4182 if (goto_cases != null) {
4183 foreach (var gc in goto_cases) {
4184 if (gc.Item1 == null) {
4185 if (DefaultLabel == null) {
4186 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
4192 var sl = FindLabel (gc.Item2);
4194 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + gc.Item2.GetValueAsLiteral (), ec.Report);
4196 gc.Item1.Label = sl;
4201 if (constant != null) {
4202 ResolveUnreachableSections (ec, constant);
4208 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4209 ResolveStringSwitchMap (ec);
4213 // Anonymous storey initialization has to happen before
4214 // any generated switch dispatch
4216 block.InsertStatement (0, new DispatchStatement (this));
4221 bool HasOnlyDefaultSection ()
4223 for (int i = 0; i < block.Statements.Count; ++i) {
4224 var s = block.Statements[i] as SwitchLabel;
4226 if (s == null || s.IsDefault)
4235 public void RegisterGotoCase (GotoCase gotoCase, Constant value)
4237 if (goto_cases == null)
4238 goto_cases = new List<Tuple<GotoCase, Constant>> ();
4240 goto_cases.Add (Tuple.Create (gotoCase, value));
4244 // Converts string switch into string hashtable
4246 void ResolveStringSwitchMap (ResolveContext ec)
4248 FullNamedExpression string_dictionary_type;
4249 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4250 string_dictionary_type = new TypeExpression (
4251 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4252 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4254 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4255 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4257 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4261 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4262 Field field = new Field (ctype, string_dictionary_type,
4263 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4264 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4265 if (!field.Define ())
4267 ctype.AddField (field);
4269 var init = new List<Expression> ();
4271 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4272 string value = null;
4274 foreach (SwitchLabel sl in case_labels) {
4276 if (sl.SectionStart)
4277 labels.Add (++counter, sl);
4279 if (sl == case_default || sl == case_null)
4282 value = (string) sl.Converted.GetValue ();
4283 var init_args = new List<Expression> (2);
4284 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4286 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4287 init_args.Add (sl.Converted);
4289 init.Add (new CollectionElementInitializer (init_args, loc));
4292 Arguments args = new Arguments (1);
4293 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4294 Expression initializer = new NewInitialize (string_dictionary_type, args,
4295 new CollectionOrObjectInitializers (init, loc), loc);
4297 switch_cache_field = new FieldExpr (field, loc);
4298 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4301 void ResolveUnreachableSections (BlockContext bc, Constant value)
4303 var constant_label = FindLabel (value) ?? case_default;
4306 bool unreachable_reported = false;
4307 for (int i = 0; i < block.Statements.Count; ++i) {
4308 var s = block.Statements[i];
4310 if (s is SwitchLabel) {
4311 if (unreachable_reported) {
4312 found = unreachable_reported = false;
4315 found |= s == constant_label;
4320 unreachable_reported = true;
4324 if (!unreachable_reported) {
4325 unreachable_reported = true;
4326 bc.Report.Warning (162, 2, s.loc, "Unreachable code detected");
4329 block.Statements[i] = new EmptyStatement (s.loc);
4333 void DoEmitStringSwitch (EmitContext ec)
4335 Label l_initialized = ec.DefineLabel ();
4338 // Skip initialization when value is null
4340 value.EmitBranchable (ec, nullLabel, false);
4343 // Check if string dictionary is initialized and initialize
4345 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4346 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4347 string_dictionary.EmitStatement (ec);
4349 ec.MarkLabel (l_initialized);
4351 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4353 ResolveContext rc = new ResolveContext (ec.MemberContext);
4355 if (switch_cache_field.Type.IsGeneric) {
4356 Arguments get_value_args = new Arguments (2);
4357 get_value_args.Add (new Argument (value));
4358 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4359 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4360 if (get_item == null)
4364 // A value was not found, go to default case
4366 get_item.EmitBranchable (ec, defaultLabel, false);
4368 Arguments get_value_args = new Arguments (1);
4369 get_value_args.Add (new Argument (value));
4371 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4372 if (get_item == null)
4375 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4376 get_item_object.EmitAssign (ec, get_item, true, false);
4377 ec.Emit (OpCodes.Brfalse, defaultLabel);
4379 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4380 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4382 get_item_int.EmitStatement (ec);
4383 get_item_object.Release (ec);
4386 EmitTableSwitch (ec, string_switch_variable);
4387 string_switch_variable.Release (ec);
4391 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4393 void EmitShortSwitch (EmitContext ec)
4395 MethodSpec equal_method = null;
4396 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4397 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4400 if (equal_method != null) {
4401 value.EmitBranchable (ec, nullLabel, false);
4404 for (int i = 0; i < case_labels.Count; ++i) {
4405 var label = case_labels [i];
4406 if (label == case_default || label == case_null)
4409 var constant = label.Converted;
4411 if (equal_method != null) {
4415 var call = new CallEmitter ();
4416 call.EmitPredefined (ec, equal_method, new Arguments (0));
4417 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4421 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4422 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4428 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4431 ec.Emit (OpCodes.Br, defaultLabel);
4434 void EmitDispatch (EmitContext ec)
4436 if (value == null) {
4438 // Constant switch, we already done the work
4443 if (string_dictionary != null) {
4444 DoEmitStringSwitch (ec);
4445 } else if (case_labels.Count < 4 || string_labels != null) {
4446 EmitShortSwitch (ec);
4448 EmitTableSwitch (ec, value);
4452 protected override void DoEmit (EmitContext ec)
4454 // Workaround broken flow-analysis
4455 block.HasUnreachableClosingBrace = true;
4458 // Setup the codegen context
4460 Label old_end = ec.LoopEnd;
4461 Switch old_switch = ec.Switch;
4463 ec.LoopEnd = ec.DefineLabel ();
4466 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4467 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4469 if (value != null) {
4472 unwrap.EmitCheck (ec);
4473 ec.Emit (OpCodes.Brfalse, nullLabel);
4474 value.EmitAssign (ec, new_expr, false, false);
4475 } else if (new_expr != value) {
4476 value.EmitAssign (ec, new_expr, false, false);
4481 // Next statement is compiler generated we don't need extra
4482 // nop when we can use the statement for sequence point
4484 ec.Mark (block.StartLocation);
4485 block.IsCompilerGenerated = true;
4490 // Restore context state.
4491 ec.MarkLabel (ec.LoopEnd);
4494 // Restore the previous context
4496 ec.LoopEnd = old_end;
4497 ec.Switch = old_switch;
4500 protected override void CloneTo (CloneContext clonectx, Statement t)
4502 Switch target = (Switch) t;
4504 target.Expr = Expr.Clone (clonectx);
4505 target.block = (ExplicitBlock) block.Clone (clonectx);
4508 public override object Accept (StructuralVisitor visitor)
4510 return visitor.Visit (this);
4514 // A place where execution can restart in an iterator
4515 public abstract class ResumableStatement : Statement
4518 protected Label resume_point;
4520 public Label PrepareForEmit (EmitContext ec)
4524 resume_point = ec.DefineLabel ();
4526 return resume_point;
4529 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4534 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4539 public abstract class TryFinallyBlock : ExceptionStatement
4541 protected Statement stmt;
4542 Label dispose_try_block;
4543 bool prepared_for_dispose, emitted_dispose;
4544 Method finally_host;
4546 protected TryFinallyBlock (Statement stmt, Location loc)
4554 public Statement Statement {
4562 protected abstract void EmitTryBody (EmitContext ec);
4563 public abstract void EmitFinallyBody (EmitContext ec);
4565 public override Label PrepareForDispose (EmitContext ec, Label end)
4567 if (!prepared_for_dispose) {
4568 prepared_for_dispose = true;
4569 dispose_try_block = ec.DefineLabel ();
4571 return dispose_try_block;
4574 protected sealed override void DoEmit (EmitContext ec)
4576 EmitTryBodyPrepare (ec);
4579 ec.BeginFinallyBlock ();
4581 Label start_finally = ec.DefineLabel ();
4582 if (resume_points != null) {
4583 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4585 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4586 ec.Emit (OpCodes.Brfalse_S, start_finally);
4587 ec.Emit (OpCodes.Endfinally);
4590 ec.MarkLabel (start_finally);
4592 if (finally_host != null) {
4593 finally_host.Define ();
4594 finally_host.PrepareEmit ();
4595 finally_host.Emit ();
4597 // Now it's safe to add, to close it properly and emit sequence points
4598 finally_host.Parent.AddMember (finally_host);
4600 var ce = new CallEmitter ();
4601 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4602 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4604 EmitFinallyBody (ec);
4607 ec.EndExceptionBlock ();
4610 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4612 if (emitted_dispose)
4615 emitted_dispose = true;
4617 Label end_of_try = ec.DefineLabel ();
4619 // Ensure that the only way we can get into this code is through a dispatcher
4620 if (have_dispatcher)
4621 ec.Emit (OpCodes.Br, end);
4623 ec.BeginExceptionBlock ();
4625 ec.MarkLabel (dispose_try_block);
4627 Label[] labels = null;
4628 for (int i = 0; i < resume_points.Count; ++i) {
4629 ResumableStatement s = resume_points[i];
4630 Label ret = s.PrepareForDispose (ec, end_of_try);
4631 if (ret.Equals (end_of_try) && labels == null)
4633 if (labels == null) {
4634 labels = new Label[resume_points.Count];
4635 for (int j = 0; j < i; ++j)
4636 labels[j] = end_of_try;
4641 if (labels != null) {
4643 for (j = 1; j < labels.Length; ++j)
4644 if (!labels[0].Equals (labels[j]))
4646 bool emit_dispatcher = j < labels.Length;
4648 if (emit_dispatcher) {
4649 ec.Emit (OpCodes.Ldloc, pc);
4650 ec.EmitInt (first_resume_pc);
4651 ec.Emit (OpCodes.Sub);
4652 ec.Emit (OpCodes.Switch, labels);
4655 foreach (ResumableStatement s in resume_points)
4656 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4659 ec.MarkLabel (end_of_try);
4661 ec.BeginFinallyBlock ();
4663 if (finally_host != null) {
4664 var ce = new CallEmitter ();
4665 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4666 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4668 EmitFinallyBody (ec);
4671 ec.EndExceptionBlock ();
4674 public override bool Resolve (BlockContext bc)
4677 // Finally block inside iterator is called from MoveNext and
4678 // Dispose methods that means we need to lift the block into
4679 // newly created host method to emit the body only once. The
4680 // original block then simply calls the newly generated method.
4682 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4683 var b = stmt as Block;
4684 if (b != null && b.Explicit.HasYield) {
4685 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4689 return base.Resolve (bc);
4694 // Base class for blocks using exception handling
4696 public abstract class ExceptionStatement : ResumableStatement
4701 protected List<ResumableStatement> resume_points;
4702 protected int first_resume_pc;
4704 protected ExceptionStatement (Location loc)
4709 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4711 StateMachineInitializer state_machine = null;
4712 if (resume_points != null) {
4713 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4715 ec.EmitInt ((int) IteratorStorey.State.Running);
4716 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4719 ec.BeginExceptionBlock ();
4721 if (resume_points != null) {
4722 ec.MarkLabel (resume_point);
4724 // For normal control flow, we want to fall-through the Switch
4725 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4726 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4727 ec.EmitInt (first_resume_pc);
4728 ec.Emit (OpCodes.Sub);
4730 Label[] labels = new Label[resume_points.Count];
4731 for (int i = 0; i < resume_points.Count; ++i)
4732 labels[i] = resume_points[i].PrepareForEmit (ec);
4733 ec.Emit (OpCodes.Switch, labels);
4737 public void SomeCodeFollows ()
4740 code_follows = true;
4744 public override bool Resolve (BlockContext ec)
4747 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4748 // So, ensure there's some IL code after this statement.
4749 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4750 ec.NeedReturnLabel ();
4755 public void AddResumePoint (ResumableStatement stmt, int pc)
4757 if (resume_points == null) {
4758 resume_points = new List<ResumableStatement> ();
4759 first_resume_pc = pc;
4762 if (pc != first_resume_pc + resume_points.Count)
4763 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4765 resume_points.Add (stmt);
4770 public class Lock : TryFinallyBlock
4773 TemporaryVariableReference expr_copy;
4774 TemporaryVariableReference lock_taken;
4776 public Lock (Expression expr, Statement stmt, Location loc)
4782 public Expression Expr {
4788 public override bool Resolve (BlockContext ec)
4790 expr = expr.Resolve (ec);
4794 if (!TypeSpec.IsReferenceType (expr.Type)) {
4795 ec.Report.Error (185, loc,
4796 "`{0}' is not a reference type as required by the lock statement",
4797 expr.Type.GetSignatureForError ());
4800 if (expr.Type.IsGenericParameter) {
4801 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4804 VariableReference lv = expr as VariableReference;
4807 locked = lv.IsLockedByStatement;
4808 lv.IsLockedByStatement = true;
4815 // Have to keep original lock value around to unlock same location
4816 // in the case of original value has changed or is null
4818 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4819 expr_copy.Resolve (ec);
4822 // Ensure Monitor methods are available
4824 if (ResolvePredefinedMethods (ec) > 1) {
4825 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4826 lock_taken.Resolve (ec);
4829 using (ec.Set (ResolveContext.Options.LockScope)) {
4830 ec.StartFlowBranching (this);
4831 Statement.Resolve (ec);
4832 ec.EndFlowBranching ();
4836 lv.IsLockedByStatement = locked;
4844 protected override void EmitTryBodyPrepare (EmitContext ec)
4846 expr_copy.EmitAssign (ec, expr);
4848 if (lock_taken != null) {
4850 // Initialize ref variable
4852 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4855 // Monitor.Enter (expr_copy)
4857 expr_copy.Emit (ec);
4858 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4861 base.EmitTryBodyPrepare (ec);
4864 protected override void EmitTryBody (EmitContext ec)
4867 // Monitor.Enter (expr_copy, ref lock_taken)
4869 if (lock_taken != null) {
4870 expr_copy.Emit (ec);
4871 lock_taken.LocalInfo.CreateBuilder (ec);
4872 lock_taken.AddressOf (ec, AddressOp.Load);
4873 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4876 Statement.Emit (ec);
4879 public override void EmitFinallyBody (EmitContext ec)
4882 // if (lock_taken) Monitor.Exit (expr_copy)
4884 Label skip = ec.DefineLabel ();
4886 if (lock_taken != null) {
4887 lock_taken.Emit (ec);
4888 ec.Emit (OpCodes.Brfalse_S, skip);
4891 expr_copy.Emit (ec);
4892 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4894 ec.Emit (OpCodes.Call, m);
4896 ec.MarkLabel (skip);
4899 int ResolvePredefinedMethods (ResolveContext rc)
4901 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4902 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4906 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4910 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4914 protected override void CloneTo (CloneContext clonectx, Statement t)
4916 Lock target = (Lock) t;
4918 target.expr = expr.Clone (clonectx);
4919 target.stmt = Statement.Clone (clonectx);
4922 public override object Accept (StructuralVisitor visitor)
4924 return visitor.Visit (this);
4929 public class Unchecked : Statement {
4932 public Unchecked (Block b, Location loc)
4939 public override bool Resolve (BlockContext ec)
4941 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4942 return Block.Resolve (ec);
4945 protected override void DoEmit (EmitContext ec)
4947 using (ec.With (EmitContext.Options.CheckedScope, false))
4951 protected override void CloneTo (CloneContext clonectx, Statement t)
4953 Unchecked target = (Unchecked) t;
4955 target.Block = clonectx.LookupBlock (Block);
4958 public override object Accept (StructuralVisitor visitor)
4960 return visitor.Visit (this);
4964 public class Checked : Statement {
4967 public Checked (Block b, Location loc)
4970 b.Unchecked = false;
4974 public override bool Resolve (BlockContext ec)
4976 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4977 return Block.Resolve (ec);
4980 protected override void DoEmit (EmitContext ec)
4982 using (ec.With (EmitContext.Options.CheckedScope, true))
4986 protected override void CloneTo (CloneContext clonectx, Statement t)
4988 Checked target = (Checked) t;
4990 target.Block = clonectx.LookupBlock (Block);
4993 public override object Accept (StructuralVisitor visitor)
4995 return visitor.Visit (this);
4999 public class Unsafe : Statement {
5002 public Unsafe (Block b, Location loc)
5005 Block.Unsafe = true;
5009 public override bool Resolve (BlockContext ec)
5011 if (ec.CurrentIterator != null)
5012 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
5014 using (ec.Set (ResolveContext.Options.UnsafeScope))
5015 return Block.Resolve (ec);
5018 protected override void DoEmit (EmitContext ec)
5023 protected override void CloneTo (CloneContext clonectx, Statement t)
5025 Unsafe target = (Unsafe) t;
5027 target.Block = clonectx.LookupBlock (Block);
5030 public override object Accept (StructuralVisitor visitor)
5032 return visitor.Visit (this);
5039 public class Fixed : Statement
5041 abstract class Emitter : ShimExpression
5043 protected LocalVariable vi;
5045 protected Emitter (Expression expr, LocalVariable li)
5051 public abstract void EmitExit (EmitContext ec);
5054 class ExpressionEmitter : Emitter {
5055 public ExpressionEmitter (Expression converted, LocalVariable li) :
5056 base (converted, li)
5060 protected override Expression DoResolve (ResolveContext rc)
5062 throw new NotImplementedException ();
5065 public override void Emit (EmitContext ec) {
5067 // Store pointer in pinned location
5073 public override void EmitExit (EmitContext ec)
5076 ec.Emit (OpCodes.Conv_U);
5081 class StringEmitter : Emitter
5083 LocalVariable pinned_string;
5085 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5090 protected override Expression DoResolve (ResolveContext rc)
5092 pinned_string = new LocalVariable (vi.Block, "$pinned",
5093 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5095 pinned_string.Type = rc.BuiltinTypes.String;
5097 eclass = ExprClass.Variable;
5098 type = rc.BuiltinTypes.Int;
5102 public override void Emit (EmitContext ec)
5104 pinned_string.CreateBuilder (ec);
5107 pinned_string.EmitAssign (ec);
5109 // TODO: Should use Binary::Add
5110 pinned_string.Emit (ec);
5111 ec.Emit (OpCodes.Conv_I);
5113 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5117 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5118 //pe.InstanceExpression = pinned_string;
5119 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5121 ec.Emit (OpCodes.Add);
5125 public override void EmitExit (EmitContext ec)
5128 pinned_string.EmitAssign (ec);
5132 public class VariableDeclaration : BlockVariable
5134 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5139 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5141 if (!Variable.Type.IsPointer && li == Variable) {
5142 bc.Report.Error (209, TypeExpression.Location,
5143 "The type of locals declared in a fixed statement must be a pointer type");
5148 // The rules for the possible declarators are pretty wise,
5149 // but the production on the grammar is more concise.
5151 // So we have to enforce these rules here.
5153 // We do not resolve before doing the case 1 test,
5154 // because the grammar is explicit in that the token &
5155 // is present, so we need to test for this particular case.
5158 if (initializer is Cast) {
5159 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5163 initializer = initializer.Resolve (bc);
5165 if (initializer == null)
5171 if (initializer.Type.IsArray) {
5172 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5175 // Provided that array_type is unmanaged,
5177 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5181 // and T* is implicitly convertible to the
5182 // pointer type given in the fixed statement.
5184 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5186 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5187 if (converted == null)
5191 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5193 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5194 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5195 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5196 new NullLiteral (loc),
5199 converted = converted.Resolve (bc);
5201 return new ExpressionEmitter (converted, li);
5207 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5208 return new StringEmitter (initializer, li, loc).Resolve (bc);
5211 // Case 3: fixed buffer
5212 if (initializer is FixedBufferPtr) {
5213 return new ExpressionEmitter (initializer, li);
5217 // Case 4: & object.
5219 bool already_fixed = true;
5220 Unary u = initializer as Unary;
5221 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5222 IVariableReference vr = u.Expr as IVariableReference;
5223 if (vr == null || !vr.IsFixed) {
5224 already_fixed = false;
5228 if (already_fixed) {
5229 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5232 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5233 return new ExpressionEmitter (initializer, li);
5238 VariableDeclaration decl;
5239 Statement statement;
5242 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5251 public Statement Statement {
5257 public BlockVariable Variables {
5265 public override bool Resolve (BlockContext ec)
5267 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5268 if (!decl.Resolve (ec))
5272 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5273 bool ok = statement.Resolve (ec);
5274 bool flow_unreachable = ec.EndFlowBranching ();
5275 has_ret = flow_unreachable;
5280 protected override void DoEmit (EmitContext ec)
5282 decl.Variable.CreateBuilder (ec);
5283 decl.Initializer.Emit (ec);
5284 if (decl.Declarators != null) {
5285 foreach (var d in decl.Declarators) {
5286 d.Variable.CreateBuilder (ec);
5287 d.Initializer.Emit (ec);
5291 statement.Emit (ec);
5297 // Clear the pinned variable
5299 ((Emitter) decl.Initializer).EmitExit (ec);
5300 if (decl.Declarators != null) {
5301 foreach (var d in decl.Declarators) {
5302 ((Emitter)d.Initializer).EmitExit (ec);
5307 protected override void CloneTo (CloneContext clonectx, Statement t)
5309 Fixed target = (Fixed) t;
5311 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5312 target.statement = statement.Clone (clonectx);
5315 public override object Accept (StructuralVisitor visitor)
5317 return visitor.Visit (this);
5321 public class Catch : Statement
5325 FullNamedExpression type_expr;
5326 CompilerAssign assign;
5329 public Catch (Block block, Location loc)
5337 public Block Block {
5343 public TypeSpec CatchType {
5349 public bool IsGeneral {
5351 return type_expr == null;
5355 public FullNamedExpression TypeExpression {
5364 public LocalVariable Variable {
5375 protected override void DoEmit (EmitContext ec)
5378 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5380 ec.BeginCatchBlock (CatchType);
5383 li.CreateBuilder (ec);
5386 // Special case hoisted catch variable, we have to use a temporary variable
5387 // to pass via anonymous storey initialization with the value still on top
5390 if (li.HoistedVariant != null) {
5391 LocalTemporary lt = new LocalTemporary (li.Type);
5394 // switch to assigning from the temporary variable and not from top of the stack
5395 assign.UpdateSource (lt);
5398 ec.Emit (OpCodes.Pop);
5404 public override bool Resolve (BlockContext ec)
5406 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5407 if (type_expr != null) {
5408 type = type_expr.ResolveAsType (ec);
5412 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5413 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5414 } else if (li != null) {
5416 li.PrepareForFlowAnalysis (ec);
5418 // source variable is at the top of the stack
5419 Expression source = new EmptyExpression (li.Type);
5420 if (li.Type.IsGenericParameter)
5421 source = new UnboxCast (source, li.Type);
5424 // Uses Location.Null to hide from symbol file
5426 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5427 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5431 return Block.Resolve (ec);
5435 protected override void CloneTo (CloneContext clonectx, Statement t)
5437 Catch target = (Catch) t;
5439 if (type_expr != null)
5440 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5442 target.block = clonectx.LookupBlock (block);
5446 public class TryFinally : TryFinallyBlock
5450 public TryFinally (Statement stmt, Block fini, Location loc)
5456 public Block Finallyblock {
5462 public override bool Resolve (BlockContext ec)
5466 ec.StartFlowBranching (this);
5468 if (!stmt.Resolve (ec))
5472 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5474 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5475 if (!fini.Resolve (ec))
5479 ec.EndFlowBranching ();
5481 ok &= base.Resolve (ec);
5486 protected override void EmitTryBody (EmitContext ec)
5491 public override void EmitFinallyBody (EmitContext ec)
5496 protected override void CloneTo (CloneContext clonectx, Statement t)
5498 TryFinally target = (TryFinally) t;
5500 target.stmt = (Statement) stmt.Clone (clonectx);
5502 target.fini = clonectx.LookupBlock (fini);
5505 public override object Accept (StructuralVisitor visitor)
5507 return visitor.Visit (this);
5511 public class TryCatch : ExceptionStatement
5514 List<Catch> clauses;
5515 readonly bool inside_try_finally;
5517 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5521 this.clauses = catch_clauses;
5522 this.inside_try_finally = inside_try_finally;
5525 public List<Catch> Clauses {
5531 public bool IsTryCatchFinally {
5533 return inside_try_finally;
5537 public override bool Resolve (BlockContext ec)
5541 ec.StartFlowBranching (this);
5543 if (!Block.Resolve (ec))
5546 for (int i = 0; i < clauses.Count; ++i) {
5548 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5550 if (!c.Resolve (ec)) {
5555 TypeSpec resolved_type = c.CatchType;
5556 for (int ii = 0; ii < clauses.Count; ++ii) {
5560 if (clauses[ii].IsGeneral) {
5561 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5564 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5567 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5570 ec.Report.Warning (1058, 1, c.loc,
5571 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5579 var ct = clauses[ii].CatchType;
5583 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5584 ec.Report.Error (160, c.loc,
5585 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5586 ct.GetSignatureForError ());
5592 ec.EndFlowBranching ();
5594 return base.Resolve (ec) && ok;
5597 protected sealed override void DoEmit (EmitContext ec)
5599 if (!inside_try_finally)
5600 EmitTryBodyPrepare (ec);
5604 foreach (Catch c in clauses)
5607 if (!inside_try_finally)
5608 ec.EndExceptionBlock ();
5611 protected override void CloneTo (CloneContext clonectx, Statement t)
5613 TryCatch target = (TryCatch) t;
5615 target.Block = clonectx.LookupBlock (Block);
5616 if (clauses != null){
5617 target.clauses = new List<Catch> ();
5618 foreach (Catch c in clauses)
5619 target.clauses.Add ((Catch) c.Clone (clonectx));
5623 public override object Accept (StructuralVisitor visitor)
5625 return visitor.Visit (this);
5629 public class Using : TryFinallyBlock
5631 public class VariableDeclaration : BlockVariable
5633 Statement dispose_call;
5635 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5640 public VariableDeclaration (LocalVariable li, Location loc)
5646 public VariableDeclaration (Expression expr)
5649 loc = expr.Location;
5655 public bool IsNested { get; private set; }
5659 public void EmitDispose (EmitContext ec)
5661 dispose_call.Emit (ec);
5664 public override bool Resolve (BlockContext bc)
5669 return base.Resolve (bc, false);
5672 public Expression ResolveExpression (BlockContext bc)
5674 var e = Initializer.Resolve (bc);
5678 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5679 Initializer = ResolveInitializer (bc, Variable, e);
5683 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5685 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5686 initializer = initializer.Resolve (bc);
5687 if (initializer == null)
5690 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5691 Arguments args = new Arguments (1);
5692 args.Add (new Argument (initializer));
5693 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5694 if (initializer == null)
5697 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5698 dispose_call = CreateDisposeCall (bc, var);
5699 dispose_call.Resolve (bc);
5701 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5704 if (li == Variable) {
5705 CheckIDiposableConversion (bc, li, initializer);
5706 dispose_call = CreateDisposeCall (bc, li);
5707 dispose_call.Resolve (bc);
5710 return base.ResolveInitializer (bc, li, initializer);
5713 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5717 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5718 if (type.IsNullableType) {
5719 // it's handled in CreateDisposeCall
5723 bc.Report.SymbolRelatedToPreviousError (type);
5724 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5725 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5726 type.GetSignatureForError ());
5732 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5734 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5736 var loc = lv.Location;
5738 var idt = bc.BuiltinTypes.IDisposable;
5739 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5741 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5742 dispose_mg.InstanceExpression = type.IsNullableType ?
5743 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5747 // Hide it from symbol file via null location
5749 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5751 // Add conditional call when disposing possible null variable
5752 if (!type.IsStruct || type.IsNullableType)
5753 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5758 public void ResolveDeclaratorInitializer (BlockContext bc)
5760 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5763 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5765 for (int i = declarators.Count - 1; i >= 0; --i) {
5766 var d = declarators [i];
5767 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5768 vd.Initializer = d.Initializer;
5770 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5771 vd.dispose_call.Resolve (bc);
5773 stmt = new Using (vd, stmt, d.Variable.Location);
5780 public override object Accept (StructuralVisitor visitor)
5782 return visitor.Visit (this);
5786 VariableDeclaration decl;
5788 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5794 public Using (Expression expr, Statement stmt, Location loc)
5797 this.decl = new VariableDeclaration (expr);
5802 public Expression Expr {
5804 return decl.Variable == null ? decl.Initializer : null;
5808 public BlockVariable Variables {
5816 public override void Emit (EmitContext ec)
5819 // Don't emit sequence point it will be set on variable declaration
5824 protected override void EmitTryBodyPrepare (EmitContext ec)
5827 base.EmitTryBodyPrepare (ec);
5830 protected override void EmitTryBody (EmitContext ec)
5835 public override void EmitFinallyBody (EmitContext ec)
5837 decl.EmitDispose (ec);
5840 public override bool Resolve (BlockContext ec)
5842 VariableReference vr;
5843 bool vr_locked = false;
5845 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5846 if (decl.Variable == null) {
5847 vr = decl.ResolveExpression (ec) as VariableReference;
5849 vr_locked = vr.IsLockedByStatement;
5850 vr.IsLockedByStatement = true;
5853 if (decl.IsNested) {
5854 decl.ResolveDeclaratorInitializer (ec);
5856 if (!decl.Resolve (ec))
5859 if (decl.Declarators != null) {
5860 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5868 ec.StartFlowBranching (this);
5872 ec.EndFlowBranching ();
5875 vr.IsLockedByStatement = vr_locked;
5882 protected override void CloneTo (CloneContext clonectx, Statement t)
5884 Using target = (Using) t;
5886 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5887 target.stmt = stmt.Clone (clonectx);
5890 public override object Accept (StructuralVisitor visitor)
5892 return visitor.Visit (this);
5897 /// Implementation of the foreach C# statement
5899 public class Foreach : Statement
5901 abstract class IteratorStatement : Statement
5903 protected readonly Foreach for_each;
5905 protected IteratorStatement (Foreach @foreach)
5907 this.for_each = @foreach;
5908 this.loc = @foreach.expr.Location;
5911 protected override void CloneTo (CloneContext clonectx, Statement target)
5913 throw new NotImplementedException ();
5916 public override void Emit (EmitContext ec)
5918 if (ec.EmitAccurateDebugInfo) {
5919 ec.Emit (OpCodes.Nop);
5926 sealed class ArrayForeach : IteratorStatement
5928 TemporaryVariableReference[] lengths;
5929 Expression [] length_exprs;
5930 StatementExpression[] counter;
5931 TemporaryVariableReference[] variables;
5933 TemporaryVariableReference copy;
5935 public ArrayForeach (Foreach @foreach, int rank)
5938 counter = new StatementExpression[rank];
5939 variables = new TemporaryVariableReference[rank];
5940 length_exprs = new Expression [rank];
5943 // Only use temporary length variables when dealing with
5944 // multi-dimensional arrays
5947 lengths = new TemporaryVariableReference [rank];
5950 public override bool Resolve (BlockContext ec)
5952 Block variables_block = for_each.variable.Block;
5953 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5956 int rank = length_exprs.Length;
5957 Arguments list = new Arguments (rank);
5958 for (int i = 0; i < rank; i++) {
5959 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5961 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5962 counter[i].Resolve (ec);
5965 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5967 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5968 lengths[i].Resolve (ec);
5970 Arguments args = new Arguments (1);
5971 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5972 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5975 list.Add (new Argument (v));
5978 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5983 if (for_each.type is VarExpr) {
5984 // Infer implicitly typed local variable from foreach array type
5985 var_type = access.Type;
5987 var_type = for_each.type.ResolveAsType (ec);
5989 if (var_type == null)
5992 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5997 for_each.variable.Type = var_type;
5999 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
6000 if (variable_ref == null)
6003 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
6007 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
6008 ec.CurrentBranching.CreateSibling ();
6010 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
6011 if (!for_each.body.Resolve (ec))
6013 ec.EndFlowBranching ();
6015 // There's no direct control flow from the end of the embedded statement to the end of the loop
6016 ec.CurrentBranching.CurrentUsageVector.Goto ();
6018 ec.EndFlowBranching ();
6023 protected override void DoEmit (EmitContext ec)
6025 copy.EmitAssign (ec, for_each.expr);
6027 int rank = length_exprs.Length;
6028 Label[] test = new Label [rank];
6029 Label[] loop = new Label [rank];
6031 for (int i = 0; i < rank; i++) {
6032 test [i] = ec.DefineLabel ();
6033 loop [i] = ec.DefineLabel ();
6035 if (lengths != null)
6036 lengths [i].EmitAssign (ec, length_exprs [i]);
6039 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
6040 for (int i = 0; i < rank; i++) {
6041 variables [i].EmitAssign (ec, zero);
6043 ec.Emit (OpCodes.Br, test [i]);
6044 ec.MarkLabel (loop [i]);
6047 for_each.body.Emit (ec);
6049 ec.MarkLabel (ec.LoopBegin);
6050 ec.Mark (for_each.expr.Location);
6052 for (int i = rank - 1; i >= 0; i--){
6053 counter [i].Emit (ec);
6055 ec.MarkLabel (test [i]);
6056 variables [i].Emit (ec);
6058 if (lengths != null)
6059 lengths [i].Emit (ec);
6061 length_exprs [i].Emit (ec);
6063 ec.Emit (OpCodes.Blt, loop [i]);
6066 ec.MarkLabel (ec.LoopEnd);
6070 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6072 class RuntimeDispose : Using.VariableDeclaration
6074 public RuntimeDispose (LocalVariable lv, Location loc)
6079 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6081 // Defered to runtime check
6084 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6086 var idt = bc.BuiltinTypes.IDisposable;
6089 // Fabricates code like
6091 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6094 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6096 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6097 dispose_variable.CreateReferenceExpression (bc, loc),
6098 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6099 loc), new NullLiteral (loc));
6101 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6103 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6104 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6106 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6107 return new If (idisaposable_test, dispose, loc);
6111 LocalVariable variable;
6113 Statement statement;
6114 ExpressionStatement init;
6115 TemporaryVariableReference enumerator_variable;
6116 bool ambiguous_getenumerator_name;
6118 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6121 this.variable = var;
6125 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6127 rc.Report.SymbolRelatedToPreviousError (enumerator);
6128 rc.Report.Error (202, loc,
6129 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6130 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6133 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6136 // Option 1: Try to match by name GetEnumerator first
6138 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6139 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6141 var mg = mexpr as MethodGroupExpr;
6143 mg.InstanceExpression = expr;
6144 Arguments args = new Arguments (0);
6145 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6147 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6148 if (ambiguous_getenumerator_name)
6151 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6157 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6160 PredefinedMember<MethodSpec> iface_candidate = null;
6161 var ptypes = rc.Module.PredefinedTypes;
6162 var gen_ienumerable = ptypes.IEnumerableGeneric;
6163 if (!gen_ienumerable.Define ())
6164 gen_ienumerable = null;
6166 var ifaces = t.Interfaces;
6167 if (ifaces != null) {
6168 foreach (var iface in ifaces) {
6169 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6170 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6171 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6172 rc.Report.Error (1640, loc,
6173 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6174 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6179 // TODO: Cache this somehow
6180 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6181 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6186 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6187 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6192 if (iface_candidate == null) {
6193 if (expr.Type != InternalType.ErrorType) {
6194 rc.Report.Error (1579, loc,
6195 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6196 expr.Type.GetSignatureForError (), "GetEnumerator");
6202 var method = iface_candidate.Resolve (loc);
6206 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6207 mg.InstanceExpression = expr;
6211 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6213 var ms = MemberCache.FindMember (enumerator.ReturnType,
6214 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6215 BindingRestriction.InstanceOnly) as MethodSpec;
6217 if (ms == null || !ms.IsPublic) {
6218 Error_WrongEnumerator (rc, enumerator);
6222 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6225 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6227 var ps = MemberCache.FindMember (enumerator.ReturnType,
6228 MemberFilter.Property ("Current", null),
6229 BindingRestriction.InstanceOnly) as PropertySpec;
6231 if (ps == null || !ps.IsPublic) {
6232 Error_WrongEnumerator (rc, enumerator);
6239 public override bool Resolve (BlockContext ec)
6241 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6244 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6245 } else if (expr.Type.IsNullableType) {
6246 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6249 var get_enumerator_mg = ResolveGetEnumerator (ec);
6250 if (get_enumerator_mg == null) {
6254 var get_enumerator = get_enumerator_mg.BestCandidate;
6255 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6256 enumerator_variable.Resolve (ec);
6258 // Prepare bool MoveNext ()
6259 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6260 if (move_next_mg == null) {
6264 move_next_mg.InstanceExpression = enumerator_variable;
6266 // Prepare ~T~ Current { get; }
6267 var current_prop = ResolveCurrent (ec, get_enumerator);
6268 if (current_prop == null) {
6272 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6273 if (current_pe == null)
6276 VarExpr ve = for_each.type as VarExpr;
6280 // Source type is dynamic, set element type to dynamic too
6281 variable.Type = ec.BuiltinTypes.Dynamic;
6283 // Infer implicitly typed local variable from foreach enumerable type
6284 variable.Type = current_pe.Type;
6288 // Explicit cast of dynamic collection elements has to be done at runtime
6289 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6292 variable.Type = for_each.type.ResolveAsType (ec);
6294 if (variable.Type == null)
6297 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6298 if (current_pe == null)
6302 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6303 if (variable_ref == null)
6306 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6308 var init = new Invocation (get_enumerator_mg, null);
6310 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6311 for_each.body, Location.Null);
6313 var enum_type = enumerator_variable.Type;
6316 // Add Dispose method call when enumerator can be IDisposable
6318 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6319 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6321 // Runtime Dispose check
6323 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6324 vd.Initializer = init;
6325 statement = new Using (vd, statement, Location.Null);
6328 // No Dispose call needed
6330 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6331 this.init.Resolve (ec);
6335 // Static Dispose check
6337 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6338 vd.Initializer = init;
6339 statement = new Using (vd, statement, Location.Null);
6342 return statement.Resolve (ec);
6345 protected override void DoEmit (EmitContext ec)
6347 enumerator_variable.LocalInfo.CreateBuilder (ec);
6350 init.EmitStatement (ec);
6352 statement.Emit (ec);
6355 #region IErrorHandler Members
6357 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6359 ec.Report.SymbolRelatedToPreviousError (best);
6360 ec.Report.Warning (278, 2, expr.Location,
6361 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6362 expr.Type.GetSignatureForError (), "enumerable",
6363 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6365 ambiguous_getenumerator_name = true;
6369 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6374 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6379 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6388 LocalVariable variable;
6390 Statement statement;
6393 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6396 this.variable = var;
6398 this.statement = stmt;
6403 public Expression Expr {
6404 get { return expr; }
6407 public Statement Statement {
6408 get { return statement; }
6411 public Expression TypeExpression {
6412 get { return type; }
6415 public LocalVariable Variable {
6416 get { return variable; }
6419 public override bool Resolve (BlockContext ec)
6421 expr = expr.Resolve (ec);
6426 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6430 body.AddStatement (statement);
6432 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6433 statement = new ArrayForeach (this, 1);
6434 } else if (expr.Type is ArrayContainer) {
6435 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6437 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6438 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6439 expr.ExprClassName);
6443 statement = new CollectionForeach (this, variable, expr);
6446 return statement.Resolve (ec);
6449 protected override void DoEmit (EmitContext ec)
6451 variable.CreateBuilder (ec);
6453 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6454 ec.LoopBegin = ec.DefineLabel ();
6455 ec.LoopEnd = ec.DefineLabel ();
6457 statement.Emit (ec);
6459 ec.LoopBegin = old_begin;
6460 ec.LoopEnd = old_end;
6463 protected override void CloneTo (CloneContext clonectx, Statement t)
6465 Foreach target = (Foreach) t;
6467 target.type = type.Clone (clonectx);
6468 target.expr = expr.Clone (clonectx);
6469 target.body = (Block) body.Clone (clonectx);
6470 target.statement = statement.Clone (clonectx);
6473 public override object Accept (StructuralVisitor visitor)
6475 return visitor.Visit (this);