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.PrepareEmit ();
2667 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2670 public void RegisterAsyncAwait ()
2673 while ((block.flags & Flags.AwaitBlock) == 0) {
2674 block.flags |= Flags.AwaitBlock;
2676 if (block is ParametersBlock)
2679 block = block.Parent.Explicit;
2683 public void RegisterIteratorYield ()
2685 ParametersBlock.TopBlock.IsIterator = true;
2688 while ((block.flags & Flags.YieldBlock) == 0) {
2689 block.flags |= Flags.YieldBlock;
2691 if (block.Parent == null)
2694 block = block.Parent.Explicit;
2698 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2700 tryBlock.statements = statements;
2701 statements = new List<Statement> (1);
2702 statements.Add (tf);
2707 // ParametersBlock was introduced to support anonymous methods
2708 // and lambda expressions
2710 public class ParametersBlock : ExplicitBlock
2712 public class ParameterInfo : INamedBlockVariable
2714 readonly ParametersBlock block;
2716 public VariableInfo VariableInfo;
2719 public ParameterInfo (ParametersBlock block, int index)
2727 public ParametersBlock Block {
2733 Block INamedBlockVariable.Block {
2739 public bool IsDeclared {
2745 public bool IsParameter {
2751 public bool IsLocked {
2760 public Location Location {
2762 return Parameter.Location;
2766 public Parameter Parameter {
2768 return block.Parameters [index];
2772 public TypeSpec ParameterType {
2774 return Parameter.Type;
2780 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2782 return new ParameterReference (this, loc);
2787 // Block is converted into an expression
2789 sealed class BlockScopeExpression : Expression
2792 readonly ParametersBlock block;
2794 public BlockScopeExpression (Expression child, ParametersBlock block)
2800 public override bool ContainsEmitWithAwait ()
2802 return child.ContainsEmitWithAwait ();
2805 public override Expression CreateExpressionTree (ResolveContext ec)
2807 throw new NotSupportedException ();
2810 protected override Expression DoResolve (ResolveContext ec)
2815 child = child.Resolve (ec);
2819 eclass = child.eclass;
2824 public override void Emit (EmitContext ec)
2826 block.EmitScopeInitializers (ec);
2831 protected ParametersCompiled parameters;
2832 protected ParameterInfo[] parameter_info;
2834 protected bool unreachable;
2835 protected ToplevelBlock top_block;
2836 protected StateMachine state_machine;
2838 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2839 : base (parent, 0, start, start)
2841 if (parameters == null)
2842 throw new ArgumentNullException ("parameters");
2844 this.parameters = parameters;
2845 ParametersBlock = this;
2847 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2849 this.top_block = parent.ParametersBlock.top_block;
2850 ProcessParameters ();
2853 protected ParametersBlock (ParametersCompiled parameters, Location start)
2854 : base (null, 0, start, start)
2856 if (parameters == null)
2857 throw new ArgumentNullException ("parameters");
2859 this.parameters = parameters;
2860 ParametersBlock = this;
2864 // It's supposed to be used by method body implementation of anonymous methods
2866 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2867 : base (null, 0, source.StartLocation, source.EndLocation)
2869 this.parameters = parameters;
2870 this.statements = source.statements;
2871 this.scope_initializers = source.scope_initializers;
2873 this.resolved = true;
2874 this.unreachable = source.unreachable;
2875 this.am_storey = source.am_storey;
2876 this.state_machine = source.state_machine;
2878 ParametersBlock = this;
2881 // Overwrite original for comparison purposes when linking cross references
2882 // between anonymous methods
2884 Original = source.Original;
2889 public bool IsAsync {
2891 return (flags & Flags.HasAsyncModifier) != 0;
2894 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2899 // Block has been converted to expression tree
2901 public bool IsExpressionTree {
2903 return (flags & Flags.IsExpressionTree) != 0;
2908 // The parameters for the block.
2910 public ParametersCompiled Parameters {
2916 public StateMachine StateMachine {
2918 return state_machine;
2922 public ToplevelBlock TopBlock {
2928 public bool Resolved {
2930 return (flags & Flags.Resolved) != 0;
2934 public int TemporaryLocalsCount { get; set; }
2939 // Check whether all `out' parameters have been assigned.
2941 public void CheckOutParameters (FlowBranching.UsageVector vector)
2943 if (vector.IsUnreachable)
2946 int n = parameter_info == null ? 0 : parameter_info.Length;
2948 for (int i = 0; i < n; i++) {
2949 VariableInfo var = parameter_info[i].VariableInfo;
2954 if (vector.IsAssigned (var, false))
2957 var p = parameter_info[i].Parameter;
2958 TopBlock.Report.Error (177, p.Location,
2959 "The out parameter `{0}' must be assigned to before control leaves the current method",
2964 public override Expression CreateExpressionTree (ResolveContext ec)
2966 if (statements.Count == 1) {
2967 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2968 if (scope_initializers != null)
2969 expr = new BlockScopeExpression (expr, this);
2974 return base.CreateExpressionTree (ec);
2977 public override void Emit (EmitContext ec)
2979 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2980 DefineStoreyContainer (ec, state_machine);
2981 state_machine.EmitStoreyInstantiation (ec, this);
2987 public void EmitEmbedded (EmitContext ec)
2989 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2990 DefineStoreyContainer (ec, state_machine);
2991 state_machine.EmitStoreyInstantiation (ec, this);
2997 public ParameterInfo GetParameterInfo (Parameter p)
2999 for (int i = 0; i < parameters.Count; ++i) {
3000 if (parameters[i] == p)
3001 return parameter_info[i];
3004 throw new ArgumentException ("Invalid parameter");
3007 public ParameterReference GetParameterReference (int index, Location loc)
3009 return new ParameterReference (parameter_info[index], loc);
3012 public Statement PerformClone ()
3014 CloneContext clonectx = new CloneContext ();
3015 return Clone (clonectx);
3018 protected void ProcessParameters ()
3020 if (parameters.Count == 0)
3023 parameter_info = new ParameterInfo[parameters.Count];
3024 for (int i = 0; i < parameter_info.Length; ++i) {
3025 var p = parameters.FixedParameters[i];
3029 // TODO: Should use Parameter only and more block there
3030 parameter_info[i] = new ParameterInfo (this, i);
3032 AddLocalName (p.Name, parameter_info[i]);
3036 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3043 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3044 flags |= Flags.IsExpressionTree;
3049 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3050 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3055 unreachable = top_level.End ();
3057 } catch (Exception e) {
3058 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3061 if (rc.CurrentBlock != null) {
3062 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3064 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3067 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3071 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3072 if (rc.CurrentAnonymousMethod == null) {
3073 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3074 if (md is StateMachineMethod) {
3077 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3082 // If an asynchronous body of F is either an expression classified as nothing, or a
3083 // statement block where no return statements have expressions, the inferred return type is Task
3086 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3087 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3088 am.ReturnTypeInference = null;
3089 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3094 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3095 rc.CurrentAnonymousMethod.GetSignatureForError ());
3103 void ResolveMeta (BlockContext ec)
3105 int orig_count = parameters.Count;
3107 for (int i = 0; i < orig_count; ++i) {
3108 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3110 if ((mod & Parameter.Modifier.OUT) == 0)
3113 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3114 parameter_info[i].VariableInfo = vi;
3115 ec.FlowOffset += vi.Length;
3119 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3121 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3122 var stateMachine = new IteratorStorey (iterator);
3124 state_machine = stateMachine;
3125 iterator.SetStateMachine (stateMachine);
3127 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3128 tlb.Original = this;
3129 tlb.IsCompilerGenerated = true;
3130 tlb.state_machine = stateMachine;
3131 tlb.AddStatement (new Return (iterator, iterator.Location));
3135 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3137 for (int i = 0; i < parameters.Count; i++) {
3138 Parameter p = parameters[i];
3139 Parameter.Modifier mod = p.ModFlags;
3140 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3141 host.Compiler.Report.Error (1988, p.Location,
3142 "Async methods cannot have ref or out parameters");
3146 if (p is ArglistParameter) {
3147 host.Compiler.Report.Error (4006, p.Location,
3148 "__arglist is not allowed in parameter list of async methods");
3152 if (parameters.Types[i].IsPointer) {
3153 host.Compiler.Report.Error (4005, p.Location,
3154 "Async methods cannot have unsafe parameters");
3160 host.Compiler.Report.Warning (1998, 1, loc,
3161 "Async block lacks `await' operator and will run synchronously");
3164 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3165 var initializer = new AsyncInitializer (this, host, block_type);
3166 initializer.Type = block_type;
3167 initializer.DelegateType = delegateType;
3169 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3171 state_machine = stateMachine;
3172 initializer.SetStateMachine (stateMachine);
3174 var b = this is ToplevelBlock ?
3175 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3176 new ParametersBlock (Parent, parameters, Location.Null) {
3181 b.IsCompilerGenerated = true;
3182 b.state_machine = stateMachine;
3183 b.AddStatement (new StatementExpression (initializer));
3191 public class ToplevelBlock : ParametersBlock
3193 LocalVariable this_variable;
3194 CompilerContext compiler;
3195 Dictionary<string, object> names;
3196 Dictionary<string, object> labels;
3198 List<ExplicitBlock> this_references;
3200 public ToplevelBlock (CompilerContext ctx, Location loc)
3201 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3205 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3206 : base (parameters, start)
3208 this.compiler = ctx;
3210 flags |= Flags.HasRet;
3212 ProcessParameters ();
3216 // Recreates a top level block from parameters block. Used for
3217 // compiler generated methods where the original block comes from
3218 // explicit child block. This works for already resolved blocks
3219 // only to ensure we resolve them in the correct flow order
3221 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3222 : base (source, parameters)
3224 this.compiler = source.TopBlock.compiler;
3226 flags |= Flags.HasRet;
3229 public bool IsIterator {
3231 return (flags & Flags.Iterator) != 0;
3234 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3238 public Report Report {
3240 return compiler.Report;
3245 // Used by anonymous blocks to track references of `this' variable
3247 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3249 return this_references;
3254 // Returns the "this" instance variable of this block.
3255 // See AddThisVariable() for more information.
3257 public LocalVariable ThisVariable {
3259 return this_variable;
3263 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3266 names = new Dictionary<string, object> ();
3269 if (!names.TryGetValue (name, out value)) {
3270 names.Add (name, li);
3274 INamedBlockVariable existing = value as INamedBlockVariable;
3275 List<INamedBlockVariable> existing_list;
3276 if (existing != null) {
3277 existing_list = new List<INamedBlockVariable> ();
3278 existing_list.Add (existing);
3279 names[name] = existing_list;
3281 existing_list = (List<INamedBlockVariable>) value;
3285 // A collision checking between local names
3287 var variable_block = li.Block.Explicit;
3288 for (int i = 0; i < existing_list.Count; ++i) {
3289 existing = existing_list[i];
3290 Block b = existing.Block.Explicit;
3292 // Collision at same level
3293 if (variable_block == b) {
3294 li.Block.Error_AlreadyDeclared (name, li);
3298 // Collision with parent
3299 Block parent = variable_block;
3300 while ((parent = parent.Parent) != null) {
3302 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3303 i = existing_list.Count;
3308 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3309 // Collision with children
3310 while ((b = b.Parent) != null) {
3311 if (variable_block == b) {
3312 li.Block.Error_AlreadyDeclared (name, li, "child");
3313 i = existing_list.Count;
3320 existing_list.Add (li);
3323 public void AddLabel (string name, LabeledStatement label)
3326 labels = new Dictionary<string, object> ();
3329 if (!labels.TryGetValue (name, out value)) {
3330 labels.Add (name, label);
3334 LabeledStatement existing = value as LabeledStatement;
3335 List<LabeledStatement> existing_list;
3336 if (existing != null) {
3337 existing_list = new List<LabeledStatement> ();
3338 existing_list.Add (existing);
3339 labels[name] = existing_list;
3341 existing_list = (List<LabeledStatement>) value;
3345 // A collision checking between labels
3347 for (int i = 0; i < existing_list.Count; ++i) {
3348 existing = existing_list[i];
3349 Block b = existing.Block;
3351 // Collision at same level
3352 if (label.Block == b) {
3353 Report.SymbolRelatedToPreviousError (existing.loc, name);
3354 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3358 // Collision with parent
3360 while ((b = b.Parent) != null) {
3361 if (existing.Block == b) {
3362 Report.Error (158, label.loc,
3363 "The label `{0}' shadows another label by the same name in a contained scope", name);
3364 i = existing_list.Count;
3369 // Collision with with children
3371 while ((b = b.Parent) != null) {
3372 if (label.Block == b) {
3373 Report.Error (158, label.loc,
3374 "The label `{0}' shadows another label by the same name in a contained scope", name);
3375 i = existing_list.Count;
3381 existing_list.Add (label);
3384 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3386 if (this_references == null)
3387 this_references = new List<ExplicitBlock> ();
3389 if (!this_references.Contains (block))
3390 this_references.Add (block);
3393 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3395 this_references.Remove (block);
3399 // Creates an arguments set from all parameters, useful for method proxy calls
3401 public Arguments GetAllParametersArguments ()
3403 int count = parameters.Count;
3404 Arguments args = new Arguments (count);
3405 for (int i = 0; i < count; ++i) {
3406 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3407 args.Add (new Argument (arg_expr));
3414 // Lookup inside a block, the returned value can represent 3 states
3416 // true+variable: A local name was found and it's valid
3417 // false+variable: A local name was found in a child block only
3418 // false+null: No local name was found
3420 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3426 if (!names.TryGetValue (name, out value))
3429 variable = value as INamedBlockVariable;
3431 if (variable != null) {
3433 if (variable.Block == b.Original)
3437 } while (b != null);
3445 } while (b != null);
3447 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3448 for (int i = 0; i < list.Count; ++i) {
3451 if (variable.Block == b.Original)
3455 } while (b != null);
3463 } while (b != null);
3473 public LabeledStatement GetLabel (string name, Block block)
3479 if (!labels.TryGetValue (name, out value)) {
3483 var label = value as LabeledStatement;
3485 if (label != null) {
3486 if (label.Block == b.Original)
3489 List<LabeledStatement> list = (List<LabeledStatement>) value;
3490 for (int i = 0; i < list.Count; ++i) {
3492 if (label.Block == b.Original)
3501 // This is used by non-static `struct' constructors which do not have an
3502 // initializer - in this case, the constructor must initialize all of the
3503 // struct's fields. To do this, we add a "this" variable and use the flow
3504 // analysis code to ensure that it's been fully initialized before control
3505 // leaves the constructor.
3507 public void AddThisVariable (BlockContext bc)
3509 if (this_variable != null)
3510 throw new InternalErrorException (StartLocation.ToString ());
3512 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3513 this_variable.Type = bc.CurrentType;
3514 this_variable.PrepareForFlowAnalysis (bc);
3517 public bool IsThisAssigned (BlockContext ec)
3519 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3522 public override void Emit (EmitContext ec)
3524 if (Report.Errors > 0)
3528 if (IsCompilerGenerated) {
3529 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3537 // If `HasReturnLabel' is set, then we already emitted a
3538 // jump to the end of the method, so we must emit a `ret'
3541 // Unfortunately, System.Reflection.Emit automatically emits
3542 // a leave to the end of a finally block. This is a problem
3543 // if no code is following the try/finally block since we may
3544 // jump to a point after the end of the method.
3545 // As a workaround, we're always creating a return label in
3548 if (ec.HasReturnLabel || !unreachable) {
3549 if (ec.HasReturnLabel)
3550 ec.MarkLabel (ec.ReturnLabel);
3552 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3553 ec.Mark (EndLocation);
3555 if (ec.ReturnType.Kind != MemberKind.Void)
3556 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3558 ec.Emit (OpCodes.Ret);
3561 } catch (Exception e) {
3562 throw new InternalErrorException (e, StartLocation);
3567 public class SwitchLabel : Statement
3575 // if expr == null, then it is the default case.
3577 public SwitchLabel (Expression expr, Location l)
3583 public bool IsDefault {
3585 return label == null;
3589 public Expression Label {
3595 public Location Location {
3601 public Constant Converted {
3610 public bool SectionStart { get; set; }
3612 public Label GetILLabel (EmitContext ec)
3614 if (il_label == null){
3615 il_label = ec.DefineLabel ();
3618 return il_label.Value;
3621 protected override void DoEmit (EmitContext ec)
3623 ec.MarkLabel (GetILLabel (ec));
3626 public override bool Resolve (BlockContext bc)
3628 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3630 return base.Resolve (bc);
3634 // Resolves the expression, reduces it to a literal if possible
3635 // and then converts it to the requested type.
3637 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3639 Expression e = label.Resolve (ec);
3644 Constant c = e as Constant;
3646 ec.Report.Error (150, loc, "A constant value is expected");
3650 if (allow_nullable && c is NullLiteral) {
3655 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3656 return converted != null;
3659 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3662 if (converted == null)
3665 label = converted.GetValueAsLiteral ();
3667 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3668 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3671 protected override void CloneTo (CloneContext clonectx, Statement target)
3673 var t = (SwitchLabel) target;
3675 t.label = label.Clone (clonectx);
3678 public override object Accept (StructuralVisitor visitor)
3680 return visitor.Visit (this);
3684 public class Switch : Statement
3686 // structure used to hold blocks of keys while calculating table switch
3687 sealed class LabelsRange : IComparable<LabelsRange>
3689 public readonly long min;
3691 public readonly List<long> label_values;
3693 public LabelsRange (long value)
3696 label_values = new List<long> ();
3697 label_values.Add (value);
3700 public LabelsRange (long min, long max, ICollection<long> values)
3704 this.label_values = new List<long> (values);
3709 return max - min + 1;
3713 public bool AddValue (long value)
3715 var gap = value - min + 1;
3716 // Ensure the range has > 50% occupancy
3717 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3721 label_values.Add (value);
3725 public int CompareTo (LabelsRange other)
3727 int nLength = label_values.Count;
3728 int nLengthOther = other.label_values.Count;
3729 if (nLengthOther == nLength)
3730 return (int) (other.min - min);
3732 return nLength - nLengthOther;
3736 sealed class DispatchStatement : Statement
3738 readonly Switch body;
3740 public DispatchStatement (Switch body)
3745 protected override void CloneTo (CloneContext clonectx, Statement target)
3747 throw new NotImplementedException ();
3750 protected override void DoEmit (EmitContext ec)
3752 body.EmitDispatch (ec);
3756 public Expression Expr;
3759 // Mapping of all labels to their SwitchLabels
3761 Dictionary<long, SwitchLabel> labels;
3762 Dictionary<string, SwitchLabel> string_labels;
3763 List<SwitchLabel> case_labels;
3766 /// The governing switch type
3768 public TypeSpec SwitchType;
3770 Expression new_expr;
3772 SwitchLabel case_null;
3773 SwitchLabel case_default;
3775 Label defaultLabel, nullLabel;
3776 VariableReference value;
3777 ExpressionStatement string_dictionary;
3778 FieldExpr switch_cache_field;
3779 ExplicitBlock block;
3782 // Nullable Types support
3784 Nullable.Unwrap unwrap;
3786 public Switch (Expression e, ExplicitBlock block, Location l)
3793 public ExplicitBlock Block {
3799 public SwitchLabel DefaultLabel {
3801 return case_default;
3805 public bool IsNullable {
3807 return unwrap != null;
3812 // Determines the governing type for a switch. The returned
3813 // expression might be the expression from the switch, or an
3814 // expression that includes any potential conversions to
3816 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3818 switch (expr.Type.BuiltinType) {
3819 case BuiltinTypeSpec.Type.Byte:
3820 case BuiltinTypeSpec.Type.SByte:
3821 case BuiltinTypeSpec.Type.UShort:
3822 case BuiltinTypeSpec.Type.Short:
3823 case BuiltinTypeSpec.Type.UInt:
3824 case BuiltinTypeSpec.Type.Int:
3825 case BuiltinTypeSpec.Type.ULong:
3826 case BuiltinTypeSpec.Type.Long:
3827 case BuiltinTypeSpec.Type.Char:
3828 case BuiltinTypeSpec.Type.String:
3829 case BuiltinTypeSpec.Type.Bool:
3833 if (expr.Type.IsEnum)
3837 // Try to find a *user* defined implicit conversion.
3839 // If there is no implicit conversion, or if there are multiple
3840 // conversions, we have to report an error
3842 Expression converted = null;
3843 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3846 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3851 // Ignore over-worked ImplicitUserConversions that do
3852 // an implicit conversion in addition to the user conversion.
3854 if (!(e is UserCast))
3857 if (converted != null){
3858 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3867 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3869 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3885 // Performs the basic sanity checks on the switch statement
3886 // (looks for duplicate keys and non-constant expressions).
3888 // It also returns a hashtable with the keys that we will later
3889 // use to compute the switch tables
3891 bool ResolveLabels (ResolveContext ec, Constant value)
3894 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3895 string_labels = new Dictionary<string, SwitchLabel> ();
3897 labels = new Dictionary<long, SwitchLabel> ();
3900 case_labels = new List<SwitchLabel> ();
3901 int default_label_index = -1;
3902 bool constant_label_found = false;
3904 for (int i = 0; i < block.Statements.Count; ++i) {
3905 var s = block.Statements[i];
3907 var sl = s as SwitchLabel;
3912 case_labels.Add (sl);
3915 if (case_default != null) {
3916 sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3920 default_label_index = i;
3925 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3931 if (string_labels != null) {
3932 string string_value = sl.Converted.GetValue () as string;
3933 if (string_value == null)
3936 string_labels.Add (string_value, sl);
3938 if (sl.Converted is NullLiteral) {
3941 labels.Add (sl.Converted.GetValueAsLong (), sl);
3944 } catch (ArgumentException) {
3945 if (string_labels != null)
3946 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3948 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3953 if (value != null) {
3954 var constant_label = constant_label_found ? null : FindLabel (value);
3955 if (constant_label == null || constant_label != sl)
3956 block.Statements[i] = new EmptyStatement (s.loc);
3958 constant_label_found = true;
3962 if (value != null && constant_label_found && default_label_index >= 0)
3963 block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3969 // This method emits code for a lookup-based switch statement (non-string)
3970 // Basically it groups the cases into blocks that are at least half full,
3971 // and then spits out individual lookup opcodes for each block.
3972 // It emits the longest blocks first, and short blocks are just
3973 // handled with direct compares.
3975 void EmitTableSwitch (EmitContext ec, Expression val)
3977 if (labels != null && labels.Count > 0) {
3978 List<LabelsRange> ranges;
3979 if (string_labels != null) {
3980 // We have done all hard work for string already
3981 // setup single range only
3982 ranges = new List<LabelsRange> (1);
3983 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3985 var element_keys = new long[labels.Count];
3986 labels.Keys.CopyTo (element_keys, 0);
3987 Array.Sort (element_keys);
3990 // Build possible ranges of switch labes to reduce number
3993 ranges = new List<LabelsRange> (element_keys.Length);
3994 var range = new LabelsRange (element_keys[0]);
3996 for (int i = 1; i < element_keys.Length; ++i) {
3997 var l = element_keys[i];
3998 if (range.AddValue (l))
4001 range = new LabelsRange (l);
4005 // sort the blocks so we can tackle the largest ones first
4009 Label lbl_default = defaultLabel;
4010 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4012 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4013 LabelsRange kb = ranges[range_index];
4014 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4016 // Optimize small ranges using simple equality check
4017 if (kb.Range <= 2) {
4018 foreach (var key in kb.label_values) {
4019 SwitchLabel sl = labels[key];
4020 if (sl == case_default || sl == case_null)
4023 if (sl.Converted.IsZeroInteger) {
4024 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4027 sl.Converted.Emit (ec);
4028 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4032 // TODO: if all the keys in the block are the same and there are
4033 // no gaps/defaults then just use a range-check.
4034 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4035 // TODO: optimize constant/I4 cases
4037 // check block range (could be > 2^31)
4039 ec.EmitLong (kb.min);
4040 ec.Emit (OpCodes.Blt, lbl_default);
4043 ec.EmitLong (kb.max);
4044 ec.Emit (OpCodes.Bgt, lbl_default);
4049 ec.EmitLong (kb.min);
4050 ec.Emit (OpCodes.Sub);
4053 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4057 int first = (int) kb.min;
4060 ec.Emit (OpCodes.Sub);
4061 } else if (first < 0) {
4062 ec.EmitInt (-first);
4063 ec.Emit (OpCodes.Add);
4067 // first, build the list of labels for the switch
4069 long cJumps = kb.Range;
4070 Label[] switch_labels = new Label[cJumps];
4071 for (int iJump = 0; iJump < cJumps; iJump++) {
4072 var key = kb.label_values[iKey];
4073 if (key == kb.min + iJump) {
4074 switch_labels[iJump] = labels[key].GetILLabel (ec);
4077 switch_labels[iJump] = lbl_default;
4081 // emit the switch opcode
4082 ec.Emit (OpCodes.Switch, switch_labels);
4085 // mark the default for this block
4086 if (range_index != 0)
4087 ec.MarkLabel (lbl_default);
4090 // the last default just goes to the end
4091 if (ranges.Count > 0)
4092 ec.Emit (OpCodes.Br, lbl_default);
4096 SwitchLabel FindLabel (Constant value)
4098 SwitchLabel sl = null;
4100 if (string_labels != null) {
4101 string s = value.GetValue () as string;
4103 if (case_null != null)
4105 else if (case_default != null)
4108 string_labels.TryGetValue (s, out sl);
4111 if (value is NullLiteral) {
4114 labels.TryGetValue (value.GetValueAsLong (), out sl);
4121 public override bool Resolve (BlockContext ec)
4123 Expr = Expr.Resolve (ec);
4127 new_expr = SwitchGoverningType (ec, Expr);
4129 if (new_expr == null && Expr.Type.IsNullableType) {
4130 unwrap = Nullable.Unwrap.Create (Expr, false);
4134 new_expr = SwitchGoverningType (ec, unwrap);
4137 if (new_expr == null) {
4138 if (Expr.Type != InternalType.ErrorType) {
4139 ec.Report.Error (151, loc,
4140 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4141 Expr.Type.GetSignatureForError ());
4148 SwitchType = new_expr.Type;
4150 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4151 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4155 if (block.Statements.Count == 0)
4158 var constant = new_expr as Constant;
4160 if (!ResolveLabels (ec, constant))
4164 // Don't need extra variable for constant switch or switch with
4165 // only default case
4167 if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4169 // Store switch expression for comparison purposes
4171 value = new_expr as VariableReference;
4172 if (value == null) {
4173 // Create temporary variable inside switch scope
4174 var current_block = ec.CurrentBlock;
4175 ec.CurrentBlock = Block;
4176 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4178 ec.CurrentBlock = current_block;
4182 Switch old_switch = ec.Switch;
4184 ec.Switch.SwitchType = SwitchType;
4186 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4188 ec.CurrentBranching.CurrentUsageVector.Goto ();
4190 var ok = block.Resolve (ec);
4192 if (case_default == null)
4193 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4195 ec.EndFlowBranching ();
4196 ec.Switch = old_switch;
4201 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4202 ResolveStringSwitchMap (ec);
4206 // Needed to emit anonymous storey initialization before
4207 // any generated switch dispatch
4209 block.AddScopeStatement (new DispatchStatement (this));
4214 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4216 var sl = FindLabel (value);
4219 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4226 // Converts string switch into string hashtable
4228 void ResolveStringSwitchMap (ResolveContext ec)
4230 FullNamedExpression string_dictionary_type;
4231 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4232 string_dictionary_type = new TypeExpression (
4233 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4234 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4236 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4237 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4239 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4243 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4244 Field field = new Field (ctype, string_dictionary_type,
4245 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4246 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4247 if (!field.Define ())
4249 ctype.AddField (field);
4251 var init = new List<Expression> ();
4253 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4254 string value = null;
4256 foreach (SwitchLabel sl in case_labels) {
4258 if (sl.SectionStart)
4259 labels.Add (++counter, sl);
4261 if (sl == case_default || sl == case_null)
4264 value = (string) sl.Converted.GetValue ();
4265 var init_args = new List<Expression> (2);
4266 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4268 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4269 init_args.Add (sl.Converted);
4271 init.Add (new CollectionElementInitializer (init_args, loc));
4274 Arguments args = new Arguments (1);
4275 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4276 Expression initializer = new NewInitialize (string_dictionary_type, args,
4277 new CollectionOrObjectInitializers (init, loc), loc);
4279 switch_cache_field = new FieldExpr (field, loc);
4280 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4283 void DoEmitStringSwitch (EmitContext ec)
4285 Label l_initialized = ec.DefineLabel ();
4288 // Skip initialization when value is null
4290 value.EmitBranchable (ec, nullLabel, false);
4293 // Check if string dictionary is initialized and initialize
4295 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4296 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4297 string_dictionary.EmitStatement (ec);
4299 ec.MarkLabel (l_initialized);
4301 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4303 ResolveContext rc = new ResolveContext (ec.MemberContext);
4305 if (switch_cache_field.Type.IsGeneric) {
4306 Arguments get_value_args = new Arguments (2);
4307 get_value_args.Add (new Argument (value));
4308 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4309 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4310 if (get_item == null)
4314 // A value was not found, go to default case
4316 get_item.EmitBranchable (ec, defaultLabel, false);
4318 Arguments get_value_args = new Arguments (1);
4319 get_value_args.Add (new Argument (value));
4321 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4322 if (get_item == null)
4325 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4326 get_item_object.EmitAssign (ec, get_item, true, false);
4327 ec.Emit (OpCodes.Brfalse, defaultLabel);
4329 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4330 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4332 get_item_int.EmitStatement (ec);
4333 get_item_object.Release (ec);
4336 EmitTableSwitch (ec, string_switch_variable);
4337 string_switch_variable.Release (ec);
4341 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4343 void EmitShortSwitch (EmitContext ec)
4345 MethodSpec equal_method = null;
4346 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4347 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4350 if (equal_method != null) {
4351 value.EmitBranchable (ec, nullLabel, false);
4354 for (int i = 0; i < case_labels.Count; ++i) {
4355 var label = case_labels [i];
4356 if (label == case_default || label == case_null)
4359 var constant = label.Converted;
4361 if (equal_method != null) {
4365 var call = new CallEmitter ();
4366 call.EmitPredefined (ec, equal_method, new Arguments (0));
4367 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4371 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4372 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4378 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4381 ec.Emit (OpCodes.Br, defaultLabel);
4384 void EmitDispatch (EmitContext ec)
4386 if (value == null) {
4388 // Constant switch, we already done the work
4394 // Mark sequence point explicitly to switch
4396 ec.Mark (block.StartLocation);
4397 block.IsCompilerGenerated = true;
4399 if (string_dictionary != null) {
4400 DoEmitStringSwitch (ec);
4401 } else if (case_labels.Count < 4 || string_labels != null) {
4402 EmitShortSwitch (ec);
4404 EmitTableSwitch (ec, value);
4408 protected override void DoEmit (EmitContext ec)
4410 // Workaround broken flow-analysis
4411 block.HasUnreachableClosingBrace = true;
4414 // Setup the codegen context
4416 Label old_end = ec.LoopEnd;
4417 Switch old_switch = ec.Switch;
4419 ec.LoopEnd = ec.DefineLabel ();
4422 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4423 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4425 if (value != null) {
4428 unwrap.EmitCheck (ec);
4429 ec.Emit (OpCodes.Brfalse, nullLabel);
4430 value.EmitAssign (ec, new_expr, false, false);
4431 } else if (new_expr != value) {
4432 value.EmitAssign (ec, new_expr, false, false);
4438 // Restore context state.
4439 ec.MarkLabel (ec.LoopEnd);
4442 // Restore the previous context
4444 ec.LoopEnd = old_end;
4445 ec.Switch = old_switch;
4448 protected override void CloneTo (CloneContext clonectx, Statement t)
4450 Switch target = (Switch) t;
4452 target.Expr = Expr.Clone (clonectx);
4453 target.block = (ExplicitBlock) block.Clone (clonectx);
4456 public override object Accept (StructuralVisitor visitor)
4458 return visitor.Visit (this);
4462 // A place where execution can restart in an iterator
4463 public abstract class ResumableStatement : Statement
4466 protected Label resume_point;
4468 public Label PrepareForEmit (EmitContext ec)
4472 resume_point = ec.DefineLabel ();
4474 return resume_point;
4477 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4482 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4487 public abstract class TryFinallyBlock : ExceptionStatement
4489 protected Statement stmt;
4490 Label dispose_try_block;
4491 bool prepared_for_dispose, emitted_dispose;
4492 Method finally_host;
4494 protected TryFinallyBlock (Statement stmt, Location loc)
4502 public Statement Statement {
4510 protected abstract void EmitTryBody (EmitContext ec);
4511 public abstract void EmitFinallyBody (EmitContext ec);
4513 public override Label PrepareForDispose (EmitContext ec, Label end)
4515 if (!prepared_for_dispose) {
4516 prepared_for_dispose = true;
4517 dispose_try_block = ec.DefineLabel ();
4519 return dispose_try_block;
4522 protected sealed override void DoEmit (EmitContext ec)
4524 EmitTryBodyPrepare (ec);
4527 ec.BeginFinallyBlock ();
4529 Label start_finally = ec.DefineLabel ();
4530 if (resume_points != null) {
4531 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4533 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4534 ec.Emit (OpCodes.Brfalse_S, start_finally);
4535 ec.Emit (OpCodes.Endfinally);
4538 ec.MarkLabel (start_finally);
4540 if (finally_host != null) {
4541 finally_host.Define ();
4542 finally_host.PrepareEmit ();
4543 finally_host.Emit ();
4545 // Now it's safe to add, to close it properly and emit sequence points
4546 finally_host.Parent.AddMember (finally_host);
4548 var ce = new CallEmitter ();
4549 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4550 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4552 EmitFinallyBody (ec);
4555 ec.EndExceptionBlock ();
4558 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4560 if (emitted_dispose)
4563 emitted_dispose = true;
4565 Label end_of_try = ec.DefineLabel ();
4567 // Ensure that the only way we can get into this code is through a dispatcher
4568 if (have_dispatcher)
4569 ec.Emit (OpCodes.Br, end);
4571 ec.BeginExceptionBlock ();
4573 ec.MarkLabel (dispose_try_block);
4575 Label[] labels = null;
4576 for (int i = 0; i < resume_points.Count; ++i) {
4577 ResumableStatement s = resume_points[i];
4578 Label ret = s.PrepareForDispose (ec, end_of_try);
4579 if (ret.Equals (end_of_try) && labels == null)
4581 if (labels == null) {
4582 labels = new Label[resume_points.Count];
4583 for (int j = 0; j < i; ++j)
4584 labels[j] = end_of_try;
4589 if (labels != null) {
4591 for (j = 1; j < labels.Length; ++j)
4592 if (!labels[0].Equals (labels[j]))
4594 bool emit_dispatcher = j < labels.Length;
4596 if (emit_dispatcher) {
4597 ec.Emit (OpCodes.Ldloc, pc);
4598 ec.EmitInt (first_resume_pc);
4599 ec.Emit (OpCodes.Sub);
4600 ec.Emit (OpCodes.Switch, labels);
4603 foreach (ResumableStatement s in resume_points)
4604 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4607 ec.MarkLabel (end_of_try);
4609 ec.BeginFinallyBlock ();
4611 if (finally_host != null) {
4612 var ce = new CallEmitter ();
4613 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4614 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4616 EmitFinallyBody (ec);
4619 ec.EndExceptionBlock ();
4622 public override bool Resolve (BlockContext bc)
4625 // Finally block inside iterator is called from MoveNext and
4626 // Dispose methods that means we need to lift the block into
4627 // newly created host method to emit the body only once. The
4628 // original block then simply calls the newly generated method.
4630 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4631 var b = stmt as Block;
4632 if (b != null && b.Explicit.HasYield) {
4633 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4637 return base.Resolve (bc);
4642 // Base class for blocks using exception handling
4644 public abstract class ExceptionStatement : ResumableStatement
4649 protected List<ResumableStatement> resume_points;
4650 protected int first_resume_pc;
4652 protected ExceptionStatement (Location loc)
4657 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4659 StateMachineInitializer state_machine = null;
4660 if (resume_points != null) {
4661 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4663 ec.EmitInt ((int) IteratorStorey.State.Running);
4664 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4667 ec.BeginExceptionBlock ();
4669 if (resume_points != null) {
4670 ec.MarkLabel (resume_point);
4672 // For normal control flow, we want to fall-through the Switch
4673 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4674 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4675 ec.EmitInt (first_resume_pc);
4676 ec.Emit (OpCodes.Sub);
4678 Label[] labels = new Label[resume_points.Count];
4679 for (int i = 0; i < resume_points.Count; ++i)
4680 labels[i] = resume_points[i].PrepareForEmit (ec);
4681 ec.Emit (OpCodes.Switch, labels);
4685 public void SomeCodeFollows ()
4688 code_follows = true;
4692 public override bool Resolve (BlockContext ec)
4695 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4696 // So, ensure there's some IL code after this statement.
4697 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4698 ec.NeedReturnLabel ();
4703 public void AddResumePoint (ResumableStatement stmt, int pc)
4705 if (resume_points == null) {
4706 resume_points = new List<ResumableStatement> ();
4707 first_resume_pc = pc;
4710 if (pc != first_resume_pc + resume_points.Count)
4711 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4713 resume_points.Add (stmt);
4718 public class Lock : TryFinallyBlock
4721 TemporaryVariableReference expr_copy;
4722 TemporaryVariableReference lock_taken;
4724 public Lock (Expression expr, Statement stmt, Location loc)
4730 public Expression Expr {
4736 public override bool Resolve (BlockContext ec)
4738 expr = expr.Resolve (ec);
4742 if (!TypeSpec.IsReferenceType (expr.Type)) {
4743 ec.Report.Error (185, loc,
4744 "`{0}' is not a reference type as required by the lock statement",
4745 expr.Type.GetSignatureForError ());
4748 if (expr.Type.IsGenericParameter) {
4749 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4752 VariableReference lv = expr as VariableReference;
4755 locked = lv.IsLockedByStatement;
4756 lv.IsLockedByStatement = true;
4763 // Have to keep original lock value around to unlock same location
4764 // in the case of original value has changed or is null
4766 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4767 expr_copy.Resolve (ec);
4770 // Ensure Monitor methods are available
4772 if (ResolvePredefinedMethods (ec) > 1) {
4773 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4774 lock_taken.Resolve (ec);
4777 using (ec.Set (ResolveContext.Options.LockScope)) {
4778 ec.StartFlowBranching (this);
4779 Statement.Resolve (ec);
4780 ec.EndFlowBranching ();
4784 lv.IsLockedByStatement = locked;
4792 protected override void EmitTryBodyPrepare (EmitContext ec)
4794 expr_copy.EmitAssign (ec, expr);
4796 if (lock_taken != null) {
4798 // Initialize ref variable
4800 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4803 // Monitor.Enter (expr_copy)
4805 expr_copy.Emit (ec);
4806 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4809 base.EmitTryBodyPrepare (ec);
4812 protected override void EmitTryBody (EmitContext ec)
4815 // Monitor.Enter (expr_copy, ref lock_taken)
4817 if (lock_taken != null) {
4818 expr_copy.Emit (ec);
4819 lock_taken.LocalInfo.CreateBuilder (ec);
4820 lock_taken.AddressOf (ec, AddressOp.Load);
4821 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4824 Statement.Emit (ec);
4827 public override void EmitFinallyBody (EmitContext ec)
4830 // if (lock_taken) Monitor.Exit (expr_copy)
4832 Label skip = ec.DefineLabel ();
4834 if (lock_taken != null) {
4835 lock_taken.Emit (ec);
4836 ec.Emit (OpCodes.Brfalse_S, skip);
4839 expr_copy.Emit (ec);
4840 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4842 ec.Emit (OpCodes.Call, m);
4844 ec.MarkLabel (skip);
4847 int ResolvePredefinedMethods (ResolveContext rc)
4849 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4850 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4854 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4858 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4862 protected override void CloneTo (CloneContext clonectx, Statement t)
4864 Lock target = (Lock) t;
4866 target.expr = expr.Clone (clonectx);
4867 target.stmt = Statement.Clone (clonectx);
4870 public override object Accept (StructuralVisitor visitor)
4872 return visitor.Visit (this);
4877 public class Unchecked : Statement {
4880 public Unchecked (Block b, Location loc)
4887 public override bool Resolve (BlockContext ec)
4889 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4890 return Block.Resolve (ec);
4893 protected override void DoEmit (EmitContext ec)
4895 using (ec.With (EmitContext.Options.CheckedScope, false))
4899 protected override void CloneTo (CloneContext clonectx, Statement t)
4901 Unchecked target = (Unchecked) t;
4903 target.Block = clonectx.LookupBlock (Block);
4906 public override object Accept (StructuralVisitor visitor)
4908 return visitor.Visit (this);
4912 public class Checked : Statement {
4915 public Checked (Block b, Location loc)
4918 b.Unchecked = false;
4922 public override bool Resolve (BlockContext ec)
4924 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4925 return Block.Resolve (ec);
4928 protected override void DoEmit (EmitContext ec)
4930 using (ec.With (EmitContext.Options.CheckedScope, true))
4934 protected override void CloneTo (CloneContext clonectx, Statement t)
4936 Checked target = (Checked) t;
4938 target.Block = clonectx.LookupBlock (Block);
4941 public override object Accept (StructuralVisitor visitor)
4943 return visitor.Visit (this);
4947 public class Unsafe : Statement {
4950 public Unsafe (Block b, Location loc)
4953 Block.Unsafe = true;
4957 public override bool Resolve (BlockContext ec)
4959 if (ec.CurrentIterator != null)
4960 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4962 using (ec.Set (ResolveContext.Options.UnsafeScope))
4963 return Block.Resolve (ec);
4966 protected override void DoEmit (EmitContext ec)
4971 protected override void CloneTo (CloneContext clonectx, Statement t)
4973 Unsafe target = (Unsafe) t;
4975 target.Block = clonectx.LookupBlock (Block);
4978 public override object Accept (StructuralVisitor visitor)
4980 return visitor.Visit (this);
4987 public class Fixed : Statement
4989 abstract class Emitter : ShimExpression
4991 protected LocalVariable vi;
4993 protected Emitter (Expression expr, LocalVariable li)
4999 public abstract void EmitExit (EmitContext ec);
5002 class ExpressionEmitter : Emitter {
5003 public ExpressionEmitter (Expression converted, LocalVariable li) :
5004 base (converted, li)
5008 protected override Expression DoResolve (ResolveContext rc)
5010 throw new NotImplementedException ();
5013 public override void Emit (EmitContext ec) {
5015 // Store pointer in pinned location
5021 public override void EmitExit (EmitContext ec)
5024 ec.Emit (OpCodes.Conv_U);
5029 class StringEmitter : Emitter
5031 LocalVariable pinned_string;
5033 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5038 protected override Expression DoResolve (ResolveContext rc)
5040 pinned_string = new LocalVariable (vi.Block, "$pinned",
5041 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5043 pinned_string.Type = rc.BuiltinTypes.String;
5045 eclass = ExprClass.Variable;
5046 type = rc.BuiltinTypes.Int;
5050 public override void Emit (EmitContext ec)
5052 pinned_string.CreateBuilder (ec);
5055 pinned_string.EmitAssign (ec);
5057 // TODO: Should use Binary::Add
5058 pinned_string.Emit (ec);
5059 ec.Emit (OpCodes.Conv_I);
5061 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5065 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5066 //pe.InstanceExpression = pinned_string;
5067 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5069 ec.Emit (OpCodes.Add);
5073 public override void EmitExit (EmitContext ec)
5076 pinned_string.EmitAssign (ec);
5080 public class VariableDeclaration : BlockVariableDeclaration
5082 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5087 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5089 if (!Variable.Type.IsPointer && li == Variable) {
5090 bc.Report.Error (209, TypeExpression.Location,
5091 "The type of locals declared in a fixed statement must be a pointer type");
5096 // The rules for the possible declarators are pretty wise,
5097 // but the production on the grammar is more concise.
5099 // So we have to enforce these rules here.
5101 // We do not resolve before doing the case 1 test,
5102 // because the grammar is explicit in that the token &
5103 // is present, so we need to test for this particular case.
5106 if (initializer is Cast) {
5107 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5111 initializer = initializer.Resolve (bc);
5113 if (initializer == null)
5119 if (initializer.Type.IsArray) {
5120 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5123 // Provided that array_type is unmanaged,
5125 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5129 // and T* is implicitly convertible to the
5130 // pointer type given in the fixed statement.
5132 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5134 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5135 if (converted == null)
5139 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5141 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5142 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5143 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5144 new NullLiteral (loc),
5147 converted = converted.Resolve (bc);
5149 return new ExpressionEmitter (converted, li);
5155 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5156 return new StringEmitter (initializer, li, loc).Resolve (bc);
5159 // Case 3: fixed buffer
5160 if (initializer is FixedBufferPtr) {
5161 return new ExpressionEmitter (initializer, li);
5165 // Case 4: & object.
5167 bool already_fixed = true;
5168 Unary u = initializer as Unary;
5169 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5170 IVariableReference vr = u.Expr as IVariableReference;
5171 if (vr == null || !vr.IsFixed) {
5172 already_fixed = false;
5176 if (already_fixed) {
5177 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5180 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5181 return new ExpressionEmitter (initializer, li);
5186 VariableDeclaration decl;
5187 Statement statement;
5190 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5199 public Statement Statement {
5205 public BlockVariableDeclaration Variables {
5213 public override bool Resolve (BlockContext ec)
5215 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5216 if (!decl.Resolve (ec))
5220 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5221 bool ok = statement.Resolve (ec);
5222 bool flow_unreachable = ec.EndFlowBranching ();
5223 has_ret = flow_unreachable;
5228 protected override void DoEmit (EmitContext ec)
5230 decl.Variable.CreateBuilder (ec);
5231 decl.Initializer.Emit (ec);
5232 if (decl.Declarators != null) {
5233 foreach (var d in decl.Declarators) {
5234 d.Variable.CreateBuilder (ec);
5235 d.Initializer.Emit (ec);
5239 statement.Emit (ec);
5245 // Clear the pinned variable
5247 ((Emitter) decl.Initializer).EmitExit (ec);
5248 if (decl.Declarators != null) {
5249 foreach (var d in decl.Declarators) {
5250 ((Emitter)d.Initializer).EmitExit (ec);
5255 protected override void CloneTo (CloneContext clonectx, Statement t)
5257 Fixed target = (Fixed) t;
5259 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5260 target.statement = statement.Clone (clonectx);
5263 public override object Accept (StructuralVisitor visitor)
5265 return visitor.Visit (this);
5269 public class Catch : Statement
5273 FullNamedExpression type_expr;
5274 CompilerAssign assign;
5277 public Catch (Block block, Location loc)
5285 public Block Block {
5291 public TypeSpec CatchType {
5297 public bool IsGeneral {
5299 return type_expr == null;
5303 public FullNamedExpression TypeExpression {
5312 public LocalVariable Variable {
5323 protected override void DoEmit (EmitContext ec)
5326 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5328 ec.BeginCatchBlock (CatchType);
5331 li.CreateBuilder (ec);
5334 // Special case hoisted catch variable, we have to use a temporary variable
5335 // to pass via anonymous storey initialization with the value still on top
5338 if (li.HoistedVariant != null) {
5339 LocalTemporary lt = new LocalTemporary (li.Type);
5342 // switch to assigning from the temporary variable and not from top of the stack
5343 assign.UpdateSource (lt);
5346 ec.Emit (OpCodes.Pop);
5352 public override bool Resolve (BlockContext ec)
5354 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5355 if (type_expr != null) {
5356 type = type_expr.ResolveAsType (ec);
5360 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5361 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5362 } else if (li != null) {
5364 li.PrepareForFlowAnalysis (ec);
5366 // source variable is at the top of the stack
5367 Expression source = new EmptyExpression (li.Type);
5368 if (li.Type.IsGenericParameter)
5369 source = new UnboxCast (source, li.Type);
5372 // Uses Location.Null to hide from symbol file
5374 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5375 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5379 return Block.Resolve (ec);
5383 protected override void CloneTo (CloneContext clonectx, Statement t)
5385 Catch target = (Catch) t;
5387 if (type_expr != null)
5388 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5390 target.block = clonectx.LookupBlock (block);
5394 public class TryFinally : TryFinallyBlock
5398 public TryFinally (Statement stmt, Block fini, Location loc)
5404 public Block Finallyblock {
5410 public override bool Resolve (BlockContext ec)
5414 ec.StartFlowBranching (this);
5416 if (!stmt.Resolve (ec))
5420 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5422 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5423 if (!fini.Resolve (ec))
5427 ec.EndFlowBranching ();
5429 ok &= base.Resolve (ec);
5434 protected override void EmitTryBody (EmitContext ec)
5439 public override void EmitFinallyBody (EmitContext ec)
5444 protected override void CloneTo (CloneContext clonectx, Statement t)
5446 TryFinally target = (TryFinally) t;
5448 target.stmt = (Statement) stmt.Clone (clonectx);
5450 target.fini = clonectx.LookupBlock (fini);
5453 public override object Accept (StructuralVisitor visitor)
5455 return visitor.Visit (this);
5459 public class TryCatch : ExceptionStatement
5462 List<Catch> clauses;
5463 readonly bool inside_try_finally;
5465 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5469 this.clauses = catch_clauses;
5470 this.inside_try_finally = inside_try_finally;
5473 public List<Catch> Clauses {
5479 public bool IsTryCatchFinally {
5481 return inside_try_finally;
5485 public override bool Resolve (BlockContext ec)
5489 ec.StartFlowBranching (this);
5491 if (!Block.Resolve (ec))
5494 for (int i = 0; i < clauses.Count; ++i) {
5496 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5498 if (!c.Resolve (ec)) {
5503 TypeSpec resolved_type = c.CatchType;
5504 for (int ii = 0; ii < clauses.Count; ++ii) {
5508 if (clauses[ii].IsGeneral) {
5509 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5512 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5515 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5518 ec.Report.Warning (1058, 1, c.loc,
5519 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5527 var ct = clauses[ii].CatchType;
5531 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5532 ec.Report.Error (160, c.loc,
5533 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5534 ct.GetSignatureForError ());
5540 ec.EndFlowBranching ();
5542 return base.Resolve (ec) && ok;
5545 protected sealed override void DoEmit (EmitContext ec)
5547 if (!inside_try_finally)
5548 EmitTryBodyPrepare (ec);
5552 foreach (Catch c in clauses)
5555 if (!inside_try_finally)
5556 ec.EndExceptionBlock ();
5559 protected override void CloneTo (CloneContext clonectx, Statement t)
5561 TryCatch target = (TryCatch) t;
5563 target.Block = clonectx.LookupBlock (Block);
5564 if (clauses != null){
5565 target.clauses = new List<Catch> ();
5566 foreach (Catch c in clauses)
5567 target.clauses.Add ((Catch) c.Clone (clonectx));
5571 public override object Accept (StructuralVisitor visitor)
5573 return visitor.Visit (this);
5577 public class Using : TryFinallyBlock
5579 public class VariableDeclaration : BlockVariableDeclaration
5581 Statement dispose_call;
5583 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5588 public VariableDeclaration (LocalVariable li, Location loc)
5594 public VariableDeclaration (Expression expr)
5597 loc = expr.Location;
5603 public bool IsNested { get; private set; }
5607 public void EmitDispose (EmitContext ec)
5609 dispose_call.Emit (ec);
5612 public override bool Resolve (BlockContext bc)
5617 return base.Resolve (bc, false);
5620 public Expression ResolveExpression (BlockContext bc)
5622 var e = Initializer.Resolve (bc);
5626 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5627 Initializer = ResolveInitializer (bc, Variable, e);
5631 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5633 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5634 initializer = initializer.Resolve (bc);
5635 if (initializer == null)
5638 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5639 Arguments args = new Arguments (1);
5640 args.Add (new Argument (initializer));
5641 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5642 if (initializer == null)
5645 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5646 dispose_call = CreateDisposeCall (bc, var);
5647 dispose_call.Resolve (bc);
5649 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5652 if (li == Variable) {
5653 CheckIDiposableConversion (bc, li, initializer);
5654 dispose_call = CreateDisposeCall (bc, li);
5655 dispose_call.Resolve (bc);
5658 return base.ResolveInitializer (bc, li, initializer);
5661 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5665 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5666 if (type.IsNullableType) {
5667 // it's handled in CreateDisposeCall
5671 bc.Report.SymbolRelatedToPreviousError (type);
5672 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5673 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5674 type.GetSignatureForError ());
5680 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5682 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5684 var loc = lv.Location;
5686 var idt = bc.BuiltinTypes.IDisposable;
5687 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5689 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5690 dispose_mg.InstanceExpression = type.IsNullableType ?
5691 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5695 // Hide it from symbol file via null location
5697 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5699 // Add conditional call when disposing possible null variable
5700 if (!type.IsStruct || type.IsNullableType)
5701 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5706 public void ResolveDeclaratorInitializer (BlockContext bc)
5708 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5711 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5713 for (int i = declarators.Count - 1; i >= 0; --i) {
5714 var d = declarators [i];
5715 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5716 vd.Initializer = d.Initializer;
5718 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5719 vd.dispose_call.Resolve (bc);
5721 stmt = new Using (vd, stmt, d.Variable.Location);
5728 public override object Accept (StructuralVisitor visitor)
5730 return visitor.Visit (this);
5734 VariableDeclaration decl;
5736 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5742 public Using (Expression expr, Statement stmt, Location loc)
5745 this.decl = new VariableDeclaration (expr);
5750 public Expression Expr {
5752 return decl.Variable == null ? decl.Initializer : null;
5756 public BlockVariableDeclaration Variables {
5764 public override void Emit (EmitContext ec)
5767 // Don't emit sequence point it will be set on variable declaration
5772 protected override void EmitTryBodyPrepare (EmitContext ec)
5775 base.EmitTryBodyPrepare (ec);
5778 protected override void EmitTryBody (EmitContext ec)
5783 public override void EmitFinallyBody (EmitContext ec)
5785 decl.EmitDispose (ec);
5788 public override bool Resolve (BlockContext ec)
5790 VariableReference vr;
5791 bool vr_locked = false;
5793 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5794 if (decl.Variable == null) {
5795 vr = decl.ResolveExpression (ec) as VariableReference;
5797 vr_locked = vr.IsLockedByStatement;
5798 vr.IsLockedByStatement = true;
5801 if (decl.IsNested) {
5802 decl.ResolveDeclaratorInitializer (ec);
5804 if (!decl.Resolve (ec))
5807 if (decl.Declarators != null) {
5808 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5816 ec.StartFlowBranching (this);
5820 ec.EndFlowBranching ();
5823 vr.IsLockedByStatement = vr_locked;
5830 protected override void CloneTo (CloneContext clonectx, Statement t)
5832 Using target = (Using) t;
5834 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5835 target.stmt = stmt.Clone (clonectx);
5838 public override object Accept (StructuralVisitor visitor)
5840 return visitor.Visit (this);
5845 /// Implementation of the foreach C# statement
5847 public class Foreach : Statement
5849 abstract class IteratorStatement : Statement
5851 protected readonly Foreach for_each;
5853 protected IteratorStatement (Foreach @foreach)
5855 this.for_each = @foreach;
5856 this.loc = @foreach.expr.Location;
5859 protected override void CloneTo (CloneContext clonectx, Statement target)
5861 throw new NotImplementedException ();
5864 public override void Emit (EmitContext ec)
5866 if (ec.EmitAccurateDebugInfo) {
5867 ec.Emit (OpCodes.Nop);
5874 sealed class ArrayForeach : IteratorStatement
5876 TemporaryVariableReference[] lengths;
5877 Expression [] length_exprs;
5878 StatementExpression[] counter;
5879 TemporaryVariableReference[] variables;
5881 TemporaryVariableReference copy;
5883 public ArrayForeach (Foreach @foreach, int rank)
5886 counter = new StatementExpression[rank];
5887 variables = new TemporaryVariableReference[rank];
5888 length_exprs = new Expression [rank];
5891 // Only use temporary length variables when dealing with
5892 // multi-dimensional arrays
5895 lengths = new TemporaryVariableReference [rank];
5898 public override bool Resolve (BlockContext ec)
5900 Block variables_block = for_each.variable.Block;
5901 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5904 int rank = length_exprs.Length;
5905 Arguments list = new Arguments (rank);
5906 for (int i = 0; i < rank; i++) {
5907 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5909 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5910 counter[i].Resolve (ec);
5913 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5915 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5916 lengths[i].Resolve (ec);
5918 Arguments args = new Arguments (1);
5919 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5920 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5923 list.Add (new Argument (v));
5926 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5931 if (for_each.type is VarExpr) {
5932 // Infer implicitly typed local variable from foreach array type
5933 var_type = access.Type;
5935 var_type = for_each.type.ResolveAsType (ec);
5937 if (var_type == null)
5940 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5945 for_each.variable.Type = var_type;
5947 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5948 if (variable_ref == null)
5951 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5955 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5956 ec.CurrentBranching.CreateSibling ();
5958 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5959 if (!for_each.body.Resolve (ec))
5961 ec.EndFlowBranching ();
5963 // There's no direct control flow from the end of the embedded statement to the end of the loop
5964 ec.CurrentBranching.CurrentUsageVector.Goto ();
5966 ec.EndFlowBranching ();
5971 protected override void DoEmit (EmitContext ec)
5973 copy.EmitAssign (ec, for_each.expr);
5975 int rank = length_exprs.Length;
5976 Label[] test = new Label [rank];
5977 Label[] loop = new Label [rank];
5979 for (int i = 0; i < rank; i++) {
5980 test [i] = ec.DefineLabel ();
5981 loop [i] = ec.DefineLabel ();
5983 if (lengths != null)
5984 lengths [i].EmitAssign (ec, length_exprs [i]);
5987 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5988 for (int i = 0; i < rank; i++) {
5989 variables [i].EmitAssign (ec, zero);
5991 ec.Emit (OpCodes.Br, test [i]);
5992 ec.MarkLabel (loop [i]);
5995 for_each.body.Emit (ec);
5997 ec.MarkLabel (ec.LoopBegin);
5998 ec.Mark (for_each.expr.Location);
6000 for (int i = rank - 1; i >= 0; i--){
6001 counter [i].Emit (ec);
6003 ec.MarkLabel (test [i]);
6004 variables [i].Emit (ec);
6006 if (lengths != null)
6007 lengths [i].Emit (ec);
6009 length_exprs [i].Emit (ec);
6011 ec.Emit (OpCodes.Blt, loop [i]);
6014 ec.MarkLabel (ec.LoopEnd);
6018 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6020 class RuntimeDispose : Using.VariableDeclaration
6022 public RuntimeDispose (LocalVariable lv, Location loc)
6027 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6029 // Defered to runtime check
6032 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6034 var idt = bc.BuiltinTypes.IDisposable;
6037 // Fabricates code like
6039 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6042 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6044 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6045 dispose_variable.CreateReferenceExpression (bc, loc),
6046 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6047 loc), new NullLiteral (loc));
6049 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6051 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6052 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6054 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6055 return new If (idisaposable_test, dispose, loc);
6059 LocalVariable variable;
6061 Statement statement;
6062 ExpressionStatement init;
6063 TemporaryVariableReference enumerator_variable;
6064 bool ambiguous_getenumerator_name;
6066 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6069 this.variable = var;
6073 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6075 rc.Report.SymbolRelatedToPreviousError (enumerator);
6076 rc.Report.Error (202, loc,
6077 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6078 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6081 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6084 // Option 1: Try to match by name GetEnumerator first
6086 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6087 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6089 var mg = mexpr as MethodGroupExpr;
6091 mg.InstanceExpression = expr;
6092 Arguments args = new Arguments (0);
6093 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6095 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6096 if (ambiguous_getenumerator_name)
6099 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6105 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6108 PredefinedMember<MethodSpec> iface_candidate = null;
6109 var ptypes = rc.Module.PredefinedTypes;
6110 var gen_ienumerable = ptypes.IEnumerableGeneric;
6111 if (!gen_ienumerable.Define ())
6112 gen_ienumerable = null;
6114 var ifaces = t.Interfaces;
6115 if (ifaces != null) {
6116 foreach (var iface in ifaces) {
6117 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6118 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6119 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6120 rc.Report.Error (1640, loc,
6121 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6122 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6127 // TODO: Cache this somehow
6128 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6129 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6134 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6135 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6140 if (iface_candidate == null) {
6141 if (expr.Type != InternalType.ErrorType) {
6142 rc.Report.Error (1579, loc,
6143 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6144 expr.Type.GetSignatureForError (), "GetEnumerator");
6150 var method = iface_candidate.Resolve (loc);
6154 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6155 mg.InstanceExpression = expr;
6159 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6161 var ms = MemberCache.FindMember (enumerator.ReturnType,
6162 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6163 BindingRestriction.InstanceOnly) as MethodSpec;
6165 if (ms == null || !ms.IsPublic) {
6166 Error_WrongEnumerator (rc, enumerator);
6170 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6173 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6175 var ps = MemberCache.FindMember (enumerator.ReturnType,
6176 MemberFilter.Property ("Current", null),
6177 BindingRestriction.InstanceOnly) as PropertySpec;
6179 if (ps == null || !ps.IsPublic) {
6180 Error_WrongEnumerator (rc, enumerator);
6187 public override bool Resolve (BlockContext ec)
6189 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6192 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6193 } else if (expr.Type.IsNullableType) {
6194 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6197 var get_enumerator_mg = ResolveGetEnumerator (ec);
6198 if (get_enumerator_mg == null) {
6202 var get_enumerator = get_enumerator_mg.BestCandidate;
6203 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6204 enumerator_variable.Resolve (ec);
6206 // Prepare bool MoveNext ()
6207 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6208 if (move_next_mg == null) {
6212 move_next_mg.InstanceExpression = enumerator_variable;
6214 // Prepare ~T~ Current { get; }
6215 var current_prop = ResolveCurrent (ec, get_enumerator);
6216 if (current_prop == null) {
6220 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6221 if (current_pe == null)
6224 VarExpr ve = for_each.type as VarExpr;
6228 // Source type is dynamic, set element type to dynamic too
6229 variable.Type = ec.BuiltinTypes.Dynamic;
6231 // Infer implicitly typed local variable from foreach enumerable type
6232 variable.Type = current_pe.Type;
6236 // Explicit cast of dynamic collection elements has to be done at runtime
6237 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6240 variable.Type = for_each.type.ResolveAsType (ec);
6242 if (variable.Type == null)
6245 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6246 if (current_pe == null)
6250 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6251 if (variable_ref == null)
6254 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6256 var init = new Invocation (get_enumerator_mg, null);
6258 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6259 for_each.body, Location.Null);
6261 var enum_type = enumerator_variable.Type;
6264 // Add Dispose method call when enumerator can be IDisposable
6266 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6267 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6269 // Runtime Dispose check
6271 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6272 vd.Initializer = init;
6273 statement = new Using (vd, statement, Location.Null);
6276 // No Dispose call needed
6278 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6279 this.init.Resolve (ec);
6283 // Static Dispose check
6285 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6286 vd.Initializer = init;
6287 statement = new Using (vd, statement, Location.Null);
6290 return statement.Resolve (ec);
6293 protected override void DoEmit (EmitContext ec)
6295 enumerator_variable.LocalInfo.CreateBuilder (ec);
6298 init.EmitStatement (ec);
6300 statement.Emit (ec);
6303 #region IErrorHandler Members
6305 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6307 ec.Report.SymbolRelatedToPreviousError (best);
6308 ec.Report.Warning (278, 2, expr.Location,
6309 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6310 expr.Type.GetSignatureForError (), "enumerable",
6311 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6313 ambiguous_getenumerator_name = true;
6317 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6322 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6327 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6336 LocalVariable variable;
6338 Statement statement;
6341 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6344 this.variable = var;
6346 this.statement = stmt;
6351 public Expression Expr {
6352 get { return expr; }
6355 public Statement Statement {
6356 get { return statement; }
6359 public Expression TypeExpression {
6360 get { return type; }
6363 public LocalVariable Variable {
6364 get { return variable; }
6367 public override bool Resolve (BlockContext ec)
6369 expr = expr.Resolve (ec);
6374 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6378 body.AddStatement (statement);
6380 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6381 statement = new ArrayForeach (this, 1);
6382 } else if (expr.Type is ArrayContainer) {
6383 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6385 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6386 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6387 expr.ExprClassName);
6391 statement = new CollectionForeach (this, variable, expr);
6394 return statement.Resolve (ec);
6397 protected override void DoEmit (EmitContext ec)
6399 variable.CreateBuilder (ec);
6401 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6402 ec.LoopBegin = ec.DefineLabel ();
6403 ec.LoopEnd = ec.DefineLabel ();
6405 statement.Emit (ec);
6407 ec.LoopBegin = old_begin;
6408 ec.LoopEnd = old_end;
6411 protected override void CloneTo (CloneContext clonectx, Statement t)
6413 Foreach target = (Foreach) t;
6415 target.type = type.Clone (clonectx);
6416 target.expr = expr.Clone (clonectx);
6417 target.body = (Block) body.Clone (clonectx);
6418 target.statement = statement.Clone (clonectx);
6421 public override object Accept (StructuralVisitor visitor)
6423 return visitor.Visit (this);