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 bool ok = Resolve (ec);
61 ec.KillFlowBranching ();
64 ec.UnreachableReported = false;
71 /// Return value indicates whether all code paths emitted return.
73 protected abstract void DoEmit (EmitContext ec);
75 public virtual void Emit (EmitContext ec)
80 if (ec.StatementEpilogue != null) {
86 // This routine must be overrided in derived classes and make copies
87 // of all the data that might be modified if resolved
89 protected abstract void CloneTo (CloneContext clonectx, Statement target);
91 public Statement Clone (CloneContext clonectx)
93 Statement s = (Statement) this.MemberwiseClone ();
94 CloneTo (clonectx, s);
98 public virtual Expression CreateExpressionTree (ResolveContext ec)
100 ec.Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
104 public virtual object Accept (StructuralVisitor visitor)
106 return visitor.Visit (this);
110 public sealed class EmptyStatement : Statement
112 public EmptyStatement (Location loc)
117 public override bool Resolve (BlockContext ec)
122 public override bool ResolveUnreachable (BlockContext ec, bool warn)
127 public override void Emit (EmitContext ec)
131 protected override void DoEmit (EmitContext ec)
133 throw new NotSupportedException ();
136 protected override void CloneTo (CloneContext clonectx, Statement target)
141 public override object Accept (StructuralVisitor visitor)
143 return visitor.Visit (this);
147 public class If : Statement {
149 public Statement TrueStatement;
150 public Statement FalseStatement;
154 public If (Expression bool_expr, Statement true_statement, Location l)
155 : this (bool_expr, true_statement, null, l)
159 public If (Expression bool_expr,
160 Statement true_statement,
161 Statement false_statement,
164 this.expr = bool_expr;
165 TrueStatement = true_statement;
166 FalseStatement = false_statement;
170 public Expression Expr {
176 public override bool Resolve (BlockContext ec)
180 expr = expr.Resolve (ec);
185 // Dead code elimination
187 if (expr is Constant) {
188 bool take = !((Constant) expr).IsDefaultValue;
191 if (!TrueStatement.Resolve (ec))
194 if ((FalseStatement != null) &&
195 !FalseStatement.ResolveUnreachable (ec, true))
197 FalseStatement = null;
199 if (!TrueStatement.ResolveUnreachable (ec, true))
201 TrueStatement = null;
203 if ((FalseStatement != null) &&
204 !FalseStatement.Resolve (ec))
212 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
214 ok &= TrueStatement.Resolve (ec);
216 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
218 ec.CurrentBranching.CreateSibling ();
220 if (FalseStatement != null)
221 ok &= FalseStatement.Resolve (ec);
223 ec.EndFlowBranching ();
228 protected override void DoEmit (EmitContext ec)
230 Label false_target = ec.DefineLabel ();
234 // If we're a boolean constant, Resolve() already
235 // eliminated dead code for us.
237 Constant c = expr as Constant;
239 c.EmitSideEffect (ec);
241 if (!c.IsDefaultValue)
242 TrueStatement.Emit (ec);
243 else if (FalseStatement != null)
244 FalseStatement.Emit (ec);
249 expr.EmitBranchable (ec, false_target, false);
251 TrueStatement.Emit (ec);
253 if (FalseStatement != null){
254 bool branch_emitted = false;
256 end = ec.DefineLabel ();
258 ec.Emit (OpCodes.Br, end);
259 branch_emitted = true;
262 ec.MarkLabel (false_target);
263 FalseStatement.Emit (ec);
268 ec.MarkLabel (false_target);
272 protected override void CloneTo (CloneContext clonectx, Statement t)
276 target.expr = expr.Clone (clonectx);
277 target.TrueStatement = TrueStatement.Clone (clonectx);
278 if (FalseStatement != null)
279 target.FalseStatement = FalseStatement.Clone (clonectx);
282 public override object Accept (StructuralVisitor visitor)
284 return visitor.Visit (this);
288 public class Do : Statement {
289 public Expression expr;
290 public Statement EmbeddedStatement;
292 public Do (Statement statement, BooleanExpression bool_expr, Location doLocation, Location whileLocation)
295 EmbeddedStatement = statement;
297 WhileLocation = whileLocation;
300 public Location WhileLocation {
304 public override bool Resolve (BlockContext ec)
308 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
310 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
312 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
313 if (!EmbeddedStatement.Resolve (ec))
315 ec.EndFlowBranching ();
317 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
318 ec.Report.Warning (162, 2, expr.Location, "Unreachable code detected");
320 expr = expr.Resolve (ec);
323 else if (expr is Constant){
324 bool infinite = !((Constant) expr).IsDefaultValue;
326 ec.CurrentBranching.CurrentUsageVector.Goto ();
329 ec.EndFlowBranching ();
334 protected override void DoEmit (EmitContext ec)
336 Label loop = ec.DefineLabel ();
337 Label old_begin = ec.LoopBegin;
338 Label old_end = ec.LoopEnd;
340 ec.LoopBegin = ec.DefineLabel ();
341 ec.LoopEnd = ec.DefineLabel ();
344 EmbeddedStatement.Emit (ec);
345 ec.MarkLabel (ec.LoopBegin);
347 // Mark start of while condition
348 ec.Mark (WhileLocation);
351 // Dead code elimination
353 if (expr is Constant) {
354 bool res = !((Constant) expr).IsDefaultValue;
356 expr.EmitSideEffect (ec);
358 ec.Emit (OpCodes.Br, loop);
360 expr.EmitBranchable (ec, loop, true);
363 ec.MarkLabel (ec.LoopEnd);
365 ec.LoopBegin = old_begin;
366 ec.LoopEnd = old_end;
369 protected override void CloneTo (CloneContext clonectx, Statement t)
373 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
374 target.expr = expr.Clone (clonectx);
377 public override object Accept (StructuralVisitor visitor)
379 return visitor.Visit (this);
383 public class While : Statement {
384 public Expression expr;
385 public Statement Statement;
386 bool infinite, empty;
388 public While (BooleanExpression bool_expr, Statement statement, Location l)
390 this.expr = bool_expr;
391 Statement = statement;
395 public override bool Resolve (BlockContext ec)
399 expr = expr.Resolve (ec);
404 // Inform whether we are infinite or not
406 if (expr is Constant){
407 bool value = !((Constant) expr).IsDefaultValue;
410 if (!Statement.ResolveUnreachable (ec, true))
418 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
420 ec.CurrentBranching.CreateSibling ();
422 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
423 if (!Statement.Resolve (ec))
425 ec.EndFlowBranching ();
427 // There's no direct control flow from the end of the embedded statement to the end of the loop
428 ec.CurrentBranching.CurrentUsageVector.Goto ();
430 ec.EndFlowBranching ();
435 protected override void DoEmit (EmitContext ec)
438 expr.EmitSideEffect (ec);
442 Label old_begin = ec.LoopBegin;
443 Label old_end = ec.LoopEnd;
445 ec.LoopBegin = ec.DefineLabel ();
446 ec.LoopEnd = ec.DefineLabel ();
449 // Inform whether we are infinite or not
451 if (expr is Constant) {
452 // expr is 'true', since the 'empty' case above handles the 'false' case
453 ec.MarkLabel (ec.LoopBegin);
455 if (ec.EmitAccurateDebugInfo)
456 ec.Emit (OpCodes.Nop);
458 expr.EmitSideEffect (ec);
460 ec.Emit (OpCodes.Br, ec.LoopBegin);
463 // Inform that we are infinite (ie, `we return'), only
464 // if we do not `break' inside the code.
466 ec.MarkLabel (ec.LoopEnd);
468 Label while_loop = ec.DefineLabel ();
470 ec.Emit (OpCodes.Br, ec.LoopBegin);
471 ec.MarkLabel (while_loop);
475 ec.MarkLabel (ec.LoopBegin);
478 expr.EmitBranchable (ec, while_loop, true);
480 ec.MarkLabel (ec.LoopEnd);
483 ec.LoopBegin = old_begin;
484 ec.LoopEnd = old_end;
487 protected override void CloneTo (CloneContext clonectx, Statement t)
489 While target = (While) t;
491 target.expr = expr.Clone (clonectx);
492 target.Statement = Statement.Clone (clonectx);
495 public override object Accept (StructuralVisitor visitor)
497 return visitor.Visit (this);
501 public class For : Statement
503 bool infinite, empty;
505 public For (Location l)
510 public Statement Initializer {
514 public Expression Condition {
518 public Statement Iterator {
522 public Statement Statement {
526 public override bool Resolve (BlockContext ec)
530 if (Initializer != null) {
531 if (!Initializer.Resolve (ec))
535 if (Condition != null) {
536 Condition = Condition.Resolve (ec);
537 if (Condition == null)
539 else if (Condition is Constant) {
540 bool value = !((Constant) Condition).IsDefaultValue;
543 if (!Statement.ResolveUnreachable (ec, true))
545 if ((Iterator != null) &&
546 !Iterator.ResolveUnreachable (ec, false))
556 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
558 ec.CurrentBranching.CreateSibling ();
560 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
562 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
563 if (!Statement.Resolve (ec))
565 ec.EndFlowBranching ();
567 if (Iterator != null){
568 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
569 if (!Iterator.ResolveUnreachable (ec, !was_unreachable))
572 if (!Iterator.Resolve (ec))
577 // There's no direct control flow from the end of the embedded statement to the end of the loop
578 ec.CurrentBranching.CurrentUsageVector.Goto ();
580 ec.EndFlowBranching ();
585 protected override void DoEmit (EmitContext ec)
587 if (Initializer != null)
588 Initializer.Emit (ec);
591 Condition.EmitSideEffect (ec);
595 Label old_begin = ec.LoopBegin;
596 Label old_end = ec.LoopEnd;
597 Label loop = ec.DefineLabel ();
598 Label test = ec.DefineLabel ();
600 ec.LoopBegin = ec.DefineLabel ();
601 ec.LoopEnd = ec.DefineLabel ();
603 ec.Emit (OpCodes.Br, test);
607 ec.MarkLabel (ec.LoopBegin);
612 // If test is null, there is no test, and we are just
615 if (Condition != null) {
616 ec.Mark (Condition.Location);
619 // The Resolve code already catches the case for
620 // Test == Constant (false) so we know that
623 if (Condition is Constant) {
624 Condition.EmitSideEffect (ec);
625 ec.Emit (OpCodes.Br, loop);
627 Condition.EmitBranchable (ec, loop, true);
631 ec.Emit (OpCodes.Br, loop);
632 ec.MarkLabel (ec.LoopEnd);
634 ec.LoopBegin = old_begin;
635 ec.LoopEnd = old_end;
638 protected override void CloneTo (CloneContext clonectx, Statement t)
640 For target = (For) t;
642 if (Initializer != null)
643 target.Initializer = Initializer.Clone (clonectx);
644 if (Condition != null)
645 target.Condition = Condition.Clone (clonectx);
646 if (Iterator != null)
647 target.Iterator = Iterator.Clone (clonectx);
648 target.Statement = Statement.Clone (clonectx);
651 public override object Accept (StructuralVisitor visitor)
653 return visitor.Visit (this);
657 public class StatementExpression : Statement
659 ExpressionStatement expr;
661 public StatementExpression (ExpressionStatement expr)
664 loc = expr.StartLocation;
667 public StatementExpression (ExpressionStatement expr, Location loc)
673 public ExpressionStatement Expr {
679 protected override void CloneTo (CloneContext clonectx, Statement t)
681 StatementExpression target = (StatementExpression) t;
682 target.expr = (ExpressionStatement) expr.Clone (clonectx);
685 protected override void DoEmit (EmitContext ec)
687 expr.EmitStatement (ec);
690 public override bool Resolve (BlockContext ec)
692 expr = expr.ResolveStatement (ec);
696 public override object Accept (StructuralVisitor visitor)
698 return visitor.Visit (this);
702 public class StatementErrorExpression : Statement
704 readonly Expression expr;
706 public StatementErrorExpression (Expression expr)
709 this.loc = expr.StartLocation;
712 public Expression Expr {
718 public override bool Resolve (BlockContext bc)
720 expr.Error_InvalidExpressionStatement (bc);
724 protected override void DoEmit (EmitContext ec)
726 throw new NotSupportedException ();
729 protected override void CloneTo (CloneContext clonectx, Statement target)
731 throw new NotImplementedException ();
734 public override object Accept (StructuralVisitor visitor)
736 return visitor.Visit (this);
741 // Simple version of statement list not requiring a block
743 public class StatementList : Statement
745 List<Statement> statements;
747 public StatementList (Statement first, Statement second)
749 statements = new List<Statement> () { first, second };
753 public IList<Statement> Statements {
760 public void Add (Statement statement)
762 statements.Add (statement);
765 public override bool Resolve (BlockContext ec)
767 foreach (var s in statements)
773 protected override void DoEmit (EmitContext ec)
775 foreach (var s in statements)
779 protected override void CloneTo (CloneContext clonectx, Statement target)
781 StatementList t = (StatementList) target;
783 t.statements = new List<Statement> (statements.Count);
784 foreach (Statement s in statements)
785 t.statements.Add (s.Clone (clonectx));
788 public override object Accept (StructuralVisitor visitor)
790 return visitor.Visit (this);
794 // A 'return' or a 'yield break'
795 public abstract class ExitStatement : Statement
797 protected bool unwind_protect;
798 protected abstract bool DoResolve (BlockContext ec);
800 public virtual void Error_FinallyClause (Report Report)
802 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
805 public sealed override bool Resolve (BlockContext ec)
807 var res = DoResolve (ec);
808 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
809 ec.CurrentBranching.CurrentUsageVector.Goto ();
815 /// Implements the return statement
817 public class Return : ExitStatement
821 public Return (Expression expr, Location l)
829 public Expression Expr {
840 protected override bool DoResolve (BlockContext ec)
843 if (ec.ReturnType.Kind == MemberKind.Void)
847 // Return must not be followed by an expression when
848 // the method return type is Task
850 if (ec.CurrentAnonymousMethod is AsyncInitializer) {
851 var storey = (AsyncTaskStorey) ec.CurrentAnonymousMethod.Storey;
852 if (storey.ReturnType == ec.Module.PredefinedTypes.Task.TypeSpec) {
854 // Extra trick not to emit ret/leave inside awaiter body
856 expr = EmptyExpression.Null;
861 if (ec.CurrentIterator != null) {
862 Error_ReturnFromIterator (ec);
863 } else if (ec.ReturnType != InternalType.ErrorType) {
864 ec.Report.Error (126, loc,
865 "An object of a type convertible to `{0}' is required for the return statement",
866 ec.ReturnType.GetSignatureForError ());
872 expr = expr.Resolve (ec);
873 TypeSpec block_return_type = ec.ReturnType;
875 AnonymousExpression am = ec.CurrentAnonymousMethod;
877 if (block_return_type.Kind == MemberKind.Void) {
878 ec.Report.Error (127, loc,
879 "`{0}': A return keyword must not be followed by any expression when method returns void",
880 ec.GetSignatureForError ());
886 Error_ReturnFromIterator (ec);
890 var async_block = am as AsyncInitializer;
891 if (async_block != null) {
893 var storey = (AsyncTaskStorey) am.Storey;
894 var async_type = storey.ReturnType;
896 if (async_type == null && async_block.ReturnTypeInference != null) {
897 async_block.ReturnTypeInference.AddCommonTypeBound (expr.Type);
901 if (async_type.Kind == MemberKind.Void) {
902 ec.Report.Error (127, loc,
903 "`{0}': A return keyword must not be followed by any expression when method returns void",
904 ec.GetSignatureForError ());
909 if (!async_type.IsGenericTask) {
910 if (this is ContextualReturn)
913 // Same error code as .NET but better error message
914 if (async_block.DelegateType != null) {
915 ec.Report.Error (1997, loc,
916 "`{0}': A return keyword must not be followed by an expression when async delegate returns `Task'. Consider using `Task<T>' return type",
917 async_block.DelegateType.GetSignatureForError ());
919 ec.Report.Error (1997, loc,
920 "`{0}': A return keyword must not be followed by an expression when async method returns `Task'. Consider using `Task<T>' return type",
921 ec.GetSignatureForError ());
929 // The return type is actually Task<T> type argument
931 if (expr.Type == async_type) {
932 ec.Report.Error (4016, loc,
933 "`{0}': The return expression type of async method must be `{1}' rather than `Task<{1}>'",
934 ec.GetSignatureForError (), async_type.TypeArguments[0].GetSignatureForError ());
936 block_return_type = async_type.TypeArguments[0];
940 // Same error code as .NET but better error message
941 if (block_return_type.Kind == MemberKind.Void) {
942 ec.Report.Error (127, loc,
943 "`{0}': A return keyword must not be followed by any expression when delegate returns void",
944 am.GetSignatureForError ());
949 var l = am as AnonymousMethodBody;
950 if (l != null && expr != null) {
951 if (l.ReturnTypeInference != null) {
952 l.ReturnTypeInference.AddCommonTypeBound (expr.Type);
957 // Try to optimize simple lambda. Only when optimizations are enabled not to cause
958 // unexpected debugging experience
960 if (this is ContextualReturn && !ec.IsInProbingMode && ec.Module.Compiler.Settings.Optimize) {
961 l.DirectMethodGroupConversion = expr.CanReduceLambda (l);
970 if (expr.Type != block_return_type && expr.Type != InternalType.ErrorType) {
971 expr = Convert.ImplicitConversionRequired (ec, expr, block_return_type, loc);
974 if (am != null && block_return_type == ec.ReturnType) {
975 ec.Report.Error (1662, loc,
976 "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",
977 am.ContainerType, am.GetSignatureForError ());
986 protected override void DoEmit (EmitContext ec)
991 var async_body = ec.CurrentAnonymousMethod as AsyncInitializer;
992 if (async_body != null) {
993 var async_return = ((AsyncTaskStorey) async_body.Storey).HoistedReturn;
995 // It's null for await without async
996 if (async_return != null) {
997 async_return.EmitAssign (ec);
1002 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, async_body.BodyEnd);
1008 if (unwind_protect || ec.EmitAccurateDebugInfo)
1009 ec.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
1012 if (unwind_protect) {
1013 ec.Emit (OpCodes.Leave, ec.CreateReturnLabel ());
1014 } else if (ec.EmitAccurateDebugInfo) {
1015 ec.Emit (OpCodes.Br, ec.CreateReturnLabel ());
1017 ec.Emit (OpCodes.Ret);
1021 void Error_ReturnFromIterator (ResolveContext rc)
1023 rc.Report.Error (1622, loc,
1024 "Cannot return a value from iterators. Use the yield return statement to return a value, or yield break to end the iteration");
1027 protected override void CloneTo (CloneContext clonectx, Statement t)
1029 Return target = (Return) t;
1030 // It's null for simple return;
1032 target.expr = expr.Clone (clonectx);
1035 public override object Accept (StructuralVisitor visitor)
1037 return visitor.Visit (this);
1041 public class Goto : Statement {
1043 LabeledStatement label;
1044 bool unwind_protect;
1046 public override bool Resolve (BlockContext ec)
1048 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
1049 ec.CurrentBranching.CurrentUsageVector.Goto ();
1053 public Goto (string label, Location l)
1059 public string Target {
1060 get { return target; }
1063 public void SetResolvedTarget (LabeledStatement label)
1066 label.AddReference ();
1069 protected override void CloneTo (CloneContext clonectx, Statement target)
1074 protected override void DoEmit (EmitContext ec)
1077 throw new InternalErrorException ("goto emitted before target resolved");
1078 Label l = label.LabelTarget (ec);
1079 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
1082 public override object Accept (StructuralVisitor visitor)
1084 return visitor.Visit (this);
1088 public class LabeledStatement : Statement {
1095 FlowBranching.UsageVector vectors;
1097 public LabeledStatement (string name, Block block, Location l)
1104 public Label LabelTarget (EmitContext ec)
1109 label = ec.DefineLabel ();
1114 public Block Block {
1120 public string Name {
1121 get { return name; }
1124 public bool IsDefined {
1125 get { return defined; }
1128 public bool HasBeenReferenced {
1129 get { return referenced; }
1132 public FlowBranching.UsageVector JumpOrigins {
1133 get { return vectors; }
1136 public void AddUsageVector (FlowBranching.UsageVector vector)
1138 vector = vector.Clone ();
1139 vector.Next = vectors;
1143 protected override void CloneTo (CloneContext clonectx, Statement target)
1148 public override bool Resolve (BlockContext ec)
1150 // this flow-branching will be terminated when the surrounding block ends
1151 ec.StartFlowBranching (this);
1155 protected override void DoEmit (EmitContext ec)
1157 if (!HasBeenReferenced)
1158 ec.Report.Warning (164, 2, loc, "This label has not been referenced");
1161 ec.MarkLabel (label);
1164 public void AddReference ()
1169 public override object Accept (StructuralVisitor visitor)
1171 return visitor.Visit (this);
1177 /// `goto default' statement
1179 public class GotoDefault : Statement {
1181 public GotoDefault (Location l)
1186 protected override void CloneTo (CloneContext clonectx, Statement target)
1191 public override bool Resolve (BlockContext ec)
1193 ec.CurrentBranching.CurrentUsageVector.Goto ();
1195 if (ec.Switch == null) {
1196 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1200 if (ec.Switch.DefaultLabel == null) {
1201 FlowBranchingBlock.Error_UnknownLabel (loc, "default", ec.Report);
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 {
1226 public GotoCase (Expression e, Location l)
1232 public Expression Expr {
1238 public override bool Resolve (BlockContext ec)
1240 if (ec.Switch == null){
1241 ec.Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1245 ec.CurrentBranching.CurrentUsageVector.Goto ();
1247 expr = expr.Resolve (ec);
1251 Constant c = expr as Constant;
1253 ec.Report.Error (150, expr.Location, "A constant value is expected");
1258 if (ec.Switch.IsNullable && c is NullLiteral) {
1261 TypeSpec type = ec.Switch.SwitchType;
1262 res = c.Reduce (ec, type);
1264 c.Error_ValueCannotBeConverted (ec, type, true);
1268 if (!Convert.ImplicitStandardConversionExists (c, type))
1269 ec.Report.Warning (469, 2, loc,
1270 "The `goto case' value is not implicitly convertible to type `{0}'",
1271 type.GetSignatureForError ());
1275 sl = ec.Switch.ResolveGotoCase (ec, res);
1279 protected override void DoEmit (EmitContext ec)
1281 ec.Emit (OpCodes.Br, sl.GetILLabel (ec));
1284 protected override void CloneTo (CloneContext clonectx, Statement t)
1286 GotoCase target = (GotoCase) t;
1288 target.expr = expr.Clone (clonectx);
1291 public override object Accept (StructuralVisitor visitor)
1293 return visitor.Visit (this);
1297 public class Throw : Statement {
1300 public Throw (Expression expr, Location l)
1306 public Expression Expr {
1312 public override bool Resolve (BlockContext ec)
1315 ec.CurrentBranching.CurrentUsageVector.Goto ();
1316 return ec.CurrentBranching.CheckRethrow (loc);
1319 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1320 ec.CurrentBranching.CurrentUsageVector.Goto ();
1325 var et = ec.BuiltinTypes.Exception;
1326 if (Convert.ImplicitConversionExists (ec, expr, et))
1327 expr = Convert.ImplicitConversion (ec, expr, et, loc);
1329 ec.Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1334 protected override void DoEmit (EmitContext ec)
1337 ec.Emit (OpCodes.Rethrow);
1341 ec.Emit (OpCodes.Throw);
1345 protected override void CloneTo (CloneContext clonectx, Statement t)
1347 Throw target = (Throw) t;
1350 target.expr = expr.Clone (clonectx);
1353 public override object Accept (StructuralVisitor visitor)
1355 return visitor.Visit (this);
1359 public class Break : Statement {
1361 public Break (Location l)
1366 bool unwind_protect;
1368 public override bool Resolve (BlockContext ec)
1370 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1371 ec.CurrentBranching.CurrentUsageVector.Goto ();
1375 protected override void DoEmit (EmitContext ec)
1377 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1380 protected override void CloneTo (CloneContext clonectx, Statement t)
1385 public override object Accept (StructuralVisitor visitor)
1387 return visitor.Visit (this);
1391 public class Continue : Statement {
1393 public Continue (Location l)
1398 bool unwind_protect;
1400 public override bool Resolve (BlockContext ec)
1402 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1403 ec.CurrentBranching.CurrentUsageVector.Goto ();
1407 protected override void DoEmit (EmitContext ec)
1409 ec.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1412 protected override void CloneTo (CloneContext clonectx, Statement t)
1417 public override object Accept (StructuralVisitor visitor)
1419 return visitor.Visit (this);
1423 public interface ILocalVariable
1425 void Emit (EmitContext ec);
1426 void EmitAssign (EmitContext ec);
1427 void EmitAddressOf (EmitContext ec);
1430 public interface INamedBlockVariable
1432 Block Block { get; }
1433 Expression CreateReferenceExpression (ResolveContext rc, Location loc);
1434 bool IsDeclared { get; }
1435 bool IsParameter { get; }
1436 Location Location { get; }
1439 public class BlockVariableDeclaration : Statement
1441 public class Declarator
1444 Expression initializer;
1446 public Declarator (LocalVariable li, Expression initializer)
1448 if (li.Type != null)
1449 throw new ArgumentException ("Expected null variable type");
1452 this.initializer = initializer;
1455 public Declarator (Declarator clone, Expression initializer)
1458 this.initializer = initializer;
1463 public LocalVariable Variable {
1469 public Expression Initializer {
1474 initializer = value;
1481 Expression initializer;
1482 protected FullNamedExpression type_expr;
1483 protected LocalVariable li;
1484 protected List<Declarator> declarators;
1487 public BlockVariableDeclaration (FullNamedExpression type, LocalVariable li)
1489 this.type_expr = type;
1491 this.loc = type_expr.Location;
1494 protected BlockVariableDeclaration (LocalVariable li)
1501 public List<Declarator> Declarators {
1507 public Expression Initializer {
1512 initializer = value;
1516 public FullNamedExpression TypeExpression {
1522 public LocalVariable Variable {
1530 public void AddDeclarator (Declarator decl)
1532 if (declarators == null)
1533 declarators = new List<Declarator> ();
1535 declarators.Add (decl);
1538 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1540 if (bc.Report.Errors != 0)
1543 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1545 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1546 new MemberName (li.Name, li.Location), null);
1548 container.AddField (f);
1551 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1555 public override bool Resolve (BlockContext bc)
1557 return Resolve (bc, true);
1560 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1562 if (type == null && !li.IsCompilerGenerated) {
1563 var vexpr = type_expr as VarExpr;
1566 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1567 // same name exists or as a keyword when no type was found
1569 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1570 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1571 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1574 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1578 if (li.IsConstant) {
1579 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1583 if (Initializer == null) {
1584 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1588 if (declarators != null) {
1589 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1593 Initializer = Initializer.Resolve (bc);
1594 if (Initializer != null) {
1595 ((VarExpr) type_expr).InferType (bc, Initializer);
1596 type = type_expr.Type;
1598 // Set error type to indicate the var was placed correctly but could
1601 // var a = missing ();
1603 type = InternalType.ErrorType;
1608 type = type_expr.ResolveAsType (bc);
1612 if (li.IsConstant && !type.IsConstantCompatible) {
1613 Const.Error_InvalidConstantType (type, loc, bc.Report);
1618 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1623 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1625 CreateEvaluatorVariable (bc, li);
1626 } else if (type != InternalType.ErrorType) {
1627 li.PrepareForFlowAnalysis (bc);
1630 if (initializer != null) {
1631 initializer = ResolveInitializer (bc, li, initializer);
1632 // li.Variable.DefinitelyAssigned
1635 if (declarators != null) {
1636 foreach (var d in declarators) {
1637 d.Variable.Type = li.Type;
1639 CreateEvaluatorVariable (bc, d.Variable);
1640 } else if (type != InternalType.ErrorType) {
1641 d.Variable.PrepareForFlowAnalysis (bc);
1644 if (d.Initializer != null && resolveDeclaratorInitializers) {
1645 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1646 // d.Variable.DefinitelyAssigned
1654 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1656 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1657 return a.ResolveStatement (bc);
1660 protected override void DoEmit (EmitContext ec)
1662 li.CreateBuilder (ec);
1664 if (Initializer != null)
1665 ((ExpressionStatement) Initializer).EmitStatement (ec);
1667 if (declarators != null) {
1668 foreach (var d in declarators) {
1669 d.Variable.CreateBuilder (ec);
1670 if (d.Initializer != null) {
1671 ec.Mark (d.Variable.Location);
1672 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1678 protected override void CloneTo (CloneContext clonectx, Statement target)
1680 BlockVariableDeclaration t = (BlockVariableDeclaration) target;
1682 if (type_expr != null)
1683 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1685 if (initializer != null)
1686 t.initializer = initializer.Clone (clonectx);
1688 if (declarators != null) {
1689 t.declarators = null;
1690 foreach (var d in declarators)
1691 t.AddDeclarator (new Declarator (d, d.Initializer == null ? null : d.Initializer.Clone (clonectx)));
1695 public override object Accept (StructuralVisitor visitor)
1697 return visitor.Visit (this);
1701 public class BlockConstantDeclaration : BlockVariableDeclaration
1703 public BlockConstantDeclaration (FullNamedExpression type, LocalVariable li)
1708 public override void Emit (EmitContext ec)
1710 // Nothing to emit, not even sequence point
1713 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1715 initializer = initializer.Resolve (bc);
1716 if (initializer == null)
1719 var c = initializer as Constant;
1721 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1725 c = c.ConvertImplicitly (li.Type);
1727 if (TypeSpec.IsReferenceType (li.Type))
1728 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1730 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1735 li.ConstantValue = c;
1739 public override object Accept (StructuralVisitor visitor)
1741 return visitor.Visit (this);
1746 // The information about a user-perceived local variable
1748 public class LocalVariable : INamedBlockVariable, ILocalVariable
1755 AddressTaken = 1 << 2,
1756 CompilerGenerated = 1 << 3,
1758 ForeachVariable = 1 << 5,
1759 FixedVariable = 1 << 6,
1760 UsingVariable = 1 << 7,
1761 // DefinitelyAssigned = 1 << 8,
1764 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1768 readonly string name;
1769 readonly Location loc;
1770 readonly Block block;
1772 Constant const_value;
1774 public VariableInfo VariableInfo;
1775 HoistedVariable hoisted_variant;
1777 LocalBuilder builder;
1779 public LocalVariable (Block block, string name, Location loc)
1786 public LocalVariable (Block block, string name, Flags flags, Location loc)
1787 : this (block, name, loc)
1793 // Used by variable declarators
1795 public LocalVariable (LocalVariable li, string name, Location loc)
1796 : this (li.block, name, li.flags, loc)
1802 public bool AddressTaken {
1804 return (flags & Flags.AddressTaken) != 0;
1808 public Block Block {
1814 public Constant ConstantValue {
1819 const_value = value;
1824 // Hoisted local variable variant
1826 public HoistedVariable HoistedVariant {
1828 return hoisted_variant;
1831 hoisted_variant = value;
1835 public bool IsDeclared {
1837 return type != null;
1841 public bool IsCompilerGenerated {
1843 return (flags & Flags.CompilerGenerated) != 0;
1847 public bool IsConstant {
1849 return (flags & Flags.Constant) != 0;
1853 public bool IsLocked {
1855 return (flags & Flags.IsLocked) != 0;
1858 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1862 public bool IsThis {
1864 return (flags & Flags.IsThis) != 0;
1868 public bool IsFixed {
1870 return (flags & Flags.FixedVariable) != 0;
1874 bool INamedBlockVariable.IsParameter {
1880 public bool IsReadonly {
1882 return (flags & Flags.ReadonlyMask) != 0;
1886 public Location Location {
1892 public string Name {
1898 public TypeSpec Type {
1909 public void CreateBuilder (EmitContext ec)
1911 if ((flags & Flags.Used) == 0) {
1912 if (VariableInfo == null) {
1913 // Missing flow analysis or wrong variable flags
1914 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1917 if (VariableInfo.IsEverAssigned)
1918 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1920 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1923 if (HoistedVariant != null)
1926 if (builder != null) {
1927 if ((flags & Flags.CompilerGenerated) != 0)
1930 // To avoid Used warning duplicates
1931 throw new InternalErrorException ("Already created variable `{0}'", name);
1935 // All fixed variabled are pinned, a slot has to be alocated
1937 builder = ec.DeclareLocal (Type, IsFixed);
1938 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1939 ec.DefineLocalVariable (name, builder);
1942 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1944 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1949 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1951 if (IsConstant && const_value != null)
1952 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1954 return new LocalVariableReference (this, loc);
1957 public void Emit (EmitContext ec)
1959 // TODO: Need something better for temporary variables
1960 if ((flags & Flags.CompilerGenerated) != 0)
1963 ec.Emit (OpCodes.Ldloc, builder);
1966 public void EmitAssign (EmitContext ec)
1968 // TODO: Need something better for temporary variables
1969 if ((flags & Flags.CompilerGenerated) != 0)
1972 ec.Emit (OpCodes.Stloc, builder);
1975 public void EmitAddressOf (EmitContext ec)
1977 ec.Emit (OpCodes.Ldloca, builder);
1980 public static string GetCompilerGeneratedName (Block block)
1982 // HACK: Debugger depends on the name semantics
1983 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1986 public string GetReadOnlyContext ()
1988 switch (flags & Flags.ReadonlyMask) {
1989 case Flags.FixedVariable:
1990 return "fixed variable";
1991 case Flags.ForeachVariable:
1992 return "foreach iteration variable";
1993 case Flags.UsingVariable:
1994 return "using variable";
1997 throw new InternalErrorException ("Variable is not readonly");
2000 public bool IsThisAssigned (BlockContext ec, Block block)
2002 if (VariableInfo == null)
2003 throw new Exception ();
2005 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
2008 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
2011 public bool IsAssigned (BlockContext ec)
2013 if (VariableInfo == null)
2014 throw new Exception ();
2016 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
2019 public void PrepareForFlowAnalysis (BlockContext bc)
2022 // No need for definitely assigned check for these guys
2024 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2027 VariableInfo = new VariableInfo (this, bc.FlowOffset);
2028 bc.FlowOffset += VariableInfo.Length;
2032 // Mark the variables as referenced in the user code
2034 public void SetIsUsed ()
2036 flags |= Flags.Used;
2039 public void SetHasAddressTaken ()
2041 flags |= (Flags.AddressTaken | Flags.Used);
2044 public override string ToString ()
2046 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2051 /// Block represents a C# block.
2055 /// This class is used in a number of places: either to represent
2056 /// explicit blocks that the programmer places or implicit blocks.
2058 /// Implicit blocks are used as labels or to introduce variable
2061 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2062 /// they contain extra information that is not necessary on normal blocks.
2064 public class Block : Statement {
2071 HasCapturedVariable = 64,
2072 HasCapturedThis = 1 << 7,
2073 IsExpressionTree = 1 << 8,
2074 CompilerGenerated = 1 << 9,
2075 HasAsyncModifier = 1 << 10,
2077 YieldBlock = 1 << 12,
2078 AwaitBlock = 1 << 13,
2082 public Block Parent;
2083 public Location StartLocation;
2084 public Location EndLocation;
2086 public ExplicitBlock Explicit;
2087 public ParametersBlock ParametersBlock;
2089 protected Flags flags;
2092 // The statements in this block
2094 protected List<Statement> statements;
2096 protected List<Statement> scope_initializers;
2098 int? resolving_init_idx;
2104 public int ID = id++;
2106 static int clone_id_counter;
2110 // int assignable_slots;
2112 public Block (Block parent, Location start, Location end)
2113 : this (parent, 0, start, end)
2117 public Block (Block parent, Flags flags, Location start, Location end)
2119 if (parent != null) {
2120 // the appropriate constructors will fixup these fields
2121 ParametersBlock = parent.ParametersBlock;
2122 Explicit = parent.Explicit;
2125 this.Parent = parent;
2127 this.StartLocation = start;
2128 this.EndLocation = end;
2130 statements = new List<Statement> (4);
2132 this.original = this;
2137 public bool HasUnreachableClosingBrace {
2139 return (flags & Flags.HasRet) != 0;
2142 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2146 public Block Original {
2155 public bool IsCompilerGenerated {
2156 get { return (flags & Flags.CompilerGenerated) != 0; }
2157 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2160 public bool Unchecked {
2161 get { return (flags & Flags.Unchecked) != 0; }
2162 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2165 public bool Unsafe {
2166 get { return (flags & Flags.Unsafe) != 0; }
2167 set { flags |= Flags.Unsafe; }
2170 public List<Statement> Statements {
2171 get { return statements; }
2176 public void SetEndLocation (Location loc)
2181 public void AddLabel (LabeledStatement target)
2183 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2186 public void AddLocalName (LocalVariable li)
2188 AddLocalName (li.Name, li);
2191 public void AddLocalName (string name, INamedBlockVariable li)
2193 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2196 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2198 if (reason == null) {
2199 Error_AlreadyDeclared (name, variable);
2203 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2204 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2205 "to `{0}', which is already used in a `{1}' scope to denote something else",
2209 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2211 var pi = variable as ParametersBlock.ParameterInfo;
2213 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2215 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2216 "A local variable named `{0}' is already defined in this scope", name);
2220 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2222 ParametersBlock.TopBlock.Report.Error (412, loc,
2223 "The type parameter name `{0}' is the same as local variable or parameter name",
2228 // It should be used by expressions which require to
2229 // register a statement during resolve process.
2231 public void AddScopeStatement (Statement s)
2233 if (scope_initializers == null)
2234 scope_initializers = new List<Statement> ();
2237 // Simple recursive helper, when resolve scope initializer another
2238 // new scope initializer can be added, this ensures it's initialized
2239 // before existing one. For now this can happen with expression trees
2240 // in base ctor initializer only
2242 if (resolving_init_idx.HasValue) {
2243 scope_initializers.Insert (resolving_init_idx.Value, s);
2244 ++resolving_init_idx;
2246 scope_initializers.Add (s);
2250 public void AddStatement (Statement s)
2255 public int AssignableSlots {
2257 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2259 // return assignable_slots;
2263 public LabeledStatement LookupLabel (string name)
2265 return ParametersBlock.TopBlock.GetLabel (name, this);
2268 public override bool Resolve (BlockContext ec)
2270 if ((flags & Flags.Resolved) != 0)
2273 Block prev_block = ec.CurrentBlock;
2275 bool unreachable = ec.IsUnreachable;
2276 bool prev_unreachable = unreachable;
2278 ec.CurrentBlock = this;
2279 ec.StartFlowBranching (this);
2282 // Compiler generated scope statements
2284 if (scope_initializers != null) {
2285 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2286 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2289 resolving_init_idx = null;
2293 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2294 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2295 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2296 // responsible for handling the situation.
2298 int statement_count = statements.Count;
2299 for (int ix = 0; ix < statement_count; ix++){
2300 Statement s = statements [ix];
2303 // Warn if we detect unreachable code.
2306 if (s is EmptyStatement)
2309 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2310 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2311 ec.UnreachableReported = true;
2316 // Note that we're not using ResolveUnreachable() for unreachable
2317 // statements here. ResolveUnreachable() creates a temporary
2318 // flow branching and kills it afterwards. This leads to problems
2319 // if you have two unreachable statements where the first one
2320 // assigns a variable and the second one tries to access it.
2323 if (!s.Resolve (ec)) {
2325 if (!ec.IsInProbingMode)
2326 statements [ix] = new EmptyStatement (s.loc);
2331 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2332 statements [ix] = new EmptyStatement (s.loc);
2334 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2336 ec.IsUnreachable = true;
2337 } else if (ec.IsUnreachable)
2338 ec.IsUnreachable = false;
2341 if (unreachable != prev_unreachable) {
2342 ec.IsUnreachable = prev_unreachable;
2343 ec.UnreachableReported = false;
2346 while (ec.CurrentBranching is FlowBranchingLabeled)
2347 ec.EndFlowBranching ();
2349 bool flow_unreachable = ec.EndFlowBranching ();
2351 ec.CurrentBlock = prev_block;
2353 if (flow_unreachable)
2354 flags |= Flags.HasRet;
2356 // If we're a non-static `struct' constructor which doesn't have an
2357 // initializer, then we must initialize all of the struct's fields.
2358 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2361 flags |= Flags.Resolved;
2365 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2367 bool unreachable = false;
2368 if (warn && !ec.UnreachableReported) {
2369 ec.UnreachableReported = true;
2371 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2374 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2375 fb.CurrentUsageVector.IsUnreachable = true;
2376 bool ok = Resolve (ec);
2377 ec.KillFlowBranching ();
2380 ec.UnreachableReported = false;
2385 protected override void DoEmit (EmitContext ec)
2387 for (int ix = 0; ix < statements.Count; ix++){
2388 statements [ix].Emit (ec);
2392 public override void Emit (EmitContext ec)
2394 if (scope_initializers != null)
2395 EmitScopeInitializers (ec);
2400 protected void EmitScopeInitializers (EmitContext ec)
2402 foreach (Statement s in scope_initializers)
2407 public override string ToString ()
2409 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2413 protected override void CloneTo (CloneContext clonectx, Statement t)
2415 Block target = (Block) t;
2417 target.clone_id = clone_id_counter++;
2420 clonectx.AddBlockMap (this, target);
2421 if (original != this)
2422 clonectx.AddBlockMap (original, target);
2424 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2425 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2428 target.Parent = clonectx.RemapBlockCopy (Parent);
2430 target.statements = new List<Statement> (statements.Count);
2431 foreach (Statement s in statements)
2432 target.statements.Add (s.Clone (clonectx));
2435 public override object Accept (StructuralVisitor visitor)
2437 return visitor.Visit (this);
2441 public class ExplicitBlock : Block
2443 protected AnonymousMethodStorey am_storey;
2445 public ExplicitBlock (Block parent, Location start, Location end)
2446 : this (parent, (Flags) 0, start, end)
2450 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2451 : base (parent, flags, start, end)
2453 this.Explicit = this;
2458 public AnonymousMethodStorey AnonymousMethodStorey {
2464 public bool HasAwait {
2466 return (flags & Flags.AwaitBlock) != 0;
2470 public bool HasCapturedThis {
2472 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2475 return (flags & Flags.HasCapturedThis) != 0;
2480 // Used to indicate that the block has reference to parent
2481 // block and cannot be made static when defining anonymous method
2483 public bool HasCapturedVariable {
2485 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2488 return (flags & Flags.HasCapturedVariable) != 0;
2492 public bool HasYield {
2494 return (flags & Flags.YieldBlock) != 0;
2501 // Creates anonymous method storey in current block
2503 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2506 // Return same story for iterator and async blocks unless we are
2507 // in nested anonymous method
2509 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2510 return ec.CurrentAnonymousMethod.Storey;
2512 if (am_storey == null) {
2513 MemberBase mc = ec.MemberContext as MemberBase;
2516 // Creates anonymous method storey for this block
2518 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2524 public override void Emit (EmitContext ec)
2526 if (am_storey != null) {
2527 DefineStoreyContainer (ec, am_storey);
2528 am_storey.EmitStoreyInstantiation (ec, this);
2531 if (scope_initializers != null)
2532 EmitScopeInitializers (ec);
2534 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2535 ec.Emit (OpCodes.Nop);
2546 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2547 ec.Emit (OpCodes.Nop);
2551 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2553 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2554 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2555 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2559 // Creates anonymous method storey
2561 storey.CreateContainer ();
2562 storey.DefineContainer ();
2564 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2567 // Only first storey in path will hold this reference. All children blocks will
2568 // reference it indirectly using $ref field
2570 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2571 if (b.Parent != null) {
2572 var s = b.Parent.Explicit.AnonymousMethodStorey;
2574 storey.HoistedThis = s.HoistedThis;
2579 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2580 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2582 if (storey.HoistedThis != null)
2588 // We are the first storey on path and 'this' has to be hoisted
2590 if (storey.HoistedThis == null) {
2591 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2593 // ThisReferencesFromChildrenBlock holds all reference even if they
2594 // are not on this path. It saves some memory otherwise it'd have to
2595 // be in every explicit block. We run this check to see if the reference
2596 // is valid for this storey
2598 Block block_on_path = ref_block;
2599 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2601 if (block_on_path == null)
2604 if (storey.HoistedThis == null) {
2605 storey.AddCapturedThisField (ec);
2608 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2609 if (b.AnonymousMethodStorey != null) {
2610 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2611 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2614 // Stop propagation inside same top block
2616 if (b.ParametersBlock == ParametersBlock.Original)
2619 b = b.ParametersBlock;
2622 var pb = b as ParametersBlock;
2623 if (pb != null && pb.StateMachine != null) {
2624 if (pb.StateMachine == storey)
2627 pb.StateMachine.AddParentStoreyReference (ec, storey);
2630 b.HasCapturedVariable = true;
2636 var ref_blocks = storey.ReferencesFromChildrenBlock;
2637 if (ref_blocks != null) {
2638 foreach (ExplicitBlock ref_block in ref_blocks) {
2639 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2640 if (b.AnonymousMethodStorey != null) {
2641 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2644 // Stop propagation inside same top block
2646 if (b.ParametersBlock == ParametersBlock.Original)
2649 b = b.ParametersBlock;
2652 var pb = b as ParametersBlock;
2653 if (pb != null && pb.StateMachine != null) {
2654 if (pb.StateMachine == storey)
2657 pb.StateMachine.AddParentStoreyReference (ec, storey);
2660 b.HasCapturedVariable = true;
2666 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2669 public void RegisterAsyncAwait ()
2672 while ((block.flags & Flags.AwaitBlock) == 0) {
2673 block.flags |= Flags.AwaitBlock;
2675 if (block is ParametersBlock)
2678 block = block.Parent.Explicit;
2682 public void RegisterIteratorYield ()
2684 ParametersBlock.TopBlock.IsIterator = true;
2687 while ((block.flags & Flags.YieldBlock) == 0) {
2688 block.flags |= Flags.YieldBlock;
2690 if (block.Parent == null)
2693 block = block.Parent.Explicit;
2697 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2699 tryBlock.statements = statements;
2700 statements = new List<Statement> (1);
2701 statements.Add (tf);
2706 // ParametersBlock was introduced to support anonymous methods
2707 // and lambda expressions
2709 public class ParametersBlock : ExplicitBlock
2711 public class ParameterInfo : INamedBlockVariable
2713 readonly ParametersBlock block;
2715 public VariableInfo VariableInfo;
2718 public ParameterInfo (ParametersBlock block, int index)
2726 public ParametersBlock Block {
2732 Block INamedBlockVariable.Block {
2738 public bool IsDeclared {
2744 public bool IsParameter {
2750 public bool IsLocked {
2759 public Location Location {
2761 return Parameter.Location;
2765 public Parameter Parameter {
2767 return block.Parameters [index];
2771 public TypeSpec ParameterType {
2773 return Parameter.Type;
2779 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2781 return new ParameterReference (this, loc);
2786 // Block is converted into an expression
2788 sealed class BlockScopeExpression : Expression
2791 readonly ParametersBlock block;
2793 public BlockScopeExpression (Expression child, ParametersBlock block)
2799 public override bool ContainsEmitWithAwait ()
2801 return child.ContainsEmitWithAwait ();
2804 public override Expression CreateExpressionTree (ResolveContext ec)
2806 throw new NotSupportedException ();
2809 protected override Expression DoResolve (ResolveContext ec)
2814 child = child.Resolve (ec);
2818 eclass = child.eclass;
2823 public override void Emit (EmitContext ec)
2825 block.EmitScopeInitializers (ec);
2830 protected ParametersCompiled parameters;
2831 protected ParameterInfo[] parameter_info;
2833 protected bool unreachable;
2834 protected ToplevelBlock top_block;
2835 protected StateMachine state_machine;
2837 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2838 : base (parent, 0, start, start)
2840 if (parameters == null)
2841 throw new ArgumentNullException ("parameters");
2843 this.parameters = parameters;
2844 ParametersBlock = this;
2846 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2848 this.top_block = parent.ParametersBlock.top_block;
2849 ProcessParameters ();
2852 protected ParametersBlock (ParametersCompiled parameters, Location start)
2853 : base (null, 0, start, start)
2855 if (parameters == null)
2856 throw new ArgumentNullException ("parameters");
2858 this.parameters = parameters;
2859 ParametersBlock = this;
2863 // It's supposed to be used by method body implementation of anonymous methods
2865 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2866 : base (null, 0, source.StartLocation, source.EndLocation)
2868 this.parameters = parameters;
2869 this.statements = source.statements;
2870 this.scope_initializers = source.scope_initializers;
2872 this.resolved = true;
2873 this.unreachable = source.unreachable;
2874 this.am_storey = source.am_storey;
2875 this.state_machine = source.state_machine;
2877 ParametersBlock = this;
2880 // Overwrite original for comparison purposes when linking cross references
2881 // between anonymous methods
2883 Original = source.Original;
2888 public bool IsAsync {
2890 return (flags & Flags.HasAsyncModifier) != 0;
2893 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2898 // Block has been converted to expression tree
2900 public bool IsExpressionTree {
2902 return (flags & Flags.IsExpressionTree) != 0;
2907 // The parameters for the block.
2909 public ParametersCompiled Parameters {
2915 public StateMachine StateMachine {
2917 return state_machine;
2921 public ToplevelBlock TopBlock {
2927 public bool Resolved {
2929 return (flags & Flags.Resolved) != 0;
2933 public int TemporaryLocalsCount { get; set; }
2938 // Check whether all `out' parameters have been assigned.
2940 public void CheckOutParameters (FlowBranching.UsageVector vector)
2942 if (vector.IsUnreachable)
2945 int n = parameter_info == null ? 0 : parameter_info.Length;
2947 for (int i = 0; i < n; i++) {
2948 VariableInfo var = parameter_info[i].VariableInfo;
2953 if (vector.IsAssigned (var, false))
2956 var p = parameter_info[i].Parameter;
2957 TopBlock.Report.Error (177, p.Location,
2958 "The out parameter `{0}' must be assigned to before control leaves the current method",
2963 public override Expression CreateExpressionTree (ResolveContext ec)
2965 if (statements.Count == 1) {
2966 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2967 if (scope_initializers != null)
2968 expr = new BlockScopeExpression (expr, this);
2973 return base.CreateExpressionTree (ec);
2976 public override void Emit (EmitContext ec)
2978 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2979 DefineStoreyContainer (ec, state_machine);
2980 state_machine.EmitStoreyInstantiation (ec, this);
2986 public void EmitEmbedded (EmitContext ec)
2988 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2989 DefineStoreyContainer (ec, state_machine);
2990 state_machine.EmitStoreyInstantiation (ec, this);
2996 public ParameterInfo GetParameterInfo (Parameter p)
2998 for (int i = 0; i < parameters.Count; ++i) {
2999 if (parameters[i] == p)
3000 return parameter_info[i];
3003 throw new ArgumentException ("Invalid parameter");
3006 public ParameterReference GetParameterReference (int index, Location loc)
3008 return new ParameterReference (parameter_info[index], loc);
3011 public Statement PerformClone ()
3013 CloneContext clonectx = new CloneContext ();
3014 return Clone (clonectx);
3017 protected void ProcessParameters ()
3019 if (parameters.Count == 0)
3022 parameter_info = new ParameterInfo[parameters.Count];
3023 for (int i = 0; i < parameter_info.Length; ++i) {
3024 var p = parameters.FixedParameters[i];
3028 // TODO: Should use Parameter only and more block there
3029 parameter_info[i] = new ParameterInfo (this, i);
3031 AddLocalName (p.Name, parameter_info[i]);
3035 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3042 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3043 flags |= Flags.IsExpressionTree;
3048 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3049 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3054 unreachable = top_level.End ();
3056 } catch (Exception e) {
3057 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3060 if (rc.CurrentBlock != null) {
3061 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3063 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3066 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3070 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3071 if (rc.CurrentAnonymousMethod == null) {
3072 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3073 if (md is StateMachineMethod) {
3076 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3081 // If an asynchronous body of F is either an expression classified as nothing, or a
3082 // statement block where no return statements have expressions, the inferred return type is Task
3085 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3086 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3087 am.ReturnTypeInference = null;
3088 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3093 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3094 rc.CurrentAnonymousMethod.GetSignatureForError ());
3102 void ResolveMeta (BlockContext ec)
3104 int orig_count = parameters.Count;
3106 for (int i = 0; i < orig_count; ++i) {
3107 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3109 if ((mod & Parameter.Modifier.OUT) == 0)
3112 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3113 parameter_info[i].VariableInfo = vi;
3114 ec.FlowOffset += vi.Length;
3118 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3120 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3121 var stateMachine = new IteratorStorey (iterator);
3123 state_machine = stateMachine;
3124 iterator.SetStateMachine (stateMachine);
3126 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3127 tlb.Original = this;
3128 tlb.IsCompilerGenerated = true;
3129 tlb.state_machine = stateMachine;
3130 tlb.AddStatement (new Return (iterator, iterator.Location));
3134 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3136 for (int i = 0; i < parameters.Count; i++) {
3137 Parameter p = parameters[i];
3138 Parameter.Modifier mod = p.ModFlags;
3139 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3140 host.Compiler.Report.Error (1988, p.Location,
3141 "Async methods cannot have ref or out parameters");
3145 if (p is ArglistParameter) {
3146 host.Compiler.Report.Error (4006, p.Location,
3147 "__arglist is not allowed in parameter list of async methods");
3151 if (parameters.Types[i].IsPointer) {
3152 host.Compiler.Report.Error (4005, p.Location,
3153 "Async methods cannot have unsafe parameters");
3159 host.Compiler.Report.Warning (1998, 1, loc,
3160 "Async block lacks `await' operator and will run synchronously");
3163 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3164 var initializer = new AsyncInitializer (this, host, block_type);
3165 initializer.Type = block_type;
3166 initializer.DelegateType = delegateType;
3168 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3170 state_machine = stateMachine;
3171 initializer.SetStateMachine (stateMachine);
3173 var b = this is ToplevelBlock ?
3174 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3175 new ParametersBlock (Parent, parameters, Location.Null) {
3180 b.IsCompilerGenerated = true;
3181 b.state_machine = stateMachine;
3182 b.AddStatement (new StatementExpression (initializer));
3190 public class ToplevelBlock : ParametersBlock
3192 LocalVariable this_variable;
3193 CompilerContext compiler;
3194 Dictionary<string, object> names;
3195 Dictionary<string, object> labels;
3197 List<ExplicitBlock> this_references;
3199 public ToplevelBlock (CompilerContext ctx, Location loc)
3200 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3204 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3205 : base (parameters, start)
3207 this.compiler = ctx;
3209 flags |= Flags.HasRet;
3211 ProcessParameters ();
3215 // Recreates a top level block from parameters block. Used for
3216 // compiler generated methods where the original block comes from
3217 // explicit child block. This works for already resolved blocks
3218 // only to ensure we resolve them in the correct flow order
3220 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3221 : base (source, parameters)
3223 this.compiler = source.TopBlock.compiler;
3225 flags |= Flags.HasRet;
3228 public bool IsIterator {
3230 return (flags & Flags.Iterator) != 0;
3233 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3237 public Report Report {
3239 return compiler.Report;
3244 // Used by anonymous blocks to track references of `this' variable
3246 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3248 return this_references;
3253 // Returns the "this" instance variable of this block.
3254 // See AddThisVariable() for more information.
3256 public LocalVariable ThisVariable {
3258 return this_variable;
3262 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3265 names = new Dictionary<string, object> ();
3268 if (!names.TryGetValue (name, out value)) {
3269 names.Add (name, li);
3273 INamedBlockVariable existing = value as INamedBlockVariable;
3274 List<INamedBlockVariable> existing_list;
3275 if (existing != null) {
3276 existing_list = new List<INamedBlockVariable> ();
3277 existing_list.Add (existing);
3278 names[name] = existing_list;
3280 existing_list = (List<INamedBlockVariable>) value;
3284 // A collision checking between local names
3286 var variable_block = li.Block.Explicit;
3287 for (int i = 0; i < existing_list.Count; ++i) {
3288 existing = existing_list[i];
3289 Block b = existing.Block.Explicit;
3291 // Collision at same level
3292 if (variable_block == b) {
3293 li.Block.Error_AlreadyDeclared (name, li);
3297 // Collision with parent
3298 Block parent = variable_block;
3299 while ((parent = parent.Parent) != null) {
3301 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3302 i = existing_list.Count;
3307 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3308 // Collision with children
3309 while ((b = b.Parent) != null) {
3310 if (variable_block == b) {
3311 li.Block.Error_AlreadyDeclared (name, li, "child");
3312 i = existing_list.Count;
3319 existing_list.Add (li);
3322 public void AddLabel (string name, LabeledStatement label)
3325 labels = new Dictionary<string, object> ();
3328 if (!labels.TryGetValue (name, out value)) {
3329 labels.Add (name, label);
3333 LabeledStatement existing = value as LabeledStatement;
3334 List<LabeledStatement> existing_list;
3335 if (existing != null) {
3336 existing_list = new List<LabeledStatement> ();
3337 existing_list.Add (existing);
3338 labels[name] = existing_list;
3340 existing_list = (List<LabeledStatement>) value;
3344 // A collision checking between labels
3346 for (int i = 0; i < existing_list.Count; ++i) {
3347 existing = existing_list[i];
3348 Block b = existing.Block;
3350 // Collision at same level
3351 if (label.Block == b) {
3352 Report.SymbolRelatedToPreviousError (existing.loc, name);
3353 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3357 // Collision with parent
3359 while ((b = b.Parent) != null) {
3360 if (existing.Block == b) {
3361 Report.Error (158, label.loc,
3362 "The label `{0}' shadows another label by the same name in a contained scope", name);
3363 i = existing_list.Count;
3368 // Collision with with children
3370 while ((b = b.Parent) != null) {
3371 if (label.Block == b) {
3372 Report.Error (158, label.loc,
3373 "The label `{0}' shadows another label by the same name in a contained scope", name);
3374 i = existing_list.Count;
3380 existing_list.Add (label);
3383 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3385 if (this_references == null)
3386 this_references = new List<ExplicitBlock> ();
3388 if (!this_references.Contains (block))
3389 this_references.Add (block);
3392 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3394 this_references.Remove (block);
3398 // Creates an arguments set from all parameters, useful for method proxy calls
3400 public Arguments GetAllParametersArguments ()
3402 int count = parameters.Count;
3403 Arguments args = new Arguments (count);
3404 for (int i = 0; i < count; ++i) {
3405 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3406 args.Add (new Argument (arg_expr));
3413 // Lookup inside a block, the returned value can represent 3 states
3415 // true+variable: A local name was found and it's valid
3416 // false+variable: A local name was found in a child block only
3417 // false+null: No local name was found
3419 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3425 if (!names.TryGetValue (name, out value))
3428 variable = value as INamedBlockVariable;
3430 if (variable != null) {
3432 if (variable.Block == b.Original)
3436 } while (b != null);
3444 } while (b != null);
3446 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3447 for (int i = 0; i < list.Count; ++i) {
3450 if (variable.Block == b.Original)
3454 } while (b != null);
3462 } while (b != null);
3472 public LabeledStatement GetLabel (string name, Block block)
3478 if (!labels.TryGetValue (name, out value)) {
3482 var label = value as LabeledStatement;
3484 if (label != null) {
3485 if (label.Block == b.Original)
3488 List<LabeledStatement> list = (List<LabeledStatement>) value;
3489 for (int i = 0; i < list.Count; ++i) {
3491 if (label.Block == b.Original)
3500 // This is used by non-static `struct' constructors which do not have an
3501 // initializer - in this case, the constructor must initialize all of the
3502 // struct's fields. To do this, we add a "this" variable and use the flow
3503 // analysis code to ensure that it's been fully initialized before control
3504 // leaves the constructor.
3506 public void AddThisVariable (BlockContext bc)
3508 if (this_variable != null)
3509 throw new InternalErrorException (StartLocation.ToString ());
3511 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3512 this_variable.Type = bc.CurrentType;
3513 this_variable.PrepareForFlowAnalysis (bc);
3516 public bool IsThisAssigned (BlockContext ec)
3518 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3521 public override void Emit (EmitContext ec)
3523 if (Report.Errors > 0)
3527 if (IsCompilerGenerated) {
3528 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3536 // If `HasReturnLabel' is set, then we already emitted a
3537 // jump to the end of the method, so we must emit a `ret'
3540 // Unfortunately, System.Reflection.Emit automatically emits
3541 // a leave to the end of a finally block. This is a problem
3542 // if no code is following the try/finally block since we may
3543 // jump to a point after the end of the method.
3544 // As a workaround, we're always creating a return label in
3547 if (ec.HasReturnLabel || !unreachable) {
3548 if (ec.HasReturnLabel)
3549 ec.MarkLabel (ec.ReturnLabel);
3551 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3552 ec.Mark (EndLocation);
3554 if (ec.ReturnType.Kind != MemberKind.Void)
3555 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3557 ec.Emit (OpCodes.Ret);
3560 } catch (Exception e) {
3561 throw new InternalErrorException (e, StartLocation);
3566 public class SwitchLabel : Statement
3574 // if expr == null, then it is the default case.
3576 public SwitchLabel (Expression expr, Location l)
3582 public bool IsDefault {
3584 return label == null;
3588 public Expression Label {
3594 public Location Location {
3600 public Constant Converted {
3609 public bool SectionStart { get; set; }
3611 public Label GetILLabel (EmitContext ec)
3613 if (il_label == null){
3614 il_label = ec.DefineLabel ();
3617 return il_label.Value;
3620 protected override void DoEmit (EmitContext ec)
3622 ec.MarkLabel (GetILLabel (ec));
3625 public override bool Resolve (BlockContext bc)
3627 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3629 return base.Resolve (bc);
3633 // Resolves the expression, reduces it to a literal if possible
3634 // and then converts it to the requested type.
3636 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3638 Expression e = label.Resolve (ec);
3643 Constant c = e as Constant;
3645 ec.Report.Error (150, loc, "A constant value is expected");
3649 if (allow_nullable && c is NullLiteral) {
3654 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3655 return converted != null;
3658 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3661 if (converted == null)
3664 label = converted.GetValueAsLiteral ();
3666 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3667 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3670 protected override void CloneTo (CloneContext clonectx, Statement target)
3672 var t = (SwitchLabel) target;
3674 t.label = label.Clone (clonectx);
3677 public override object Accept (StructuralVisitor visitor)
3679 return visitor.Visit (this);
3683 public class Switch : Statement
3685 // structure used to hold blocks of keys while calculating table switch
3686 sealed class LabelsRange : IComparable<LabelsRange>
3688 public readonly long min;
3690 public readonly List<long> label_values;
3692 public LabelsRange (long value)
3695 label_values = new List<long> ();
3696 label_values.Add (value);
3699 public LabelsRange (long min, long max, ICollection<long> values)
3703 this.label_values = new List<long> (values);
3708 return max - min + 1;
3712 public bool AddValue (long value)
3714 var gap = value - min + 1;
3715 // Ensure the range has > 50% occupancy
3716 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3720 label_values.Add (value);
3724 public int CompareTo (LabelsRange other)
3726 int nLength = label_values.Count;
3727 int nLengthOther = other.label_values.Count;
3728 if (nLengthOther == nLength)
3729 return (int) (other.min - min);
3731 return nLength - nLengthOther;
3735 sealed class DispatchStatement : Statement
3737 readonly Switch body;
3739 public DispatchStatement (Switch body)
3744 protected override void CloneTo (CloneContext clonectx, Statement target)
3746 throw new NotImplementedException ();
3749 protected override void DoEmit (EmitContext ec)
3751 body.EmitDispatch (ec);
3755 public Expression Expr;
3758 // Mapping of all labels to their SwitchLabels
3760 Dictionary<long, SwitchLabel> labels;
3761 Dictionary<string, SwitchLabel> string_labels;
3762 List<SwitchLabel> case_labels;
3765 /// The governing switch type
3767 public TypeSpec SwitchType;
3769 Expression new_expr;
3771 SwitchLabel case_null;
3772 SwitchLabel case_default;
3774 Label defaultLabel, nullLabel;
3775 VariableReference value;
3776 ExpressionStatement string_dictionary;
3777 FieldExpr switch_cache_field;
3778 ExplicitBlock block;
3781 // Nullable Types support
3783 Nullable.Unwrap unwrap;
3785 public Switch (Expression e, ExplicitBlock block, Location l)
3792 public ExplicitBlock Block {
3798 public SwitchLabel DefaultLabel {
3800 return case_default;
3804 public bool IsNullable {
3806 return unwrap != null;
3811 // Determines the governing type for a switch. The returned
3812 // expression might be the expression from the switch, or an
3813 // expression that includes any potential conversions to
3815 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3817 switch (expr.Type.BuiltinType) {
3818 case BuiltinTypeSpec.Type.Byte:
3819 case BuiltinTypeSpec.Type.SByte:
3820 case BuiltinTypeSpec.Type.UShort:
3821 case BuiltinTypeSpec.Type.Short:
3822 case BuiltinTypeSpec.Type.UInt:
3823 case BuiltinTypeSpec.Type.Int:
3824 case BuiltinTypeSpec.Type.ULong:
3825 case BuiltinTypeSpec.Type.Long:
3826 case BuiltinTypeSpec.Type.Char:
3827 case BuiltinTypeSpec.Type.String:
3828 case BuiltinTypeSpec.Type.Bool:
3832 if (expr.Type.IsEnum)
3836 // Try to find a *user* defined implicit conversion.
3838 // If there is no implicit conversion, or if there are multiple
3839 // conversions, we have to report an error
3841 Expression converted = null;
3842 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3845 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3850 // Ignore over-worked ImplicitUserConversions that do
3851 // an implicit conversion in addition to the user conversion.
3853 if (!(e is UserCast))
3856 if (converted != null){
3857 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3866 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3868 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3884 // Performs the basic sanity checks on the switch statement
3885 // (looks for duplicate keys and non-constant expressions).
3887 // It also returns a hashtable with the keys that we will later
3888 // use to compute the switch tables
3890 bool ResolveLabels (ResolveContext ec, Constant value)
3893 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3894 string_labels = new Dictionary<string, SwitchLabel> ();
3896 labels = new Dictionary<long, SwitchLabel> ();
3899 case_labels = new List<SwitchLabel> ();
3900 int default_label_index = -1;
3901 bool constant_label_found = false;
3903 for (int i = 0; i < block.Statements.Count; ++i) {
3904 var s = block.Statements[i];
3906 var sl = s as SwitchLabel;
3911 case_labels.Add (sl);
3914 if (case_default != null) {
3915 sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3919 default_label_index = i;
3924 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3930 if (string_labels != null) {
3931 string string_value = sl.Converted.GetValue () as string;
3932 if (string_value == null)
3935 string_labels.Add (string_value, sl);
3937 if (sl.Converted is NullLiteral) {
3940 labels.Add (sl.Converted.GetValueAsLong (), sl);
3943 } catch (ArgumentException) {
3944 if (string_labels != null)
3945 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3947 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3952 if (value != null) {
3953 var constant_label = constant_label_found ? null : FindLabel (value);
3954 if (constant_label == null || constant_label != sl)
3955 block.Statements[i] = new EmptyStatement (s.loc);
3957 constant_label_found = true;
3961 if (value != null && constant_label_found && default_label_index >= 0)
3962 block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3968 // This method emits code for a lookup-based switch statement (non-string)
3969 // Basically it groups the cases into blocks that are at least half full,
3970 // and then spits out individual lookup opcodes for each block.
3971 // It emits the longest blocks first, and short blocks are just
3972 // handled with direct compares.
3974 void EmitTableSwitch (EmitContext ec, Expression val)
3976 if (labels != null && labels.Count > 0) {
3977 List<LabelsRange> ranges;
3978 if (string_labels != null) {
3979 // We have done all hard work for string already
3980 // setup single range only
3981 ranges = new List<LabelsRange> (1);
3982 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3984 var element_keys = new long[labels.Count];
3985 labels.Keys.CopyTo (element_keys, 0);
3986 Array.Sort (element_keys);
3989 // Build possible ranges of switch labes to reduce number
3992 ranges = new List<LabelsRange> (element_keys.Length);
3993 var range = new LabelsRange (element_keys[0]);
3995 for (int i = 1; i < element_keys.Length; ++i) {
3996 var l = element_keys[i];
3997 if (range.AddValue (l))
4000 range = new LabelsRange (l);
4004 // sort the blocks so we can tackle the largest ones first
4008 Label lbl_default = defaultLabel;
4009 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4011 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4012 LabelsRange kb = ranges[range_index];
4013 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4015 // Optimize small ranges using simple equality check
4016 if (kb.Range <= 2) {
4017 foreach (var key in kb.label_values) {
4018 SwitchLabel sl = labels[key];
4019 if (sl == case_default || sl == case_null)
4022 if (sl.Converted.IsZeroInteger) {
4023 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4026 sl.Converted.Emit (ec);
4027 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4031 // TODO: if all the keys in the block are the same and there are
4032 // no gaps/defaults then just use a range-check.
4033 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4034 // TODO: optimize constant/I4 cases
4036 // check block range (could be > 2^31)
4038 ec.EmitLong (kb.min);
4039 ec.Emit (OpCodes.Blt, lbl_default);
4042 ec.EmitLong (kb.max);
4043 ec.Emit (OpCodes.Bgt, lbl_default);
4048 ec.EmitLong (kb.min);
4049 ec.Emit (OpCodes.Sub);
4052 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4056 int first = (int) kb.min;
4059 ec.Emit (OpCodes.Sub);
4060 } else if (first < 0) {
4061 ec.EmitInt (-first);
4062 ec.Emit (OpCodes.Add);
4066 // first, build the list of labels for the switch
4068 long cJumps = kb.Range;
4069 Label[] switch_labels = new Label[cJumps];
4070 for (int iJump = 0; iJump < cJumps; iJump++) {
4071 var key = kb.label_values[iKey];
4072 if (key == kb.min + iJump) {
4073 switch_labels[iJump] = labels[key].GetILLabel (ec);
4076 switch_labels[iJump] = lbl_default;
4080 // emit the switch opcode
4081 ec.Emit (OpCodes.Switch, switch_labels);
4084 // mark the default for this block
4085 if (range_index != 0)
4086 ec.MarkLabel (lbl_default);
4089 // the last default just goes to the end
4090 if (ranges.Count > 0)
4091 ec.Emit (OpCodes.Br, lbl_default);
4095 SwitchLabel FindLabel (Constant value)
4097 SwitchLabel sl = null;
4099 if (string_labels != null) {
4100 string s = value.GetValue () as string;
4102 if (case_null != null)
4104 else if (case_default != null)
4107 string_labels.TryGetValue (s, out sl);
4110 if (value is NullLiteral) {
4113 labels.TryGetValue (value.GetValueAsLong (), out sl);
4120 public override bool Resolve (BlockContext ec)
4122 Expr = Expr.Resolve (ec);
4126 new_expr = SwitchGoverningType (ec, Expr);
4128 if (new_expr == null && Expr.Type.IsNullableType) {
4129 unwrap = Nullable.Unwrap.Create (Expr, false);
4133 new_expr = SwitchGoverningType (ec, unwrap);
4136 if (new_expr == null) {
4137 if (Expr.Type != InternalType.ErrorType) {
4138 ec.Report.Error (151, loc,
4139 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4140 Expr.Type.GetSignatureForError ());
4147 SwitchType = new_expr.Type;
4149 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4150 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4154 if (block.Statements.Count == 0)
4157 var constant = new_expr as Constant;
4159 if (!ResolveLabels (ec, constant))
4163 // Don't need extra variable for constant switch or switch with
4164 // only default case
4166 if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4168 // Store switch expression for comparison purposes
4170 value = new_expr as VariableReference;
4171 if (value == null) {
4172 // Create temporary variable inside switch scope
4173 var current_block = ec.CurrentBlock;
4174 ec.CurrentBlock = Block;
4175 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4177 ec.CurrentBlock = current_block;
4181 Switch old_switch = ec.Switch;
4183 ec.Switch.SwitchType = SwitchType;
4185 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4187 ec.CurrentBranching.CurrentUsageVector.Goto ();
4189 var ok = block.Resolve (ec);
4191 if (case_default == null)
4192 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4194 ec.EndFlowBranching ();
4195 ec.Switch = old_switch;
4200 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4201 ResolveStringSwitchMap (ec);
4205 // Needed to emit anonymous storey initialization before
4206 // any generated switch dispatch
4208 block.AddScopeStatement (new DispatchStatement (this));
4213 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4215 var sl = FindLabel (value);
4218 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4225 // Converts string switch into string hashtable
4227 void ResolveStringSwitchMap (ResolveContext ec)
4229 FullNamedExpression string_dictionary_type;
4230 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4231 string_dictionary_type = new TypeExpression (
4232 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4233 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4235 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4236 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4238 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4242 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4243 Field field = new Field (ctype, string_dictionary_type,
4244 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4245 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4246 if (!field.Define ())
4248 ctype.AddField (field);
4250 var init = new List<Expression> ();
4252 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4253 string value = null;
4255 foreach (SwitchLabel sl in case_labels) {
4257 if (sl.SectionStart)
4258 labels.Add (++counter, sl);
4260 if (sl == case_default || sl == case_null)
4263 value = (string) sl.Converted.GetValue ();
4264 var init_args = new List<Expression> (2);
4265 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4267 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4268 init_args.Add (sl.Converted);
4270 init.Add (new CollectionElementInitializer (init_args, loc));
4273 Arguments args = new Arguments (1);
4274 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4275 Expression initializer = new NewInitialize (string_dictionary_type, args,
4276 new CollectionOrObjectInitializers (init, loc), loc);
4278 switch_cache_field = new FieldExpr (field, loc);
4279 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4282 void DoEmitStringSwitch (EmitContext ec)
4284 Label l_initialized = ec.DefineLabel ();
4287 // Skip initialization when value is null
4289 value.EmitBranchable (ec, nullLabel, false);
4292 // Check if string dictionary is initialized and initialize
4294 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4295 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4296 string_dictionary.EmitStatement (ec);
4298 ec.MarkLabel (l_initialized);
4300 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4302 ResolveContext rc = new ResolveContext (ec.MemberContext);
4304 if (switch_cache_field.Type.IsGeneric) {
4305 Arguments get_value_args = new Arguments (2);
4306 get_value_args.Add (new Argument (value));
4307 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4308 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4309 if (get_item == null)
4313 // A value was not found, go to default case
4315 get_item.EmitBranchable (ec, defaultLabel, false);
4317 Arguments get_value_args = new Arguments (1);
4318 get_value_args.Add (new Argument (value));
4320 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4321 if (get_item == null)
4324 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4325 get_item_object.EmitAssign (ec, get_item, true, false);
4326 ec.Emit (OpCodes.Brfalse, defaultLabel);
4328 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4329 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4331 get_item_int.EmitStatement (ec);
4332 get_item_object.Release (ec);
4335 EmitTableSwitch (ec, string_switch_variable);
4336 string_switch_variable.Release (ec);
4340 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4342 void EmitShortSwitch (EmitContext ec)
4344 MethodSpec equal_method = null;
4345 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4346 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4349 if (equal_method != null) {
4350 value.EmitBranchable (ec, nullLabel, false);
4353 for (int i = 0; i < case_labels.Count; ++i) {
4354 var label = case_labels [i];
4355 if (label == case_default || label == case_null)
4358 var constant = label.Converted;
4360 if (equal_method != null) {
4364 var call = new CallEmitter ();
4365 call.EmitPredefined (ec, equal_method, new Arguments (0));
4366 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4370 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4371 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4377 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4380 ec.Emit (OpCodes.Br, defaultLabel);
4383 void EmitDispatch (EmitContext ec)
4385 if (value == null) {
4387 // Constant switch, we already done the work
4393 // Mark sequence point explicitly to switch
4395 ec.Mark (block.StartLocation);
4396 block.IsCompilerGenerated = true;
4398 if (string_dictionary != null) {
4399 DoEmitStringSwitch (ec);
4400 } else if (case_labels.Count < 4 || string_labels != null) {
4401 EmitShortSwitch (ec);
4403 EmitTableSwitch (ec, value);
4407 protected override void DoEmit (EmitContext ec)
4409 // Workaround broken flow-analysis
4410 block.HasUnreachableClosingBrace = true;
4413 // Setup the codegen context
4415 Label old_end = ec.LoopEnd;
4416 Switch old_switch = ec.Switch;
4418 ec.LoopEnd = ec.DefineLabel ();
4421 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4422 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4424 if (value != null) {
4427 unwrap.EmitCheck (ec);
4428 ec.Emit (OpCodes.Brfalse, nullLabel);
4429 value.EmitAssign (ec, new_expr, false, false);
4430 } else if (new_expr != value) {
4431 value.EmitAssign (ec, new_expr, false, false);
4437 // Restore context state.
4438 ec.MarkLabel (ec.LoopEnd);
4441 // Restore the previous context
4443 ec.LoopEnd = old_end;
4444 ec.Switch = old_switch;
4447 protected override void CloneTo (CloneContext clonectx, Statement t)
4449 Switch target = (Switch) t;
4451 target.Expr = Expr.Clone (clonectx);
4452 target.block = (ExplicitBlock) block.Clone (clonectx);
4455 public override object Accept (StructuralVisitor visitor)
4457 return visitor.Visit (this);
4461 // A place where execution can restart in an iterator
4462 public abstract class ResumableStatement : Statement
4465 protected Label resume_point;
4467 public Label PrepareForEmit (EmitContext ec)
4471 resume_point = ec.DefineLabel ();
4473 return resume_point;
4476 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4481 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4486 public abstract class TryFinallyBlock : ExceptionStatement
4488 protected Statement stmt;
4489 Label dispose_try_block;
4490 bool prepared_for_dispose, emitted_dispose;
4491 Method finally_host;
4493 protected TryFinallyBlock (Statement stmt, Location loc)
4501 public Statement Statement {
4509 protected abstract void EmitTryBody (EmitContext ec);
4510 public abstract void EmitFinallyBody (EmitContext ec);
4512 public override Label PrepareForDispose (EmitContext ec, Label end)
4514 if (!prepared_for_dispose) {
4515 prepared_for_dispose = true;
4516 dispose_try_block = ec.DefineLabel ();
4518 return dispose_try_block;
4521 protected sealed override void DoEmit (EmitContext ec)
4523 EmitTryBodyPrepare (ec);
4526 ec.BeginFinallyBlock ();
4528 Label start_finally = ec.DefineLabel ();
4529 if (resume_points != null) {
4530 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4532 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4533 ec.Emit (OpCodes.Brfalse_S, start_finally);
4534 ec.Emit (OpCodes.Endfinally);
4537 ec.MarkLabel (start_finally);
4539 if (finally_host != null) {
4540 finally_host.Define ();
4541 finally_host.Emit ();
4543 // Now it's safe to add, to close it properly and emit sequence points
4544 finally_host.Parent.AddMember (finally_host);
4546 var ce = new CallEmitter ();
4547 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4548 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4550 EmitFinallyBody (ec);
4553 ec.EndExceptionBlock ();
4556 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4558 if (emitted_dispose)
4561 emitted_dispose = true;
4563 Label end_of_try = ec.DefineLabel ();
4565 // Ensure that the only way we can get into this code is through a dispatcher
4566 if (have_dispatcher)
4567 ec.Emit (OpCodes.Br, end);
4569 ec.BeginExceptionBlock ();
4571 ec.MarkLabel (dispose_try_block);
4573 Label[] labels = null;
4574 for (int i = 0; i < resume_points.Count; ++i) {
4575 ResumableStatement s = resume_points[i];
4576 Label ret = s.PrepareForDispose (ec, end_of_try);
4577 if (ret.Equals (end_of_try) && labels == null)
4579 if (labels == null) {
4580 labels = new Label[resume_points.Count];
4581 for (int j = 0; j < i; ++j)
4582 labels[j] = end_of_try;
4587 if (labels != null) {
4589 for (j = 1; j < labels.Length; ++j)
4590 if (!labels[0].Equals (labels[j]))
4592 bool emit_dispatcher = j < labels.Length;
4594 if (emit_dispatcher) {
4595 ec.Emit (OpCodes.Ldloc, pc);
4596 ec.EmitInt (first_resume_pc);
4597 ec.Emit (OpCodes.Sub);
4598 ec.Emit (OpCodes.Switch, labels);
4601 foreach (ResumableStatement s in resume_points)
4602 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4605 ec.MarkLabel (end_of_try);
4607 ec.BeginFinallyBlock ();
4609 if (finally_host != null) {
4610 var ce = new CallEmitter ();
4611 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4612 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4614 EmitFinallyBody (ec);
4617 ec.EndExceptionBlock ();
4620 public override bool Resolve (BlockContext bc)
4623 // Finally block inside iterator is called from MoveNext and
4624 // Dispose methods that means we need to lift the block into
4625 // newly created host method to emit the body only once. The
4626 // original block then simply calls the newly generated method.
4628 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4629 var b = stmt as Block;
4630 if (b != null && b.Explicit.HasYield) {
4631 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4635 return base.Resolve (bc);
4640 // Base class for blocks using exception handling
4642 public abstract class ExceptionStatement : ResumableStatement
4647 protected List<ResumableStatement> resume_points;
4648 protected int first_resume_pc;
4650 protected ExceptionStatement (Location loc)
4655 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4657 StateMachineInitializer state_machine = null;
4658 if (resume_points != null) {
4659 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4661 ec.EmitInt ((int) IteratorStorey.State.Running);
4662 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4665 ec.BeginExceptionBlock ();
4667 if (resume_points != null) {
4668 ec.MarkLabel (resume_point);
4670 // For normal control flow, we want to fall-through the Switch
4671 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4672 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4673 ec.EmitInt (first_resume_pc);
4674 ec.Emit (OpCodes.Sub);
4676 Label[] labels = new Label[resume_points.Count];
4677 for (int i = 0; i < resume_points.Count; ++i)
4678 labels[i] = resume_points[i].PrepareForEmit (ec);
4679 ec.Emit (OpCodes.Switch, labels);
4683 public void SomeCodeFollows ()
4686 code_follows = true;
4690 public override bool Resolve (BlockContext ec)
4693 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4694 // So, ensure there's some IL code after this statement.
4695 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4696 ec.NeedReturnLabel ();
4701 public void AddResumePoint (ResumableStatement stmt, int pc)
4703 if (resume_points == null) {
4704 resume_points = new List<ResumableStatement> ();
4705 first_resume_pc = pc;
4708 if (pc != first_resume_pc + resume_points.Count)
4709 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4711 resume_points.Add (stmt);
4716 public class Lock : TryFinallyBlock
4719 TemporaryVariableReference expr_copy;
4720 TemporaryVariableReference lock_taken;
4722 public Lock (Expression expr, Statement stmt, Location loc)
4728 public Expression Expr {
4734 public override bool Resolve (BlockContext ec)
4736 expr = expr.Resolve (ec);
4740 if (!TypeSpec.IsReferenceType (expr.Type)) {
4741 ec.Report.Error (185, loc,
4742 "`{0}' is not a reference type as required by the lock statement",
4743 expr.Type.GetSignatureForError ());
4746 if (expr.Type.IsGenericParameter) {
4747 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4750 VariableReference lv = expr as VariableReference;
4753 locked = lv.IsLockedByStatement;
4754 lv.IsLockedByStatement = true;
4761 // Have to keep original lock value around to unlock same location
4762 // in the case of original value has changed or is null
4764 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4765 expr_copy.Resolve (ec);
4768 // Ensure Monitor methods are available
4770 if (ResolvePredefinedMethods (ec) > 1) {
4771 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4772 lock_taken.Resolve (ec);
4775 using (ec.Set (ResolveContext.Options.LockScope)) {
4776 ec.StartFlowBranching (this);
4777 Statement.Resolve (ec);
4778 ec.EndFlowBranching ();
4782 lv.IsLockedByStatement = locked;
4790 protected override void EmitTryBodyPrepare (EmitContext ec)
4792 expr_copy.EmitAssign (ec, expr);
4794 if (lock_taken != null) {
4796 // Initialize ref variable
4798 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4801 // Monitor.Enter (expr_copy)
4803 expr_copy.Emit (ec);
4804 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4807 base.EmitTryBodyPrepare (ec);
4810 protected override void EmitTryBody (EmitContext ec)
4813 // Monitor.Enter (expr_copy, ref lock_taken)
4815 if (lock_taken != null) {
4816 expr_copy.Emit (ec);
4817 lock_taken.LocalInfo.CreateBuilder (ec);
4818 lock_taken.AddressOf (ec, AddressOp.Load);
4819 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4822 Statement.Emit (ec);
4825 public override void EmitFinallyBody (EmitContext ec)
4828 // if (lock_taken) Monitor.Exit (expr_copy)
4830 Label skip = ec.DefineLabel ();
4832 if (lock_taken != null) {
4833 lock_taken.Emit (ec);
4834 ec.Emit (OpCodes.Brfalse_S, skip);
4837 expr_copy.Emit (ec);
4838 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4840 ec.Emit (OpCodes.Call, m);
4842 ec.MarkLabel (skip);
4845 int ResolvePredefinedMethods (ResolveContext rc)
4847 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4848 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4852 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4856 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4860 protected override void CloneTo (CloneContext clonectx, Statement t)
4862 Lock target = (Lock) t;
4864 target.expr = expr.Clone (clonectx);
4865 target.stmt = Statement.Clone (clonectx);
4868 public override object Accept (StructuralVisitor visitor)
4870 return visitor.Visit (this);
4875 public class Unchecked : Statement {
4878 public Unchecked (Block b, Location loc)
4885 public override bool Resolve (BlockContext ec)
4887 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4888 return Block.Resolve (ec);
4891 protected override void DoEmit (EmitContext ec)
4893 using (ec.With (EmitContext.Options.CheckedScope, false))
4897 protected override void CloneTo (CloneContext clonectx, Statement t)
4899 Unchecked target = (Unchecked) t;
4901 target.Block = clonectx.LookupBlock (Block);
4904 public override object Accept (StructuralVisitor visitor)
4906 return visitor.Visit (this);
4910 public class Checked : Statement {
4913 public Checked (Block b, Location loc)
4916 b.Unchecked = false;
4920 public override bool Resolve (BlockContext ec)
4922 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4923 return Block.Resolve (ec);
4926 protected override void DoEmit (EmitContext ec)
4928 using (ec.With (EmitContext.Options.CheckedScope, true))
4932 protected override void CloneTo (CloneContext clonectx, Statement t)
4934 Checked target = (Checked) t;
4936 target.Block = clonectx.LookupBlock (Block);
4939 public override object Accept (StructuralVisitor visitor)
4941 return visitor.Visit (this);
4945 public class Unsafe : Statement {
4948 public Unsafe (Block b, Location loc)
4951 Block.Unsafe = true;
4955 public override bool Resolve (BlockContext ec)
4957 if (ec.CurrentIterator != null)
4958 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4960 using (ec.Set (ResolveContext.Options.UnsafeScope))
4961 return Block.Resolve (ec);
4964 protected override void DoEmit (EmitContext ec)
4969 protected override void CloneTo (CloneContext clonectx, Statement t)
4971 Unsafe target = (Unsafe) t;
4973 target.Block = clonectx.LookupBlock (Block);
4976 public override object Accept (StructuralVisitor visitor)
4978 return visitor.Visit (this);
4985 public class Fixed : Statement
4987 abstract class Emitter : ShimExpression
4989 protected LocalVariable vi;
4991 protected Emitter (Expression expr, LocalVariable li)
4997 public abstract void EmitExit (EmitContext ec);
5000 class ExpressionEmitter : Emitter {
5001 public ExpressionEmitter (Expression converted, LocalVariable li) :
5002 base (converted, li)
5006 protected override Expression DoResolve (ResolveContext rc)
5008 throw new NotImplementedException ();
5011 public override void Emit (EmitContext ec) {
5013 // Store pointer in pinned location
5019 public override void EmitExit (EmitContext ec)
5022 ec.Emit (OpCodes.Conv_U);
5027 class StringEmitter : Emitter
5029 LocalVariable pinned_string;
5031 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5036 protected override Expression DoResolve (ResolveContext rc)
5038 pinned_string = new LocalVariable (vi.Block, "$pinned",
5039 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5041 pinned_string.Type = rc.BuiltinTypes.String;
5043 eclass = ExprClass.Variable;
5044 type = rc.BuiltinTypes.Int;
5048 public override void Emit (EmitContext ec)
5050 pinned_string.CreateBuilder (ec);
5053 pinned_string.EmitAssign (ec);
5055 // TODO: Should use Binary::Add
5056 pinned_string.Emit (ec);
5057 ec.Emit (OpCodes.Conv_I);
5059 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5063 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5064 //pe.InstanceExpression = pinned_string;
5065 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5067 ec.Emit (OpCodes.Add);
5071 public override void EmitExit (EmitContext ec)
5074 pinned_string.EmitAssign (ec);
5078 public class VariableDeclaration : BlockVariableDeclaration
5080 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5085 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5087 if (!Variable.Type.IsPointer && li == Variable) {
5088 bc.Report.Error (209, TypeExpression.Location,
5089 "The type of locals declared in a fixed statement must be a pointer type");
5094 // The rules for the possible declarators are pretty wise,
5095 // but the production on the grammar is more concise.
5097 // So we have to enforce these rules here.
5099 // We do not resolve before doing the case 1 test,
5100 // because the grammar is explicit in that the token &
5101 // is present, so we need to test for this particular case.
5104 if (initializer is Cast) {
5105 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5109 initializer = initializer.Resolve (bc);
5111 if (initializer == null)
5117 if (initializer.Type.IsArray) {
5118 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5121 // Provided that array_type is unmanaged,
5123 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5127 // and T* is implicitly convertible to the
5128 // pointer type given in the fixed statement.
5130 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5132 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5133 if (converted == null)
5137 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5139 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5140 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5141 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5142 new NullLiteral (loc),
5145 converted = converted.Resolve (bc);
5147 return new ExpressionEmitter (converted, li);
5153 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5154 return new StringEmitter (initializer, li, loc).Resolve (bc);
5157 // Case 3: fixed buffer
5158 if (initializer is FixedBufferPtr) {
5159 return new ExpressionEmitter (initializer, li);
5163 // Case 4: & object.
5165 bool already_fixed = true;
5166 Unary u = initializer as Unary;
5167 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5168 IVariableReference vr = u.Expr as IVariableReference;
5169 if (vr == null || !vr.IsFixed) {
5170 already_fixed = false;
5174 if (already_fixed) {
5175 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5178 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5179 return new ExpressionEmitter (initializer, li);
5184 VariableDeclaration decl;
5185 Statement statement;
5188 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5197 public Statement Statement {
5203 public BlockVariableDeclaration Variables {
5211 public override bool Resolve (BlockContext ec)
5213 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5214 if (!decl.Resolve (ec))
5218 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5219 bool ok = statement.Resolve (ec);
5220 bool flow_unreachable = ec.EndFlowBranching ();
5221 has_ret = flow_unreachable;
5226 protected override void DoEmit (EmitContext ec)
5228 decl.Variable.CreateBuilder (ec);
5229 decl.Initializer.Emit (ec);
5230 if (decl.Declarators != null) {
5231 foreach (var d in decl.Declarators) {
5232 d.Variable.CreateBuilder (ec);
5233 d.Initializer.Emit (ec);
5237 statement.Emit (ec);
5243 // Clear the pinned variable
5245 ((Emitter) decl.Initializer).EmitExit (ec);
5246 if (decl.Declarators != null) {
5247 foreach (var d in decl.Declarators) {
5248 ((Emitter)d.Initializer).EmitExit (ec);
5253 protected override void CloneTo (CloneContext clonectx, Statement t)
5255 Fixed target = (Fixed) t;
5257 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5258 target.statement = statement.Clone (clonectx);
5261 public override object Accept (StructuralVisitor visitor)
5263 return visitor.Visit (this);
5267 public class Catch : Statement
5271 FullNamedExpression type_expr;
5272 CompilerAssign assign;
5275 public Catch (Block block, Location loc)
5283 public Block Block {
5289 public TypeSpec CatchType {
5295 public bool IsGeneral {
5297 return type_expr == null;
5301 public FullNamedExpression TypeExpression {
5310 public LocalVariable Variable {
5321 protected override void DoEmit (EmitContext ec)
5324 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5326 ec.BeginCatchBlock (CatchType);
5329 li.CreateBuilder (ec);
5332 // Special case hoisted catch variable, we have to use a temporary variable
5333 // to pass via anonymous storey initialization with the value still on top
5336 if (li.HoistedVariant != null) {
5337 LocalTemporary lt = new LocalTemporary (li.Type);
5340 // switch to assigning from the temporary variable and not from top of the stack
5341 assign.UpdateSource (lt);
5344 ec.Emit (OpCodes.Pop);
5350 public override bool Resolve (BlockContext ec)
5352 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5353 if (type_expr != null) {
5354 type = type_expr.ResolveAsType (ec);
5358 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5359 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5360 } else if (li != null) {
5362 li.PrepareForFlowAnalysis (ec);
5364 // source variable is at the top of the stack
5365 Expression source = new EmptyExpression (li.Type);
5366 if (li.Type.IsGenericParameter)
5367 source = new UnboxCast (source, li.Type);
5370 // Uses Location.Null to hide from symbol file
5372 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5373 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5377 return Block.Resolve (ec);
5381 protected override void CloneTo (CloneContext clonectx, Statement t)
5383 Catch target = (Catch) t;
5385 if (type_expr != null)
5386 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5388 target.block = clonectx.LookupBlock (block);
5392 public class TryFinally : TryFinallyBlock
5396 public TryFinally (Statement stmt, Block fini, Location loc)
5402 public Block Finallyblock {
5408 public override bool Resolve (BlockContext ec)
5412 ec.StartFlowBranching (this);
5414 if (!stmt.Resolve (ec))
5418 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5420 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5421 if (!fini.Resolve (ec))
5425 ec.EndFlowBranching ();
5427 ok &= base.Resolve (ec);
5432 protected override void EmitTryBody (EmitContext ec)
5437 public override void EmitFinallyBody (EmitContext ec)
5442 protected override void CloneTo (CloneContext clonectx, Statement t)
5444 TryFinally target = (TryFinally) t;
5446 target.stmt = (Statement) stmt.Clone (clonectx);
5448 target.fini = clonectx.LookupBlock (fini);
5451 public override object Accept (StructuralVisitor visitor)
5453 return visitor.Visit (this);
5457 public class TryCatch : ExceptionStatement
5460 List<Catch> clauses;
5461 readonly bool inside_try_finally;
5463 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5467 this.clauses = catch_clauses;
5468 this.inside_try_finally = inside_try_finally;
5471 public List<Catch> Clauses {
5477 public bool IsTryCatchFinally {
5479 return inside_try_finally;
5483 public override bool Resolve (BlockContext ec)
5487 ec.StartFlowBranching (this);
5489 if (!Block.Resolve (ec))
5492 for (int i = 0; i < clauses.Count; ++i) {
5494 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5496 if (!c.Resolve (ec)) {
5501 TypeSpec resolved_type = c.CatchType;
5502 for (int ii = 0; ii < clauses.Count; ++ii) {
5506 if (clauses[ii].IsGeneral) {
5507 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5510 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5513 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5516 ec.Report.Warning (1058, 1, c.loc,
5517 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5525 var ct = clauses[ii].CatchType;
5529 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5530 ec.Report.Error (160, c.loc,
5531 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5532 ct.GetSignatureForError ());
5538 ec.EndFlowBranching ();
5540 return base.Resolve (ec) && ok;
5543 protected sealed override void DoEmit (EmitContext ec)
5545 if (!inside_try_finally)
5546 EmitTryBodyPrepare (ec);
5550 foreach (Catch c in clauses)
5553 if (!inside_try_finally)
5554 ec.EndExceptionBlock ();
5557 protected override void CloneTo (CloneContext clonectx, Statement t)
5559 TryCatch target = (TryCatch) t;
5561 target.Block = clonectx.LookupBlock (Block);
5562 if (clauses != null){
5563 target.clauses = new List<Catch> ();
5564 foreach (Catch c in clauses)
5565 target.clauses.Add ((Catch) c.Clone (clonectx));
5569 public override object Accept (StructuralVisitor visitor)
5571 return visitor.Visit (this);
5575 public class Using : TryFinallyBlock
5577 public class VariableDeclaration : BlockVariableDeclaration
5579 Statement dispose_call;
5581 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5586 public VariableDeclaration (LocalVariable li, Location loc)
5592 public VariableDeclaration (Expression expr)
5595 loc = expr.Location;
5601 public bool IsNested { get; private set; }
5605 public void EmitDispose (EmitContext ec)
5607 dispose_call.Emit (ec);
5610 public override bool Resolve (BlockContext bc)
5615 return base.Resolve (bc, false);
5618 public Expression ResolveExpression (BlockContext bc)
5620 var e = Initializer.Resolve (bc);
5624 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5625 Initializer = ResolveInitializer (bc, Variable, e);
5629 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5631 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5632 initializer = initializer.Resolve (bc);
5633 if (initializer == null)
5636 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5637 Arguments args = new Arguments (1);
5638 args.Add (new Argument (initializer));
5639 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5640 if (initializer == null)
5643 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5644 dispose_call = CreateDisposeCall (bc, var);
5645 dispose_call.Resolve (bc);
5647 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5650 if (li == Variable) {
5651 CheckIDiposableConversion (bc, li, initializer);
5652 dispose_call = CreateDisposeCall (bc, li);
5653 dispose_call.Resolve (bc);
5656 return base.ResolveInitializer (bc, li, initializer);
5659 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5663 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5664 if (type.IsNullableType) {
5665 // it's handled in CreateDisposeCall
5669 bc.Report.SymbolRelatedToPreviousError (type);
5670 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5671 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5672 type.GetSignatureForError ());
5678 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5680 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5682 var loc = lv.Location;
5684 var idt = bc.BuiltinTypes.IDisposable;
5685 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5687 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5688 dispose_mg.InstanceExpression = type.IsNullableType ?
5689 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5693 // Hide it from symbol file via null location
5695 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5697 // Add conditional call when disposing possible null variable
5698 if (!type.IsStruct || type.IsNullableType)
5699 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5704 public void ResolveDeclaratorInitializer (BlockContext bc)
5706 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5709 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5711 for (int i = declarators.Count - 1; i >= 0; --i) {
5712 var d = declarators [i];
5713 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5714 vd.Initializer = d.Initializer;
5716 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5717 vd.dispose_call.Resolve (bc);
5719 stmt = new Using (vd, stmt, d.Variable.Location);
5726 public override object Accept (StructuralVisitor visitor)
5728 return visitor.Visit (this);
5732 VariableDeclaration decl;
5734 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5740 public Using (Expression expr, Statement stmt, Location loc)
5743 this.decl = new VariableDeclaration (expr);
5748 public Expression Expr {
5750 return decl.Variable == null ? decl.Initializer : null;
5754 public BlockVariableDeclaration Variables {
5762 public override void Emit (EmitContext ec)
5765 // Don't emit sequence point it will be set on variable declaration
5770 protected override void EmitTryBodyPrepare (EmitContext ec)
5773 base.EmitTryBodyPrepare (ec);
5776 protected override void EmitTryBody (EmitContext ec)
5781 public override void EmitFinallyBody (EmitContext ec)
5783 decl.EmitDispose (ec);
5786 public override bool Resolve (BlockContext ec)
5788 VariableReference vr;
5789 bool vr_locked = false;
5791 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5792 if (decl.Variable == null) {
5793 vr = decl.ResolveExpression (ec) as VariableReference;
5795 vr_locked = vr.IsLockedByStatement;
5796 vr.IsLockedByStatement = true;
5799 if (decl.IsNested) {
5800 decl.ResolveDeclaratorInitializer (ec);
5802 if (!decl.Resolve (ec))
5805 if (decl.Declarators != null) {
5806 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5814 ec.StartFlowBranching (this);
5818 ec.EndFlowBranching ();
5821 vr.IsLockedByStatement = vr_locked;
5828 protected override void CloneTo (CloneContext clonectx, Statement t)
5830 Using target = (Using) t;
5832 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5833 target.stmt = stmt.Clone (clonectx);
5836 public override object Accept (StructuralVisitor visitor)
5838 return visitor.Visit (this);
5843 /// Implementation of the foreach C# statement
5845 public class Foreach : Statement
5847 abstract class IteratorStatement : Statement
5849 protected readonly Foreach for_each;
5851 protected IteratorStatement (Foreach @foreach)
5853 this.for_each = @foreach;
5854 this.loc = @foreach.expr.Location;
5857 protected override void CloneTo (CloneContext clonectx, Statement target)
5859 throw new NotImplementedException ();
5862 public override void Emit (EmitContext ec)
5864 if (ec.EmitAccurateDebugInfo) {
5865 ec.Emit (OpCodes.Nop);
5872 sealed class ArrayForeach : IteratorStatement
5874 TemporaryVariableReference[] lengths;
5875 Expression [] length_exprs;
5876 StatementExpression[] counter;
5877 TemporaryVariableReference[] variables;
5879 TemporaryVariableReference copy;
5881 public ArrayForeach (Foreach @foreach, int rank)
5884 counter = new StatementExpression[rank];
5885 variables = new TemporaryVariableReference[rank];
5886 length_exprs = new Expression [rank];
5889 // Only use temporary length variables when dealing with
5890 // multi-dimensional arrays
5893 lengths = new TemporaryVariableReference [rank];
5896 public override bool Resolve (BlockContext ec)
5898 Block variables_block = for_each.variable.Block;
5899 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5902 int rank = length_exprs.Length;
5903 Arguments list = new Arguments (rank);
5904 for (int i = 0; i < rank; i++) {
5905 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5907 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5908 counter[i].Resolve (ec);
5911 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5913 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5914 lengths[i].Resolve (ec);
5916 Arguments args = new Arguments (1);
5917 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5918 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5921 list.Add (new Argument (v));
5924 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5929 if (for_each.type is VarExpr) {
5930 // Infer implicitly typed local variable from foreach array type
5931 var_type = access.Type;
5933 var_type = for_each.type.ResolveAsType (ec);
5935 if (var_type == null)
5938 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5943 for_each.variable.Type = var_type;
5945 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5946 if (variable_ref == null)
5949 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5953 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5954 ec.CurrentBranching.CreateSibling ();
5956 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5957 if (!for_each.body.Resolve (ec))
5959 ec.EndFlowBranching ();
5961 // There's no direct control flow from the end of the embedded statement to the end of the loop
5962 ec.CurrentBranching.CurrentUsageVector.Goto ();
5964 ec.EndFlowBranching ();
5969 protected override void DoEmit (EmitContext ec)
5971 copy.EmitAssign (ec, for_each.expr);
5973 int rank = length_exprs.Length;
5974 Label[] test = new Label [rank];
5975 Label[] loop = new Label [rank];
5977 for (int i = 0; i < rank; i++) {
5978 test [i] = ec.DefineLabel ();
5979 loop [i] = ec.DefineLabel ();
5981 if (lengths != null)
5982 lengths [i].EmitAssign (ec, length_exprs [i]);
5985 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5986 for (int i = 0; i < rank; i++) {
5987 variables [i].EmitAssign (ec, zero);
5989 ec.Emit (OpCodes.Br, test [i]);
5990 ec.MarkLabel (loop [i]);
5993 for_each.body.Emit (ec);
5995 ec.MarkLabel (ec.LoopBegin);
5996 ec.Mark (for_each.expr.Location);
5998 for (int i = rank - 1; i >= 0; i--){
5999 counter [i].Emit (ec);
6001 ec.MarkLabel (test [i]);
6002 variables [i].Emit (ec);
6004 if (lengths != null)
6005 lengths [i].Emit (ec);
6007 length_exprs [i].Emit (ec);
6009 ec.Emit (OpCodes.Blt, loop [i]);
6012 ec.MarkLabel (ec.LoopEnd);
6016 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6018 class RuntimeDispose : Using.VariableDeclaration
6020 public RuntimeDispose (LocalVariable lv, Location loc)
6025 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6027 // Defered to runtime check
6030 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6032 var idt = bc.BuiltinTypes.IDisposable;
6035 // Fabricates code like
6037 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6040 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6042 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6043 dispose_variable.CreateReferenceExpression (bc, loc),
6044 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6045 loc), new NullLiteral (loc));
6047 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6049 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6050 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6052 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6053 return new If (idisaposable_test, dispose, loc);
6057 LocalVariable variable;
6059 Statement statement;
6060 ExpressionStatement init;
6061 TemporaryVariableReference enumerator_variable;
6062 bool ambiguous_getenumerator_name;
6064 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6067 this.variable = var;
6071 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6073 rc.Report.SymbolRelatedToPreviousError (enumerator);
6074 rc.Report.Error (202, loc,
6075 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6076 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6079 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6082 // Option 1: Try to match by name GetEnumerator first
6084 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6085 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6087 var mg = mexpr as MethodGroupExpr;
6089 mg.InstanceExpression = expr;
6090 Arguments args = new Arguments (0);
6091 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6093 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6094 if (ambiguous_getenumerator_name)
6097 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6103 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6106 PredefinedMember<MethodSpec> iface_candidate = null;
6107 var ptypes = rc.Module.PredefinedTypes;
6108 var gen_ienumerable = ptypes.IEnumerableGeneric;
6109 if (!gen_ienumerable.Define ())
6110 gen_ienumerable = null;
6112 var ifaces = t.Interfaces;
6113 if (ifaces != null) {
6114 foreach (var iface in ifaces) {
6115 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6116 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6117 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6118 rc.Report.Error (1640, loc,
6119 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6120 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6125 // TODO: Cache this somehow
6126 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6127 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6132 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6133 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6138 if (iface_candidate == null) {
6139 if (expr.Type != InternalType.ErrorType) {
6140 rc.Report.Error (1579, loc,
6141 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6142 expr.Type.GetSignatureForError (), "GetEnumerator");
6148 var method = iface_candidate.Resolve (loc);
6152 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6153 mg.InstanceExpression = expr;
6157 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6159 var ms = MemberCache.FindMember (enumerator.ReturnType,
6160 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6161 BindingRestriction.InstanceOnly) as MethodSpec;
6163 if (ms == null || !ms.IsPublic) {
6164 Error_WrongEnumerator (rc, enumerator);
6168 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6171 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6173 var ps = MemberCache.FindMember (enumerator.ReturnType,
6174 MemberFilter.Property ("Current", null),
6175 BindingRestriction.InstanceOnly) as PropertySpec;
6177 if (ps == null || !ps.IsPublic) {
6178 Error_WrongEnumerator (rc, enumerator);
6185 public override bool Resolve (BlockContext ec)
6187 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6190 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6191 } else if (expr.Type.IsNullableType) {
6192 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6195 var get_enumerator_mg = ResolveGetEnumerator (ec);
6196 if (get_enumerator_mg == null) {
6200 var get_enumerator = get_enumerator_mg.BestCandidate;
6201 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6202 enumerator_variable.Resolve (ec);
6204 // Prepare bool MoveNext ()
6205 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6206 if (move_next_mg == null) {
6210 move_next_mg.InstanceExpression = enumerator_variable;
6212 // Prepare ~T~ Current { get; }
6213 var current_prop = ResolveCurrent (ec, get_enumerator);
6214 if (current_prop == null) {
6218 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6219 if (current_pe == null)
6222 VarExpr ve = for_each.type as VarExpr;
6226 // Source type is dynamic, set element type to dynamic too
6227 variable.Type = ec.BuiltinTypes.Dynamic;
6229 // Infer implicitly typed local variable from foreach enumerable type
6230 variable.Type = current_pe.Type;
6234 // Explicit cast of dynamic collection elements has to be done at runtime
6235 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6238 variable.Type = for_each.type.ResolveAsType (ec);
6240 if (variable.Type == null)
6243 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6244 if (current_pe == null)
6248 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6249 if (variable_ref == null)
6252 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6254 var init = new Invocation (get_enumerator_mg, null);
6256 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6257 for_each.body, Location.Null);
6259 var enum_type = enumerator_variable.Type;
6262 // Add Dispose method call when enumerator can be IDisposable
6264 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6265 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6267 // Runtime Dispose check
6269 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6270 vd.Initializer = init;
6271 statement = new Using (vd, statement, Location.Null);
6274 // No Dispose call needed
6276 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6277 this.init.Resolve (ec);
6281 // Static Dispose check
6283 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6284 vd.Initializer = init;
6285 statement = new Using (vd, statement, Location.Null);
6288 return statement.Resolve (ec);
6291 protected override void DoEmit (EmitContext ec)
6293 enumerator_variable.LocalInfo.CreateBuilder (ec);
6296 init.EmitStatement (ec);
6298 statement.Emit (ec);
6301 #region IErrorHandler Members
6303 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6305 ec.Report.SymbolRelatedToPreviousError (best);
6306 ec.Report.Warning (278, 2, expr.Location,
6307 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6308 expr.Type.GetSignatureForError (), "enumerable",
6309 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6311 ambiguous_getenumerator_name = true;
6315 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6320 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6325 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6334 LocalVariable variable;
6336 Statement statement;
6339 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6342 this.variable = var;
6344 this.statement = stmt;
6349 public Expression Expr {
6350 get { return expr; }
6353 public Statement Statement {
6354 get { return statement; }
6357 public Expression TypeExpression {
6358 get { return type; }
6361 public LocalVariable Variable {
6362 get { return variable; }
6365 public override bool Resolve (BlockContext ec)
6367 expr = expr.Resolve (ec);
6372 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6376 body.AddStatement (statement);
6378 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6379 statement = new ArrayForeach (this, 1);
6380 } else if (expr.Type is ArrayContainer) {
6381 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6383 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6384 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6385 expr.ExprClassName);
6389 statement = new CollectionForeach (this, variable, expr);
6392 return statement.Resolve (ec);
6395 protected override void DoEmit (EmitContext ec)
6397 variable.CreateBuilder (ec);
6399 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6400 ec.LoopBegin = ec.DefineLabel ();
6401 ec.LoopEnd = ec.DefineLabel ();
6403 statement.Emit (ec);
6405 ec.LoopBegin = old_begin;
6406 ec.LoopEnd = old_end;
6409 protected override void CloneTo (CloneContext clonectx, Statement t)
6411 Foreach target = (Foreach) t;
6413 target.type = type.Clone (clonectx);
6414 target.expr = expr.Clone (clonectx);
6415 target.body = (Block) body.Clone (clonectx);
6416 target.statement = statement.Clone (clonectx);
6419 public override object Accept (StructuralVisitor visitor)
6421 return visitor.Visit (this);