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 BlockVariableDeclarator
1442 Expression initializer;
1444 public BlockVariableDeclarator (LocalVariable li, Expression initializer)
1446 if (li.Type != null)
1447 throw new ArgumentException ("Expected null variable type");
1450 this.initializer = initializer;
1455 public LocalVariable Variable {
1461 public Expression Initializer {
1466 initializer = value;
1472 public virtual BlockVariableDeclarator Clone (CloneContext cloneCtx)
1474 var t = (BlockVariableDeclarator) MemberwiseClone ();
1475 if (initializer != null)
1476 t.initializer = initializer.Clone (cloneCtx);
1482 public class BlockVariable : Statement
1484 Expression initializer;
1485 protected FullNamedExpression type_expr;
1486 protected LocalVariable li;
1487 protected List<BlockVariableDeclarator> declarators;
1490 public BlockVariable (FullNamedExpression type, LocalVariable li)
1492 this.type_expr = type;
1494 this.loc = type_expr.Location;
1497 protected BlockVariable (LocalVariable li)
1504 public List<BlockVariableDeclarator> Declarators {
1510 public Expression Initializer {
1515 initializer = value;
1519 public FullNamedExpression TypeExpression {
1525 public LocalVariable Variable {
1533 public void AddDeclarator (BlockVariableDeclarator decl)
1535 if (declarators == null)
1536 declarators = new List<BlockVariableDeclarator> ();
1538 declarators.Add (decl);
1541 static void CreateEvaluatorVariable (BlockContext bc, LocalVariable li)
1543 if (bc.Report.Errors != 0)
1546 var container = bc.CurrentMemberDefinition.Parent.PartialContainer;
1548 Field f = new Field (container, new TypeExpression (li.Type, li.Location), Modifiers.PUBLIC | Modifiers.STATIC,
1549 new MemberName (li.Name, li.Location), null);
1551 container.AddField (f);
1554 li.HoistedVariant = new HoistedEvaluatorVariable (f);
1558 public override bool Resolve (BlockContext bc)
1560 return Resolve (bc, true);
1563 public bool Resolve (BlockContext bc, bool resolveDeclaratorInitializers)
1565 if (type == null && !li.IsCompilerGenerated) {
1566 var vexpr = type_expr as VarExpr;
1569 // C# 3.0 introduced contextual keywords (var) which behaves like a type if type with
1570 // same name exists or as a keyword when no type was found
1572 if (vexpr != null && !vexpr.IsPossibleTypeOrNamespace (bc)) {
1573 if (bc.Module.Compiler.Settings.Version < LanguageVersion.V_3)
1574 bc.Report.FeatureIsNotAvailable (bc.Module.Compiler, loc, "implicitly typed local variable");
1577 bc.Report.Error (821, loc, "A fixed statement cannot use an implicitly typed local variable");
1581 if (li.IsConstant) {
1582 bc.Report.Error (822, loc, "An implicitly typed local variable cannot be a constant");
1586 if (Initializer == null) {
1587 bc.Report.Error (818, loc, "An implicitly typed local variable declarator must include an initializer");
1591 if (declarators != null) {
1592 bc.Report.Error (819, loc, "An implicitly typed local variable declaration cannot include multiple declarators");
1596 Initializer = Initializer.Resolve (bc);
1597 if (Initializer != null) {
1598 ((VarExpr) type_expr).InferType (bc, Initializer);
1599 type = type_expr.Type;
1601 // Set error type to indicate the var was placed correctly but could
1604 // var a = missing ();
1606 type = InternalType.ErrorType;
1611 type = type_expr.ResolveAsType (bc);
1615 if (li.IsConstant && !type.IsConstantCompatible) {
1616 Const.Error_InvalidConstantType (type, loc, bc.Report);
1621 FieldBase.Error_VariableOfStaticClass (loc, li.Name, type, bc.Report);
1626 bool eval_global = bc.Module.Compiler.Settings.StatementMode && bc.CurrentBlock is ToplevelBlock;
1628 CreateEvaluatorVariable (bc, li);
1629 } else if (type != InternalType.ErrorType) {
1630 li.PrepareForFlowAnalysis (bc);
1633 if (initializer != null) {
1634 initializer = ResolveInitializer (bc, li, initializer);
1635 // li.Variable.DefinitelyAssigned
1638 if (declarators != null) {
1639 foreach (var d in declarators) {
1640 d.Variable.Type = li.Type;
1642 CreateEvaluatorVariable (bc, d.Variable);
1643 } else if (type != InternalType.ErrorType) {
1644 d.Variable.PrepareForFlowAnalysis (bc);
1647 if (d.Initializer != null && resolveDeclaratorInitializers) {
1648 d.Initializer = ResolveInitializer (bc, d.Variable, d.Initializer);
1649 // d.Variable.DefinitelyAssigned
1657 protected virtual Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1659 var a = new SimpleAssign (li.CreateReferenceExpression (bc, li.Location), initializer, li.Location);
1660 return a.ResolveStatement (bc);
1663 protected override void DoEmit (EmitContext ec)
1665 li.CreateBuilder (ec);
1667 if (Initializer != null)
1668 ((ExpressionStatement) Initializer).EmitStatement (ec);
1670 if (declarators != null) {
1671 foreach (var d in declarators) {
1672 d.Variable.CreateBuilder (ec);
1673 if (d.Initializer != null) {
1674 ec.Mark (d.Variable.Location);
1675 ((ExpressionStatement) d.Initializer).EmitStatement (ec);
1681 protected override void CloneTo (CloneContext clonectx, Statement target)
1683 BlockVariable t = (BlockVariable) target;
1685 if (type_expr != null)
1686 t.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
1688 if (initializer != null)
1689 t.initializer = initializer.Clone (clonectx);
1691 if (declarators != null) {
1692 t.declarators = null;
1693 foreach (var d in declarators)
1694 t.AddDeclarator (d.Clone (clonectx));
1698 public override object Accept (StructuralVisitor visitor)
1700 return visitor.Visit (this);
1704 public class BlockConstant : BlockVariable
1706 public BlockConstant (FullNamedExpression type, LocalVariable li)
1711 public override void Emit (EmitContext ec)
1713 // Nothing to emit, not even sequence point
1716 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
1718 initializer = initializer.Resolve (bc);
1719 if (initializer == null)
1722 var c = initializer as Constant;
1724 initializer.Error_ExpressionMustBeConstant (bc, initializer.Location, li.Name);
1728 c = c.ConvertImplicitly (li.Type);
1730 if (TypeSpec.IsReferenceType (li.Type))
1731 initializer.Error_ConstantCanBeInitializedWithNullOnly (bc, li.Type, initializer.Location, li.Name);
1733 initializer.Error_ValueCannotBeConverted (bc, li.Type, false);
1738 li.ConstantValue = c;
1742 public override object Accept (StructuralVisitor visitor)
1744 return visitor.Visit (this);
1749 // The information about a user-perceived local variable
1751 public class LocalVariable : INamedBlockVariable, ILocalVariable
1758 AddressTaken = 1 << 2,
1759 CompilerGenerated = 1 << 3,
1761 ForeachVariable = 1 << 5,
1762 FixedVariable = 1 << 6,
1763 UsingVariable = 1 << 7,
1764 // DefinitelyAssigned = 1 << 8,
1767 ReadonlyMask = ForeachVariable | FixedVariable | UsingVariable
1771 readonly string name;
1772 readonly Location loc;
1773 readonly Block block;
1775 Constant const_value;
1777 public VariableInfo VariableInfo;
1778 HoistedVariable hoisted_variant;
1780 LocalBuilder builder;
1782 public LocalVariable (Block block, string name, Location loc)
1789 public LocalVariable (Block block, string name, Flags flags, Location loc)
1790 : this (block, name, loc)
1796 // Used by variable declarators
1798 public LocalVariable (LocalVariable li, string name, Location loc)
1799 : this (li.block, name, li.flags, loc)
1805 public bool AddressTaken {
1807 return (flags & Flags.AddressTaken) != 0;
1811 public Block Block {
1817 public Constant ConstantValue {
1822 const_value = value;
1827 // Hoisted local variable variant
1829 public HoistedVariable HoistedVariant {
1831 return hoisted_variant;
1834 hoisted_variant = value;
1838 public bool IsDeclared {
1840 return type != null;
1844 public bool IsCompilerGenerated {
1846 return (flags & Flags.CompilerGenerated) != 0;
1850 public bool IsConstant {
1852 return (flags & Flags.Constant) != 0;
1856 public bool IsLocked {
1858 return (flags & Flags.IsLocked) != 0;
1861 flags = value ? flags | Flags.IsLocked : flags & ~Flags.IsLocked;
1865 public bool IsThis {
1867 return (flags & Flags.IsThis) != 0;
1871 public bool IsFixed {
1873 return (flags & Flags.FixedVariable) != 0;
1877 bool INamedBlockVariable.IsParameter {
1883 public bool IsReadonly {
1885 return (flags & Flags.ReadonlyMask) != 0;
1889 public Location Location {
1895 public string Name {
1901 public TypeSpec Type {
1912 public void CreateBuilder (EmitContext ec)
1914 if ((flags & Flags.Used) == 0) {
1915 if (VariableInfo == null) {
1916 // Missing flow analysis or wrong variable flags
1917 throw new InternalErrorException ("VariableInfo is null and the variable `{0}' is not used", name);
1920 if (VariableInfo.IsEverAssigned)
1921 ec.Report.Warning (219, 3, Location, "The variable `{0}' is assigned but its value is never used", Name);
1923 ec.Report.Warning (168, 3, Location, "The variable `{0}' is declared but never used", Name);
1926 if (HoistedVariant != null)
1929 if (builder != null) {
1930 if ((flags & Flags.CompilerGenerated) != 0)
1933 // To avoid Used warning duplicates
1934 throw new InternalErrorException ("Already created variable `{0}'", name);
1938 // All fixed variabled are pinned, a slot has to be alocated
1940 builder = ec.DeclareLocal (Type, IsFixed);
1941 if (!ec.HasSet (BuilderContext.Options.OmitDebugInfo) && (flags & Flags.CompilerGenerated) == 0)
1942 ec.DefineLocalVariable (name, builder);
1945 public static LocalVariable CreateCompilerGenerated (TypeSpec type, Block block, Location loc)
1947 LocalVariable li = new LocalVariable (block, GetCompilerGeneratedName (block), Flags.CompilerGenerated | Flags.Used, loc);
1952 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
1954 if (IsConstant && const_value != null)
1955 return Constant.CreateConstantFromValue (Type, const_value.GetValue (), loc);
1957 return new LocalVariableReference (this, loc);
1960 public void Emit (EmitContext ec)
1962 // TODO: Need something better for temporary variables
1963 if ((flags & Flags.CompilerGenerated) != 0)
1966 ec.Emit (OpCodes.Ldloc, builder);
1969 public void EmitAssign (EmitContext ec)
1971 // TODO: Need something better for temporary variables
1972 if ((flags & Flags.CompilerGenerated) != 0)
1975 ec.Emit (OpCodes.Stloc, builder);
1978 public void EmitAddressOf (EmitContext ec)
1980 ec.Emit (OpCodes.Ldloca, builder);
1983 public static string GetCompilerGeneratedName (Block block)
1985 // HACK: Debugger depends on the name semantics
1986 return "$locvar" + block.ParametersBlock.TemporaryLocalsCount++.ToString ("X");
1989 public string GetReadOnlyContext ()
1991 switch (flags & Flags.ReadonlyMask) {
1992 case Flags.FixedVariable:
1993 return "fixed variable";
1994 case Flags.ForeachVariable:
1995 return "foreach iteration variable";
1996 case Flags.UsingVariable:
1997 return "using variable";
2000 throw new InternalErrorException ("Variable is not readonly");
2003 public bool IsThisAssigned (BlockContext ec, Block block)
2005 if (VariableInfo == null)
2006 throw new Exception ();
2008 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
2011 return VariableInfo.IsFullyInitialized (ec, block.StartLocation);
2014 public bool IsAssigned (BlockContext ec)
2016 if (VariableInfo == null)
2017 throw new Exception ();
2019 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
2022 public void PrepareForFlowAnalysis (BlockContext bc)
2025 // No need for definitely assigned check for these guys
2027 if ((flags & (Flags.Constant | Flags.ReadonlyMask | Flags.CompilerGenerated)) != 0)
2030 VariableInfo = new VariableInfo (this, bc.FlowOffset);
2031 bc.FlowOffset += VariableInfo.Length;
2035 // Mark the variables as referenced in the user code
2037 public void SetIsUsed ()
2039 flags |= Flags.Used;
2042 public void SetHasAddressTaken ()
2044 flags |= (Flags.AddressTaken | Flags.Used);
2047 public override string ToString ()
2049 return string.Format ("LocalInfo ({0},{1},{2},{3})", name, type, VariableInfo, Location);
2054 /// Block represents a C# block.
2058 /// This class is used in a number of places: either to represent
2059 /// explicit blocks that the programmer places or implicit blocks.
2061 /// Implicit blocks are used as labels or to introduce variable
2064 /// Top-level blocks derive from Block, and they are called ToplevelBlock
2065 /// they contain extra information that is not necessary on normal blocks.
2067 public class Block : Statement {
2074 HasCapturedVariable = 64,
2075 HasCapturedThis = 1 << 7,
2076 IsExpressionTree = 1 << 8,
2077 CompilerGenerated = 1 << 9,
2078 HasAsyncModifier = 1 << 10,
2080 YieldBlock = 1 << 12,
2081 AwaitBlock = 1 << 13,
2085 public Block Parent;
2086 public Location StartLocation;
2087 public Location EndLocation;
2089 public ExplicitBlock Explicit;
2090 public ParametersBlock ParametersBlock;
2092 protected Flags flags;
2095 // The statements in this block
2097 protected List<Statement> statements;
2099 protected List<Statement> scope_initializers;
2101 int? resolving_init_idx;
2107 public int ID = id++;
2109 static int clone_id_counter;
2113 // int assignable_slots;
2115 public Block (Block parent, Location start, Location end)
2116 : this (parent, 0, start, end)
2120 public Block (Block parent, Flags flags, Location start, Location end)
2122 if (parent != null) {
2123 // the appropriate constructors will fixup these fields
2124 ParametersBlock = parent.ParametersBlock;
2125 Explicit = parent.Explicit;
2128 this.Parent = parent;
2130 this.StartLocation = start;
2131 this.EndLocation = end;
2133 statements = new List<Statement> (4);
2135 this.original = this;
2140 public bool HasUnreachableClosingBrace {
2142 return (flags & Flags.HasRet) != 0;
2145 flags = value ? flags | Flags.HasRet : flags & ~Flags.HasRet;
2149 public Block Original {
2158 public bool IsCompilerGenerated {
2159 get { return (flags & Flags.CompilerGenerated) != 0; }
2160 set { flags = value ? flags | Flags.CompilerGenerated : flags & ~Flags.CompilerGenerated; }
2163 public bool Unchecked {
2164 get { return (flags & Flags.Unchecked) != 0; }
2165 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
2168 public bool Unsafe {
2169 get { return (flags & Flags.Unsafe) != 0; }
2170 set { flags |= Flags.Unsafe; }
2173 public List<Statement> Statements {
2174 get { return statements; }
2179 public void SetEndLocation (Location loc)
2184 public void AddLabel (LabeledStatement target)
2186 ParametersBlock.TopBlock.AddLabel (target.Name, target);
2189 public void AddLocalName (LocalVariable li)
2191 AddLocalName (li.Name, li);
2194 public void AddLocalName (string name, INamedBlockVariable li)
2196 ParametersBlock.TopBlock.AddLocalName (name, li, false);
2199 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable, string reason)
2201 if (reason == null) {
2202 Error_AlreadyDeclared (name, variable);
2206 ParametersBlock.TopBlock.Report.Error (136, variable.Location,
2207 "A local variable named `{0}' cannot be declared in this scope because it would give a different meaning " +
2208 "to `{0}', which is already used in a `{1}' scope to denote something else",
2212 public virtual void Error_AlreadyDeclared (string name, INamedBlockVariable variable)
2214 var pi = variable as ParametersBlock.ParameterInfo;
2216 pi.Parameter.Error_DuplicateName (ParametersBlock.TopBlock.Report);
2218 ParametersBlock.TopBlock.Report.Error (128, variable.Location,
2219 "A local variable named `{0}' is already defined in this scope", name);
2223 public virtual void Error_AlreadyDeclaredTypeParameter (string name, Location loc)
2225 ParametersBlock.TopBlock.Report.Error (412, loc,
2226 "The type parameter name `{0}' is the same as local variable or parameter name",
2231 // It should be used by expressions which require to
2232 // register a statement during resolve process.
2234 public void AddScopeStatement (Statement s)
2236 if (scope_initializers == null)
2237 scope_initializers = new List<Statement> ();
2240 // Simple recursive helper, when resolve scope initializer another
2241 // new scope initializer can be added, this ensures it's initialized
2242 // before existing one. For now this can happen with expression trees
2243 // in base ctor initializer only
2245 if (resolving_init_idx.HasValue) {
2246 scope_initializers.Insert (resolving_init_idx.Value, s);
2247 ++resolving_init_idx;
2249 scope_initializers.Add (s);
2253 public void AddStatement (Statement s)
2258 public int AssignableSlots {
2260 // FIXME: HACK, we don't know the block available variables count now, so set this high enough
2262 // return assignable_slots;
2266 public LabeledStatement LookupLabel (string name)
2268 return ParametersBlock.TopBlock.GetLabel (name, this);
2271 public override bool Resolve (BlockContext ec)
2273 if ((flags & Flags.Resolved) != 0)
2276 Block prev_block = ec.CurrentBlock;
2278 bool unreachable = ec.IsUnreachable;
2279 bool prev_unreachable = unreachable;
2281 ec.CurrentBlock = this;
2282 ec.StartFlowBranching (this);
2285 // Compiler generated scope statements
2287 if (scope_initializers != null) {
2288 for (resolving_init_idx = 0; resolving_init_idx < scope_initializers.Count; ++resolving_init_idx) {
2289 scope_initializers[resolving_init_idx.Value].Resolve (ec);
2292 resolving_init_idx = null;
2296 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2297 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2298 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2299 // responsible for handling the situation.
2301 int statement_count = statements.Count;
2302 for (int ix = 0; ix < statement_count; ix++){
2303 Statement s = statements [ix];
2306 // Warn if we detect unreachable code.
2309 if (s is EmptyStatement)
2312 if (!ec.UnreachableReported && !(s is LabeledStatement) && !(s is SwitchLabel)) {
2313 ec.Report.Warning (162, 2, s.loc, "Unreachable code detected");
2314 ec.UnreachableReported = true;
2319 // Note that we're not using ResolveUnreachable() for unreachable
2320 // statements here. ResolveUnreachable() creates a temporary
2321 // flow branching and kills it afterwards. This leads to problems
2322 // if you have two unreachable statements where the first one
2323 // assigns a variable and the second one tries to access it.
2326 if (!s.Resolve (ec)) {
2328 if (!ec.IsInProbingMode)
2329 statements [ix] = new EmptyStatement (s.loc);
2334 if (unreachable && !(s is LabeledStatement) && !(s is SwitchLabel) && !(s is Block))
2335 statements [ix] = new EmptyStatement (s.loc);
2337 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2339 ec.IsUnreachable = true;
2340 } else if (ec.IsUnreachable)
2341 ec.IsUnreachable = false;
2344 if (unreachable != prev_unreachable) {
2345 ec.IsUnreachable = prev_unreachable;
2346 ec.UnreachableReported = false;
2349 while (ec.CurrentBranching is FlowBranchingLabeled)
2350 ec.EndFlowBranching ();
2352 bool flow_unreachable = ec.EndFlowBranching ();
2354 ec.CurrentBlock = prev_block;
2356 if (flow_unreachable)
2357 flags |= Flags.HasRet;
2359 // If we're a non-static `struct' constructor which doesn't have an
2360 // initializer, then we must initialize all of the struct's fields.
2361 if (this == ParametersBlock.TopBlock && !ParametersBlock.TopBlock.IsThisAssigned (ec) && !flow_unreachable)
2364 flags |= Flags.Resolved;
2368 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2370 bool unreachable = false;
2371 if (warn && !ec.UnreachableReported) {
2372 ec.UnreachableReported = true;
2374 ec.Report.Warning (162, 2, loc, "Unreachable code detected");
2377 var fb = ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2378 fb.CurrentUsageVector.IsUnreachable = true;
2379 bool ok = Resolve (ec);
2380 ec.KillFlowBranching ();
2383 ec.UnreachableReported = false;
2388 protected override void DoEmit (EmitContext ec)
2390 for (int ix = 0; ix < statements.Count; ix++){
2391 statements [ix].Emit (ec);
2395 public override void Emit (EmitContext ec)
2397 if (scope_initializers != null)
2398 EmitScopeInitializers (ec);
2403 protected void EmitScopeInitializers (EmitContext ec)
2405 foreach (Statement s in scope_initializers)
2410 public override string ToString ()
2412 return String.Format ("{0} ({1}:{2})", GetType (), ID, StartLocation);
2416 protected override void CloneTo (CloneContext clonectx, Statement t)
2418 Block target = (Block) t;
2420 target.clone_id = clone_id_counter++;
2423 clonectx.AddBlockMap (this, target);
2424 if (original != this)
2425 clonectx.AddBlockMap (original, target);
2427 target.ParametersBlock = (ParametersBlock) (ParametersBlock == this ? target : clonectx.RemapBlockCopy (ParametersBlock));
2428 target.Explicit = (ExplicitBlock) (Explicit == this ? target : clonectx.LookupBlock (Explicit));
2431 target.Parent = clonectx.RemapBlockCopy (Parent);
2433 target.statements = new List<Statement> (statements.Count);
2434 foreach (Statement s in statements)
2435 target.statements.Add (s.Clone (clonectx));
2438 public override object Accept (StructuralVisitor visitor)
2440 return visitor.Visit (this);
2444 public class ExplicitBlock : Block
2446 protected AnonymousMethodStorey am_storey;
2448 public ExplicitBlock (Block parent, Location start, Location end)
2449 : this (parent, (Flags) 0, start, end)
2453 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2454 : base (parent, flags, start, end)
2456 this.Explicit = this;
2461 public AnonymousMethodStorey AnonymousMethodStorey {
2467 public bool HasAwait {
2469 return (flags & Flags.AwaitBlock) != 0;
2473 public bool HasCapturedThis {
2475 flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis;
2478 return (flags & Flags.HasCapturedThis) != 0;
2483 // Used to indicate that the block has reference to parent
2484 // block and cannot be made static when defining anonymous method
2486 public bool HasCapturedVariable {
2488 flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable;
2491 return (flags & Flags.HasCapturedVariable) != 0;
2495 public bool HasYield {
2497 return (flags & Flags.YieldBlock) != 0;
2504 // Creates anonymous method storey in current block
2506 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2509 // Return same story for iterator and async blocks unless we are
2510 // in nested anonymous method
2512 if (ec.CurrentAnonymousMethod is StateMachineInitializer && ParametersBlock.Original == ec.CurrentAnonymousMethod.Block.Original)
2513 return ec.CurrentAnonymousMethod.Storey;
2515 if (am_storey == null) {
2516 MemberBase mc = ec.MemberContext as MemberBase;
2519 // Creates anonymous method storey for this block
2521 am_storey = new AnonymousMethodStorey (this, ec.CurrentMemberDefinition.Parent.PartialContainer, mc, ec.CurrentTypeParameters, "AnonStorey", MemberKind.Class);
2527 public override void Emit (EmitContext ec)
2529 if (am_storey != null) {
2530 DefineStoreyContainer (ec, am_storey);
2531 am_storey.EmitStoreyInstantiation (ec, this);
2534 if (scope_initializers != null)
2535 EmitScopeInitializers (ec);
2537 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated && ec.Mark (StartLocation)) {
2538 ec.Emit (OpCodes.Nop);
2549 if (ec.EmitAccurateDebugInfo && !HasUnreachableClosingBrace && !IsCompilerGenerated && ec.Mark (EndLocation)) {
2550 ec.Emit (OpCodes.Nop);
2554 protected void DefineStoreyContainer (EmitContext ec, AnonymousMethodStorey storey)
2556 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2557 storey.SetNestedStoryParent (ec.CurrentAnonymousMethod.Storey);
2558 storey.Mutator = ec.CurrentAnonymousMethod.Storey.Mutator;
2562 // Creates anonymous method storey
2564 storey.CreateContainer ();
2565 storey.DefineContainer ();
2567 if (Original.Explicit.HasCapturedThis && Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock != null) {
2570 // Only first storey in path will hold this reference. All children blocks will
2571 // reference it indirectly using $ref field
2573 for (Block b = Original.Explicit; b != null; b = b.Parent) {
2574 if (b.Parent != null) {
2575 var s = b.Parent.Explicit.AnonymousMethodStorey;
2577 storey.HoistedThis = s.HoistedThis;
2582 if (b.Explicit == b.Explicit.ParametersBlock && b.Explicit.ParametersBlock.StateMachine != null) {
2583 storey.HoistedThis = b.Explicit.ParametersBlock.StateMachine.HoistedThis;
2585 if (storey.HoistedThis != null)
2591 // We are the first storey on path and 'this' has to be hoisted
2593 if (storey.HoistedThis == null) {
2594 foreach (ExplicitBlock ref_block in Original.ParametersBlock.TopBlock.ThisReferencesFromChildrenBlock) {
2596 // ThisReferencesFromChildrenBlock holds all reference even if they
2597 // are not on this path. It saves some memory otherwise it'd have to
2598 // be in every explicit block. We run this check to see if the reference
2599 // is valid for this storey
2601 Block block_on_path = ref_block;
2602 for (; block_on_path != null && block_on_path != Original; block_on_path = block_on_path.Parent);
2604 if (block_on_path == null)
2607 if (storey.HoistedThis == null) {
2608 storey.AddCapturedThisField (ec);
2611 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2612 if (b.AnonymousMethodStorey != null) {
2613 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2614 b.AnonymousMethodStorey.HoistedThis = storey.HoistedThis;
2617 // Stop propagation inside same top block
2619 if (b.ParametersBlock == ParametersBlock.Original)
2622 b = b.ParametersBlock;
2625 var pb = b as ParametersBlock;
2626 if (pb != null && pb.StateMachine != null) {
2627 if (pb.StateMachine == storey)
2630 pb.StateMachine.AddParentStoreyReference (ec, storey);
2633 b.HasCapturedVariable = true;
2639 var ref_blocks = storey.ReferencesFromChildrenBlock;
2640 if (ref_blocks != null) {
2641 foreach (ExplicitBlock ref_block in ref_blocks) {
2642 for (ExplicitBlock b = ref_block; b.AnonymousMethodStorey != storey; b = b.Parent.Explicit) {
2643 if (b.AnonymousMethodStorey != null) {
2644 b.AnonymousMethodStorey.AddParentStoreyReference (ec, storey);
2647 // Stop propagation inside same top block
2649 if (b.ParametersBlock == ParametersBlock.Original)
2652 b = b.ParametersBlock;
2655 var pb = b as ParametersBlock;
2656 if (pb != null && pb.StateMachine != null) {
2657 if (pb.StateMachine == storey)
2660 pb.StateMachine.AddParentStoreyReference (ec, storey);
2663 b.HasCapturedVariable = true;
2669 storey.PrepareEmit ();
2670 storey.Parent.PartialContainer.AddCompilerGeneratedClass (storey);
2673 public void RegisterAsyncAwait ()
2676 while ((block.flags & Flags.AwaitBlock) == 0) {
2677 block.flags |= Flags.AwaitBlock;
2679 if (block is ParametersBlock)
2682 block = block.Parent.Explicit;
2686 public void RegisterIteratorYield ()
2688 ParametersBlock.TopBlock.IsIterator = true;
2691 while ((block.flags & Flags.YieldBlock) == 0) {
2692 block.flags |= Flags.YieldBlock;
2694 if (block.Parent == null)
2697 block = block.Parent.Explicit;
2701 public void WrapIntoDestructor (TryFinally tf, ExplicitBlock tryBlock)
2703 tryBlock.statements = statements;
2704 statements = new List<Statement> (1);
2705 statements.Add (tf);
2710 // ParametersBlock was introduced to support anonymous methods
2711 // and lambda expressions
2713 public class ParametersBlock : ExplicitBlock
2715 public class ParameterInfo : INamedBlockVariable
2717 readonly ParametersBlock block;
2719 public VariableInfo VariableInfo;
2722 public ParameterInfo (ParametersBlock block, int index)
2730 public ParametersBlock Block {
2736 Block INamedBlockVariable.Block {
2742 public bool IsDeclared {
2748 public bool IsParameter {
2754 public bool IsLocked {
2763 public Location Location {
2765 return Parameter.Location;
2769 public Parameter Parameter {
2771 return block.Parameters [index];
2775 public TypeSpec ParameterType {
2777 return Parameter.Type;
2783 public Expression CreateReferenceExpression (ResolveContext rc, Location loc)
2785 return new ParameterReference (this, loc);
2790 // Block is converted into an expression
2792 sealed class BlockScopeExpression : Expression
2795 readonly ParametersBlock block;
2797 public BlockScopeExpression (Expression child, ParametersBlock block)
2803 public override bool ContainsEmitWithAwait ()
2805 return child.ContainsEmitWithAwait ();
2808 public override Expression CreateExpressionTree (ResolveContext ec)
2810 throw new NotSupportedException ();
2813 protected override Expression DoResolve (ResolveContext ec)
2818 child = child.Resolve (ec);
2822 eclass = child.eclass;
2827 public override void Emit (EmitContext ec)
2829 block.EmitScopeInitializers (ec);
2834 protected ParametersCompiled parameters;
2835 protected ParameterInfo[] parameter_info;
2837 protected bool unreachable;
2838 protected ToplevelBlock top_block;
2839 protected StateMachine state_machine;
2841 public ParametersBlock (Block parent, ParametersCompiled parameters, Location start)
2842 : base (parent, 0, start, start)
2844 if (parameters == null)
2845 throw new ArgumentNullException ("parameters");
2847 this.parameters = parameters;
2848 ParametersBlock = this;
2850 flags |= (parent.ParametersBlock.flags & (Flags.YieldBlock | Flags.AwaitBlock));
2852 this.top_block = parent.ParametersBlock.top_block;
2853 ProcessParameters ();
2856 protected ParametersBlock (ParametersCompiled parameters, Location start)
2857 : base (null, 0, start, start)
2859 if (parameters == null)
2860 throw new ArgumentNullException ("parameters");
2862 this.parameters = parameters;
2863 ParametersBlock = this;
2867 // It's supposed to be used by method body implementation of anonymous methods
2869 protected ParametersBlock (ParametersBlock source, ParametersCompiled parameters)
2870 : base (null, 0, source.StartLocation, source.EndLocation)
2872 this.parameters = parameters;
2873 this.statements = source.statements;
2874 this.scope_initializers = source.scope_initializers;
2876 this.resolved = true;
2877 this.unreachable = source.unreachable;
2878 this.am_storey = source.am_storey;
2879 this.state_machine = source.state_machine;
2881 ParametersBlock = this;
2884 // Overwrite original for comparison purposes when linking cross references
2885 // between anonymous methods
2887 Original = source.Original;
2892 public bool IsAsync {
2894 return (flags & Flags.HasAsyncModifier) != 0;
2897 flags = value ? flags | Flags.HasAsyncModifier : flags & ~Flags.HasAsyncModifier;
2902 // Block has been converted to expression tree
2904 public bool IsExpressionTree {
2906 return (flags & Flags.IsExpressionTree) != 0;
2911 // The parameters for the block.
2913 public ParametersCompiled Parameters {
2919 public StateMachine StateMachine {
2921 return state_machine;
2925 public ToplevelBlock TopBlock {
2931 public bool Resolved {
2933 return (flags & Flags.Resolved) != 0;
2937 public int TemporaryLocalsCount { get; set; }
2942 // Check whether all `out' parameters have been assigned.
2944 public void CheckOutParameters (FlowBranching.UsageVector vector)
2946 if (vector.IsUnreachable)
2949 int n = parameter_info == null ? 0 : parameter_info.Length;
2951 for (int i = 0; i < n; i++) {
2952 VariableInfo var = parameter_info[i].VariableInfo;
2957 if (vector.IsAssigned (var, false))
2960 var p = parameter_info[i].Parameter;
2961 TopBlock.Report.Error (177, p.Location,
2962 "The out parameter `{0}' must be assigned to before control leaves the current method",
2967 public override Expression CreateExpressionTree (ResolveContext ec)
2969 if (statements.Count == 1) {
2970 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2971 if (scope_initializers != null)
2972 expr = new BlockScopeExpression (expr, this);
2977 return base.CreateExpressionTree (ec);
2980 public override void Emit (EmitContext ec)
2982 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2983 DefineStoreyContainer (ec, state_machine);
2984 state_machine.EmitStoreyInstantiation (ec, this);
2990 public void EmitEmbedded (EmitContext ec)
2992 if (state_machine != null && state_machine.OriginalSourceBlock != this) {
2993 DefineStoreyContainer (ec, state_machine);
2994 state_machine.EmitStoreyInstantiation (ec, this);
3000 public ParameterInfo GetParameterInfo (Parameter p)
3002 for (int i = 0; i < parameters.Count; ++i) {
3003 if (parameters[i] == p)
3004 return parameter_info[i];
3007 throw new ArgumentException ("Invalid parameter");
3010 public ParameterReference GetParameterReference (int index, Location loc)
3012 return new ParameterReference (parameter_info[index], loc);
3015 public Statement PerformClone ()
3017 CloneContext clonectx = new CloneContext ();
3018 return Clone (clonectx);
3021 protected void ProcessParameters ()
3023 if (parameters.Count == 0)
3026 parameter_info = new ParameterInfo[parameters.Count];
3027 for (int i = 0; i < parameter_info.Length; ++i) {
3028 var p = parameters.FixedParameters[i];
3032 // TODO: Should use Parameter only and more block there
3033 parameter_info[i] = new ParameterInfo (this, i);
3035 AddLocalName (p.Name, parameter_info[i]);
3039 public bool Resolve (FlowBranching parent, BlockContext rc, IMethodData md)
3046 if (rc.HasSet (ResolveContext.Options.ExpressionTreeConversion))
3047 flags |= Flags.IsExpressionTree;
3052 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
3053 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
3058 unreachable = top_level.End ();
3060 } catch (Exception e) {
3061 if (e is CompletionResult || rc.Report.IsDisabled || e is FatalException)
3064 if (rc.CurrentBlock != null) {
3065 rc.Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: {0}", e.Message);
3067 rc.Report.Error (587, "Internal compiler error: {0}", e.Message);
3070 if (rc.Module.Compiler.Settings.DebugFlags > 0)
3074 if (rc.ReturnType.Kind != MemberKind.Void && !unreachable) {
3075 if (rc.CurrentAnonymousMethod == null) {
3076 // FIXME: Missing FlowAnalysis for generated iterator MoveNext method
3077 if (md is StateMachineMethod) {
3080 rc.Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
3085 // If an asynchronous body of F is either an expression classified as nothing, or a
3086 // statement block where no return statements have expressions, the inferred return type is Task
3089 var am = rc.CurrentAnonymousMethod as AnonymousMethodBody;
3090 if (am != null && am.ReturnTypeInference != null && !am.ReturnTypeInference.HasBounds (0)) {
3091 am.ReturnTypeInference = null;
3092 am.ReturnType = rc.Module.PredefinedTypes.Task.TypeSpec;
3097 rc.Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
3098 rc.CurrentAnonymousMethod.GetSignatureForError ());
3106 void ResolveMeta (BlockContext ec)
3108 int orig_count = parameters.Count;
3110 for (int i = 0; i < orig_count; ++i) {
3111 Parameter.Modifier mod = parameters.FixedParameters[i].ModFlags;
3113 if ((mod & Parameter.Modifier.OUT) == 0)
3116 VariableInfo vi = new VariableInfo (parameters, i, ec.FlowOffset);
3117 parameter_info[i].VariableInfo = vi;
3118 ec.FlowOffset += vi.Length;
3122 public ToplevelBlock ConvertToIterator (IMethodData method, TypeDefinition host, TypeSpec iterator_type, bool is_enumerable)
3124 var iterator = new Iterator (this, method, host, iterator_type, is_enumerable);
3125 var stateMachine = new IteratorStorey (iterator);
3127 state_machine = stateMachine;
3128 iterator.SetStateMachine (stateMachine);
3130 var tlb = new ToplevelBlock (host.Compiler, Parameters, Location.Null);
3131 tlb.Original = this;
3132 tlb.IsCompilerGenerated = true;
3133 tlb.state_machine = stateMachine;
3134 tlb.AddStatement (new Return (iterator, iterator.Location));
3138 public ParametersBlock ConvertToAsyncTask (IMemberContext context, TypeDefinition host, ParametersCompiled parameters, TypeSpec returnType, TypeSpec delegateType, Location loc)
3140 for (int i = 0; i < parameters.Count; i++) {
3141 Parameter p = parameters[i];
3142 Parameter.Modifier mod = p.ModFlags;
3143 if ((mod & Parameter.Modifier.RefOutMask) != 0) {
3144 host.Compiler.Report.Error (1988, p.Location,
3145 "Async methods cannot have ref or out parameters");
3149 if (p is ArglistParameter) {
3150 host.Compiler.Report.Error (4006, p.Location,
3151 "__arglist is not allowed in parameter list of async methods");
3155 if (parameters.Types[i].IsPointer) {
3156 host.Compiler.Report.Error (4005, p.Location,
3157 "Async methods cannot have unsafe parameters");
3163 host.Compiler.Report.Warning (1998, 1, loc,
3164 "Async block lacks `await' operator and will run synchronously");
3167 var block_type = host.Module.Compiler.BuiltinTypes.Void;
3168 var initializer = new AsyncInitializer (this, host, block_type);
3169 initializer.Type = block_type;
3170 initializer.DelegateType = delegateType;
3172 var stateMachine = new AsyncTaskStorey (this, context, initializer, returnType);
3174 state_machine = stateMachine;
3175 initializer.SetStateMachine (stateMachine);
3177 var b = this is ToplevelBlock ?
3178 new ToplevelBlock (host.Compiler, Parameters, Location.Null) :
3179 new ParametersBlock (Parent, parameters, Location.Null) {
3184 b.IsCompilerGenerated = true;
3185 b.state_machine = stateMachine;
3186 b.AddStatement (new StatementExpression (initializer));
3194 public class ToplevelBlock : ParametersBlock
3196 LocalVariable this_variable;
3197 CompilerContext compiler;
3198 Dictionary<string, object> names;
3199 Dictionary<string, object> labels;
3201 List<ExplicitBlock> this_references;
3203 public ToplevelBlock (CompilerContext ctx, Location loc)
3204 : this (ctx, ParametersCompiled.EmptyReadOnlyParameters, loc)
3208 public ToplevelBlock (CompilerContext ctx, ParametersCompiled parameters, Location start)
3209 : base (parameters, start)
3211 this.compiler = ctx;
3213 flags |= Flags.HasRet;
3215 ProcessParameters ();
3219 // Recreates a top level block from parameters block. Used for
3220 // compiler generated methods where the original block comes from
3221 // explicit child block. This works for already resolved blocks
3222 // only to ensure we resolve them in the correct flow order
3224 public ToplevelBlock (ParametersBlock source, ParametersCompiled parameters)
3225 : base (source, parameters)
3227 this.compiler = source.TopBlock.compiler;
3229 flags |= Flags.HasRet;
3232 public bool IsIterator {
3234 return (flags & Flags.Iterator) != 0;
3237 flags = value ? flags | Flags.Iterator : flags & ~Flags.Iterator;
3241 public Report Report {
3243 return compiler.Report;
3248 // Used by anonymous blocks to track references of `this' variable
3250 public List<ExplicitBlock> ThisReferencesFromChildrenBlock {
3252 return this_references;
3257 // Returns the "this" instance variable of this block.
3258 // See AddThisVariable() for more information.
3260 public LocalVariable ThisVariable {
3262 return this_variable;
3266 public void AddLocalName (string name, INamedBlockVariable li, bool ignoreChildrenBlocks)
3269 names = new Dictionary<string, object> ();
3272 if (!names.TryGetValue (name, out value)) {
3273 names.Add (name, li);
3277 INamedBlockVariable existing = value as INamedBlockVariable;
3278 List<INamedBlockVariable> existing_list;
3279 if (existing != null) {
3280 existing_list = new List<INamedBlockVariable> ();
3281 existing_list.Add (existing);
3282 names[name] = existing_list;
3284 existing_list = (List<INamedBlockVariable>) value;
3288 // A collision checking between local names
3290 var variable_block = li.Block.Explicit;
3291 for (int i = 0; i < existing_list.Count; ++i) {
3292 existing = existing_list[i];
3293 Block b = existing.Block.Explicit;
3295 // Collision at same level
3296 if (variable_block == b) {
3297 li.Block.Error_AlreadyDeclared (name, li);
3301 // Collision with parent
3302 Block parent = variable_block;
3303 while ((parent = parent.Parent) != null) {
3305 li.Block.Error_AlreadyDeclared (name, li, "parent or current");
3306 i = existing_list.Count;
3311 if (!ignoreChildrenBlocks && variable_block.Parent != b.Parent) {
3312 // Collision with children
3313 while ((b = b.Parent) != null) {
3314 if (variable_block == b) {
3315 li.Block.Error_AlreadyDeclared (name, li, "child");
3316 i = existing_list.Count;
3323 existing_list.Add (li);
3326 public void AddLabel (string name, LabeledStatement label)
3329 labels = new Dictionary<string, object> ();
3332 if (!labels.TryGetValue (name, out value)) {
3333 labels.Add (name, label);
3337 LabeledStatement existing = value as LabeledStatement;
3338 List<LabeledStatement> existing_list;
3339 if (existing != null) {
3340 existing_list = new List<LabeledStatement> ();
3341 existing_list.Add (existing);
3342 labels[name] = existing_list;
3344 existing_list = (List<LabeledStatement>) value;
3348 // A collision checking between labels
3350 for (int i = 0; i < existing_list.Count; ++i) {
3351 existing = existing_list[i];
3352 Block b = existing.Block;
3354 // Collision at same level
3355 if (label.Block == b) {
3356 Report.SymbolRelatedToPreviousError (existing.loc, name);
3357 Report.Error (140, label.loc, "The label `{0}' is a duplicate", name);
3361 // Collision with parent
3363 while ((b = b.Parent) != null) {
3364 if (existing.Block == b) {
3365 Report.Error (158, label.loc,
3366 "The label `{0}' shadows another label by the same name in a contained scope", name);
3367 i = existing_list.Count;
3372 // Collision with with children
3374 while ((b = b.Parent) != null) {
3375 if (label.Block == b) {
3376 Report.Error (158, label.loc,
3377 "The label `{0}' shadows another label by the same name in a contained scope", name);
3378 i = existing_list.Count;
3384 existing_list.Add (label);
3387 public void AddThisReferenceFromChildrenBlock (ExplicitBlock block)
3389 if (this_references == null)
3390 this_references = new List<ExplicitBlock> ();
3392 if (!this_references.Contains (block))
3393 this_references.Add (block);
3396 public void RemoveThisReferenceFromChildrenBlock (ExplicitBlock block)
3398 this_references.Remove (block);
3402 // Creates an arguments set from all parameters, useful for method proxy calls
3404 public Arguments GetAllParametersArguments ()
3406 int count = parameters.Count;
3407 Arguments args = new Arguments (count);
3408 for (int i = 0; i < count; ++i) {
3409 var arg_expr = GetParameterReference (i, parameter_info[i].Location);
3410 args.Add (new Argument (arg_expr));
3417 // Lookup inside a block, the returned value can represent 3 states
3419 // true+variable: A local name was found and it's valid
3420 // false+variable: A local name was found in a child block only
3421 // false+null: No local name was found
3423 public bool GetLocalName (string name, Block block, ref INamedBlockVariable variable)
3429 if (!names.TryGetValue (name, out value))
3432 variable = value as INamedBlockVariable;
3434 if (variable != null) {
3436 if (variable.Block == b.Original)
3440 } while (b != null);
3448 } while (b != null);
3450 List<INamedBlockVariable> list = (List<INamedBlockVariable>) value;
3451 for (int i = 0; i < list.Count; ++i) {
3454 if (variable.Block == b.Original)
3458 } while (b != null);
3466 } while (b != null);
3476 public LabeledStatement GetLabel (string name, Block block)
3482 if (!labels.TryGetValue (name, out value)) {
3486 var label = value as LabeledStatement;
3488 if (label != null) {
3489 if (label.Block == b.Original)
3492 List<LabeledStatement> list = (List<LabeledStatement>) value;
3493 for (int i = 0; i < list.Count; ++i) {
3495 if (label.Block == b.Original)
3504 // This is used by non-static `struct' constructors which do not have an
3505 // initializer - in this case, the constructor must initialize all of the
3506 // struct's fields. To do this, we add a "this" variable and use the flow
3507 // analysis code to ensure that it's been fully initialized before control
3508 // leaves the constructor.
3510 public void AddThisVariable (BlockContext bc)
3512 if (this_variable != null)
3513 throw new InternalErrorException (StartLocation.ToString ());
3515 this_variable = new LocalVariable (this, "this", LocalVariable.Flags.IsThis | LocalVariable.Flags.Used, StartLocation);
3516 this_variable.Type = bc.CurrentType;
3517 this_variable.PrepareForFlowAnalysis (bc);
3520 public bool IsThisAssigned (BlockContext ec)
3522 return this_variable == null || this_variable.IsThisAssigned (ec, this);
3525 public override void Emit (EmitContext ec)
3527 if (Report.Errors > 0)
3531 if (IsCompilerGenerated) {
3532 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
3540 // If `HasReturnLabel' is set, then we already emitted a
3541 // jump to the end of the method, so we must emit a `ret'
3544 // Unfortunately, System.Reflection.Emit automatically emits
3545 // a leave to the end of a finally block. This is a problem
3546 // if no code is following the try/finally block since we may
3547 // jump to a point after the end of the method.
3548 // As a workaround, we're always creating a return label in
3551 if (ec.HasReturnLabel || !unreachable) {
3552 if (ec.HasReturnLabel)
3553 ec.MarkLabel (ec.ReturnLabel);
3555 if (ec.EmitAccurateDebugInfo && !IsCompilerGenerated)
3556 ec.Mark (EndLocation);
3558 if (ec.ReturnType.Kind != MemberKind.Void)
3559 ec.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3561 ec.Emit (OpCodes.Ret);
3564 } catch (Exception e) {
3565 throw new InternalErrorException (e, StartLocation);
3570 public class SwitchLabel : Statement
3578 // if expr == null, then it is the default case.
3580 public SwitchLabel (Expression expr, Location l)
3586 public bool IsDefault {
3588 return label == null;
3592 public Expression Label {
3598 public Location Location {
3604 public Constant Converted {
3613 public bool SectionStart { get; set; }
3615 public Label GetILLabel (EmitContext ec)
3617 if (il_label == null){
3618 il_label = ec.DefineLabel ();
3621 return il_label.Value;
3624 protected override void DoEmit (EmitContext ec)
3626 ec.MarkLabel (GetILLabel (ec));
3629 public override bool Resolve (BlockContext bc)
3631 bc.CurrentBranching.CurrentUsageVector.ResetBarrier ();
3633 return base.Resolve (bc);
3637 // Resolves the expression, reduces it to a literal if possible
3638 // and then converts it to the requested type.
3640 public bool ResolveAndReduce (ResolveContext ec, TypeSpec required_type, bool allow_nullable)
3642 Expression e = label.Resolve (ec);
3647 Constant c = e as Constant;
3649 ec.Report.Error (150, loc, "A constant value is expected");
3653 if (allow_nullable && c is NullLiteral) {
3658 converted = c.ImplicitConversionRequired (ec, required_type, loc);
3659 return converted != null;
3662 public void Error_AlreadyOccurs (ResolveContext ec, TypeSpec switch_type, SwitchLabel collision_with)
3665 if (converted == null)
3668 label = converted.GetValueAsLiteral ();
3670 ec.Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3671 ec.Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3674 protected override void CloneTo (CloneContext clonectx, Statement target)
3676 var t = (SwitchLabel) target;
3678 t.label = label.Clone (clonectx);
3681 public override object Accept (StructuralVisitor visitor)
3683 return visitor.Visit (this);
3687 public class Switch : Statement
3689 // structure used to hold blocks of keys while calculating table switch
3690 sealed class LabelsRange : IComparable<LabelsRange>
3692 public readonly long min;
3694 public readonly List<long> label_values;
3696 public LabelsRange (long value)
3699 label_values = new List<long> ();
3700 label_values.Add (value);
3703 public LabelsRange (long min, long max, ICollection<long> values)
3707 this.label_values = new List<long> (values);
3712 return max - min + 1;
3716 public bool AddValue (long value)
3718 var gap = value - min + 1;
3719 // Ensure the range has > 50% occupancy
3720 if (gap > 2 * (label_values.Count + 1) || gap <= 0)
3724 label_values.Add (value);
3728 public int CompareTo (LabelsRange other)
3730 int nLength = label_values.Count;
3731 int nLengthOther = other.label_values.Count;
3732 if (nLengthOther == nLength)
3733 return (int) (other.min - min);
3735 return nLength - nLengthOther;
3739 sealed class DispatchStatement : Statement
3741 readonly Switch body;
3743 public DispatchStatement (Switch body)
3748 protected override void CloneTo (CloneContext clonectx, Statement target)
3750 throw new NotImplementedException ();
3753 protected override void DoEmit (EmitContext ec)
3755 body.EmitDispatch (ec);
3759 public Expression Expr;
3762 // Mapping of all labels to their SwitchLabels
3764 Dictionary<long, SwitchLabel> labels;
3765 Dictionary<string, SwitchLabel> string_labels;
3766 List<SwitchLabel> case_labels;
3769 /// The governing switch type
3771 public TypeSpec SwitchType;
3773 Expression new_expr;
3775 SwitchLabel case_null;
3776 SwitchLabel case_default;
3778 Label defaultLabel, nullLabel;
3779 VariableReference value;
3780 ExpressionStatement string_dictionary;
3781 FieldExpr switch_cache_field;
3782 ExplicitBlock block;
3785 // Nullable Types support
3787 Nullable.Unwrap unwrap;
3789 public Switch (Expression e, ExplicitBlock block, Location l)
3796 public ExplicitBlock Block {
3802 public SwitchLabel DefaultLabel {
3804 return case_default;
3808 public bool IsNullable {
3810 return unwrap != null;
3815 // Determines the governing type for a switch. The returned
3816 // expression might be the expression from the switch, or an
3817 // expression that includes any potential conversions to
3819 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3821 switch (expr.Type.BuiltinType) {
3822 case BuiltinTypeSpec.Type.Byte:
3823 case BuiltinTypeSpec.Type.SByte:
3824 case BuiltinTypeSpec.Type.UShort:
3825 case BuiltinTypeSpec.Type.Short:
3826 case BuiltinTypeSpec.Type.UInt:
3827 case BuiltinTypeSpec.Type.Int:
3828 case BuiltinTypeSpec.Type.ULong:
3829 case BuiltinTypeSpec.Type.Long:
3830 case BuiltinTypeSpec.Type.Char:
3831 case BuiltinTypeSpec.Type.String:
3832 case BuiltinTypeSpec.Type.Bool:
3836 if (expr.Type.IsEnum)
3840 // Try to find a *user* defined implicit conversion.
3842 // If there is no implicit conversion, or if there are multiple
3843 // conversions, we have to report an error
3845 Expression converted = null;
3846 foreach (TypeSpec tt in ec.BuiltinTypes.SwitchUserTypes) {
3849 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3854 // Ignore over-worked ImplicitUserConversions that do
3855 // an implicit conversion in addition to the user conversion.
3857 if (!(e is UserCast))
3860 if (converted != null){
3861 ec.Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3870 public static TypeSpec[] CreateSwitchUserTypes (BuiltinTypes types)
3872 // LAMESPEC: For some reason it does not contain bool which looks like csc bug
3888 // Performs the basic sanity checks on the switch statement
3889 // (looks for duplicate keys and non-constant expressions).
3891 // It also returns a hashtable with the keys that we will later
3892 // use to compute the switch tables
3894 bool ResolveLabels (ResolveContext ec, Constant value)
3897 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
3898 string_labels = new Dictionary<string, SwitchLabel> ();
3900 labels = new Dictionary<long, SwitchLabel> ();
3903 case_labels = new List<SwitchLabel> ();
3904 int default_label_index = -1;
3905 bool constant_label_found = false;
3907 for (int i = 0; i < block.Statements.Count; ++i) {
3908 var s = block.Statements[i];
3910 var sl = s as SwitchLabel;
3915 case_labels.Add (sl);
3918 if (case_default != null) {
3919 sl.Error_AlreadyOccurs (ec, SwitchType, case_default);
3923 default_label_index = i;
3928 if (!sl.ResolveAndReduce (ec, SwitchType, IsNullable)) {
3934 if (string_labels != null) {
3935 string string_value = sl.Converted.GetValue () as string;
3936 if (string_value == null)
3939 string_labels.Add (string_value, sl);
3941 if (sl.Converted is NullLiteral) {
3944 labels.Add (sl.Converted.GetValueAsLong (), sl);
3947 } catch (ArgumentException) {
3948 if (string_labels != null)
3949 sl.Error_AlreadyOccurs (ec, SwitchType, string_labels[(string) sl.Converted.GetValue ()]);
3951 sl.Error_AlreadyOccurs (ec, SwitchType, labels[sl.Converted.GetValueAsLong ()]);
3956 if (value != null) {
3957 var constant_label = constant_label_found ? null : FindLabel (value);
3958 if (constant_label == null || constant_label != sl)
3959 block.Statements[i] = new EmptyStatement (s.loc);
3961 constant_label_found = true;
3965 if (value != null && constant_label_found && default_label_index >= 0)
3966 block.Statements[default_label_index] = new EmptyStatement (case_default.loc);
3972 // This method emits code for a lookup-based switch statement (non-string)
3973 // Basically it groups the cases into blocks that are at least half full,
3974 // and then spits out individual lookup opcodes for each block.
3975 // It emits the longest blocks first, and short blocks are just
3976 // handled with direct compares.
3978 void EmitTableSwitch (EmitContext ec, Expression val)
3980 if (labels != null && labels.Count > 0) {
3981 List<LabelsRange> ranges;
3982 if (string_labels != null) {
3983 // We have done all hard work for string already
3984 // setup single range only
3985 ranges = new List<LabelsRange> (1);
3986 ranges.Add (new LabelsRange (0, labels.Count - 1, labels.Keys));
3988 var element_keys = new long[labels.Count];
3989 labels.Keys.CopyTo (element_keys, 0);
3990 Array.Sort (element_keys);
3993 // Build possible ranges of switch labes to reduce number
3996 ranges = new List<LabelsRange> (element_keys.Length);
3997 var range = new LabelsRange (element_keys[0]);
3999 for (int i = 1; i < element_keys.Length; ++i) {
4000 var l = element_keys[i];
4001 if (range.AddValue (l))
4004 range = new LabelsRange (l);
4008 // sort the blocks so we can tackle the largest ones first
4012 Label lbl_default = defaultLabel;
4013 TypeSpec compare_type = SwitchType.IsEnum ? EnumSpec.GetUnderlyingType (SwitchType) : SwitchType;
4015 for (int range_index = ranges.Count - 1; range_index >= 0; --range_index) {
4016 LabelsRange kb = ranges[range_index];
4017 lbl_default = (range_index == 0) ? defaultLabel : ec.DefineLabel ();
4019 // Optimize small ranges using simple equality check
4020 if (kb.Range <= 2) {
4021 foreach (var key in kb.label_values) {
4022 SwitchLabel sl = labels[key];
4023 if (sl == case_default || sl == case_null)
4026 if (sl.Converted.IsZeroInteger) {
4027 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
4030 sl.Converted.Emit (ec);
4031 ec.Emit (OpCodes.Beq, sl.GetILLabel (ec));
4035 // TODO: if all the keys in the block are the same and there are
4036 // no gaps/defaults then just use a range-check.
4037 if (compare_type.BuiltinType == BuiltinTypeSpec.Type.Long || compare_type.BuiltinType == BuiltinTypeSpec.Type.ULong) {
4038 // TODO: optimize constant/I4 cases
4040 // check block range (could be > 2^31)
4042 ec.EmitLong (kb.min);
4043 ec.Emit (OpCodes.Blt, lbl_default);
4046 ec.EmitLong (kb.max);
4047 ec.Emit (OpCodes.Bgt, lbl_default);
4052 ec.EmitLong (kb.min);
4053 ec.Emit (OpCodes.Sub);
4056 ec.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
4060 int first = (int) kb.min;
4063 ec.Emit (OpCodes.Sub);
4064 } else if (first < 0) {
4065 ec.EmitInt (-first);
4066 ec.Emit (OpCodes.Add);
4070 // first, build the list of labels for the switch
4072 long cJumps = kb.Range;
4073 Label[] switch_labels = new Label[cJumps];
4074 for (int iJump = 0; iJump < cJumps; iJump++) {
4075 var key = kb.label_values[iKey];
4076 if (key == kb.min + iJump) {
4077 switch_labels[iJump] = labels[key].GetILLabel (ec);
4080 switch_labels[iJump] = lbl_default;
4084 // emit the switch opcode
4085 ec.Emit (OpCodes.Switch, switch_labels);
4088 // mark the default for this block
4089 if (range_index != 0)
4090 ec.MarkLabel (lbl_default);
4093 // the last default just goes to the end
4094 if (ranges.Count > 0)
4095 ec.Emit (OpCodes.Br, lbl_default);
4099 SwitchLabel FindLabel (Constant value)
4101 SwitchLabel sl = null;
4103 if (string_labels != null) {
4104 string s = value.GetValue () as string;
4106 if (case_null != null)
4108 else if (case_default != null)
4111 string_labels.TryGetValue (s, out sl);
4114 if (value is NullLiteral) {
4117 labels.TryGetValue (value.GetValueAsLong (), out sl);
4124 public override bool Resolve (BlockContext ec)
4126 Expr = Expr.Resolve (ec);
4130 new_expr = SwitchGoverningType (ec, Expr);
4132 if (new_expr == null && Expr.Type.IsNullableType) {
4133 unwrap = Nullable.Unwrap.Create (Expr, false);
4137 new_expr = SwitchGoverningType (ec, unwrap);
4140 if (new_expr == null) {
4141 if (Expr.Type != InternalType.ErrorType) {
4142 ec.Report.Error (151, loc,
4143 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
4144 Expr.Type.GetSignatureForError ());
4151 SwitchType = new_expr.Type;
4153 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.Bool && ec.Module.Compiler.Settings.Version == LanguageVersion.ISO_1) {
4154 ec.Report.FeatureIsNotAvailable (ec.Module.Compiler, loc, "switch expression of boolean type");
4158 if (block.Statements.Count == 0)
4161 var constant = new_expr as Constant;
4163 if (!ResolveLabels (ec, constant))
4167 // Don't need extra variable for constant switch or switch with
4168 // only default case
4170 if (constant == null && (case_labels.Count - (case_default != null ? 1 : 0)) != 0) {
4172 // Store switch expression for comparison purposes
4174 value = new_expr as VariableReference;
4175 if (value == null) {
4176 // Create temporary variable inside switch scope
4177 var current_block = ec.CurrentBlock;
4178 ec.CurrentBlock = Block;
4179 value = TemporaryVariableReference.Create (SwitchType, ec.CurrentBlock, loc);
4181 ec.CurrentBlock = current_block;
4185 Switch old_switch = ec.Switch;
4187 ec.Switch.SwitchType = SwitchType;
4189 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
4191 ec.CurrentBranching.CurrentUsageVector.Goto ();
4193 var ok = block.Resolve (ec);
4195 if (case_default == null)
4196 ec.CurrentBranching.CurrentUsageVector.ResetBarrier ();
4198 ec.EndFlowBranching ();
4199 ec.Switch = old_switch;
4204 if (constant == null && SwitchType.BuiltinType == BuiltinTypeSpec.Type.String && string_labels.Count > 6) {
4205 ResolveStringSwitchMap (ec);
4209 // Needed to emit anonymous storey initialization before
4210 // any generated switch dispatch
4212 block.AddScopeStatement (new DispatchStatement (this));
4217 public SwitchLabel ResolveGotoCase (ResolveContext rc, Constant value)
4219 var sl = FindLabel (value);
4222 FlowBranchingBlock.Error_UnknownLabel (loc, "case " + value.GetValueAsLiteral (), rc.Report);
4229 // Converts string switch into string hashtable
4231 void ResolveStringSwitchMap (ResolveContext ec)
4233 FullNamedExpression string_dictionary_type;
4234 if (ec.Module.PredefinedTypes.Dictionary.Define ()) {
4235 string_dictionary_type = new TypeExpression (
4236 ec.Module.PredefinedTypes.Dictionary.TypeSpec.MakeGenericType (ec,
4237 new [] { ec.BuiltinTypes.String, ec.BuiltinTypes.Int }),
4239 } else if (ec.Module.PredefinedTypes.Hashtable.Define ()) {
4240 string_dictionary_type = new TypeExpression (ec.Module.PredefinedTypes.Hashtable.TypeSpec, loc);
4242 ec.Module.PredefinedTypes.Dictionary.Resolve ();
4246 var ctype = ec.CurrentMemberDefinition.Parent.PartialContainer;
4247 Field field = new Field (ctype, string_dictionary_type,
4248 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
4249 new MemberName (CompilerGeneratedContainer.MakeName (null, "f", "switch$map", ec.Module.CounterSwitchTypes++), loc), null);
4250 if (!field.Define ())
4252 ctype.AddField (field);
4254 var init = new List<Expression> ();
4256 labels = new Dictionary<long, SwitchLabel> (string_labels.Count);
4257 string value = null;
4259 foreach (SwitchLabel sl in case_labels) {
4261 if (sl.SectionStart)
4262 labels.Add (++counter, sl);
4264 if (sl == case_default || sl == case_null)
4267 value = (string) sl.Converted.GetValue ();
4268 var init_args = new List<Expression> (2);
4269 init_args.Add (new StringLiteral (ec.BuiltinTypes, value, sl.Location));
4271 sl.Converted = new IntConstant (ec.BuiltinTypes, counter, loc);
4272 init_args.Add (sl.Converted);
4274 init.Add (new CollectionElementInitializer (init_args, loc));
4277 Arguments args = new Arguments (1);
4278 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, init.Count, loc)));
4279 Expression initializer = new NewInitialize (string_dictionary_type, args,
4280 new CollectionOrObjectInitializers (init, loc), loc);
4282 switch_cache_field = new FieldExpr (field, loc);
4283 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
4286 void DoEmitStringSwitch (EmitContext ec)
4288 Label l_initialized = ec.DefineLabel ();
4291 // Skip initialization when value is null
4293 value.EmitBranchable (ec, nullLabel, false);
4296 // Check if string dictionary is initialized and initialize
4298 switch_cache_field.EmitBranchable (ec, l_initialized, true);
4299 using (ec.With (BuilderContext.Options.OmitDebugInfo, true)) {
4300 string_dictionary.EmitStatement (ec);
4302 ec.MarkLabel (l_initialized);
4304 LocalTemporary string_switch_variable = new LocalTemporary (ec.BuiltinTypes.Int);
4306 ResolveContext rc = new ResolveContext (ec.MemberContext);
4308 if (switch_cache_field.Type.IsGeneric) {
4309 Arguments get_value_args = new Arguments (2);
4310 get_value_args.Add (new Argument (value));
4311 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
4312 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
4313 if (get_item == null)
4317 // A value was not found, go to default case
4319 get_item.EmitBranchable (ec, defaultLabel, false);
4321 Arguments get_value_args = new Arguments (1);
4322 get_value_args.Add (new Argument (value));
4324 Expression get_item = new ElementAccess (switch_cache_field, get_value_args, loc).Resolve (rc);
4325 if (get_item == null)
4328 LocalTemporary get_item_object = new LocalTemporary (ec.BuiltinTypes.Object);
4329 get_item_object.EmitAssign (ec, get_item, true, false);
4330 ec.Emit (OpCodes.Brfalse, defaultLabel);
4332 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
4333 new Cast (new TypeExpression (ec.BuiltinTypes.Int, loc), get_item_object, loc)).Resolve (rc);
4335 get_item_int.EmitStatement (ec);
4336 get_item_object.Release (ec);
4339 EmitTableSwitch (ec, string_switch_variable);
4340 string_switch_variable.Release (ec);
4344 // Emits switch using simple if/else comparison for small label count (4 + optional default)
4346 void EmitShortSwitch (EmitContext ec)
4348 MethodSpec equal_method = null;
4349 if (SwitchType.BuiltinType == BuiltinTypeSpec.Type.String) {
4350 equal_method = ec.Module.PredefinedMembers.StringEqual.Resolve (loc);
4353 if (equal_method != null) {
4354 value.EmitBranchable (ec, nullLabel, false);
4357 for (int i = 0; i < case_labels.Count; ++i) {
4358 var label = case_labels [i];
4359 if (label == case_default || label == case_null)
4362 var constant = label.Converted;
4364 if (equal_method != null) {
4368 var call = new CallEmitter ();
4369 call.EmitPredefined (ec, equal_method, new Arguments (0));
4370 ec.Emit (OpCodes.Brtrue, label.GetILLabel (ec));
4374 if (constant.IsZeroInteger && constant.Type.BuiltinType != BuiltinTypeSpec.Type.Long && constant.Type.BuiltinType != BuiltinTypeSpec.Type.ULong) {
4375 value.EmitBranchable (ec, label.GetILLabel (ec), false);
4381 ec.Emit (OpCodes.Beq, label.GetILLabel (ec));
4384 ec.Emit (OpCodes.Br, defaultLabel);
4387 void EmitDispatch (EmitContext ec)
4389 if (value == null) {
4391 // Constant switch, we already done the work
4397 // Mark sequence point explicitly to switch
4399 ec.Mark (block.StartLocation);
4400 block.IsCompilerGenerated = true;
4402 if (string_dictionary != null) {
4403 DoEmitStringSwitch (ec);
4404 } else if (case_labels.Count < 4 || string_labels != null) {
4405 EmitShortSwitch (ec);
4407 EmitTableSwitch (ec, value);
4411 protected override void DoEmit (EmitContext ec)
4413 // Workaround broken flow-analysis
4414 block.HasUnreachableClosingBrace = true;
4417 // Setup the codegen context
4419 Label old_end = ec.LoopEnd;
4420 Switch old_switch = ec.Switch;
4422 ec.LoopEnd = ec.DefineLabel ();
4425 defaultLabel = case_default == null ? ec.LoopEnd : case_default.GetILLabel (ec);
4426 nullLabel = case_null == null ? defaultLabel : case_null.GetILLabel (ec);
4428 if (value != null) {
4431 unwrap.EmitCheck (ec);
4432 ec.Emit (OpCodes.Brfalse, nullLabel);
4433 value.EmitAssign (ec, new_expr, false, false);
4434 } else if (new_expr != value) {
4435 value.EmitAssign (ec, new_expr, false, false);
4441 // Restore context state.
4442 ec.MarkLabel (ec.LoopEnd);
4445 // Restore the previous context
4447 ec.LoopEnd = old_end;
4448 ec.Switch = old_switch;
4451 protected override void CloneTo (CloneContext clonectx, Statement t)
4453 Switch target = (Switch) t;
4455 target.Expr = Expr.Clone (clonectx);
4456 target.block = (ExplicitBlock) block.Clone (clonectx);
4459 public override object Accept (StructuralVisitor visitor)
4461 return visitor.Visit (this);
4465 // A place where execution can restart in an iterator
4466 public abstract class ResumableStatement : Statement
4469 protected Label resume_point;
4471 public Label PrepareForEmit (EmitContext ec)
4475 resume_point = ec.DefineLabel ();
4477 return resume_point;
4480 public virtual Label PrepareForDispose (EmitContext ec, Label end)
4485 public virtual void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4490 public abstract class TryFinallyBlock : ExceptionStatement
4492 protected Statement stmt;
4493 Label dispose_try_block;
4494 bool prepared_for_dispose, emitted_dispose;
4495 Method finally_host;
4497 protected TryFinallyBlock (Statement stmt, Location loc)
4505 public Statement Statement {
4513 protected abstract void EmitTryBody (EmitContext ec);
4514 public abstract void EmitFinallyBody (EmitContext ec);
4516 public override Label PrepareForDispose (EmitContext ec, Label end)
4518 if (!prepared_for_dispose) {
4519 prepared_for_dispose = true;
4520 dispose_try_block = ec.DefineLabel ();
4522 return dispose_try_block;
4525 protected sealed override void DoEmit (EmitContext ec)
4527 EmitTryBodyPrepare (ec);
4530 ec.BeginFinallyBlock ();
4532 Label start_finally = ec.DefineLabel ();
4533 if (resume_points != null) {
4534 var state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4536 ec.Emit (OpCodes.Ldloc, state_machine.SkipFinally);
4537 ec.Emit (OpCodes.Brfalse_S, start_finally);
4538 ec.Emit (OpCodes.Endfinally);
4541 ec.MarkLabel (start_finally);
4543 if (finally_host != null) {
4544 finally_host.Define ();
4545 finally_host.PrepareEmit ();
4546 finally_host.Emit ();
4548 // Now it's safe to add, to close it properly and emit sequence points
4549 finally_host.Parent.AddMember (finally_host);
4551 var ce = new CallEmitter ();
4552 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4553 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4555 EmitFinallyBody (ec);
4558 ec.EndExceptionBlock ();
4561 public override void EmitForDispose (EmitContext ec, LocalBuilder pc, Label end, bool have_dispatcher)
4563 if (emitted_dispose)
4566 emitted_dispose = true;
4568 Label end_of_try = ec.DefineLabel ();
4570 // Ensure that the only way we can get into this code is through a dispatcher
4571 if (have_dispatcher)
4572 ec.Emit (OpCodes.Br, end);
4574 ec.BeginExceptionBlock ();
4576 ec.MarkLabel (dispose_try_block);
4578 Label[] labels = null;
4579 for (int i = 0; i < resume_points.Count; ++i) {
4580 ResumableStatement s = resume_points[i];
4581 Label ret = s.PrepareForDispose (ec, end_of_try);
4582 if (ret.Equals (end_of_try) && labels == null)
4584 if (labels == null) {
4585 labels = new Label[resume_points.Count];
4586 for (int j = 0; j < i; ++j)
4587 labels[j] = end_of_try;
4592 if (labels != null) {
4594 for (j = 1; j < labels.Length; ++j)
4595 if (!labels[0].Equals (labels[j]))
4597 bool emit_dispatcher = j < labels.Length;
4599 if (emit_dispatcher) {
4600 ec.Emit (OpCodes.Ldloc, pc);
4601 ec.EmitInt (first_resume_pc);
4602 ec.Emit (OpCodes.Sub);
4603 ec.Emit (OpCodes.Switch, labels);
4606 foreach (ResumableStatement s in resume_points)
4607 s.EmitForDispose (ec, pc, end_of_try, emit_dispatcher);
4610 ec.MarkLabel (end_of_try);
4612 ec.BeginFinallyBlock ();
4614 if (finally_host != null) {
4615 var ce = new CallEmitter ();
4616 ce.InstanceExpression = new CompilerGeneratedThis (ec.CurrentType, loc);
4617 ce.EmitPredefined (ec, finally_host.Spec, new Arguments (0));
4619 EmitFinallyBody (ec);
4622 ec.EndExceptionBlock ();
4625 public override bool Resolve (BlockContext bc)
4628 // Finally block inside iterator is called from MoveNext and
4629 // Dispose methods that means we need to lift the block into
4630 // newly created host method to emit the body only once. The
4631 // original block then simply calls the newly generated method.
4633 if (bc.CurrentIterator != null && !bc.IsInProbingMode) {
4634 var b = stmt as Block;
4635 if (b != null && b.Explicit.HasYield) {
4636 finally_host = bc.CurrentIterator.CreateFinallyHost (this);
4640 return base.Resolve (bc);
4645 // Base class for blocks using exception handling
4647 public abstract class ExceptionStatement : ResumableStatement
4652 protected List<ResumableStatement> resume_points;
4653 protected int first_resume_pc;
4655 protected ExceptionStatement (Location loc)
4660 protected virtual void EmitTryBodyPrepare (EmitContext ec)
4662 StateMachineInitializer state_machine = null;
4663 if (resume_points != null) {
4664 state_machine = (StateMachineInitializer) ec.CurrentAnonymousMethod;
4666 ec.EmitInt ((int) IteratorStorey.State.Running);
4667 ec.Emit (OpCodes.Stloc, state_machine.CurrentPC);
4670 ec.BeginExceptionBlock ();
4672 if (resume_points != null) {
4673 ec.MarkLabel (resume_point);
4675 // For normal control flow, we want to fall-through the Switch
4676 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4677 ec.Emit (OpCodes.Ldloc, state_machine.CurrentPC);
4678 ec.EmitInt (first_resume_pc);
4679 ec.Emit (OpCodes.Sub);
4681 Label[] labels = new Label[resume_points.Count];
4682 for (int i = 0; i < resume_points.Count; ++i)
4683 labels[i] = resume_points[i].PrepareForEmit (ec);
4684 ec.Emit (OpCodes.Switch, labels);
4688 public void SomeCodeFollows ()
4691 code_follows = true;
4695 public override bool Resolve (BlockContext ec)
4698 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4699 // So, ensure there's some IL code after this statement.
4700 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4701 ec.NeedReturnLabel ();
4706 public void AddResumePoint (ResumableStatement stmt, int pc)
4708 if (resume_points == null) {
4709 resume_points = new List<ResumableStatement> ();
4710 first_resume_pc = pc;
4713 if (pc != first_resume_pc + resume_points.Count)
4714 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4716 resume_points.Add (stmt);
4721 public class Lock : TryFinallyBlock
4724 TemporaryVariableReference expr_copy;
4725 TemporaryVariableReference lock_taken;
4727 public Lock (Expression expr, Statement stmt, Location loc)
4733 public Expression Expr {
4739 public override bool Resolve (BlockContext ec)
4741 expr = expr.Resolve (ec);
4745 if (!TypeSpec.IsReferenceType (expr.Type)) {
4746 ec.Report.Error (185, loc,
4747 "`{0}' is not a reference type as required by the lock statement",
4748 expr.Type.GetSignatureForError ());
4751 if (expr.Type.IsGenericParameter) {
4752 expr = Convert.ImplicitTypeParameterConversion (expr, (TypeParameterSpec)expr.Type, ec.BuiltinTypes.Object);
4755 VariableReference lv = expr as VariableReference;
4758 locked = lv.IsLockedByStatement;
4759 lv.IsLockedByStatement = true;
4766 // Have to keep original lock value around to unlock same location
4767 // in the case of original value has changed or is null
4769 expr_copy = TemporaryVariableReference.Create (ec.BuiltinTypes.Object, ec.CurrentBlock, loc);
4770 expr_copy.Resolve (ec);
4773 // Ensure Monitor methods are available
4775 if (ResolvePredefinedMethods (ec) > 1) {
4776 lock_taken = TemporaryVariableReference.Create (ec.BuiltinTypes.Bool, ec.CurrentBlock, loc);
4777 lock_taken.Resolve (ec);
4780 using (ec.Set (ResolveContext.Options.LockScope)) {
4781 ec.StartFlowBranching (this);
4782 Statement.Resolve (ec);
4783 ec.EndFlowBranching ();
4787 lv.IsLockedByStatement = locked;
4795 protected override void EmitTryBodyPrepare (EmitContext ec)
4797 expr_copy.EmitAssign (ec, expr);
4799 if (lock_taken != null) {
4801 // Initialize ref variable
4803 lock_taken.EmitAssign (ec, new BoolLiteral (ec.BuiltinTypes, false, loc));
4806 // Monitor.Enter (expr_copy)
4808 expr_copy.Emit (ec);
4809 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter.Get ());
4812 base.EmitTryBodyPrepare (ec);
4815 protected override void EmitTryBody (EmitContext ec)
4818 // Monitor.Enter (expr_copy, ref lock_taken)
4820 if (lock_taken != null) {
4821 expr_copy.Emit (ec);
4822 lock_taken.LocalInfo.CreateBuilder (ec);
4823 lock_taken.AddressOf (ec, AddressOp.Load);
4824 ec.Emit (OpCodes.Call, ec.Module.PredefinedMembers.MonitorEnter_v4.Get ());
4827 Statement.Emit (ec);
4830 public override void EmitFinallyBody (EmitContext ec)
4833 // if (lock_taken) Monitor.Exit (expr_copy)
4835 Label skip = ec.DefineLabel ();
4837 if (lock_taken != null) {
4838 lock_taken.Emit (ec);
4839 ec.Emit (OpCodes.Brfalse_S, skip);
4842 expr_copy.Emit (ec);
4843 var m = ec.Module.PredefinedMembers.MonitorExit.Resolve (loc);
4845 ec.Emit (OpCodes.Call, m);
4847 ec.MarkLabel (skip);
4850 int ResolvePredefinedMethods (ResolveContext rc)
4852 // Try 4.0 Monitor.Enter (object, ref bool) overload first
4853 var m = rc.Module.PredefinedMembers.MonitorEnter_v4.Get ();
4857 m = rc.Module.PredefinedMembers.MonitorEnter.Get ();
4861 rc.Module.PredefinedMembers.MonitorEnter_v4.Resolve (loc);
4865 protected override void CloneTo (CloneContext clonectx, Statement t)
4867 Lock target = (Lock) t;
4869 target.expr = expr.Clone (clonectx);
4870 target.stmt = Statement.Clone (clonectx);
4873 public override object Accept (StructuralVisitor visitor)
4875 return visitor.Visit (this);
4880 public class Unchecked : Statement {
4883 public Unchecked (Block b, Location loc)
4890 public override bool Resolve (BlockContext ec)
4892 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4893 return Block.Resolve (ec);
4896 protected override void DoEmit (EmitContext ec)
4898 using (ec.With (EmitContext.Options.CheckedScope, false))
4902 protected override void CloneTo (CloneContext clonectx, Statement t)
4904 Unchecked target = (Unchecked) t;
4906 target.Block = clonectx.LookupBlock (Block);
4909 public override object Accept (StructuralVisitor visitor)
4911 return visitor.Visit (this);
4915 public class Checked : Statement {
4918 public Checked (Block b, Location loc)
4921 b.Unchecked = false;
4925 public override bool Resolve (BlockContext ec)
4927 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4928 return Block.Resolve (ec);
4931 protected override void DoEmit (EmitContext ec)
4933 using (ec.With (EmitContext.Options.CheckedScope, true))
4937 protected override void CloneTo (CloneContext clonectx, Statement t)
4939 Checked target = (Checked) t;
4941 target.Block = clonectx.LookupBlock (Block);
4944 public override object Accept (StructuralVisitor visitor)
4946 return visitor.Visit (this);
4950 public class Unsafe : Statement {
4953 public Unsafe (Block b, Location loc)
4956 Block.Unsafe = true;
4960 public override bool Resolve (BlockContext ec)
4962 if (ec.CurrentIterator != null)
4963 ec.Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4965 using (ec.Set (ResolveContext.Options.UnsafeScope))
4966 return Block.Resolve (ec);
4969 protected override void DoEmit (EmitContext ec)
4974 protected override void CloneTo (CloneContext clonectx, Statement t)
4976 Unsafe target = (Unsafe) t;
4978 target.Block = clonectx.LookupBlock (Block);
4981 public override object Accept (StructuralVisitor visitor)
4983 return visitor.Visit (this);
4990 public class Fixed : Statement
4992 abstract class Emitter : ShimExpression
4994 protected LocalVariable vi;
4996 protected Emitter (Expression expr, LocalVariable li)
5002 public abstract void EmitExit (EmitContext ec);
5005 class ExpressionEmitter : Emitter {
5006 public ExpressionEmitter (Expression converted, LocalVariable li) :
5007 base (converted, li)
5011 protected override Expression DoResolve (ResolveContext rc)
5013 throw new NotImplementedException ();
5016 public override void Emit (EmitContext ec) {
5018 // Store pointer in pinned location
5024 public override void EmitExit (EmitContext ec)
5027 ec.Emit (OpCodes.Conv_U);
5032 class StringEmitter : Emitter
5034 LocalVariable pinned_string;
5036 public StringEmitter (Expression expr, LocalVariable li, Location loc)
5041 protected override Expression DoResolve (ResolveContext rc)
5043 pinned_string = new LocalVariable (vi.Block, "$pinned",
5044 LocalVariable.Flags.FixedVariable | LocalVariable.Flags.CompilerGenerated | LocalVariable.Flags.Used,
5046 pinned_string.Type = rc.BuiltinTypes.String;
5048 eclass = ExprClass.Variable;
5049 type = rc.BuiltinTypes.Int;
5053 public override void Emit (EmitContext ec)
5055 pinned_string.CreateBuilder (ec);
5058 pinned_string.EmitAssign (ec);
5060 // TODO: Should use Binary::Add
5061 pinned_string.Emit (ec);
5062 ec.Emit (OpCodes.Conv_I);
5064 var m = ec.Module.PredefinedMembers.RuntimeHelpersOffsetToStringData.Resolve (loc);
5068 PropertyExpr pe = new PropertyExpr (m, pinned_string.Location);
5069 //pe.InstanceExpression = pinned_string;
5070 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
5072 ec.Emit (OpCodes.Add);
5076 public override void EmitExit (EmitContext ec)
5079 pinned_string.EmitAssign (ec);
5083 public class VariableDeclaration : BlockVariable
5085 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5090 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5092 if (!Variable.Type.IsPointer && li == Variable) {
5093 bc.Report.Error (209, TypeExpression.Location,
5094 "The type of locals declared in a fixed statement must be a pointer type");
5099 // The rules for the possible declarators are pretty wise,
5100 // but the production on the grammar is more concise.
5102 // So we have to enforce these rules here.
5104 // We do not resolve before doing the case 1 test,
5105 // because the grammar is explicit in that the token &
5106 // is present, so we need to test for this particular case.
5109 if (initializer is Cast) {
5110 bc.Report.Error (254, initializer.Location, "The right hand side of a fixed statement assignment may not be a cast expression");
5114 initializer = initializer.Resolve (bc);
5116 if (initializer == null)
5122 if (initializer.Type.IsArray) {
5123 TypeSpec array_type = TypeManager.GetElementType (initializer.Type);
5126 // Provided that array_type is unmanaged,
5128 if (!TypeManager.VerifyUnmanaged (bc.Module, array_type, loc))
5132 // and T* is implicitly convertible to the
5133 // pointer type given in the fixed statement.
5135 ArrayPtr array_ptr = new ArrayPtr (initializer, array_type, loc);
5137 Expression converted = Convert.ImplicitConversionRequired (bc, array_ptr.Resolve (bc), li.Type, loc);
5138 if (converted == null)
5142 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
5144 converted = new Conditional (new BooleanExpression (new Binary (Binary.Operator.LogicalOr,
5145 new Binary (Binary.Operator.Equality, initializer, new NullLiteral (loc)),
5146 new Binary (Binary.Operator.Equality, new MemberAccess (initializer, "Length"), new IntConstant (bc.BuiltinTypes, 0, loc)))),
5147 new NullLiteral (loc),
5150 converted = converted.Resolve (bc);
5152 return new ExpressionEmitter (converted, li);
5158 if (initializer.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
5159 return new StringEmitter (initializer, li, loc).Resolve (bc);
5162 // Case 3: fixed buffer
5163 if (initializer is FixedBufferPtr) {
5164 return new ExpressionEmitter (initializer, li);
5168 // Case 4: & object.
5170 bool already_fixed = true;
5171 Unary u = initializer as Unary;
5172 if (u != null && u.Oper == Unary.Operator.AddressOf) {
5173 IVariableReference vr = u.Expr as IVariableReference;
5174 if (vr == null || !vr.IsFixed) {
5175 already_fixed = false;
5179 if (already_fixed) {
5180 bc.Report.Error (213, loc, "You cannot use the fixed statement to take the address of an already fixed expression");
5183 initializer = Convert.ImplicitConversionRequired (bc, initializer, li.Type, loc);
5184 return new ExpressionEmitter (initializer, li);
5189 VariableDeclaration decl;
5190 Statement statement;
5193 public Fixed (VariableDeclaration decl, Statement stmt, Location l)
5202 public Statement Statement {
5208 public BlockVariable Variables {
5216 public override bool Resolve (BlockContext ec)
5218 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
5219 if (!decl.Resolve (ec))
5223 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
5224 bool ok = statement.Resolve (ec);
5225 bool flow_unreachable = ec.EndFlowBranching ();
5226 has_ret = flow_unreachable;
5231 protected override void DoEmit (EmitContext ec)
5233 decl.Variable.CreateBuilder (ec);
5234 decl.Initializer.Emit (ec);
5235 if (decl.Declarators != null) {
5236 foreach (var d in decl.Declarators) {
5237 d.Variable.CreateBuilder (ec);
5238 d.Initializer.Emit (ec);
5242 statement.Emit (ec);
5248 // Clear the pinned variable
5250 ((Emitter) decl.Initializer).EmitExit (ec);
5251 if (decl.Declarators != null) {
5252 foreach (var d in decl.Declarators) {
5253 ((Emitter)d.Initializer).EmitExit (ec);
5258 protected override void CloneTo (CloneContext clonectx, Statement t)
5260 Fixed target = (Fixed) t;
5262 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5263 target.statement = statement.Clone (clonectx);
5266 public override object Accept (StructuralVisitor visitor)
5268 return visitor.Visit (this);
5272 public class Catch : Statement
5276 FullNamedExpression type_expr;
5277 CompilerAssign assign;
5280 public Catch (Block block, Location loc)
5288 public Block Block {
5294 public TypeSpec CatchType {
5300 public bool IsGeneral {
5302 return type_expr == null;
5306 public FullNamedExpression TypeExpression {
5315 public LocalVariable Variable {
5326 protected override void DoEmit (EmitContext ec)
5329 ec.BeginCatchBlock (ec.BuiltinTypes.Object);
5331 ec.BeginCatchBlock (CatchType);
5334 li.CreateBuilder (ec);
5337 // Special case hoisted catch variable, we have to use a temporary variable
5338 // to pass via anonymous storey initialization with the value still on top
5341 if (li.HoistedVariant != null) {
5342 LocalTemporary lt = new LocalTemporary (li.Type);
5345 // switch to assigning from the temporary variable and not from top of the stack
5346 assign.UpdateSource (lt);
5349 ec.Emit (OpCodes.Pop);
5355 public override bool Resolve (BlockContext ec)
5357 using (ec.With (ResolveContext.Options.CatchScope, true)) {
5358 if (type_expr != null) {
5359 type = type_expr.ResolveAsType (ec);
5363 if (type.BuiltinType != BuiltinTypeSpec.Type.Exception && !TypeSpec.IsBaseClass (type, ec.BuiltinTypes.Exception, false)) {
5364 ec.Report.Error (155, loc, "The type caught or thrown must be derived from System.Exception");
5365 } else if (li != null) {
5367 li.PrepareForFlowAnalysis (ec);
5369 // source variable is at the top of the stack
5370 Expression source = new EmptyExpression (li.Type);
5371 if (li.Type.IsGenericParameter)
5372 source = new UnboxCast (source, li.Type);
5375 // Uses Location.Null to hide from symbol file
5377 assign = new CompilerAssign (new LocalVariableReference (li, Location.Null), source, Location.Null);
5378 Block.AddScopeStatement (new StatementExpression (assign, Location.Null));
5382 return Block.Resolve (ec);
5386 protected override void CloneTo (CloneContext clonectx, Statement t)
5388 Catch target = (Catch) t;
5390 if (type_expr != null)
5391 target.type_expr = (FullNamedExpression) type_expr.Clone (clonectx);
5393 target.block = clonectx.LookupBlock (block);
5397 public class TryFinally : TryFinallyBlock
5401 public TryFinally (Statement stmt, Block fini, Location loc)
5407 public Block Finallyblock {
5413 public override bool Resolve (BlockContext ec)
5417 ec.StartFlowBranching (this);
5419 if (!stmt.Resolve (ec))
5423 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
5425 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
5426 if (!fini.Resolve (ec))
5430 ec.EndFlowBranching ();
5432 ok &= base.Resolve (ec);
5437 protected override void EmitTryBody (EmitContext ec)
5442 public override void EmitFinallyBody (EmitContext ec)
5447 protected override void CloneTo (CloneContext clonectx, Statement t)
5449 TryFinally target = (TryFinally) t;
5451 target.stmt = (Statement) stmt.Clone (clonectx);
5453 target.fini = clonectx.LookupBlock (fini);
5456 public override object Accept (StructuralVisitor visitor)
5458 return visitor.Visit (this);
5462 public class TryCatch : ExceptionStatement
5465 List<Catch> clauses;
5466 readonly bool inside_try_finally;
5468 public TryCatch (Block block, List<Catch> catch_clauses, Location l, bool inside_try_finally)
5472 this.clauses = catch_clauses;
5473 this.inside_try_finally = inside_try_finally;
5476 public List<Catch> Clauses {
5482 public bool IsTryCatchFinally {
5484 return inside_try_finally;
5488 public override bool Resolve (BlockContext ec)
5492 ec.StartFlowBranching (this);
5494 if (!Block.Resolve (ec))
5497 for (int i = 0; i < clauses.Count; ++i) {
5499 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
5501 if (!c.Resolve (ec)) {
5506 TypeSpec resolved_type = c.CatchType;
5507 for (int ii = 0; ii < clauses.Count; ++ii) {
5511 if (clauses[ii].IsGeneral) {
5512 if (resolved_type.BuiltinType != BuiltinTypeSpec.Type.Exception)
5515 if (!ec.Module.DeclaringAssembly.WrapNonExceptionThrows)
5518 if (!ec.Module.PredefinedAttributes.RuntimeCompatibility.IsDefined)
5521 ec.Report.Warning (1058, 1, c.loc,
5522 "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
5530 var ct = clauses[ii].CatchType;
5534 if (resolved_type == ct || TypeSpec.IsBaseClass (resolved_type, ct, true)) {
5535 ec.Report.Error (160, c.loc,
5536 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
5537 ct.GetSignatureForError ());
5543 ec.EndFlowBranching ();
5545 return base.Resolve (ec) && ok;
5548 protected sealed override void DoEmit (EmitContext ec)
5550 if (!inside_try_finally)
5551 EmitTryBodyPrepare (ec);
5555 foreach (Catch c in clauses)
5558 if (!inside_try_finally)
5559 ec.EndExceptionBlock ();
5562 protected override void CloneTo (CloneContext clonectx, Statement t)
5564 TryCatch target = (TryCatch) t;
5566 target.Block = clonectx.LookupBlock (Block);
5567 if (clauses != null){
5568 target.clauses = new List<Catch> ();
5569 foreach (Catch c in clauses)
5570 target.clauses.Add ((Catch) c.Clone (clonectx));
5574 public override object Accept (StructuralVisitor visitor)
5576 return visitor.Visit (this);
5580 public class Using : TryFinallyBlock
5582 public class VariableDeclaration : BlockVariable
5584 Statement dispose_call;
5586 public VariableDeclaration (FullNamedExpression type, LocalVariable li)
5591 public VariableDeclaration (LocalVariable li, Location loc)
5597 public VariableDeclaration (Expression expr)
5600 loc = expr.Location;
5606 public bool IsNested { get; private set; }
5610 public void EmitDispose (EmitContext ec)
5612 dispose_call.Emit (ec);
5615 public override bool Resolve (BlockContext bc)
5620 return base.Resolve (bc, false);
5623 public Expression ResolveExpression (BlockContext bc)
5625 var e = Initializer.Resolve (bc);
5629 li = LocalVariable.CreateCompilerGenerated (e.Type, bc.CurrentBlock, loc);
5630 Initializer = ResolveInitializer (bc, Variable, e);
5634 protected override Expression ResolveInitializer (BlockContext bc, LocalVariable li, Expression initializer)
5636 if (li.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic) {
5637 initializer = initializer.Resolve (bc);
5638 if (initializer == null)
5641 // Once there is dynamic used defer conversion to runtime even if we know it will never succeed
5642 Arguments args = new Arguments (1);
5643 args.Add (new Argument (initializer));
5644 initializer = new DynamicConversion (bc.BuiltinTypes.IDisposable, 0, args, initializer.Location).Resolve (bc);
5645 if (initializer == null)
5648 var var = LocalVariable.CreateCompilerGenerated (initializer.Type, bc.CurrentBlock, loc);
5649 dispose_call = CreateDisposeCall (bc, var);
5650 dispose_call.Resolve (bc);
5652 return base.ResolveInitializer (bc, li, new SimpleAssign (var.CreateReferenceExpression (bc, loc), initializer, loc));
5655 if (li == Variable) {
5656 CheckIDiposableConversion (bc, li, initializer);
5657 dispose_call = CreateDisposeCall (bc, li);
5658 dispose_call.Resolve (bc);
5661 return base.ResolveInitializer (bc, li, initializer);
5664 protected virtual void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
5668 if (type.BuiltinType != BuiltinTypeSpec.Type.IDisposable && !type.ImplementsInterface (bc.BuiltinTypes.IDisposable, false)) {
5669 if (type.IsNullableType) {
5670 // it's handled in CreateDisposeCall
5674 bc.Report.SymbolRelatedToPreviousError (type);
5675 var loc = type_expr == null ? initializer.Location : type_expr.Location;
5676 bc.Report.Error (1674, loc, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5677 type.GetSignatureForError ());
5683 protected virtual Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
5685 var lvr = lv.CreateReferenceExpression (bc, lv.Location);
5687 var loc = lv.Location;
5689 var idt = bc.BuiltinTypes.IDisposable;
5690 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
5692 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
5693 dispose_mg.InstanceExpression = type.IsNullableType ?
5694 new Cast (new TypeExpression (idt, loc), lvr, loc).Resolve (bc) :
5698 // Hide it from symbol file via null location
5700 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null), Location.Null);
5702 // Add conditional call when disposing possible null variable
5703 if (!type.IsStruct || type.IsNullableType)
5704 dispose = new If (new Binary (Binary.Operator.Inequality, lvr, new NullLiteral (loc)), dispose, dispose.loc);
5709 public void ResolveDeclaratorInitializer (BlockContext bc)
5711 Initializer = base.ResolveInitializer (bc, Variable, Initializer);
5714 public Statement RewriteUsingDeclarators (BlockContext bc, Statement stmt)
5716 for (int i = declarators.Count - 1; i >= 0; --i) {
5717 var d = declarators [i];
5718 var vd = new VariableDeclaration (d.Variable, d.Variable.Location);
5719 vd.Initializer = d.Initializer;
5721 vd.dispose_call = CreateDisposeCall (bc, d.Variable);
5722 vd.dispose_call.Resolve (bc);
5724 stmt = new Using (vd, stmt, d.Variable.Location);
5731 public override object Accept (StructuralVisitor visitor)
5733 return visitor.Visit (this);
5737 VariableDeclaration decl;
5739 public Using (VariableDeclaration decl, Statement stmt, Location loc)
5745 public Using (Expression expr, Statement stmt, Location loc)
5748 this.decl = new VariableDeclaration (expr);
5753 public Expression Expr {
5755 return decl.Variable == null ? decl.Initializer : null;
5759 public BlockVariable Variables {
5767 public override void Emit (EmitContext ec)
5770 // Don't emit sequence point it will be set on variable declaration
5775 protected override void EmitTryBodyPrepare (EmitContext ec)
5778 base.EmitTryBodyPrepare (ec);
5781 protected override void EmitTryBody (EmitContext ec)
5786 public override void EmitFinallyBody (EmitContext ec)
5788 decl.EmitDispose (ec);
5791 public override bool Resolve (BlockContext ec)
5793 VariableReference vr;
5794 bool vr_locked = false;
5796 using (ec.Set (ResolveContext.Options.UsingInitializerScope)) {
5797 if (decl.Variable == null) {
5798 vr = decl.ResolveExpression (ec) as VariableReference;
5800 vr_locked = vr.IsLockedByStatement;
5801 vr.IsLockedByStatement = true;
5804 if (decl.IsNested) {
5805 decl.ResolveDeclaratorInitializer (ec);
5807 if (!decl.Resolve (ec))
5810 if (decl.Declarators != null) {
5811 stmt = decl.RewriteUsingDeclarators (ec, stmt);
5819 ec.StartFlowBranching (this);
5823 ec.EndFlowBranching ();
5826 vr.IsLockedByStatement = vr_locked;
5833 protected override void CloneTo (CloneContext clonectx, Statement t)
5835 Using target = (Using) t;
5837 target.decl = (VariableDeclaration) decl.Clone (clonectx);
5838 target.stmt = stmt.Clone (clonectx);
5841 public override object Accept (StructuralVisitor visitor)
5843 return visitor.Visit (this);
5848 /// Implementation of the foreach C# statement
5850 public class Foreach : Statement
5852 abstract class IteratorStatement : Statement
5854 protected readonly Foreach for_each;
5856 protected IteratorStatement (Foreach @foreach)
5858 this.for_each = @foreach;
5859 this.loc = @foreach.expr.Location;
5862 protected override void CloneTo (CloneContext clonectx, Statement target)
5864 throw new NotImplementedException ();
5867 public override void Emit (EmitContext ec)
5869 if (ec.EmitAccurateDebugInfo) {
5870 ec.Emit (OpCodes.Nop);
5877 sealed class ArrayForeach : IteratorStatement
5879 TemporaryVariableReference[] lengths;
5880 Expression [] length_exprs;
5881 StatementExpression[] counter;
5882 TemporaryVariableReference[] variables;
5884 TemporaryVariableReference copy;
5886 public ArrayForeach (Foreach @foreach, int rank)
5889 counter = new StatementExpression[rank];
5890 variables = new TemporaryVariableReference[rank];
5891 length_exprs = new Expression [rank];
5894 // Only use temporary length variables when dealing with
5895 // multi-dimensional arrays
5898 lengths = new TemporaryVariableReference [rank];
5901 public override bool Resolve (BlockContext ec)
5903 Block variables_block = for_each.variable.Block;
5904 copy = TemporaryVariableReference.Create (for_each.expr.Type, variables_block, loc);
5907 int rank = length_exprs.Length;
5908 Arguments list = new Arguments (rank);
5909 for (int i = 0; i < rank; i++) {
5910 var v = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5912 counter[i] = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, v, Location.Null));
5913 counter[i].Resolve (ec);
5916 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5918 lengths[i] = TemporaryVariableReference.Create (ec.BuiltinTypes.Int, variables_block, loc);
5919 lengths[i].Resolve (ec);
5921 Arguments args = new Arguments (1);
5922 args.Add (new Argument (new IntConstant (ec.BuiltinTypes, i, loc)));
5923 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5926 list.Add (new Argument (v));
5929 var access = new ElementAccess (copy, list, loc).Resolve (ec);
5934 if (for_each.type is VarExpr) {
5935 // Infer implicitly typed local variable from foreach array type
5936 var_type = access.Type;
5938 var_type = for_each.type.ResolveAsType (ec);
5940 if (var_type == null)
5943 access = Convert.ExplicitConversion (ec, access, var_type, loc);
5948 for_each.variable.Type = var_type;
5950 var variable_ref = new LocalVariableReference (for_each.variable, loc).Resolve (ec);
5951 if (variable_ref == null)
5954 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, access, Location.Null), for_each.type.Location));
5958 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5959 ec.CurrentBranching.CreateSibling ();
5961 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5962 if (!for_each.body.Resolve (ec))
5964 ec.EndFlowBranching ();
5966 // There's no direct control flow from the end of the embedded statement to the end of the loop
5967 ec.CurrentBranching.CurrentUsageVector.Goto ();
5969 ec.EndFlowBranching ();
5974 protected override void DoEmit (EmitContext ec)
5976 copy.EmitAssign (ec, for_each.expr);
5978 int rank = length_exprs.Length;
5979 Label[] test = new Label [rank];
5980 Label[] loop = new Label [rank];
5982 for (int i = 0; i < rank; i++) {
5983 test [i] = ec.DefineLabel ();
5984 loop [i] = ec.DefineLabel ();
5986 if (lengths != null)
5987 lengths [i].EmitAssign (ec, length_exprs [i]);
5990 IntConstant zero = new IntConstant (ec.BuiltinTypes, 0, loc);
5991 for (int i = 0; i < rank; i++) {
5992 variables [i].EmitAssign (ec, zero);
5994 ec.Emit (OpCodes.Br, test [i]);
5995 ec.MarkLabel (loop [i]);
5998 for_each.body.Emit (ec);
6000 ec.MarkLabel (ec.LoopBegin);
6001 ec.Mark (for_each.expr.Location);
6003 for (int i = rank - 1; i >= 0; i--){
6004 counter [i].Emit (ec);
6006 ec.MarkLabel (test [i]);
6007 variables [i].Emit (ec);
6009 if (lengths != null)
6010 lengths [i].Emit (ec);
6012 length_exprs [i].Emit (ec);
6014 ec.Emit (OpCodes.Blt, loop [i]);
6017 ec.MarkLabel (ec.LoopEnd);
6021 sealed class CollectionForeach : IteratorStatement, OverloadResolver.IErrorHandler
6023 class RuntimeDispose : Using.VariableDeclaration
6025 public RuntimeDispose (LocalVariable lv, Location loc)
6030 protected override void CheckIDiposableConversion (BlockContext bc, LocalVariable li, Expression initializer)
6032 // Defered to runtime check
6035 protected override Statement CreateDisposeCall (BlockContext bc, LocalVariable lv)
6037 var idt = bc.BuiltinTypes.IDisposable;
6040 // Fabricates code like
6042 // if ((temp = vr as IDisposable) != null) temp.Dispose ();
6045 var dispose_variable = LocalVariable.CreateCompilerGenerated (idt, bc.CurrentBlock, loc);
6047 var idisaposable_test = new Binary (Binary.Operator.Inequality, new CompilerAssign (
6048 dispose_variable.CreateReferenceExpression (bc, loc),
6049 new As (lv.CreateReferenceExpression (bc, loc), new TypeExpression (dispose_variable.Type, loc), loc),
6050 loc), new NullLiteral (loc));
6052 var m = bc.Module.PredefinedMembers.IDisposableDispose.Resolve (loc);
6054 var dispose_mg = MethodGroupExpr.CreatePredefined (m, idt, loc);
6055 dispose_mg.InstanceExpression = dispose_variable.CreateReferenceExpression (bc, loc);
6057 Statement dispose = new StatementExpression (new Invocation (dispose_mg, null));
6058 return new If (idisaposable_test, dispose, loc);
6062 LocalVariable variable;
6064 Statement statement;
6065 ExpressionStatement init;
6066 TemporaryVariableReference enumerator_variable;
6067 bool ambiguous_getenumerator_name;
6069 public CollectionForeach (Foreach @foreach, LocalVariable var, Expression expr)
6072 this.variable = var;
6076 void Error_WrongEnumerator (ResolveContext rc, MethodSpec enumerator)
6078 rc.Report.SymbolRelatedToPreviousError (enumerator);
6079 rc.Report.Error (202, loc,
6080 "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
6081 enumerator.ReturnType.GetSignatureForError (), enumerator.GetSignatureForError ());
6084 MethodGroupExpr ResolveGetEnumerator (ResolveContext rc)
6087 // Option 1: Try to match by name GetEnumerator first
6089 var mexpr = Expression.MemberLookup (rc, false, expr.Type,
6090 "GetEnumerator", 0, Expression.MemberLookupRestrictions.ExactArity, loc); // TODO: What if CS0229 ?
6092 var mg = mexpr as MethodGroupExpr;
6094 mg.InstanceExpression = expr;
6095 Arguments args = new Arguments (0);
6096 mg = mg.OverloadResolve (rc, ref args, this, OverloadResolver.Restrictions.ProbingOnly);
6098 // For ambiguous GetEnumerator name warning CS0278 was reported, but Option 2 could still apply
6099 if (ambiguous_getenumerator_name)
6102 if (mg != null && args.Count == 0 && !mg.BestCandidate.IsStatic && mg.BestCandidate.IsPublic) {
6108 // Option 2: Try to match using IEnumerable interfaces with preference of generic version
6111 PredefinedMember<MethodSpec> iface_candidate = null;
6112 var ptypes = rc.Module.PredefinedTypes;
6113 var gen_ienumerable = ptypes.IEnumerableGeneric;
6114 if (!gen_ienumerable.Define ())
6115 gen_ienumerable = null;
6117 var ifaces = t.Interfaces;
6118 if (ifaces != null) {
6119 foreach (var iface in ifaces) {
6120 if (gen_ienumerable != null && iface.MemberDefinition == gen_ienumerable.TypeSpec.MemberDefinition) {
6121 if (iface_candidate != null && iface_candidate != rc.Module.PredefinedMembers.IEnumerableGetEnumerator) {
6122 rc.Report.SymbolRelatedToPreviousError (expr.Type);
6123 rc.Report.Error (1640, loc,
6124 "foreach statement cannot operate on variables of type `{0}' because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
6125 expr.Type.GetSignatureForError (), gen_ienumerable.TypeSpec.GetSignatureForError ());
6130 // TODO: Cache this somehow
6131 iface_candidate = new PredefinedMember<MethodSpec> (rc.Module, iface,
6132 MemberFilter.Method ("GetEnumerator", 0, ParametersCompiled.EmptyReadOnlyParameters, null));
6137 if (iface.BuiltinType == BuiltinTypeSpec.Type.IEnumerable && iface_candidate == null) {
6138 iface_candidate = rc.Module.PredefinedMembers.IEnumerableGetEnumerator;
6143 if (iface_candidate == null) {
6144 if (expr.Type != InternalType.ErrorType) {
6145 rc.Report.Error (1579, loc,
6146 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `{1}' or is inaccessible",
6147 expr.Type.GetSignatureForError (), "GetEnumerator");
6153 var method = iface_candidate.Resolve (loc);
6157 mg = MethodGroupExpr.CreatePredefined (method, expr.Type, loc);
6158 mg.InstanceExpression = expr;
6162 MethodGroupExpr ResolveMoveNext (ResolveContext rc, MethodSpec enumerator)
6164 var ms = MemberCache.FindMember (enumerator.ReturnType,
6165 MemberFilter.Method ("MoveNext", 0, ParametersCompiled.EmptyReadOnlyParameters, rc.BuiltinTypes.Bool),
6166 BindingRestriction.InstanceOnly) as MethodSpec;
6168 if (ms == null || !ms.IsPublic) {
6169 Error_WrongEnumerator (rc, enumerator);
6173 return MethodGroupExpr.CreatePredefined (ms, enumerator.ReturnType, expr.Location);
6176 PropertySpec ResolveCurrent (ResolveContext rc, MethodSpec enumerator)
6178 var ps = MemberCache.FindMember (enumerator.ReturnType,
6179 MemberFilter.Property ("Current", null),
6180 BindingRestriction.InstanceOnly) as PropertySpec;
6182 if (ps == null || !ps.IsPublic) {
6183 Error_WrongEnumerator (rc, enumerator);
6190 public override bool Resolve (BlockContext ec)
6192 bool is_dynamic = expr.Type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
6195 expr = Convert.ImplicitConversionRequired (ec, expr, ec.BuiltinTypes.IEnumerable, loc);
6196 } else if (expr.Type.IsNullableType) {
6197 expr = new Nullable.UnwrapCall (expr).Resolve (ec);
6200 var get_enumerator_mg = ResolveGetEnumerator (ec);
6201 if (get_enumerator_mg == null) {
6205 var get_enumerator = get_enumerator_mg.BestCandidate;
6206 enumerator_variable = TemporaryVariableReference.Create (get_enumerator.ReturnType, variable.Block, loc);
6207 enumerator_variable.Resolve (ec);
6209 // Prepare bool MoveNext ()
6210 var move_next_mg = ResolveMoveNext (ec, get_enumerator);
6211 if (move_next_mg == null) {
6215 move_next_mg.InstanceExpression = enumerator_variable;
6217 // Prepare ~T~ Current { get; }
6218 var current_prop = ResolveCurrent (ec, get_enumerator);
6219 if (current_prop == null) {
6223 var current_pe = new PropertyExpr (current_prop, loc) { InstanceExpression = enumerator_variable }.Resolve (ec);
6224 if (current_pe == null)
6227 VarExpr ve = for_each.type as VarExpr;
6231 // Source type is dynamic, set element type to dynamic too
6232 variable.Type = ec.BuiltinTypes.Dynamic;
6234 // Infer implicitly typed local variable from foreach enumerable type
6235 variable.Type = current_pe.Type;
6239 // Explicit cast of dynamic collection elements has to be done at runtime
6240 current_pe = EmptyCast.Create (current_pe, ec.BuiltinTypes.Dynamic);
6243 variable.Type = for_each.type.ResolveAsType (ec);
6245 if (variable.Type == null)
6248 current_pe = Convert.ExplicitConversion (ec, current_pe, variable.Type, loc);
6249 if (current_pe == null)
6253 var variable_ref = new LocalVariableReference (variable, loc).Resolve (ec);
6254 if (variable_ref == null)
6257 for_each.body.AddScopeStatement (new StatementExpression (new CompilerAssign (variable_ref, current_pe, Location.Null), for_each.type.Location));
6259 var init = new Invocation (get_enumerator_mg, null);
6261 statement = new While (new BooleanExpression (new Invocation (move_next_mg, null)),
6262 for_each.body, Location.Null);
6264 var enum_type = enumerator_variable.Type;
6267 // Add Dispose method call when enumerator can be IDisposable
6269 if (!enum_type.ImplementsInterface (ec.BuiltinTypes.IDisposable, false)) {
6270 if (!enum_type.IsSealed && !TypeSpec.IsValueType (enum_type)) {
6272 // Runtime Dispose check
6274 var vd = new RuntimeDispose (enumerator_variable.LocalInfo, Location.Null);
6275 vd.Initializer = init;
6276 statement = new Using (vd, statement, Location.Null);
6279 // No Dispose call needed
6281 this.init = new SimpleAssign (enumerator_variable, init, Location.Null);
6282 this.init.Resolve (ec);
6286 // Static Dispose check
6288 var vd = new Using.VariableDeclaration (enumerator_variable.LocalInfo, Location.Null);
6289 vd.Initializer = init;
6290 statement = new Using (vd, statement, Location.Null);
6293 return statement.Resolve (ec);
6296 protected override void DoEmit (EmitContext ec)
6298 enumerator_variable.LocalInfo.CreateBuilder (ec);
6301 init.EmitStatement (ec);
6303 statement.Emit (ec);
6306 #region IErrorHandler Members
6308 bool OverloadResolver.IErrorHandler.AmbiguousCandidates (ResolveContext ec, MemberSpec best, MemberSpec ambiguous)
6310 ec.Report.SymbolRelatedToPreviousError (best);
6311 ec.Report.Warning (278, 2, expr.Location,
6312 "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
6313 expr.Type.GetSignatureForError (), "enumerable",
6314 best.GetSignatureForError (), ambiguous.GetSignatureForError ());
6316 ambiguous_getenumerator_name = true;
6320 bool OverloadResolver.IErrorHandler.ArgumentMismatch (ResolveContext rc, MemberSpec best, Argument arg, int index)
6325 bool OverloadResolver.IErrorHandler.NoArgumentMatch (ResolveContext rc, MemberSpec best)
6330 bool OverloadResolver.IErrorHandler.TypeInferenceFailed (ResolveContext rc, MemberSpec best)
6339 LocalVariable variable;
6341 Statement statement;
6344 public Foreach (Expression type, LocalVariable var, Expression expr, Statement stmt, Block body, Location l)
6347 this.variable = var;
6349 this.statement = stmt;
6354 public Expression Expr {
6355 get { return expr; }
6358 public Statement Statement {
6359 get { return statement; }
6362 public Expression TypeExpression {
6363 get { return type; }
6366 public LocalVariable Variable {
6367 get { return variable; }
6370 public override bool Resolve (BlockContext ec)
6372 expr = expr.Resolve (ec);
6377 ec.Report.Error (186, loc, "Use of null is not valid in this context");
6381 body.AddStatement (statement);
6383 if (expr.Type.BuiltinType == BuiltinTypeSpec.Type.String) {
6384 statement = new ArrayForeach (this, 1);
6385 } else if (expr.Type is ArrayContainer) {
6386 statement = new ArrayForeach (this, ((ArrayContainer) expr.Type).Rank);
6388 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
6389 ec.Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
6390 expr.ExprClassName);
6394 statement = new CollectionForeach (this, variable, expr);
6397 return statement.Resolve (ec);
6400 protected override void DoEmit (EmitContext ec)
6402 variable.CreateBuilder (ec);
6404 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
6405 ec.LoopBegin = ec.DefineLabel ();
6406 ec.LoopEnd = ec.DefineLabel ();
6408 statement.Emit (ec);
6410 ec.LoopBegin = old_begin;
6411 ec.LoopEnd = old_end;
6414 protected override void CloneTo (CloneContext clonectx, Statement t)
6416 Foreach target = (Foreach) t;
6418 target.type = type.Clone (clonectx);
6419 target.expr = expr.Clone (clonectx);
6420 target.body = (Block) body.Clone (clonectx);
6421 target.statement = statement.Clone (clonectx);
6424 public override object Accept (StructuralVisitor visitor)
6426 return visitor.Visit (this);