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@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections;
19 using System.Collections.Specialized;
21 namespace Mono.CSharp {
23 public abstract class Statement {
27 /// Resolves the statement, true means that all sub-statements
30 public virtual bool Resolve (BlockContext ec)
36 /// We already know that the statement is unreachable, but we still
37 /// need to resolve it to catch errors.
39 public virtual bool ResolveUnreachable (BlockContext ec, bool warn)
42 // This conflicts with csc's way of doing this, but IMHO it's
43 // the right thing to do.
45 // If something is unreachable, we still check whether it's
46 // correct. This means that you cannot use unassigned variables
47 // in unreachable code, for instance.
51 Report.Warning (162, 2, loc, "Unreachable code detected");
53 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
54 bool ok = Resolve (ec);
55 ec.KillFlowBranching ();
61 /// Return value indicates whether all code paths emitted return.
63 protected abstract void DoEmit (EmitContext ec);
66 /// Utility wrapper routine for Error, just to beautify the code
68 public void Error (int error, string format, params object[] args)
70 Error (error, String.Format (format, args));
73 public void Error (int error, string s)
76 Report.Error (error, loc, s);
78 Report.Error (error, s);
81 public virtual void Emit (EmitContext ec)
88 // This routine must be overrided in derived classes and make copies
89 // of all the data that might be modified if resolved
91 protected abstract void CloneTo (CloneContext clonectx, Statement target);
93 public Statement Clone (CloneContext clonectx)
95 Statement s = (Statement) this.MemberwiseClone ();
96 CloneTo (clonectx, s);
100 public virtual Expression CreateExpressionTree (ResolveContext ec)
102 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
106 public Statement PerformClone ()
108 CloneContext clonectx = new CloneContext ();
110 return Clone (clonectx);
113 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
117 // This class is used during the Statement.Clone operation
118 // to remap objects that have been cloned.
120 // Since blocks are cloned by Block.Clone, we need a way for
121 // expressions that must reference the block to be cloned
122 // pointing to the new cloned block.
124 public class CloneContext {
125 Hashtable block_map = new Hashtable ();
126 Hashtable variable_map;
128 public void AddBlockMap (Block from, Block to)
130 if (block_map.Contains (from))
132 block_map [from] = to;
135 public Block LookupBlock (Block from)
137 Block result = (Block) block_map [from];
140 result = (Block) from.Clone (this);
141 block_map [from] = result;
148 /// Remaps block to cloned copy if one exists.
150 public Block RemapBlockCopy (Block from)
152 Block mapped_to = (Block)block_map[from];
153 if (mapped_to == null)
159 public void AddVariableMap (LocalInfo from, LocalInfo to)
161 if (variable_map == null)
162 variable_map = new Hashtable ();
164 if (variable_map.Contains (from))
166 variable_map [from] = to;
169 public LocalInfo LookupVariable (LocalInfo from)
171 LocalInfo result = (LocalInfo) variable_map [from];
174 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
180 public sealed class EmptyStatement : Statement {
182 private EmptyStatement () {}
184 public static readonly EmptyStatement Value = new EmptyStatement ();
186 public override bool Resolve (BlockContext ec)
191 public override bool ResolveUnreachable (BlockContext ec, bool warn)
196 protected override void DoEmit (EmitContext ec)
200 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
204 protected override void CloneTo (CloneContext clonectx, Statement target)
210 public class If : Statement {
212 public Statement TrueStatement;
213 public Statement FalseStatement;
217 public If (Expression expr, Statement true_statement, Location l)
220 TrueStatement = true_statement;
224 public If (Expression expr,
225 Statement true_statement,
226 Statement false_statement,
230 TrueStatement = true_statement;
231 FalseStatement = false_statement;
235 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
237 expr.MutateHoistedGenericType (storey);
238 TrueStatement.MutateHoistedGenericType (storey);
239 if (FalseStatement != null)
240 FalseStatement.MutateHoistedGenericType (storey);
243 public override bool Resolve (BlockContext ec)
247 Report.Debug (1, "START IF BLOCK", loc);
249 expr = Expression.ResolveBoolean (ec, expr, loc);
255 Assign ass = expr as Assign;
256 if (ass != null && ass.Source is Constant) {
257 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
261 // Dead code elimination
263 if (expr is Constant){
264 bool take = !((Constant) expr).IsDefaultValue;
267 if (!TrueStatement.Resolve (ec))
270 if ((FalseStatement != null) &&
271 !FalseStatement.ResolveUnreachable (ec, true))
273 FalseStatement = null;
275 if (!TrueStatement.ResolveUnreachable (ec, true))
277 TrueStatement = null;
279 if ((FalseStatement != null) &&
280 !FalseStatement.Resolve (ec))
287 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
289 ok &= TrueStatement.Resolve (ec);
291 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
293 ec.CurrentBranching.CreateSibling ();
295 if (FalseStatement != null)
296 ok &= FalseStatement.Resolve (ec);
298 ec.EndFlowBranching ();
300 Report.Debug (1, "END IF BLOCK", loc);
305 protected override void DoEmit (EmitContext ec)
307 ILGenerator ig = ec.ig;
308 Label false_target = ig.DefineLabel ();
312 // If we're a boolean constant, Resolve() already
313 // eliminated dead code for us.
315 Constant c = expr as Constant;
317 c.EmitSideEffect (ec);
319 if (!c.IsDefaultValue)
320 TrueStatement.Emit (ec);
321 else if (FalseStatement != null)
322 FalseStatement.Emit (ec);
327 expr.EmitBranchable (ec, false_target, false);
329 TrueStatement.Emit (ec);
331 if (FalseStatement != null){
332 bool branch_emitted = false;
334 end = ig.DefineLabel ();
336 ig.Emit (OpCodes.Br, end);
337 branch_emitted = true;
340 ig.MarkLabel (false_target);
341 FalseStatement.Emit (ec);
346 ig.MarkLabel (false_target);
350 protected override void CloneTo (CloneContext clonectx, Statement t)
354 target.expr = expr.Clone (clonectx);
355 target.TrueStatement = TrueStatement.Clone (clonectx);
356 if (FalseStatement != null)
357 target.FalseStatement = FalseStatement.Clone (clonectx);
361 public class Do : Statement {
362 public Expression expr;
363 public Statement EmbeddedStatement;
365 public Do (Statement statement, Expression bool_expr, Location l)
368 EmbeddedStatement = statement;
372 public override bool Resolve (BlockContext ec)
376 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
378 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
380 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
381 if (!EmbeddedStatement.Resolve (ec))
383 ec.EndFlowBranching ();
385 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
386 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
388 expr = Expression.ResolveBoolean (ec, expr, loc);
391 else if (expr is Constant){
392 bool infinite = !((Constant) expr).IsDefaultValue;
394 ec.CurrentBranching.CurrentUsageVector.Goto ();
397 ec.EndFlowBranching ();
402 protected override void DoEmit (EmitContext ec)
404 ILGenerator ig = ec.ig;
405 Label loop = ig.DefineLabel ();
406 Label old_begin = ec.LoopBegin;
407 Label old_end = ec.LoopEnd;
409 ec.LoopBegin = ig.DefineLabel ();
410 ec.LoopEnd = ig.DefineLabel ();
413 EmbeddedStatement.Emit (ec);
414 ig.MarkLabel (ec.LoopBegin);
417 // Dead code elimination
419 if (expr is Constant){
420 bool res = !((Constant) expr).IsDefaultValue;
422 expr.EmitSideEffect (ec);
424 ec.ig.Emit (OpCodes.Br, loop);
426 expr.EmitBranchable (ec, loop, true);
428 ig.MarkLabel (ec.LoopEnd);
430 ec.LoopBegin = old_begin;
431 ec.LoopEnd = old_end;
434 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
436 expr.MutateHoistedGenericType (storey);
437 EmbeddedStatement.MutateHoistedGenericType (storey);
440 protected override void CloneTo (CloneContext clonectx, Statement t)
444 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
445 target.expr = expr.Clone (clonectx);
449 public class While : Statement {
450 public Expression expr;
451 public Statement Statement;
452 bool infinite, empty;
454 public While (Expression bool_expr, Statement statement, Location l)
456 this.expr = bool_expr;
457 Statement = statement;
461 public override bool Resolve (BlockContext ec)
465 expr = Expression.ResolveBoolean (ec, expr, loc);
470 // Inform whether we are infinite or not
472 if (expr is Constant){
473 bool value = !((Constant) expr).IsDefaultValue;
476 if (!Statement.ResolveUnreachable (ec, true))
484 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
486 ec.CurrentBranching.CreateSibling ();
488 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
489 if (!Statement.Resolve (ec))
491 ec.EndFlowBranching ();
493 // There's no direct control flow from the end of the embedded statement to the end of the loop
494 ec.CurrentBranching.CurrentUsageVector.Goto ();
496 ec.EndFlowBranching ();
501 protected override void DoEmit (EmitContext ec)
504 expr.EmitSideEffect (ec);
508 ILGenerator ig = ec.ig;
509 Label old_begin = ec.LoopBegin;
510 Label old_end = ec.LoopEnd;
512 ec.LoopBegin = ig.DefineLabel ();
513 ec.LoopEnd = ig.DefineLabel ();
516 // Inform whether we are infinite or not
518 if (expr is Constant){
519 // expr is 'true', since the 'empty' case above handles the 'false' case
520 ig.MarkLabel (ec.LoopBegin);
521 expr.EmitSideEffect (ec);
523 ig.Emit (OpCodes.Br, ec.LoopBegin);
526 // Inform that we are infinite (ie, `we return'), only
527 // if we do not `break' inside the code.
529 ig.MarkLabel (ec.LoopEnd);
531 Label while_loop = ig.DefineLabel ();
533 ig.Emit (OpCodes.Br, ec.LoopBegin);
534 ig.MarkLabel (while_loop);
538 ig.MarkLabel (ec.LoopBegin);
541 expr.EmitBranchable (ec, while_loop, true);
543 ig.MarkLabel (ec.LoopEnd);
546 ec.LoopBegin = old_begin;
547 ec.LoopEnd = old_end;
550 public override void Emit (EmitContext ec)
555 protected override void CloneTo (CloneContext clonectx, Statement t)
557 While target = (While) t;
559 target.expr = expr.Clone (clonectx);
560 target.Statement = Statement.Clone (clonectx);
563 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
565 expr.MutateHoistedGenericType (storey);
566 Statement.MutateHoistedGenericType (storey);
570 public class For : Statement {
572 Statement InitStatement;
574 public Statement Statement;
575 bool infinite, empty;
577 public For (Statement init_statement,
583 InitStatement = init_statement;
585 Increment = increment;
586 Statement = statement;
590 public override bool Resolve (BlockContext ec)
594 if (InitStatement != null){
595 if (!InitStatement.Resolve (ec))
600 Test = Expression.ResolveBoolean (ec, Test, loc);
603 else if (Test is Constant){
604 bool value = !((Constant) Test).IsDefaultValue;
607 if (!Statement.ResolveUnreachable (ec, true))
609 if ((Increment != null) &&
610 !Increment.ResolveUnreachable (ec, false))
620 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
622 ec.CurrentBranching.CreateSibling ();
624 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
626 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
627 if (!Statement.Resolve (ec))
629 ec.EndFlowBranching ();
631 if (Increment != null){
632 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
633 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
636 if (!Increment.Resolve (ec))
641 // There's no direct control flow from the end of the embedded statement to the end of the loop
642 ec.CurrentBranching.CurrentUsageVector.Goto ();
644 ec.EndFlowBranching ();
649 protected override void DoEmit (EmitContext ec)
651 if (InitStatement != null && InitStatement != EmptyStatement.Value)
652 InitStatement.Emit (ec);
655 Test.EmitSideEffect (ec);
659 ILGenerator ig = ec.ig;
660 Label old_begin = ec.LoopBegin;
661 Label old_end = ec.LoopEnd;
662 Label loop = ig.DefineLabel ();
663 Label test = ig.DefineLabel ();
665 ec.LoopBegin = ig.DefineLabel ();
666 ec.LoopEnd = ig.DefineLabel ();
668 ig.Emit (OpCodes.Br, test);
672 ig.MarkLabel (ec.LoopBegin);
673 if (Increment != EmptyStatement.Value)
678 // If test is null, there is no test, and we are just
683 // The Resolve code already catches the case for
684 // Test == Constant (false) so we know that
687 if (Test is Constant) {
688 Test.EmitSideEffect (ec);
689 ig.Emit (OpCodes.Br, loop);
691 Test.EmitBranchable (ec, loop, true);
695 ig.Emit (OpCodes.Br, loop);
696 ig.MarkLabel (ec.LoopEnd);
698 ec.LoopBegin = old_begin;
699 ec.LoopEnd = old_end;
702 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
704 if (InitStatement != null)
705 InitStatement.MutateHoistedGenericType (storey);
707 Test.MutateHoistedGenericType (storey);
708 if (Increment != null)
709 Increment.MutateHoistedGenericType (storey);
711 Statement.MutateHoistedGenericType (storey);
714 protected override void CloneTo (CloneContext clonectx, Statement t)
716 For target = (For) t;
718 if (InitStatement != null)
719 target.InitStatement = InitStatement.Clone (clonectx);
721 target.Test = Test.Clone (clonectx);
722 if (Increment != null)
723 target.Increment = Increment.Clone (clonectx);
724 target.Statement = Statement.Clone (clonectx);
728 public class StatementExpression : Statement {
729 ExpressionStatement expr;
731 public StatementExpression (ExpressionStatement expr)
737 public override bool Resolve (BlockContext ec)
739 if (expr != null && expr.eclass == ExprClass.Invalid)
740 expr = expr.ResolveStatement (ec);
744 protected override void DoEmit (EmitContext ec)
746 expr.EmitStatement (ec);
749 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
751 expr.MutateHoistedGenericType (storey);
754 public override string ToString ()
756 return "StatementExpression (" + expr + ")";
759 protected override void CloneTo (CloneContext clonectx, Statement t)
761 StatementExpression target = (StatementExpression) t;
763 target.expr = (ExpressionStatement) expr.Clone (clonectx);
767 // A 'return' or a 'yield break'
768 public abstract class ExitStatement : Statement
770 protected bool unwind_protect;
771 protected abstract bool DoResolve (BlockContext ec);
773 public virtual void Error_FinallyClause ()
775 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
778 public sealed override bool Resolve (BlockContext ec)
783 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
785 ec.NeedReturnLabel ();
786 ec.CurrentBranching.CurrentUsageVector.Goto ();
792 /// Implements the return statement
794 public class Return : ExitStatement {
795 protected Expression Expr;
796 public Return (Expression expr, Location l)
802 protected override bool DoResolve (BlockContext ec)
805 if (ec.ReturnType == TypeManager.void_type)
808 Error (126, "An object of a type convertible to `{0}' is required " +
809 "for the return statement",
810 TypeManager.CSharpName (ec.ReturnType));
814 if (ec.CurrentBlock.Toplevel.IsIterator) {
815 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
816 "statement to return a value, or yield break to end the iteration");
819 AnonymousExpression am = ec.CurrentAnonymousMethod;
820 if (am == null && ec.ReturnType == TypeManager.void_type) {
821 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
822 ec.GetSignatureForError ());
825 Expr = Expr.Resolve (ec);
829 if (ec.HasSet (ResolveContext.Options.InferReturnType)) {
830 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
834 if (Expr.Type != ec.ReturnType) {
835 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
839 Report.Error (1662, loc,
840 "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",
841 am.ContainerType, am.GetSignatureForError ());
850 protected override void DoEmit (EmitContext ec)
856 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
860 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
862 ec.ig.Emit (OpCodes.Ret);
865 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
868 Expr.MutateHoistedGenericType (storey);
871 protected override void CloneTo (CloneContext clonectx, Statement t)
873 Return target = (Return) t;
874 // It's null for simple return;
876 target.Expr = Expr.Clone (clonectx);
880 public class Goto : Statement {
882 LabeledStatement label;
885 public override bool Resolve (BlockContext ec)
887 int errors = Report.Errors;
888 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
889 ec.CurrentBranching.CurrentUsageVector.Goto ();
890 return errors == Report.Errors;
893 public Goto (string label, Location l)
899 public string Target {
900 get { return target; }
903 public void SetResolvedTarget (LabeledStatement label)
906 label.AddReference ();
909 protected override void CloneTo (CloneContext clonectx, Statement target)
914 protected override void DoEmit (EmitContext ec)
917 throw new InternalErrorException ("goto emitted before target resolved");
918 Label l = label.LabelTarget (ec);
919 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
922 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
927 public class LabeledStatement : Statement {
934 FlowBranching.UsageVector vectors;
936 public LabeledStatement (string name, Location l)
942 public Label LabelTarget (EmitContext ec)
947 label = ec.ig.DefineLabel ();
957 public bool IsDefined {
958 get { return defined; }
961 public bool HasBeenReferenced {
962 get { return referenced; }
965 public FlowBranching.UsageVector JumpOrigins {
966 get { return vectors; }
969 public void AddUsageVector (FlowBranching.UsageVector vector)
971 vector = vector.Clone ();
972 vector.Next = vectors;
976 protected override void CloneTo (CloneContext clonectx, Statement target)
981 public override bool Resolve (BlockContext ec)
983 // this flow-branching will be terminated when the surrounding block ends
984 ec.StartFlowBranching (this);
988 protected override void DoEmit (EmitContext ec)
990 if (ig != null && ig != ec.ig)
991 throw new InternalErrorException ("cannot happen");
993 ec.ig.MarkLabel (label);
996 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1000 public void AddReference ()
1008 /// `goto default' statement
1010 public class GotoDefault : Statement {
1012 public GotoDefault (Location l)
1017 protected override void CloneTo (CloneContext clonectx, Statement target)
1022 public override bool Resolve (BlockContext ec)
1024 ec.CurrentBranching.CurrentUsageVector.Goto ();
1028 protected override void DoEmit (EmitContext ec)
1030 if (ec.Switch == null){
1031 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1035 if (!ec.Switch.GotDefault){
1036 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1039 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1042 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1048 /// `goto case' statement
1050 public class GotoCase : Statement {
1054 public GotoCase (Expression e, Location l)
1060 public override bool Resolve (BlockContext ec)
1062 if (ec.Switch == null){
1063 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1067 ec.CurrentBranching.CurrentUsageVector.Goto ();
1069 expr = expr.Resolve (ec);
1073 Constant c = expr as Constant;
1075 Error (150, "A constant value is expected");
1079 Type type = ec.Switch.SwitchType;
1080 Constant res = c.TryReduce (ec, type, c.Location);
1082 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1086 if (!Convert.ImplicitStandardConversionExists (c, type))
1087 Report.Warning (469, 2, loc,
1088 "The `goto case' value is not implicitly convertible to type `{0}'",
1089 TypeManager.CSharpName (type));
1091 object val = res.GetValue ();
1093 val = SwitchLabel.NullStringCase;
1095 sl = (SwitchLabel) ec.Switch.Elements [val];
1098 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1099 (c.GetValue () == null ? "null" : val.ToString ()));
1106 protected override void DoEmit (EmitContext ec)
1108 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1111 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1113 expr.MutateHoistedGenericType (storey);
1116 protected override void CloneTo (CloneContext clonectx, Statement t)
1118 GotoCase target = (GotoCase) t;
1120 target.expr = expr.Clone (clonectx);
1124 public class Throw : Statement {
1127 public Throw (Expression expr, Location l)
1133 public override bool Resolve (BlockContext ec)
1136 ec.CurrentBranching.CurrentUsageVector.Goto ();
1137 return ec.CurrentBranching.CheckRethrow (loc);
1140 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1141 ec.CurrentBranching.CurrentUsageVector.Goto ();
1146 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1147 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1149 Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1154 protected override void DoEmit (EmitContext ec)
1157 ec.ig.Emit (OpCodes.Rethrow);
1161 ec.ig.Emit (OpCodes.Throw);
1165 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1168 expr.MutateHoistedGenericType (storey);
1171 protected override void CloneTo (CloneContext clonectx, Statement t)
1173 Throw target = (Throw) t;
1176 target.expr = expr.Clone (clonectx);
1180 public class Break : Statement {
1182 public Break (Location l)
1187 bool unwind_protect;
1189 public override bool Resolve (BlockContext ec)
1191 int errors = Report.Errors;
1192 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1193 ec.CurrentBranching.CurrentUsageVector.Goto ();
1194 return errors == Report.Errors;
1197 protected override void DoEmit (EmitContext ec)
1199 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1202 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1206 protected override void CloneTo (CloneContext clonectx, Statement t)
1212 public class Continue : Statement {
1214 public Continue (Location l)
1219 bool unwind_protect;
1221 public override bool Resolve (BlockContext ec)
1223 int errors = Report.Errors;
1224 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1225 ec.CurrentBranching.CurrentUsageVector.Goto ();
1226 return errors == Report.Errors;
1229 protected override void DoEmit (EmitContext ec)
1231 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1234 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1238 protected override void CloneTo (CloneContext clonectx, Statement t)
1244 public interface ILocalVariable
1246 void Emit (EmitContext ec);
1247 void EmitAssign (EmitContext ec);
1248 void EmitAddressOf (EmitContext ec);
1251 public interface IKnownVariable {
1252 Block Block { get; }
1253 Location Location { get; }
1257 // The information about a user-perceived local variable
1259 public class LocalInfo : IKnownVariable, ILocalVariable {
1260 public readonly FullNamedExpression Type;
1262 public Type VariableType;
1263 public readonly string Name;
1264 public readonly Location Location;
1265 public readonly Block Block;
1267 public VariableInfo VariableInfo;
1268 public HoistedVariable HoistedVariableReference;
1277 CompilerGenerated = 64,
1281 public enum ReadOnlyContext: byte {
1288 ReadOnlyContext ro_context;
1289 LocalBuilder builder;
1291 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1299 public LocalInfo (DeclSpace ds, Block block, Location l)
1301 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1306 public void ResolveVariable (EmitContext ec)
1308 if (HoistedVariableReference != null)
1311 if (builder == null) {
1314 // This is needed to compile on both .NET 1.x and .NET 2.x
1315 // the later introduced `DeclareLocal (Type t, bool pinned)'
1317 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1319 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1323 public void Emit (EmitContext ec)
1325 ec.ig.Emit (OpCodes.Ldloc, builder);
1328 public void EmitAssign (EmitContext ec)
1330 ec.ig.Emit (OpCodes.Stloc, builder);
1333 public void EmitAddressOf (EmitContext ec)
1335 ec.ig.Emit (OpCodes.Ldloca, builder);
1338 public void EmitSymbolInfo (EmitContext ec)
1340 if (builder != null)
1341 ec.DefineLocalVariable (Name, builder);
1344 public bool IsThisAssigned (BlockContext ec, Block block)
1346 if (VariableInfo == null)
1347 throw new Exception ();
1349 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1352 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, block.StartLocation);
1355 public bool IsAssigned (BlockContext ec)
1357 if (VariableInfo == null)
1358 throw new Exception ();
1360 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1363 public bool Resolve (ResolveContext ec)
1365 if (VariableType != null)
1368 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1372 VariableType = texpr.Type;
1374 if (TypeManager.IsGenericParameter (VariableType))
1377 if (VariableType.IsAbstract && VariableType.IsSealed) {
1378 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1382 if (VariableType.IsPointer && !ec.IsUnsafe)
1383 Expression.UnsafeError (Location);
1388 public bool IsConstant {
1389 get { return (flags & Flags.IsConstant) != 0; }
1390 set { flags |= Flags.IsConstant; }
1393 public bool AddressTaken {
1394 get { return (flags & Flags.AddressTaken) != 0; }
1395 set { flags |= Flags.AddressTaken; }
1398 public bool CompilerGenerated {
1399 get { return (flags & Flags.CompilerGenerated) != 0; }
1400 set { flags |= Flags.CompilerGenerated; }
1403 public override string ToString ()
1405 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1406 Name, Type, VariableInfo, Location);
1410 get { return (flags & Flags.Used) != 0; }
1411 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1414 public bool ReadOnly {
1415 get { return (flags & Flags.ReadOnly) != 0; }
1418 public void SetReadOnlyContext (ReadOnlyContext context)
1420 flags |= Flags.ReadOnly;
1421 ro_context = context;
1424 public string GetReadOnlyContext ()
1427 throw new InternalErrorException ("Variable is not readonly");
1429 switch (ro_context) {
1430 case ReadOnlyContext.Fixed:
1431 return "fixed variable";
1432 case ReadOnlyContext.Foreach:
1433 return "foreach iteration variable";
1434 case ReadOnlyContext.Using:
1435 return "using variable";
1437 throw new NotImplementedException ();
1441 // Whether the variable is pinned, if Pinned the variable has been
1442 // allocated in a pinned slot with DeclareLocal.
1444 public bool Pinned {
1445 get { return (flags & Flags.Pinned) != 0; }
1446 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1449 public bool IsThis {
1450 get { return (flags & Flags.IsThis) != 0; }
1451 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1454 Block IKnownVariable.Block {
1455 get { return Block; }
1458 Location IKnownVariable.Location {
1459 get { return Location; }
1462 public LocalInfo Clone (CloneContext clonectx)
1465 // Variables in anonymous block are not resolved yet
1467 if (VariableType == null)
1468 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1471 // Variables in method block are resolved
1473 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1474 li.VariableType = VariableType;
1480 /// Block represents a C# block.
1484 /// This class is used in a number of places: either to represent
1485 /// explicit blocks that the programmer places or implicit blocks.
1487 /// Implicit blocks are used as labels or to introduce variable
1490 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1491 /// they contain extra information that is not necessary on normal blocks.
1493 public class Block : Statement {
1494 public Block Parent;
1495 public Location StartLocation;
1496 public Location EndLocation = Location.Null;
1498 public ExplicitBlock Explicit;
1499 public ToplevelBlock Toplevel; // TODO: Use Explicit
1502 public enum Flags : byte {
1505 VariablesInitialized = 4,
1509 HasCapturedVariable = 64,
1510 HasCapturedThis = 128
1512 protected Flags flags;
1514 public bool Unchecked {
1515 get { return (flags & Flags.Unchecked) != 0; }
1516 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1519 public bool Unsafe {
1520 get { return (flags & Flags.Unsafe) != 0; }
1521 set { flags |= Flags.Unsafe; }
1525 // The statements in this block
1527 protected ArrayList statements;
1530 // An array of Blocks. We keep track of children just
1531 // to generate the local variable declarations.
1533 // Statements and child statements are handled through the
1539 // Labels. (label, block) pairs.
1541 protected HybridDictionary labels;
1544 // Keeps track of (name, type) pairs
1546 IDictionary variables;
1549 // Keeps track of constants
1550 HybridDictionary constants;
1553 // Temporary variables.
1555 ArrayList temporary_variables;
1558 // If this is a switch section, the enclosing switch block.
1562 protected ArrayList scope_initializers;
1564 ArrayList anonymous_children;
1566 protected static int id;
1570 int assignable_slots;
1571 bool unreachable_shown;
1574 public Block (Block parent)
1575 : this (parent, (Flags) 0, Location.Null, Location.Null)
1578 public Block (Block parent, Flags flags)
1579 : this (parent, flags, Location.Null, Location.Null)
1582 public Block (Block parent, Location start, Location end)
1583 : this (parent, (Flags) 0, start, end)
1587 // Useful when TopLevel block is downgraded to normal block
1589 public Block (ToplevelBlock parent, ToplevelBlock source)
1590 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1592 statements = source.statements;
1593 children = source.children;
1594 labels = source.labels;
1595 variables = source.variables;
1596 constants = source.constants;
1597 switch_block = source.switch_block;
1600 public Block (Block parent, Flags flags, Location start, Location end)
1602 if (parent != null) {
1603 parent.AddChild (this);
1605 // the appropriate constructors will fixup these fields
1606 Toplevel = parent.Toplevel;
1607 Explicit = parent.Explicit;
1610 this.Parent = parent;
1612 this.StartLocation = start;
1613 this.EndLocation = end;
1616 statements = new ArrayList (4);
1619 public Block CreateSwitchBlock (Location start)
1621 // FIXME: should this be implicit?
1622 Block new_block = new ExplicitBlock (this, start, start);
1623 new_block.switch_block = this;
1628 get { return this_id; }
1631 public IDictionary Variables {
1633 if (variables == null)
1634 variables = new ListDictionary ();
1639 void AddChild (Block b)
1641 if (children == null)
1642 children = new ArrayList (1);
1647 public void SetEndLocation (Location loc)
1652 protected static void Error_158 (string name, Location loc)
1654 Report.Error (158, loc, "The label `{0}' shadows another label " +
1655 "by the same name in a contained scope", name);
1659 /// Adds a label to the current block.
1663 /// false if the name already exists in this block. true
1667 public bool AddLabel (LabeledStatement target)
1669 if (switch_block != null)
1670 return switch_block.AddLabel (target);
1672 string name = target.Name;
1675 while (cur != null) {
1676 LabeledStatement s = cur.DoLookupLabel (name);
1678 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1679 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1683 if (this == Explicit)
1689 while (cur != null) {
1690 if (cur.DoLookupLabel (name) != null) {
1691 Error_158 (name, target.loc);
1695 if (children != null) {
1696 foreach (Block b in children) {
1697 LabeledStatement s = b.DoLookupLabel (name);
1701 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1702 Error_158 (name, target.loc);
1710 Toplevel.CheckError158 (name, target.loc);
1713 labels = new HybridDictionary();
1715 labels.Add (name, target);
1719 public LabeledStatement LookupLabel (string name)
1721 LabeledStatement s = DoLookupLabel (name);
1725 if (children == null)
1728 foreach (Block child in children) {
1729 if (Explicit != child.Explicit)
1732 s = child.LookupLabel (name);
1740 LabeledStatement DoLookupLabel (string name)
1742 if (switch_block != null)
1743 return switch_block.LookupLabel (name);
1746 if (labels.Contains (name))
1747 return ((LabeledStatement) labels [name]);
1752 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1755 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1756 while (kvi == null) {
1757 b = b.Explicit.Parent;
1760 kvi = b.Explicit.GetKnownVariable (name);
1766 // Is kvi.Block nested inside 'b'
1767 if (b.Explicit != kvi.Block.Explicit) {
1769 // If a variable by the same name it defined in a nested block of this
1770 // block, we violate the invariant meaning in a block.
1773 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1774 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1779 // It's ok if the definition is in a nested subblock of b, but not
1780 // nested inside this block -- a definition in a sibling block
1781 // should not affect us.
1787 // Block 'b' and kvi.Block are the same textual block.
1788 // However, different variables are extant.
1790 // Check if the variable is in scope in both blocks. We use
1791 // an indirect check that depends on AddVariable doing its
1792 // part in maintaining the invariant-meaning-in-block property.
1794 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1797 if (this is ToplevelBlock) {
1798 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1799 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1804 // Even though we detected the error when the name is used, we
1805 // treat it as if the variable declaration was in error.
1807 Report.SymbolRelatedToPreviousError (loc, name);
1808 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1812 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1814 LocalInfo vi = GetLocalInfo (name);
1816 Report.SymbolRelatedToPreviousError (vi.Location, name);
1817 if (Explicit == vi.Block.Explicit) {
1818 Error_AlreadyDeclared (l, name, null);
1820 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1821 "parent or current" : "parent");
1826 if (block != null) {
1827 Expression e = block.GetParameterReference (name, Location.Null);
1829 ParameterReference pr = e as ParameterReference;
1830 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1831 Error_AlreadyDeclared (loc, name);
1833 Error_AlreadyDeclared (loc, name, "parent or current");
1841 public LocalInfo AddVariable (Expression type, string name, Location l)
1843 if (!CheckParentConflictName (Toplevel, name, l))
1846 if (Toplevel.GenericMethod != null) {
1847 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1848 if (tp.Name == name) {
1849 Report.SymbolRelatedToPreviousError (tp);
1850 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1856 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1858 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1859 Error_AlreadyDeclared (l, name, "child");
1863 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1866 if ((flags & Flags.VariablesInitialized) != 0)
1867 throw new InternalErrorException ("block has already been resolved");
1872 protected virtual void AddVariable (LocalInfo li)
1874 Variables.Add (li.Name, li);
1875 Explicit.AddKnownVariable (li.Name, li);
1878 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1880 if (reason == null) {
1881 Error_AlreadyDeclared (loc, var);
1885 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1886 "in this scope because it would give a different meaning " +
1887 "to `{0}', which is already used in a `{1}' scope " +
1888 "to denote something else", var, reason);
1891 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1893 Report.Error (128, loc,
1894 "A local variable named `{0}' is already defined in this scope", name);
1897 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1899 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1903 public bool AddConstant (Expression type, string name, Expression value, Location l)
1905 if (AddVariable (type, name, l) == null)
1908 if (constants == null)
1909 constants = new HybridDictionary();
1911 constants.Add (name, value);
1913 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1918 static int next_temp_id = 0;
1920 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1922 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1924 if (temporary_variables == null)
1925 temporary_variables = new ArrayList ();
1927 int id = ++next_temp_id;
1928 string name = "$s_" + id.ToString ();
1930 LocalInfo li = new LocalInfo (te, name, this, loc);
1931 li.CompilerGenerated = true;
1932 temporary_variables.Add (li);
1936 public LocalInfo GetLocalInfo (string name)
1939 for (Block b = this; b != null; b = b.Parent) {
1940 if (b.variables != null) {
1941 ret = (LocalInfo) b.variables [name];
1950 public Expression GetVariableType (string name)
1952 LocalInfo vi = GetLocalInfo (name);
1953 return vi == null ? null : vi.Type;
1956 public Expression GetConstantExpression (string name)
1958 for (Block b = this; b != null; b = b.Parent) {
1959 if (b.constants != null) {
1960 Expression ret = b.constants [name] as Expression;
1969 // It should be used by expressions which require to
1970 // register a statement during resolve process.
1972 public void AddScopeStatement (Statement s)
1974 if (scope_initializers == null)
1975 scope_initializers = new ArrayList ();
1977 scope_initializers.Add (s);
1980 public void AddStatement (Statement s)
1983 flags |= Flags.BlockUsed;
1987 get { return (flags & Flags.BlockUsed) != 0; }
1992 flags |= Flags.BlockUsed;
1995 public bool HasRet {
1996 get { return (flags & Flags.HasRet) != 0; }
1999 public int AssignableSlots {
2002 // if ((flags & Flags.VariablesInitialized) == 0)
2003 // throw new Exception ("Variables have not been initialized yet");
2004 return assignable_slots;
2008 public ArrayList AnonymousChildren {
2009 get { return anonymous_children; }
2012 public void AddAnonymousChild (ToplevelBlock b)
2014 if (anonymous_children == null)
2015 anonymous_children = new ArrayList ();
2017 anonymous_children.Add (b);
2020 void DoResolveConstants (BlockContext ec)
2022 if (constants == null)
2025 if (variables == null)
2026 throw new InternalErrorException ("cannot happen");
2028 foreach (DictionaryEntry de in variables) {
2029 string name = (string) de.Key;
2030 LocalInfo vi = (LocalInfo) de.Value;
2031 Type variable_type = vi.VariableType;
2033 if (variable_type == null) {
2034 if (vi.Type is VarExpr)
2035 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2040 Expression cv = (Expression) constants [name];
2044 // Don't let 'const int Foo = Foo;' succeed.
2045 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2046 // which in turn causes the 'must be constant' error to be triggered.
2047 constants.Remove (name);
2049 if (!Const.IsConstantTypeValid (variable_type)) {
2050 Const.Error_InvalidConstantType (variable_type, loc);
2054 ec.CurrentBlock = this;
2056 using (ec.With (ResolveContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2057 e = cv.Resolve (ec);
2062 Constant ce = e as Constant;
2064 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2068 e = ce.ConvertImplicitly (variable_type);
2070 if (TypeManager.IsReferenceType (variable_type))
2071 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2073 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2077 constants.Add (name, e);
2078 vi.IsConstant = true;
2082 protected void ResolveMeta (BlockContext ec, int offset)
2084 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2086 // If some parent block was unsafe, we remain unsafe even if this block
2087 // isn't explicitly marked as such.
2088 using (ec.With (ResolveContext.Options.UnsafeScope, ec.IsUnsafe | Unsafe)) {
2089 flags |= Flags.VariablesInitialized;
2091 if (variables != null) {
2092 foreach (LocalInfo li in variables.Values) {
2093 if (!li.Resolve (ec))
2095 li.VariableInfo = new VariableInfo (li, offset);
2096 offset += li.VariableInfo.Length;
2099 assignable_slots = offset;
2101 DoResolveConstants (ec);
2103 if (children == null)
2105 foreach (Block b in children)
2106 b.ResolveMeta (ec, offset);
2111 // Emits the local variable declarations for a block
2113 public virtual void EmitMeta (EmitContext ec)
2115 if (variables != null){
2116 foreach (LocalInfo vi in variables.Values)
2117 vi.ResolveVariable (ec);
2120 if (temporary_variables != null) {
2121 for (int i = 0; i < temporary_variables.Count; i++)
2122 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2125 if (children != null) {
2126 for (int i = 0; i < children.Count; i++)
2127 ((Block)children[i]).EmitMeta(ec);
2131 void UsageWarning ()
2133 if (variables == null || Report.WarningLevel < 3)
2136 foreach (DictionaryEntry de in variables) {
2137 LocalInfo vi = (LocalInfo) de.Value;
2140 string name = (string) de.Key;
2142 // vi.VariableInfo can be null for 'catch' variables
2143 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2144 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2146 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2151 static void CheckPossibleMistakenEmptyStatement (Statement s)
2155 // Some statements are wrapped by a Block. Since
2156 // others' internal could be changed, here I treat
2157 // them as possibly wrapped by Block equally.
2158 Block b = s as Block;
2159 if (b != null && b.statements.Count == 1)
2160 s = (Statement) b.statements [0];
2163 body = ((Lock) s).Statement;
2165 body = ((For) s).Statement;
2166 else if (s is Foreach)
2167 body = ((Foreach) s).Statement;
2168 else if (s is While)
2169 body = ((While) s).Statement;
2170 else if (s is Fixed)
2171 body = ((Fixed) s).Statement;
2172 else if (s is Using)
2173 body = ((Using) s).EmbeddedStatement;
2174 else if (s is UsingTemporary)
2175 body = ((UsingTemporary) s).Statement;
2179 if (body == null || body is EmptyStatement)
2180 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2183 public override bool Resolve (BlockContext ec)
2185 Block prev_block = ec.CurrentBlock;
2188 int errors = Report.Errors;
2190 ec.CurrentBlock = this;
2191 ec.StartFlowBranching (this);
2193 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2196 // Compiler generated scope statements
2198 if (scope_initializers != null) {
2199 foreach (Statement s in scope_initializers)
2204 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2205 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2206 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2207 // responsible for handling the situation.
2209 int statement_count = statements.Count;
2210 for (int ix = 0; ix < statement_count; ix++){
2211 Statement s = (Statement) statements [ix];
2212 // Check possible empty statement (CS0642)
2213 if (Report.WarningLevel >= 3 &&
2214 ix + 1 < statement_count &&
2215 statements [ix + 1] is ExplicitBlock)
2216 CheckPossibleMistakenEmptyStatement (s);
2219 // Warn if we detect unreachable code.
2222 if (s is EmptyStatement)
2225 if (!unreachable_shown && !(s is LabeledStatement)) {
2226 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2227 unreachable_shown = true;
2230 Block c_block = s as Block;
2231 if (c_block != null)
2232 c_block.unreachable = c_block.unreachable_shown = true;
2236 // Note that we're not using ResolveUnreachable() for unreachable
2237 // statements here. ResolveUnreachable() creates a temporary
2238 // flow branching and kills it afterwards. This leads to problems
2239 // if you have two unreachable statements where the first one
2240 // assigns a variable and the second one tries to access it.
2243 if (!s.Resolve (ec)) {
2245 if (ec.IsInProbingMode)
2248 statements [ix] = EmptyStatement.Value;
2252 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2253 statements [ix] = EmptyStatement.Value;
2255 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2256 if (unreachable && s is LabeledStatement)
2257 throw new InternalErrorException ("should not happen");
2260 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2261 ec.CurrentBranching, statement_count);
2263 while (ec.CurrentBranching is FlowBranchingLabeled)
2264 ec.EndFlowBranching ();
2266 bool flow_unreachable = ec.EndFlowBranching ();
2268 ec.CurrentBlock = prev_block;
2270 if (flow_unreachable)
2271 flags |= Flags.HasRet;
2273 // If we're a non-static `struct' constructor which doesn't have an
2274 // initializer, then we must initialize all of the struct's fields.
2275 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2278 if ((labels != null) && (Report.WarningLevel >= 2)) {
2279 foreach (LabeledStatement label in labels.Values)
2280 if (!label.HasBeenReferenced)
2281 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2284 if (ok && errors == Report.Errors)
2290 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2292 unreachable_shown = true;
2296 Report.Warning (162, 2, loc, "Unreachable code detected");
2298 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2299 bool ok = Resolve (ec);
2300 ec.KillFlowBranching ();
2305 protected override void DoEmit (EmitContext ec)
2307 for (int ix = 0; ix < statements.Count; ix++){
2308 Statement s = (Statement) statements [ix];
2313 public override void Emit (EmitContext ec)
2315 if (scope_initializers != null)
2316 EmitScopeInitializers (ec);
2318 ec.Mark (StartLocation);
2321 if (SymbolWriter.HasSymbolWriter)
2322 EmitSymbolInfo (ec);
2325 protected void EmitScopeInitializers (EmitContext ec)
2327 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2329 using (ec.With (EmitContext.Options.OmitDebugInfo, true)) {
2330 foreach (Statement s in scope_initializers)
2334 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2337 protected virtual void EmitSymbolInfo (EmitContext ec)
2339 if (variables != null) {
2340 foreach (LocalInfo vi in variables.Values) {
2341 vi.EmitSymbolInfo (ec);
2346 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2348 MutateVariables (storey);
2350 if (scope_initializers != null) {
2351 foreach (Statement s in scope_initializers)
2352 s.MutateHoistedGenericType (storey);
2355 foreach (Statement s in statements)
2356 s.MutateHoistedGenericType (storey);
2359 void MutateVariables (AnonymousMethodStorey storey)
2361 if (variables != null) {
2362 foreach (LocalInfo vi in variables.Values) {
2363 vi.VariableType = storey.MutateType (vi.VariableType);
2367 if (temporary_variables != null) {
2368 foreach (LocalInfo vi in temporary_variables)
2369 vi.VariableType = storey.MutateType (vi.VariableType);
2373 public override string ToString ()
2375 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2378 protected override void CloneTo (CloneContext clonectx, Statement t)
2380 Block target = (Block) t;
2382 clonectx.AddBlockMap (this, target);
2384 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2385 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2387 target.Parent = clonectx.RemapBlockCopy (Parent);
2389 if (variables != null){
2390 target.variables = new Hashtable ();
2392 foreach (DictionaryEntry de in variables){
2393 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2394 target.variables [de.Key] = newlocal;
2395 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2399 target.statements = new ArrayList (statements.Count);
2400 foreach (Statement s in statements)
2401 target.statements.Add (s.Clone (clonectx));
2403 if (target.children != null){
2404 target.children = new ArrayList (children.Count);
2405 foreach (Block b in children){
2406 target.children.Add (clonectx.LookupBlock (b));
2411 // TODO: labels, switch_block, constants (?), anonymous_children
2416 public class ExplicitBlock : Block {
2417 HybridDictionary known_variables;
2418 protected AnonymousMethodStorey am_storey;
2420 public ExplicitBlock (Block parent, Location start, Location end)
2421 : this (parent, (Flags) 0, start, end)
2425 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2426 : base (parent, flags, start, end)
2428 this.Explicit = this;
2432 // Marks a variable with name @name as being used in this or a child block.
2433 // If a variable name has been used in a child block, it's illegal to
2434 // declare a variable with the same name in the current block.
2436 internal void AddKnownVariable (string name, IKnownVariable info)
2438 if (known_variables == null)
2439 known_variables = new HybridDictionary();
2441 known_variables [name] = info;
2444 Parent.Explicit.AddKnownVariable (name, info);
2447 public AnonymousMethodStorey AnonymousMethodStorey {
2448 get { return am_storey; }
2452 // Creates anonymous method storey in current block
2454 public AnonymousMethodStorey CreateAnonymousMethodStorey (ResolveContext ec)
2457 // When referencing a variable in iterator storey from children anonymous method
2459 if (Toplevel.am_storey is IteratorStorey) {
2460 return Toplevel.am_storey;
2464 // An iterator has only 1 storey block
2466 if (ec.CurrentIterator != null)
2467 return ec.CurrentIterator.Storey;
2469 if (am_storey == null) {
2470 MemberBase mc = ec.MemberContext as MemberBase;
2471 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2474 // Creates anonymous method storey for this block
2476 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2482 public override void Emit (EmitContext ec)
2484 if (am_storey != null)
2485 am_storey.EmitStoreyInstantiation (ec);
2487 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2488 if (emit_debug_info)
2493 if (emit_debug_info)
2497 public override void EmitMeta (EmitContext ec)
2500 // Creates anonymous method storey
2502 if (am_storey != null) {
2503 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2505 // Creates parent storey reference when hoisted this is accessible
2507 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2508 ExplicitBlock parent = Toplevel.Parent.Explicit;
2511 // Hoisted this exists in top-level parent storey only
2513 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2514 parent = parent.Parent.Explicit;
2516 am_storey.AddParentStoreyReference (parent.am_storey);
2519 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2522 am_storey.DefineType ();
2523 am_storey.ResolveType ();
2524 am_storey.Define ();
2525 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2527 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2528 if (ref_blocks != null) {
2529 foreach (ExplicitBlock ref_block in ref_blocks) {
2530 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2531 if (b.am_storey != null) {
2532 b.am_storey.AddParentStoreyReference (am_storey);
2534 // Stop propagation inside same top block
2535 if (b.Toplevel == Toplevel)
2540 b.HasCapturedVariable = true;
2549 internal IKnownVariable GetKnownVariable (string name)
2551 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2554 public bool HasCapturedThis
2556 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2557 get { return (flags & Flags.HasCapturedThis) != 0; }
2560 public bool HasCapturedVariable
2562 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2563 get { return (flags & Flags.HasCapturedVariable) != 0; }
2566 protected override void CloneTo (CloneContext clonectx, Statement t)
2568 ExplicitBlock target = (ExplicitBlock) t;
2569 target.known_variables = null;
2570 base.CloneTo (clonectx, t);
2574 public class ToplevelParameterInfo : IKnownVariable {
2575 public readonly ToplevelBlock Block;
2576 public readonly int Index;
2577 public VariableInfo VariableInfo;
2579 Block IKnownVariable.Block {
2580 get { return Block; }
2582 public Parameter Parameter {
2583 get { return Block.Parameters [Index]; }
2586 public Type ParameterType {
2587 get { return Block.Parameters.Types [Index]; }
2590 public Location Location {
2591 get { return Parameter.Location; }
2594 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2602 // A toplevel block contains extra information, the split is done
2603 // only to separate information that would otherwise bloat the more
2604 // lightweight Block.
2606 // In particular, this was introduced when the support for Anonymous
2607 // Methods was implemented.
2609 public class ToplevelBlock : ExplicitBlock
2612 // Block is converted to an expression
2614 sealed class BlockScopeExpression : Expression
2617 readonly ToplevelBlock block;
2619 public BlockScopeExpression (Expression child, ToplevelBlock block)
2625 public override Expression CreateExpressionTree (ResolveContext ec)
2627 throw new NotSupportedException ();
2630 public override Expression DoResolve (ResolveContext ec)
2635 child = child.Resolve (ec);
2639 eclass = child.eclass;
2644 public override void Emit (EmitContext ec)
2646 block.EmitMeta (ec);
2647 block.EmitScopeInitializers (ec);
2651 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2653 type = storey.MutateType (type);
2654 child.MutateHoistedGenericType (storey);
2655 block.MutateHoistedGenericType (storey);
2659 GenericMethod generic;
2660 protected ParametersCompiled parameters;
2661 ToplevelParameterInfo[] parameter_info;
2662 LocalInfo this_variable;
2666 public HoistedVariable HoistedThisVariable;
2668 public bool Resolved {
2675 // The parameters for the block.
2677 public ParametersCompiled Parameters {
2678 get { return parameters; }
2681 public GenericMethod GenericMethod {
2682 get { return generic; }
2685 public ToplevelBlock Container {
2686 get { return Parent == null ? null : Parent.Toplevel; }
2689 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2690 this (parent, (Flags) 0, parameters, start)
2694 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2695 this (parent, parameters, start)
2697 this.generic = generic;
2700 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2701 this (null, (Flags) 0, parameters, start)
2705 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2706 this (null, flags, parameters, start)
2710 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2711 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2712 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2713 base (null, flags, start, Location.Null)
2715 this.Toplevel = this;
2717 this.parameters = parameters;
2718 this.Parent = parent;
2720 parent.AddAnonymousChild (this);
2722 if (!this.parameters.IsEmpty)
2723 ProcessParameters ();
2726 public ToplevelBlock (Location loc)
2727 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2731 protected override void CloneTo (CloneContext clonectx, Statement t)
2733 ToplevelBlock target = (ToplevelBlock) t;
2734 base.CloneTo (clonectx, t);
2736 if (parameters.Count != 0)
2737 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2738 for (int i = 0; i < parameters.Count; ++i)
2739 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2742 public bool CheckError158 (string name, Location loc)
2744 if (AnonymousChildren != null) {
2745 foreach (ToplevelBlock child in AnonymousChildren) {
2746 if (!child.CheckError158 (name, loc))
2751 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2752 if (!c.DoCheckError158 (name, loc))
2759 void ProcessParameters ()
2761 int n = parameters.Count;
2762 parameter_info = new ToplevelParameterInfo [n];
2763 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2764 for (int i = 0; i < n; ++i) {
2765 parameter_info [i] = new ToplevelParameterInfo (this, i);
2767 Parameter p = parameters [i];
2771 string name = p.Name;
2772 if (CheckParentConflictName (top_parent, name, loc))
2773 AddKnownVariable (name, parameter_info [i]);
2776 // mark this block as "used" so that we create local declarations in a sub-block
2777 // FIXME: This appears to uncover a lot of bugs
2781 bool DoCheckError158 (string name, Location loc)
2783 LabeledStatement s = LookupLabel (name);
2785 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2786 Error_158 (name, loc);
2793 public override Expression CreateExpressionTree (ResolveContext ec)
2795 if (statements.Count == 1) {
2796 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2797 if (scope_initializers != null)
2798 expr = new BlockScopeExpression (expr, this);
2803 return base.CreateExpressionTree (ec);
2807 // Reformats this block to be top-level iterator block
2809 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2813 // Creates block with original statements
2814 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2816 source.statements = new ArrayList (1);
2817 source.AddStatement (new Return (iterator, iterator.Location));
2818 source.IsIterator = false;
2820 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2821 source.am_storey = iterator_storey;
2822 return iterator_storey;
2826 // Returns a parameter reference expression for the given name,
2827 // or null if there is no such parameter
2829 public Expression GetParameterReference (string name, Location loc)
2831 for (ToplevelBlock t = this; t != null; t = t.Container) {
2832 Expression expr = t.GetParameterReferenceExpression (name, loc);
2840 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2842 int idx = parameters.GetParameterIndexByName (name);
2844 null : new ParameterReference (parameter_info [idx], loc);
2848 // Returns the "this" instance variable of this block.
2849 // See AddThisVariable() for more information.
2851 public LocalInfo ThisVariable {
2852 get { return this_variable; }
2856 // This is used by non-static `struct' constructors which do not have an
2857 // initializer - in this case, the constructor must initialize all of the
2858 // struct's fields. To do this, we add a "this" variable and use the flow
2859 // analysis code to ensure that it's been fully initialized before control
2860 // leaves the constructor.
2862 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2864 if (this_variable == null) {
2865 this_variable = new LocalInfo (ds, this, l);
2866 this_variable.Used = true;
2867 this_variable.IsThis = true;
2869 Variables.Add ("this", this_variable);
2872 return this_variable;
2875 public bool IsIterator {
2876 get { return (flags & Flags.IsIterator) != 0; }
2877 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2880 public bool IsThisAssigned (BlockContext ec)
2882 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2885 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2893 if (!ResolveMeta (rc, ip))
2896 using (rc.With (ResolveContext.Options.DoFlowAnalysis, true)) {
2897 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2902 unreachable = top_level.End ();
2904 } catch (Exception) {
2906 if (rc.CurrentBlock != null) {
2907 Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2909 Report.Error (587, "Internal compiler error: Phase Resolve");
2915 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2916 if (rc.CurrentAnonymousMethod == null) {
2917 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2919 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2920 Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2921 rc.CurrentAnonymousMethod.GetSignatureForError ());
2929 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2931 int errors = Report.Errors;
2932 int orig_count = parameters.Count;
2937 // Assert: orig_count != parameter.Count => orig_count == 0
2938 if (orig_count != 0 && orig_count != parameters.Count)
2939 throw new InternalErrorException ("parameter information mismatch");
2941 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2943 for (int i = 0; i < orig_count; ++i) {
2944 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2946 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2949 VariableInfo vi = new VariableInfo (ip, i, offset);
2950 parameter_info [i].VariableInfo = vi;
2951 offset += vi.Length;
2954 ResolveMeta (ec, offset);
2956 return Report.Errors == errors;
2960 // Check whether all `out' parameters have been assigned.
2962 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2964 if (vector.IsUnreachable)
2967 int n = parameter_info == null ? 0 : parameter_info.Length;
2969 for (int i = 0; i < n; i++) {
2970 VariableInfo var = parameter_info [i].VariableInfo;
2975 if (vector.IsAssigned (var, false))
2978 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2983 public override void Emit (EmitContext ec)
2985 if (Report.Errors > 0)
2993 if (ec.HasReturnLabel)
2994 ec.ReturnLabel = ec.ig.DefineLabel ();
2998 ec.Mark (EndLocation);
3000 if (ec.HasReturnLabel)
3001 ec.ig.MarkLabel (ec.ReturnLabel);
3003 if (ec.return_value != null) {
3004 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
3005 ec.ig.Emit (OpCodes.Ret);
3008 // If `HasReturnLabel' is set, then we already emitted a
3009 // jump to the end of the method, so we must emit a `ret'
3012 // Unfortunately, System.Reflection.Emit automatically emits
3013 // a leave to the end of a finally block. This is a problem
3014 // if no code is following the try/finally block since we may
3015 // jump to a point after the end of the method.
3016 // As a workaround, we're always creating a return label in
3020 if (ec.HasReturnLabel || !unreachable) {
3021 if (ec.ReturnType != TypeManager.void_type)
3022 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3023 ec.ig.Emit (OpCodes.Ret);
3028 } catch (Exception e){
3029 Console.WriteLine ("Exception caught by the compiler while emitting:");
3030 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3032 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3038 public override void EmitMeta (EmitContext ec)
3040 parameters.ResolveVariable ();
3042 // Avoid declaring an IL variable for this_variable since it is not accessed
3043 // from the generated IL
3044 if (this_variable != null)
3045 Variables.Remove ("this");
3049 protected override void EmitSymbolInfo (EmitContext ec)
3051 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3052 if ((ae != null) && (ae.Storey != null))
3053 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3055 base.EmitSymbolInfo (ec);
3059 public class SwitchLabel {
3066 Label il_label_code;
3067 bool il_label_code_set;
3069 public static readonly object NullStringCase = new object ();
3072 // if expr == null, then it is the default case.
3074 public SwitchLabel (Expression expr, Location l)
3080 public Expression Label {
3086 public Location Location {
3090 public object Converted {
3096 public Label GetILLabel (EmitContext ec)
3099 il_label = ec.ig.DefineLabel ();
3100 il_label_set = true;
3105 public Label GetILLabelCode (EmitContext ec)
3107 if (!il_label_code_set){
3108 il_label_code = ec.ig.DefineLabel ();
3109 il_label_code_set = true;
3111 return il_label_code;
3115 // Resolves the expression, reduces it to a literal if possible
3116 // and then converts it to the requested type.
3118 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3120 Expression e = label.Resolve (ec);
3125 Constant c = e as Constant;
3127 Report.Error (150, loc, "A constant value is expected");
3131 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3132 converted = NullStringCase;
3136 if (allow_nullable && c.GetValue () == null) {
3137 converted = NullStringCase;
3141 c = c.ImplicitConversionRequired (ec, required_type, loc);
3145 converted = c.GetValue ();
3149 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3152 if (converted == null)
3154 else if (converted == NullStringCase)
3157 label = converted.ToString ();
3159 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3160 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3163 public SwitchLabel Clone (CloneContext clonectx)
3165 return new SwitchLabel (label.Clone (clonectx), loc);
3169 public class SwitchSection {
3170 // An array of SwitchLabels.
3171 public readonly ArrayList Labels;
3172 public readonly Block Block;
3174 public SwitchSection (ArrayList labels, Block block)
3180 public SwitchSection Clone (CloneContext clonectx)
3182 ArrayList cloned_labels = new ArrayList ();
3184 foreach (SwitchLabel sl in cloned_labels)
3185 cloned_labels.Add (sl.Clone (clonectx));
3187 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3191 public class Switch : Statement {
3192 public ArrayList Sections;
3193 public Expression Expr;
3196 /// Maps constants whose type type SwitchType to their SwitchLabels.
3198 public IDictionary Elements;
3201 /// The governing switch type
3203 public Type SwitchType;
3208 Label default_target;
3210 Expression new_expr;
3213 SwitchSection constant_section;
3214 SwitchSection default_section;
3216 ExpressionStatement string_dictionary;
3217 FieldExpr switch_cache_field;
3218 static int unique_counter;
3221 // Nullable Types support
3223 Nullable.Unwrap unwrap;
3225 protected bool HaveUnwrap {
3226 get { return unwrap != null; }
3230 // The types allowed to be implicitly cast from
3231 // on the governing type
3233 static Type [] allowed_types;
3235 public Switch (Expression e, ArrayList sects, Location l)
3242 public bool GotDefault {
3244 return default_section != null;
3248 public Label DefaultTarget {
3250 return default_target;
3255 // Determines the governing type for a switch. The returned
3256 // expression might be the expression from the switch, or an
3257 // expression that includes any potential conversions to the
3258 // integral types or to string.
3260 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3264 if (t == TypeManager.byte_type ||
3265 t == TypeManager.sbyte_type ||
3266 t == TypeManager.ushort_type ||
3267 t == TypeManager.short_type ||
3268 t == TypeManager.uint32_type ||
3269 t == TypeManager.int32_type ||
3270 t == TypeManager.uint64_type ||
3271 t == TypeManager.int64_type ||
3272 t == TypeManager.char_type ||
3273 t == TypeManager.string_type ||
3274 t == TypeManager.bool_type ||
3275 TypeManager.IsEnumType (t))
3278 if (allowed_types == null){
3279 allowed_types = new Type [] {
3280 TypeManager.sbyte_type,
3281 TypeManager.byte_type,
3282 TypeManager.short_type,
3283 TypeManager.ushort_type,
3284 TypeManager.int32_type,
3285 TypeManager.uint32_type,
3286 TypeManager.int64_type,
3287 TypeManager.uint64_type,
3288 TypeManager.char_type,
3289 TypeManager.string_type
3294 // Try to find a *user* defined implicit conversion.
3296 // If there is no implicit conversion, or if there are multiple
3297 // conversions, we have to report an error
3299 Expression converted = null;
3300 foreach (Type tt in allowed_types){
3303 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3308 // Ignore over-worked ImplicitUserConversions that do
3309 // an implicit conversion in addition to the user conversion.
3311 if (!(e is UserCast))
3314 if (converted != null){
3315 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3325 // Performs the basic sanity checks on the switch statement
3326 // (looks for duplicate keys and non-constant expressions).
3328 // It also returns a hashtable with the keys that we will later
3329 // use to compute the switch tables
3331 bool CheckSwitch (ResolveContext ec)
3334 Elements = Sections.Count > 10 ?
3335 (IDictionary)new Hashtable () :
3336 (IDictionary)new ListDictionary ();
3338 foreach (SwitchSection ss in Sections){
3339 foreach (SwitchLabel sl in ss.Labels){
3340 if (sl.Label == null){
3341 if (default_section != null){
3342 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3345 default_section = ss;
3349 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3354 object key = sl.Converted;
3355 if (key == SwitchLabel.NullStringCase)
3356 has_null_case = true;
3359 Elements.Add (key, sl);
3360 } catch (ArgumentException) {
3361 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3369 void EmitObjectInteger (ILGenerator ig, object k)
3372 IntConstant.EmitInt (ig, (int) k);
3373 else if (k is Constant) {
3374 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3377 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3380 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3382 IntConstant.EmitInt (ig, (int) (long) k);
3383 ig.Emit (OpCodes.Conv_I8);
3386 LongConstant.EmitLong (ig, (long) k);
3388 else if (k is ulong)
3390 ulong ul = (ulong) k;
3393 IntConstant.EmitInt (ig, unchecked ((int) ul));
3394 ig.Emit (OpCodes.Conv_U8);
3398 LongConstant.EmitLong (ig, unchecked ((long) ul));
3402 IntConstant.EmitInt (ig, (int) ((char) k));
3403 else if (k is sbyte)
3404 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3406 IntConstant.EmitInt (ig, (int) ((byte) k));
3407 else if (k is short)
3408 IntConstant.EmitInt (ig, (int) ((short) k));
3409 else if (k is ushort)
3410 IntConstant.EmitInt (ig, (int) ((ushort) k));
3412 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3414 throw new Exception ("Unhandled case");
3417 // structure used to hold blocks of keys while calculating table switch
3418 class KeyBlock : IComparable
3420 public KeyBlock (long _first)
3422 first = last = _first;
3426 public ArrayList element_keys = null;
3427 // how many items are in the bucket
3428 public int Size = 1;
3431 get { return (int) (last - first + 1); }
3433 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3435 return kb_last.last - kb_first.first + 1;
3437 public int CompareTo (object obj)
3439 KeyBlock kb = (KeyBlock) obj;
3440 int nLength = Length;
3441 int nLengthOther = kb.Length;
3442 if (nLengthOther == nLength)
3443 return (int) (kb.first - first);
3444 return nLength - nLengthOther;
3449 /// This method emits code for a lookup-based switch statement (non-string)
3450 /// Basically it groups the cases into blocks that are at least half full,
3451 /// and then spits out individual lookup opcodes for each block.
3452 /// It emits the longest blocks first, and short blocks are just
3453 /// handled with direct compares.
3455 /// <param name="ec"></param>
3456 /// <param name="val"></param>
3457 /// <returns></returns>
3458 void TableSwitchEmit (EmitContext ec, Expression val)
3460 int element_count = Elements.Count;
3461 object [] element_keys = new object [element_count];
3462 Elements.Keys.CopyTo (element_keys, 0);
3463 Array.Sort (element_keys);
3465 // initialize the block list with one element per key
3466 ArrayList key_blocks = new ArrayList (element_count);
3467 foreach (object key in element_keys)
3468 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3470 KeyBlock current_kb;
3471 // iteratively merge the blocks while they are at least half full
3472 // there's probably a really cool way to do this with a tree...
3473 while (key_blocks.Count > 1)
3475 ArrayList key_blocks_new = new ArrayList ();
3476 current_kb = (KeyBlock) key_blocks [0];
3477 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3479 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3480 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3483 current_kb.last = kb.last;
3484 current_kb.Size += kb.Size;
3488 // start a new block
3489 key_blocks_new.Add (current_kb);
3493 key_blocks_new.Add (current_kb);
3494 if (key_blocks.Count == key_blocks_new.Count)
3496 key_blocks = key_blocks_new;
3499 // initialize the key lists
3500 foreach (KeyBlock kb in key_blocks)
3501 kb.element_keys = new ArrayList ();
3503 // fill the key lists
3505 if (key_blocks.Count > 0) {
3506 current_kb = (KeyBlock) key_blocks [0];
3507 foreach (object key in element_keys)
3509 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3510 System.Convert.ToInt64 (key) > current_kb.last;
3512 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3513 current_kb.element_keys.Add (key);
3517 // sort the blocks so we can tackle the largest ones first
3520 // okay now we can start...
3521 ILGenerator ig = ec.ig;
3522 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3523 Label lbl_default = default_target;
3525 Type type_keys = null;
3526 if (element_keys.Length > 0)
3527 type_keys = element_keys [0].GetType (); // used for conversions
3531 if (TypeManager.IsEnumType (SwitchType))
3532 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3534 compare_type = SwitchType;
3536 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3538 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3539 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3542 foreach (object key in kb.element_keys) {
3543 SwitchLabel sl = (SwitchLabel) Elements [key];
3544 if (key is int && (int) key == 0) {
3545 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3548 EmitObjectInteger (ig, key);
3549 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3555 // TODO: if all the keys in the block are the same and there are
3556 // no gaps/defaults then just use a range-check.
3557 if (compare_type == TypeManager.int64_type ||
3558 compare_type == TypeManager.uint64_type)
3560 // TODO: optimize constant/I4 cases
3562 // check block range (could be > 2^31)
3564 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3565 ig.Emit (OpCodes.Blt, lbl_default);
3567 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3568 ig.Emit (OpCodes.Bgt, lbl_default);
3574 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3575 ig.Emit (OpCodes.Sub);
3577 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3583 int first = (int) kb.first;
3586 IntConstant.EmitInt (ig, first);
3587 ig.Emit (OpCodes.Sub);
3591 IntConstant.EmitInt (ig, -first);
3592 ig.Emit (OpCodes.Add);
3596 // first, build the list of labels for the switch
3598 int cJumps = kb.Length;
3599 Label [] switch_labels = new Label [cJumps];
3600 for (int iJump = 0; iJump < cJumps; iJump++)
3602 object key = kb.element_keys [iKey];
3603 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3605 SwitchLabel sl = (SwitchLabel) Elements [key];
3606 switch_labels [iJump] = sl.GetILLabel (ec);
3610 switch_labels [iJump] = lbl_default;
3612 // emit the switch opcode
3613 ig.Emit (OpCodes.Switch, switch_labels);
3616 // mark the default for this block
3618 ig.MarkLabel (lbl_default);
3621 // TODO: find the default case and emit it here,
3622 // to prevent having to do the following jump.
3623 // make sure to mark other labels in the default section
3625 // the last default just goes to the end
3626 if (element_keys.Length > 0)
3627 ig.Emit (OpCodes.Br, lbl_default);
3629 // now emit the code for the sections
3630 bool found_default = false;
3632 foreach (SwitchSection ss in Sections) {
3633 foreach (SwitchLabel sl in ss.Labels) {
3634 if (sl.Converted == SwitchLabel.NullStringCase) {
3635 ig.MarkLabel (null_target);
3636 } else if (sl.Label == null) {
3637 ig.MarkLabel (lbl_default);
3638 found_default = true;
3640 ig.MarkLabel (null_target);
3642 ig.MarkLabel (sl.GetILLabel (ec));
3643 ig.MarkLabel (sl.GetILLabelCode (ec));
3648 if (!found_default) {
3649 ig.MarkLabel (lbl_default);
3650 if (!has_null_case) {
3651 ig.MarkLabel (null_target);
3655 ig.MarkLabel (lbl_end);
3658 SwitchSection FindSection (SwitchLabel label)
3660 foreach (SwitchSection ss in Sections){
3661 foreach (SwitchLabel sl in ss.Labels){
3670 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3672 foreach (SwitchSection ss in Sections)
3673 ss.Block.MutateHoistedGenericType (storey);
3676 public static void Reset ()
3679 allowed_types = null;
3682 public override bool Resolve (BlockContext ec)
3684 Expr = Expr.Resolve (ec);
3688 new_expr = SwitchGoverningType (ec, Expr);
3690 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3691 unwrap = Nullable.Unwrap.Create (Expr, false);
3695 new_expr = SwitchGoverningType (ec, unwrap);
3698 if (new_expr == null){
3699 Report.Error (151, loc,
3700 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3701 TypeManager.CSharpName (Expr.Type));
3706 SwitchType = new_expr.Type;
3708 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3709 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3713 if (!CheckSwitch (ec))
3717 Elements.Remove (SwitchLabel.NullStringCase);
3719 Switch old_switch = ec.Switch;
3721 ec.Switch.SwitchType = SwitchType;
3723 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3724 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3726 is_constant = new_expr is Constant;
3728 object key = ((Constant) new_expr).GetValue ();
3729 SwitchLabel label = (SwitchLabel) Elements [key];
3731 constant_section = FindSection (label);
3732 if (constant_section == null)
3733 constant_section = default_section;
3738 foreach (SwitchSection ss in Sections){
3740 ec.CurrentBranching.CreateSibling (
3741 null, FlowBranching.SiblingType.SwitchSection);
3745 if (is_constant && (ss != constant_section)) {
3746 // If we're a constant switch, we're only emitting
3747 // one single section - mark all the others as
3749 ec.CurrentBranching.CurrentUsageVector.Goto ();
3750 if (!ss.Block.ResolveUnreachable (ec, true)) {
3754 if (!ss.Block.Resolve (ec))
3759 if (default_section == null)
3760 ec.CurrentBranching.CreateSibling (
3761 null, FlowBranching.SiblingType.SwitchSection);
3763 ec.EndFlowBranching ();
3764 ec.Switch = old_switch;
3766 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3771 if (SwitchType == TypeManager.string_type && !is_constant) {
3772 // TODO: Optimize single case, and single+default case
3773 ResolveStringSwitchMap (ec);
3779 void ResolveStringSwitchMap (ResolveContext ec)
3781 FullNamedExpression string_dictionary_type;
3782 if (TypeManager.generic_ienumerable_type != null) {
3783 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3784 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3786 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3788 new TypeExpression (TypeManager.string_type, loc),
3789 new TypeExpression (TypeManager.int32_type, loc)), loc);
3791 MemberAccess system_collections_generic = new MemberAccess (
3792 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3794 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3797 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3798 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3799 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3800 if (!field.Define ())
3802 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3804 ArrayList init = new ArrayList ();
3807 string value = null;
3808 foreach (SwitchSection section in Sections) {
3809 int last_count = init.Count;
3810 foreach (SwitchLabel sl in section.Labels) {
3811 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3814 value = (string) sl.Converted;
3815 ArrayList init_args = new ArrayList (2);
3816 init_args.Add (new StringLiteral (value, sl.Location));
3817 init_args.Add (new IntConstant (counter, loc));
3818 init.Add (new CollectionElementInitializer (init_args, loc));
3822 // Don't add empty sections
3824 if (last_count == init.Count)
3827 Elements.Add (counter, section.Labels [0]);
3831 Arguments args = new Arguments (1);
3832 args.Add (new Argument (new IntConstant (init.Count, loc)));
3833 Expression initializer = new NewInitialize (string_dictionary_type, args,
3834 new CollectionOrObjectInitializers (init, loc), loc);
3836 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3837 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3840 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3842 ILGenerator ig = ec.ig;
3843 Label l_initialized = ig.DefineLabel ();
3846 // Skip initialization when value is null
3848 value.EmitBranchable (ec, null_target, false);
3851 // Check if string dictionary is initialized and initialize
3853 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3854 string_dictionary.EmitStatement (ec);
3855 ig.MarkLabel (l_initialized);
3857 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3859 ResolveContext rc = new ResolveContext (ec.MemberContext);
3861 if (TypeManager.generic_ienumerable_type != null) {
3862 Arguments get_value_args = new Arguments (2);
3863 get_value_args.Add (new Argument (value));
3864 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3865 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3866 if (get_item == null)
3870 // A value was not found, go to default case
3872 get_item.EmitBranchable (ec, default_target, false);
3874 Arguments get_value_args = new Arguments (1);
3875 get_value_args.Add (new Argument (value));
3877 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3878 if (get_item == null)
3881 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3882 get_item_object.EmitAssign (ec, get_item, true, false);
3883 ec.ig.Emit (OpCodes.Brfalse, default_target);
3885 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3886 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3888 get_item_int.EmitStatement (ec);
3889 get_item_object.Release (ec);
3892 TableSwitchEmit (ec, string_switch_variable);
3893 string_switch_variable.Release (ec);
3896 protected override void DoEmit (EmitContext ec)
3898 ILGenerator ig = ec.ig;
3900 default_target = ig.DefineLabel ();
3901 null_target = ig.DefineLabel ();
3903 // Store variable for comparission purposes
3904 // TODO: Don't duplicate non-captured VariableReference
3905 LocalTemporary value;
3907 value = new LocalTemporary (SwitchType);
3908 unwrap.EmitCheck (ec);
3909 ig.Emit (OpCodes.Brfalse, null_target);
3912 } else if (!is_constant) {
3913 value = new LocalTemporary (SwitchType);
3920 // Setup the codegen context
3922 Label old_end = ec.LoopEnd;
3923 Switch old_switch = ec.Switch;
3925 ec.LoopEnd = ig.DefineLabel ();
3930 if (constant_section != null)
3931 constant_section.Block.Emit (ec);
3932 } else if (string_dictionary != null) {
3933 DoEmitStringSwitch (value, ec);
3935 TableSwitchEmit (ec, value);
3941 // Restore context state.
3942 ig.MarkLabel (ec.LoopEnd);
3945 // Restore the previous context
3947 ec.LoopEnd = old_end;
3948 ec.Switch = old_switch;
3951 protected override void CloneTo (CloneContext clonectx, Statement t)
3953 Switch target = (Switch) t;
3955 target.Expr = Expr.Clone (clonectx);
3956 target.Sections = new ArrayList ();
3957 foreach (SwitchSection ss in Sections){
3958 target.Sections.Add (ss.Clone (clonectx));
3963 // A place where execution can restart in an iterator
3964 public abstract class ResumableStatement : Statement
3967 protected Label resume_point;
3969 public Label PrepareForEmit (EmitContext ec)
3973 resume_point = ec.ig.DefineLabel ();
3975 return resume_point;
3978 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3982 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3987 // Base class for statements that are implemented in terms of try...finally
3988 public abstract class ExceptionStatement : ResumableStatement
3993 protected abstract void EmitPreTryBody (EmitContext ec);
3994 protected abstract void EmitTryBody (EmitContext ec);
3995 protected abstract void EmitFinallyBody (EmitContext ec);
3997 protected sealed override void DoEmit (EmitContext ec)
3999 ILGenerator ig = ec.ig;
4001 EmitPreTryBody (ec);
4003 if (resume_points != null) {
4004 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
4005 ig.Emit (OpCodes.Stloc, iter.CurrentPC);
4008 ig.BeginExceptionBlock ();
4010 if (resume_points != null) {
4011 ig.MarkLabel (resume_point);
4013 // For normal control flow, we want to fall-through the Switch
4014 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4015 ig.Emit (OpCodes.Ldloc, iter.CurrentPC);
4016 IntConstant.EmitInt (ig, first_resume_pc);
4017 ig.Emit (OpCodes.Sub);
4019 Label [] labels = new Label [resume_points.Count];
4020 for (int i = 0; i < resume_points.Count; ++i)
4021 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
4022 ig.Emit (OpCodes.Switch, labels);
4027 ig.BeginFinallyBlock ();
4029 Label start_finally = ec.ig.DefineLabel ();
4030 if (resume_points != null) {
4031 ig.Emit (OpCodes.Ldloc, iter.SkipFinally);
4032 ig.Emit (OpCodes.Brfalse_S, start_finally);
4033 ig.Emit (OpCodes.Endfinally);
4036 ig.MarkLabel (start_finally);
4037 EmitFinallyBody (ec);
4039 ig.EndExceptionBlock ();
4042 public void SomeCodeFollows ()
4044 code_follows = true;
4047 public override bool Resolve (BlockContext ec)
4049 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4050 // So, ensure there's some IL code after this statement.
4051 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4052 ec.NeedReturnLabel ();
4054 iter = ec.CurrentIterator;
4058 ArrayList resume_points;
4059 int first_resume_pc;
4060 public void AddResumePoint (ResumableStatement stmt, int pc)
4062 if (resume_points == null) {
4063 resume_points = new ArrayList ();
4064 first_resume_pc = pc;
4067 if (pc != first_resume_pc + resume_points.Count)
4068 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4070 resume_points.Add (stmt);
4073 Label dispose_try_block;
4074 bool prepared_for_dispose, emitted_dispose;
4075 public override Label PrepareForDispose (EmitContext ec, Label end)
4077 if (!prepared_for_dispose) {
4078 prepared_for_dispose = true;
4079 dispose_try_block = ec.ig.DefineLabel ();
4081 return dispose_try_block;
4084 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4086 if (emitted_dispose)
4089 emitted_dispose = true;
4091 ILGenerator ig = ec.ig;
4093 Label end_of_try = ig.DefineLabel ();
4095 // Ensure that the only way we can get into this code is through a dispatcher
4096 if (have_dispatcher)
4097 ig.Emit (OpCodes.Br, end);
4099 ig.BeginExceptionBlock ();
4101 ig.MarkLabel (dispose_try_block);
4103 Label [] labels = null;
4104 for (int i = 0; i < resume_points.Count; ++i) {
4105 ResumableStatement s = (ResumableStatement) resume_points [i];
4106 Label ret = s.PrepareForDispose (ec, end_of_try);
4107 if (ret.Equals (end_of_try) && labels == null)
4109 if (labels == null) {
4110 labels = new Label [resume_points.Count];
4111 for (int j = 0; j < i; ++j)
4112 labels [j] = end_of_try;
4117 if (labels != null) {
4119 for (j = 1; j < labels.Length; ++j)
4120 if (!labels [0].Equals (labels [j]))
4122 bool emit_dispatcher = j < labels.Length;
4124 if (emit_dispatcher) {
4125 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4126 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4127 IntConstant.EmitInt (ig, first_resume_pc);
4128 ig.Emit (OpCodes.Sub);
4129 ig.Emit (OpCodes.Switch, labels);
4130 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4133 foreach (ResumableStatement s in resume_points)
4134 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4137 ig.MarkLabel (end_of_try);
4139 ig.BeginFinallyBlock ();
4141 EmitFinallyBody (ec);
4143 ig.EndExceptionBlock ();
4147 public class Lock : ExceptionStatement {
4149 public Statement Statement;
4150 TemporaryVariable temp;
4152 public Lock (Expression expr, Statement stmt, Location l)
4159 public override bool Resolve (BlockContext ec)
4161 expr = expr.Resolve (ec);
4165 if (!TypeManager.IsReferenceType (expr.Type)){
4166 Report.Error (185, loc,
4167 "`{0}' is not a reference type as required by the lock statement",
4168 TypeManager.CSharpName (expr.Type));
4172 ec.StartFlowBranching (this);
4173 bool ok = Statement.Resolve (ec);
4174 ec.EndFlowBranching ();
4176 ok &= base.Resolve (ec);
4178 // Avoid creating libraries that reference the internal
4181 if (t == TypeManager.null_type)
4182 t = TypeManager.object_type;
4184 temp = new TemporaryVariable (t, loc);
4187 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4188 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4189 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4190 monitor_type, "Enter", loc, TypeManager.object_type);
4191 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4192 monitor_type, "Exit", loc, TypeManager.object_type);
4198 protected override void EmitPreTryBody (EmitContext ec)
4200 ILGenerator ig = ec.ig;
4202 temp.EmitAssign (ec, expr);
4204 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4207 protected override void EmitTryBody (EmitContext ec)
4209 Statement.Emit (ec);
4212 protected override void EmitFinallyBody (EmitContext ec)
4215 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4218 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4220 expr.MutateHoistedGenericType (storey);
4221 temp.MutateHoistedGenericType (storey);
4222 Statement.MutateHoistedGenericType (storey);
4225 protected override void CloneTo (CloneContext clonectx, Statement t)
4227 Lock target = (Lock) t;
4229 target.expr = expr.Clone (clonectx);
4230 target.Statement = Statement.Clone (clonectx);
4234 public class Unchecked : Statement {
4237 public Unchecked (Block b)
4243 public override bool Resolve (BlockContext ec)
4245 using (ec.With (ResolveContext.Options.AllCheckStateFlags, false))
4246 return Block.Resolve (ec);
4249 protected override void DoEmit (EmitContext ec)
4251 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4255 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4257 Block.MutateHoistedGenericType (storey);
4260 protected override void CloneTo (CloneContext clonectx, Statement t)
4262 Unchecked target = (Unchecked) t;
4264 target.Block = clonectx.LookupBlock (Block);
4268 public class Checked : Statement {
4271 public Checked (Block b)
4274 b.Unchecked = false;
4277 public override bool Resolve (BlockContext ec)
4279 using (ec.With (ResolveContext.Options.AllCheckStateFlags, true))
4280 return Block.Resolve (ec);
4283 protected override void DoEmit (EmitContext ec)
4285 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4289 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4291 Block.MutateHoistedGenericType (storey);
4294 protected override void CloneTo (CloneContext clonectx, Statement t)
4296 Checked target = (Checked) t;
4298 target.Block = clonectx.LookupBlock (Block);
4302 public class Unsafe : Statement {
4305 public Unsafe (Block b)
4308 Block.Unsafe = true;
4309 loc = b.StartLocation;
4312 public override bool Resolve (BlockContext ec)
4314 if (ec.CurrentIterator != null)
4315 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4317 using (ec.Set (ResolveContext.Options.UnsafeScope))
4318 return Block.Resolve (ec);
4321 protected override void DoEmit (EmitContext ec)
4326 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4328 Block.MutateHoistedGenericType (storey);
4331 protected override void CloneTo (CloneContext clonectx, Statement t)
4333 Unsafe target = (Unsafe) t;
4335 target.Block = clonectx.LookupBlock (Block);
4342 public class Fixed : Statement {
4344 ArrayList declarators;
4345 Statement statement;
4350 abstract class Emitter
4352 protected LocalInfo vi;
4353 protected Expression converted;
4355 protected Emitter (Expression expr, LocalInfo li)
4361 public abstract void Emit (EmitContext ec);
4362 public abstract void EmitExit (EmitContext ec);
4365 class ExpressionEmitter : Emitter {
4366 public ExpressionEmitter (Expression converted, LocalInfo li) :
4367 base (converted, li)
4371 public override void Emit (EmitContext ec) {
4373 // Store pointer in pinned location
4375 converted.Emit (ec);
4379 public override void EmitExit (EmitContext ec)
4381 ec.ig.Emit (OpCodes.Ldc_I4_0);
4382 ec.ig.Emit (OpCodes.Conv_U);
4387 class StringEmitter : Emitter
4389 LocalInfo pinned_string;
4391 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4394 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4395 pinned_string.Pinned = true;
4398 public StringEmitter Resolve (ResolveContext rc)
4400 pinned_string.Resolve (rc);
4402 if (TypeManager.int_get_offset_to_string_data == null) {
4403 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4404 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4410 public override void Emit (EmitContext ec)
4412 pinned_string.ResolveVariable (ec);
4414 converted.Emit (ec);
4415 pinned_string.EmitAssign (ec);
4417 // TODO: Should use Binary::Add
4418 pinned_string.Emit (ec);
4419 ec.ig.Emit (OpCodes.Conv_I);
4421 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4422 //pe.InstanceExpression = pinned_string;
4423 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4425 ec.ig.Emit (OpCodes.Add);
4429 public override void EmitExit (EmitContext ec)
4431 ec.ig.Emit (OpCodes.Ldnull);
4432 pinned_string.EmitAssign (ec);
4436 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4439 declarators = decls;
4444 public Statement Statement {
4445 get { return statement; }
4448 public override bool Resolve (BlockContext ec)
4451 Expression.UnsafeError (loc);
4455 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4456 if (texpr == null) {
4457 if (type is VarExpr)
4458 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4463 expr_type = texpr.Type;
4465 data = new Emitter [declarators.Count];
4467 if (!expr_type.IsPointer){
4468 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4473 foreach (Pair p in declarators){
4474 LocalInfo vi = (LocalInfo) p.First;
4475 Expression e = (Expression) p.Second;
4477 vi.VariableInfo.SetAssigned (ec);
4478 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4481 // The rules for the possible declarators are pretty wise,
4482 // but the production on the grammar is more concise.
4484 // So we have to enforce these rules here.
4486 // We do not resolve before doing the case 1 test,
4487 // because the grammar is explicit in that the token &
4488 // is present, so we need to test for this particular case.
4492 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4496 using (ec.Set (ResolveContext.Options.FixedInitializerScope)) {
4506 if (e.Type.IsArray){
4507 Type array_type = TypeManager.GetElementType (e.Type);
4510 // Provided that array_type is unmanaged,
4512 if (!TypeManager.VerifyUnManaged (array_type, loc))
4516 // and T* is implicitly convertible to the
4517 // pointer type given in the fixed statement.
4519 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4521 Expression converted = Convert.ImplicitConversionRequired (
4522 ec, array_ptr, vi.VariableType, loc);
4523 if (converted == null)
4527 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4529 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4530 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4531 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4532 new NullPointer (loc),
4535 converted = converted.Resolve (ec);
4537 data [i] = new ExpressionEmitter (converted, vi);
4546 if (e.Type == TypeManager.string_type){
4547 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4552 // Case 4: fixed buffer
4553 if (e is FixedBufferPtr) {
4554 data [i++] = new ExpressionEmitter (e, vi);
4559 // Case 1: & object.
4561 Unary u = e as Unary;
4562 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4563 IVariableReference vr = u.Expr as IVariableReference;
4564 if (vr == null || !vr.IsFixed) {
4565 data [i] = new ExpressionEmitter (e, vi);
4569 if (data [i++] == null)
4570 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4572 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4575 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4576 bool ok = statement.Resolve (ec);
4577 bool flow_unreachable = ec.EndFlowBranching ();
4578 has_ret = flow_unreachable;
4583 protected override void DoEmit (EmitContext ec)
4585 for (int i = 0; i < data.Length; i++) {
4589 statement.Emit (ec);
4595 // Clear the pinned variable
4597 for (int i = 0; i < data.Length; i++) {
4598 data [i].EmitExit (ec);
4602 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4604 // Fixed statement cannot be used inside anonymous methods or lambdas
4605 throw new NotSupportedException ();
4608 protected override void CloneTo (CloneContext clonectx, Statement t)
4610 Fixed target = (Fixed) t;
4612 target.type = type.Clone (clonectx);
4613 target.declarators = new ArrayList (declarators.Count);
4614 foreach (Pair p in declarators) {
4615 LocalInfo vi = (LocalInfo) p.First;
4616 Expression e = (Expression) p.Second;
4618 target.declarators.Add (
4619 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4622 target.statement = statement.Clone (clonectx);
4626 public class Catch : Statement {
4627 public readonly string Name;
4629 public Block VarBlock;
4631 Expression type_expr;
4634 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4639 VarBlock = var_block;
4643 public Type CatchType {
4649 public bool IsGeneral {
4651 return type_expr == null;
4655 protected override void DoEmit (EmitContext ec)
4657 ILGenerator ig = ec.ig;
4659 if (CatchType != null)
4660 ig.BeginCatchBlock (CatchType);
4662 ig.BeginCatchBlock (TypeManager.object_type);
4664 if (VarBlock != null)
4668 // TODO: Move to resolve
4669 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4670 lvr.Resolve (new ResolveContext (ec.MemberContext));
4673 // Only to make verifier happy
4674 if (TypeManager.IsGenericParameter (lvr.Type))
4675 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4679 if (lvr.IsHoisted) {
4680 LocalTemporary lt = new LocalTemporary (lvr.Type);
4684 // Variable is at the top of the stack
4685 source = EmptyExpression.Null;
4688 lvr.EmitAssign (ec, source, false, false);
4690 ig.Emit (OpCodes.Pop);
4695 public override bool Resolve (BlockContext ec)
4697 using (ec.With (ResolveContext.Options.CatchScope, true)) {
4698 if (type_expr != null) {
4699 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4705 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4706 Error (155, "The type caught or thrown must be derived from System.Exception");
4712 if (!Block.Resolve (ec))
4715 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4716 // emit the "unused variable" warnings.
4717 if (VarBlock != null)
4718 return VarBlock.Resolve (ec);
4724 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4727 type = storey.MutateType (type);
4728 if (VarBlock != null)
4729 VarBlock.MutateHoistedGenericType (storey);
4730 Block.MutateHoistedGenericType (storey);
4733 protected override void CloneTo (CloneContext clonectx, Statement t)
4735 Catch target = (Catch) t;
4737 if (type_expr != null)
4738 target.type_expr = type_expr.Clone (clonectx);
4739 if (VarBlock != null)
4740 target.VarBlock = clonectx.LookupBlock (VarBlock);
4741 target.Block = clonectx.LookupBlock (Block);
4745 public class TryFinally : ExceptionStatement {
4749 public TryFinally (Statement stmt, Block fini, Location l)
4756 public override bool Resolve (BlockContext ec)
4760 ec.StartFlowBranching (this);
4762 if (!stmt.Resolve (ec))
4766 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4767 using (ec.With (ResolveContext.Options.FinallyScope, true)) {
4768 if (!fini.Resolve (ec))
4772 ec.EndFlowBranching ();
4774 ok &= base.Resolve (ec);
4779 protected override void EmitPreTryBody (EmitContext ec)
4783 protected override void EmitTryBody (EmitContext ec)
4788 protected override void EmitFinallyBody (EmitContext ec)
4793 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4795 stmt.MutateHoistedGenericType (storey);
4796 fini.MutateHoistedGenericType (storey);
4799 protected override void CloneTo (CloneContext clonectx, Statement t)
4801 TryFinally target = (TryFinally) t;
4803 target.stmt = (Statement) stmt.Clone (clonectx);
4805 target.fini = clonectx.LookupBlock (fini);
4809 public class TryCatch : Statement {
4811 public ArrayList Specific;
4812 public Catch General;
4813 bool inside_try_finally, code_follows;
4815 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4818 this.Specific = catch_clauses;
4819 this.General = null;
4820 this.inside_try_finally = inside_try_finally;
4822 for (int i = 0; i < catch_clauses.Count; ++i) {
4823 Catch c = (Catch) catch_clauses [i];
4825 if (i != catch_clauses.Count - 1)
4826 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4828 catch_clauses.RemoveAt (i);
4836 public override bool Resolve (BlockContext ec)
4840 ec.StartFlowBranching (this);
4842 if (!Block.Resolve (ec))
4845 Type[] prev_catches = new Type [Specific.Count];
4847 foreach (Catch c in Specific){
4848 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4850 if (c.Name != null) {
4851 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4853 throw new Exception ();
4855 vi.VariableInfo = null;
4858 if (!c.Resolve (ec)) {
4863 Type resolved_type = c.CatchType;
4864 for (int ii = 0; ii < last_index; ++ii) {
4865 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4866 Report.Error (160, c.loc,
4867 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4868 TypeManager.CSharpName (prev_catches [ii]));
4873 prev_catches [last_index++] = resolved_type;
4876 if (General != null) {
4877 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4878 foreach (Catch c in Specific){
4879 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4880 Report.Warning (1058, 1, c.loc, "A previous catch clause already catches all exceptions. All non-exceptions thrown will be wrapped in a `System.Runtime.CompilerServices.RuntimeWrappedException'");
4885 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4887 if (!General.Resolve (ec))
4891 ec.EndFlowBranching ();
4893 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4894 // So, ensure there's some IL code after this statement
4895 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4896 ec.NeedReturnLabel ();
4901 public void SomeCodeFollows ()
4903 code_follows = true;
4906 protected override void DoEmit (EmitContext ec)
4908 ILGenerator ig = ec.ig;
4910 if (!inside_try_finally)
4911 ig.BeginExceptionBlock ();
4915 foreach (Catch c in Specific)
4918 if (General != null)
4921 if (!inside_try_finally)
4922 ig.EndExceptionBlock ();
4925 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4927 Block.MutateHoistedGenericType (storey);
4929 if (General != null)
4930 General.MutateHoistedGenericType (storey);
4931 if (Specific != null) {
4932 foreach (Catch c in Specific)
4933 c.MutateHoistedGenericType (storey);
4937 protected override void CloneTo (CloneContext clonectx, Statement t)
4939 TryCatch target = (TryCatch) t;
4941 target.Block = clonectx.LookupBlock (Block);
4942 if (General != null)
4943 target.General = (Catch) General.Clone (clonectx);
4944 if (Specific != null){
4945 target.Specific = new ArrayList ();
4946 foreach (Catch c in Specific)
4947 target.Specific.Add (c.Clone (clonectx));
4952 // FIXME: Why is it almost exact copy of Using ??
4953 public class UsingTemporary : ExceptionStatement {
4954 TemporaryVariable local_copy;
4955 public Statement Statement;
4959 public UsingTemporary (Expression expr, Statement stmt, Location l)
4966 public override bool Resolve (BlockContext ec)
4968 expr = expr.Resolve (ec);
4972 expr_type = expr.Type;
4974 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4975 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4976 Using.Error_IsNotConvertibleToIDisposable (expr);
4981 local_copy = new TemporaryVariable (expr_type, loc);
4982 local_copy.Resolve (ec);
4984 ec.StartFlowBranching (this);
4986 bool ok = Statement.Resolve (ec);
4988 ec.EndFlowBranching ();
4990 ok &= base.Resolve (ec);
4992 if (TypeManager.void_dispose_void == null) {
4993 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4994 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5000 protected override void EmitPreTryBody (EmitContext ec)
5002 local_copy.EmitAssign (ec, expr);
5005 protected override void EmitTryBody (EmitContext ec)
5007 Statement.Emit (ec);
5010 protected override void EmitFinallyBody (EmitContext ec)
5012 ILGenerator ig = ec.ig;
5013 if (!TypeManager.IsStruct (expr_type)) {
5014 Label skip = ig.DefineLabel ();
5015 local_copy.Emit (ec);
5016 ig.Emit (OpCodes.Brfalse, skip);
5017 local_copy.Emit (ec);
5018 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5019 ig.MarkLabel (skip);
5023 Expression ml = Expression.MemberLookup (
5024 ec.CurrentType, TypeManager.idisposable_type, expr_type,
5025 "Dispose", Location.Null);
5027 if (!(ml is MethodGroupExpr)) {
5028 local_copy.Emit (ec);
5029 ig.Emit (OpCodes.Box, expr_type);
5030 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5034 MethodInfo mi = null;
5036 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5037 if (TypeManager.GetParameterData (mk).Count == 0) {
5044 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5048 local_copy.AddressOf (ec, AddressOp.Load);
5049 ig.Emit (OpCodes.Call, mi);
5052 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5054 expr_type = storey.MutateType (expr_type);
5055 local_copy.MutateHoistedGenericType (storey);
5056 Statement.MutateHoistedGenericType (storey);
5059 protected override void CloneTo (CloneContext clonectx, Statement t)
5061 UsingTemporary target = (UsingTemporary) t;
5063 target.expr = expr.Clone (clonectx);
5064 target.Statement = Statement.Clone (clonectx);
5068 public class Using : ExceptionStatement {
5070 public Statement EmbeddedStatement {
5071 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5077 ExpressionStatement assign;
5079 public Using (Expression var, Expression init, Statement stmt, Location l)
5087 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5089 Report.SymbolRelatedToPreviousError (expr.Type);
5090 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5091 expr.GetSignatureForError ());
5094 protected override void EmitPreTryBody (EmitContext ec)
5096 assign.EmitStatement (ec);
5099 protected override void EmitTryBody (EmitContext ec)
5104 protected override void EmitFinallyBody (EmitContext ec)
5106 ILGenerator ig = ec.ig;
5107 Label skip = ig.DefineLabel ();
5109 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5110 if (emit_null_check) {
5112 ig.Emit (OpCodes.Brfalse, skip);
5115 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5117 if (emit_null_check)
5118 ig.MarkLabel (skip);
5121 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5123 assign.MutateHoistedGenericType (storey);
5124 var.MutateHoistedGenericType (storey);
5125 stmt.MutateHoistedGenericType (storey);
5128 public override bool Resolve (BlockContext ec)
5130 if (!ResolveVariable (ec))
5133 ec.StartFlowBranching (this);
5135 bool ok = stmt.Resolve (ec);
5137 ec.EndFlowBranching ();
5139 ok &= base.Resolve (ec);
5141 if (TypeManager.void_dispose_void == null) {
5142 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5143 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5149 bool ResolveVariable (BlockContext ec)
5151 assign = new SimpleAssign (var, init, loc);
5152 assign = assign.ResolveStatement (ec);
5156 if (assign.Type == TypeManager.idisposable_type ||
5157 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5161 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5163 Error_IsNotConvertibleToIDisposable (var);
5167 throw new NotImplementedException ("covariance?");
5170 protected override void CloneTo (CloneContext clonectx, Statement t)
5172 Using target = (Using) t;
5174 target.var = var.Clone (clonectx);
5175 target.init = init.Clone (clonectx);
5176 target.stmt = stmt.Clone (clonectx);
5181 /// Implementation of the foreach C# statement
5183 public class Foreach : Statement {
5185 sealed class ArrayForeach : Statement
5187 class ArrayCounter : TemporaryVariable
5189 StatementExpression increment;
5191 public ArrayCounter (Location loc)
5192 : base (TypeManager.int32_type, loc)
5196 public void ResolveIncrement (BlockContext ec)
5198 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5199 increment.Resolve (ec);
5202 public void EmitIncrement (EmitContext ec)
5204 increment.Emit (ec);
5208 readonly Foreach for_each;
5209 readonly Statement statement;
5212 TemporaryVariable[] lengths;
5213 Expression [] length_exprs;
5214 ArrayCounter[] counter;
5216 TemporaryVariable copy;
5219 public ArrayForeach (Foreach @foreach, int rank)
5221 for_each = @foreach;
5222 statement = for_each.statement;
5225 counter = new ArrayCounter [rank];
5226 length_exprs = new Expression [rank];
5229 // Only use temporary length variables when dealing with
5230 // multi-dimensional arrays
5233 lengths = new TemporaryVariable [rank];
5236 protected override void CloneTo (CloneContext clonectx, Statement target)
5238 throw new NotImplementedException ();
5241 public override bool Resolve (BlockContext ec)
5243 copy = new TemporaryVariable (for_each.expr.Type, loc);
5246 int rank = length_exprs.Length;
5247 Arguments list = new Arguments (rank);
5248 for (int i = 0; i < rank; i++) {
5249 counter [i] = new ArrayCounter (loc);
5250 counter [i].ResolveIncrement (ec);
5253 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5255 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5256 lengths [i].Resolve (ec);
5258 Arguments args = new Arguments (1);
5259 args.Add (new Argument (new IntConstant (i, loc)));
5260 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5263 list.Add (new Argument (counter [i]));
5266 access = new ElementAccess (copy, list).Resolve (ec);
5270 Expression var_type = for_each.type;
5271 VarExpr ve = var_type as VarExpr;
5273 // Infer implicitly typed local variable from foreach array type
5274 var_type = new TypeExpression (access.Type, ve.Location);
5277 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5278 if (var_type == null)
5281 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5287 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5288 ec.CurrentBranching.CreateSibling ();
5290 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5291 if (for_each.variable == null)
5294 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5295 if (!statement.Resolve (ec))
5297 ec.EndFlowBranching ();
5299 // There's no direct control flow from the end of the embedded statement to the end of the loop
5300 ec.CurrentBranching.CurrentUsageVector.Goto ();
5302 ec.EndFlowBranching ();
5307 protected override void DoEmit (EmitContext ec)
5309 ILGenerator ig = ec.ig;
5311 copy.EmitAssign (ec, for_each.expr);
5313 int rank = length_exprs.Length;
5314 Label[] test = new Label [rank];
5315 Label[] loop = new Label [rank];
5317 for (int i = 0; i < rank; i++) {
5318 test [i] = ig.DefineLabel ();
5319 loop [i] = ig.DefineLabel ();
5321 if (lengths != null)
5322 lengths [i].EmitAssign (ec, length_exprs [i]);
5325 IntConstant zero = new IntConstant (0, loc);
5326 for (int i = 0; i < rank; i++) {
5327 counter [i].EmitAssign (ec, zero);
5329 ig.Emit (OpCodes.Br, test [i]);
5330 ig.MarkLabel (loop [i]);
5333 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5335 statement.Emit (ec);
5337 ig.MarkLabel (ec.LoopBegin);
5339 for (int i = rank - 1; i >= 0; i--){
5340 counter [i].EmitIncrement (ec);
5342 ig.MarkLabel (test [i]);
5343 counter [i].Emit (ec);
5345 if (lengths != null)
5346 lengths [i].Emit (ec);
5348 length_exprs [i].Emit (ec);
5350 ig.Emit (OpCodes.Blt, loop [i]);
5353 ig.MarkLabel (ec.LoopEnd);
5356 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5358 for_each.expr.MutateHoistedGenericType (storey);
5360 copy.MutateHoistedGenericType (storey);
5361 conv.MutateHoistedGenericType (storey);
5362 statement.MutateHoistedGenericType (storey);
5364 for (int i = 0; i < counter.Length; i++) {
5365 counter [i].MutateHoistedGenericType (storey);
5366 if (lengths != null)
5367 lengths [i].MutateHoistedGenericType (storey);
5372 sealed class CollectionForeach : Statement
5374 class CollectionForeachStatement : Statement
5377 Expression variable, current, conv;
5378 Statement statement;
5381 public CollectionForeachStatement (Type type, Expression variable,
5382 Expression current, Statement statement,
5386 this.variable = variable;
5387 this.current = current;
5388 this.statement = statement;
5392 protected override void CloneTo (CloneContext clonectx, Statement target)
5394 throw new NotImplementedException ();
5397 public override bool Resolve (BlockContext ec)
5399 current = current.Resolve (ec);
5400 if (current == null)
5403 conv = Convert.ExplicitConversion (ec, current, type, loc);
5407 assign = new SimpleAssign (variable, conv, loc);
5408 if (assign.Resolve (ec) == null)
5411 if (!statement.Resolve (ec))
5417 protected override void DoEmit (EmitContext ec)
5419 assign.EmitStatement (ec);
5420 statement.Emit (ec);
5423 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5425 assign.MutateHoistedGenericType (storey);
5426 statement.MutateHoistedGenericType (storey);
5430 Expression variable, expr;
5431 Statement statement;
5433 TemporaryVariable enumerator;
5438 MethodGroupExpr get_enumerator;
5439 PropertyExpr get_current;
5440 MethodInfo move_next;
5441 Expression var_type;
5442 Type enumerator_type;
5443 bool enumerator_found;
5445 public CollectionForeach (Expression var_type, Expression var,
5446 Expression expr, Statement stmt, Location l)
5448 this.var_type = var_type;
5449 this.variable = var;
5455 protected override void CloneTo (CloneContext clonectx, Statement target)
5457 throw new NotImplementedException ();
5460 bool GetEnumeratorFilter (ResolveContext ec, MethodInfo mi)
5462 Type return_type = mi.ReturnType;
5465 // Ok, we can access it, now make sure that we can do something
5466 // with this `GetEnumerator'
5469 if (return_type == TypeManager.ienumerator_type ||
5470 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5472 // If it is not an interface, lets try to find the methods ourselves.
5473 // For example, if we have:
5474 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5475 // We can avoid the iface call. This is a runtime perf boost.
5476 // even bigger if we have a ValueType, because we avoid the cost
5479 // We have to make sure that both methods exist for us to take
5480 // this path. If one of the methods does not exist, we will just
5481 // use the interface. Sadly, this complex if statement is the only
5482 // way I could do this without a goto
5485 if (TypeManager.bool_movenext_void == null) {
5486 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5487 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5490 if (TypeManager.ienumerator_getcurrent == null) {
5491 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5492 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5496 // Prefer a generic enumerator over a non-generic one.
5498 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5499 enumerator_type = return_type;
5500 if (!FetchGetCurrent (ec, return_type))
5501 get_current = new PropertyExpr (
5502 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5503 if (!FetchMoveNext (return_type))
5504 move_next = TypeManager.bool_movenext_void;
5508 if (return_type.IsInterface ||
5509 !FetchMoveNext (return_type) ||
5510 !FetchGetCurrent (ec, return_type)) {
5511 enumerator_type = return_type;
5512 move_next = TypeManager.bool_movenext_void;
5513 get_current = new PropertyExpr (
5514 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5519 // Ok, so they dont return an IEnumerable, we will have to
5520 // find if they support the GetEnumerator pattern.
5523 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5524 Report.Error (202, loc, "foreach statement requires that the return type `{0}' of `{1}' must have a suitable public MoveNext method and public Current property",
5525 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5530 enumerator_type = return_type;
5536 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5538 bool FetchMoveNext (Type t)
5540 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5542 BindingFlags.Public | BindingFlags.Instance,
5545 if (move_next_list == null)
5548 foreach (MemberInfo m in move_next_list){
5549 MethodInfo mi = (MethodInfo) m;
5551 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5552 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5562 // Retrieves a `public T get_Current ()' method from the Type `t'
5564 bool FetchGetCurrent (ResolveContext ec, Type t)
5566 PropertyExpr pe = Expression.MemberLookup (
5567 ec.CurrentType, t, "Current", MemberTypes.Property,
5568 Expression.AllBindingFlags, loc) as PropertyExpr;
5576 void Error_Enumerator ()
5578 if (enumerator_found) {
5582 Report.Error (1579, loc,
5583 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5584 TypeManager.CSharpName (expr.Type));
5587 bool IsOverride (MethodInfo m)
5589 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5591 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5593 if (m is MethodBuilder)
5596 MethodInfo base_method = m.GetBaseDefinition ();
5597 return base_method != m;
5600 bool TryType (ResolveContext ec, Type t)
5602 MethodGroupExpr mg = Expression.MemberLookup (
5603 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5604 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5608 MethodInfo result = null;
5609 MethodInfo tmp_move_next = null;
5610 PropertyExpr tmp_get_cur = null;
5611 Type tmp_enumerator_type = enumerator_type;
5612 foreach (MethodInfo mi in mg.Methods) {
5613 if (TypeManager.GetParameterData (mi).Count != 0)
5616 // Check whether GetEnumerator is public
5617 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5620 if (IsOverride (mi))
5623 enumerator_found = true;
5625 if (!GetEnumeratorFilter (ec, mi))
5628 if (result != null) {
5629 if (TypeManager.IsGenericType (result.ReturnType)) {
5630 if (!TypeManager.IsGenericType (mi.ReturnType))
5633 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5634 Report.SymbolRelatedToPreviousError (t);
5635 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5636 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5637 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5641 // Always prefer generics enumerators
5642 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5643 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5644 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5647 Report.SymbolRelatedToPreviousError (result);
5648 Report.SymbolRelatedToPreviousError (mi);
5649 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5650 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5655 tmp_move_next = move_next;
5656 tmp_get_cur = get_current;
5657 tmp_enumerator_type = enumerator_type;
5658 if (mi.DeclaringType == t)
5662 if (result != null) {
5663 move_next = tmp_move_next;
5664 get_current = tmp_get_cur;
5665 enumerator_type = tmp_enumerator_type;
5666 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5667 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5669 if (t != expr.Type) {
5670 expr = Convert.ExplicitConversion (
5673 throw new InternalErrorException ();
5676 get_enumerator.InstanceExpression = expr;
5677 get_enumerator.IsBase = t != expr.Type;
5685 bool ProbeCollectionType (ResolveContext ec, Type t)
5687 int errors = Report.Errors;
5688 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5689 if (TryType (ec, tt))
5694 if (Report.Errors > errors)
5698 // Now try to find the method in the interfaces
5700 Type [] ifaces = TypeManager.GetInterfaces (t);
5701 foreach (Type i in ifaces){
5702 if (TryType (ec, i))
5709 public override bool Resolve (BlockContext ec)
5711 enumerator_type = TypeManager.ienumerator_type;
5713 if (!ProbeCollectionType (ec, expr.Type)) {
5714 Error_Enumerator ();
5718 VarExpr ve = var_type as VarExpr;
5720 // Infer implicitly typed local variable from foreach enumerable type
5721 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5724 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5725 if (var_type == null)
5728 enumerator = new TemporaryVariable (enumerator_type, loc);
5729 enumerator.Resolve (ec);
5731 init = new Invocation (get_enumerator, null);
5732 init = init.Resolve (ec);
5736 Expression move_next_expr;
5738 MemberInfo[] mi = new MemberInfo[] { move_next };
5739 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5740 mg.InstanceExpression = enumerator;
5742 move_next_expr = new Invocation (mg, null);
5745 get_current.InstanceExpression = enumerator;
5747 Statement block = new CollectionForeachStatement (
5748 var_type.Type, variable, get_current, statement, loc);
5750 loop = new While (move_next_expr, block, loc);
5753 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5754 if (implements_idisposable || !enumerator_type.IsSealed) {
5755 wrapper = new DisposableWrapper (this, implements_idisposable);
5757 wrapper = new NonDisposableWrapper (this);
5760 return wrapper.Resolve (ec);
5763 protected override void DoEmit (EmitContext ec)
5768 class NonDisposableWrapper : Statement {
5769 CollectionForeach parent;
5771 internal NonDisposableWrapper (CollectionForeach parent)
5773 this.parent = parent;
5776 protected override void CloneTo (CloneContext clonectx, Statement target)
5778 throw new NotSupportedException ();
5781 public override bool Resolve (BlockContext ec)
5783 return parent.ResolveLoop (ec);
5786 protected override void DoEmit (EmitContext ec)
5788 parent.EmitLoopInit (ec);
5789 parent.EmitLoopBody (ec);
5792 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5794 throw new NotSupportedException ();
5798 sealed class DisposableWrapper : ExceptionStatement
5800 CollectionForeach parent;
5801 bool implements_idisposable;
5803 internal DisposableWrapper (CollectionForeach parent, bool implements)
5805 this.parent = parent;
5806 this.implements_idisposable = implements;
5809 protected override void CloneTo (CloneContext clonectx, Statement target)
5811 throw new NotSupportedException ();
5814 public override bool Resolve (BlockContext ec)
5818 ec.StartFlowBranching (this);
5820 if (!parent.ResolveLoop (ec))
5823 ec.EndFlowBranching ();
5825 ok &= base.Resolve (ec);
5827 if (TypeManager.void_dispose_void == null) {
5828 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5829 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5834 protected override void EmitPreTryBody (EmitContext ec)
5836 parent.EmitLoopInit (ec);
5839 protected override void EmitTryBody (EmitContext ec)
5841 parent.EmitLoopBody (ec);
5844 protected override void EmitFinallyBody (EmitContext ec)
5846 Expression instance = parent.enumerator;
5847 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5848 ILGenerator ig = ec.ig;
5850 parent.enumerator.Emit (ec);
5852 Label call_dispose = ig.DefineLabel ();
5854 if (!implements_idisposable) {
5855 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5856 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5862 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5864 // using 'endfinally' to empty the evaluation stack
5865 ig.Emit (OpCodes.Endfinally);
5866 ig.MarkLabel (call_dispose);
5869 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5872 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5874 throw new NotSupportedException ();
5878 bool ResolveLoop (BlockContext ec)
5880 return loop.Resolve (ec);
5883 void EmitLoopInit (EmitContext ec)
5885 enumerator.EmitAssign (ec, init);
5888 void EmitLoopBody (EmitContext ec)
5893 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5895 enumerator_type = storey.MutateType (enumerator_type);
5896 init.MutateHoistedGenericType (storey);
5897 loop.MutateHoistedGenericType (storey);
5902 Expression variable;
5904 Statement statement;
5906 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5907 Statement stmt, Location l)
5910 this.variable = var;
5916 public Statement Statement {
5917 get { return statement; }
5920 public override bool Resolve (BlockContext ec)
5922 expr = expr.Resolve (ec);
5927 Report.Error (186, loc, "Use of null is not valid in this context");
5931 if (expr.Type == TypeManager.string_type) {
5932 statement = new ArrayForeach (this, 1);
5933 } else if (expr.Type.IsArray) {
5934 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5936 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5937 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5938 expr.ExprClassName);
5942 statement = new CollectionForeach (type, variable, expr, statement, loc);
5945 return statement.Resolve (ec);
5948 protected override void DoEmit (EmitContext ec)
5950 ILGenerator ig = ec.ig;
5952 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5953 ec.LoopBegin = ig.DefineLabel ();
5954 ec.LoopEnd = ig.DefineLabel ();
5956 statement.Emit (ec);
5958 ec.LoopBegin = old_begin;
5959 ec.LoopEnd = old_end;
5962 protected override void CloneTo (CloneContext clonectx, Statement t)
5964 Foreach target = (Foreach) t;
5966 target.type = type.Clone (clonectx);
5967 target.variable = variable.Clone (clonectx);
5968 target.expr = expr.Clone (clonectx);
5969 target.statement = statement.Clone (clonectx);
5972 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5974 statement.MutateHoistedGenericType (storey);