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 MemberCore mc = ec.MemberContext as MemberCore;
822 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
823 mc.GetSignatureForError ());
826 Expr = Expr.Resolve (ec);
830 if (ec.HasSet (EmitContext.Options.InferReturnType)) {
831 ec.ReturnTypeInference.AddCommonTypeBound (Expr.Type);
835 if (Expr.Type != ec.ReturnType) {
836 Expr = Convert.ImplicitConversionRequired (ec, Expr, ec.ReturnType, loc);
840 Report.Error (1662, loc,
841 "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",
842 am.ContainerType, am.GetSignatureForError ());
851 protected override void DoEmit (EmitContext ec)
857 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
861 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
863 ec.ig.Emit (OpCodes.Ret);
866 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
869 Expr.MutateHoistedGenericType (storey);
872 protected override void CloneTo (CloneContext clonectx, Statement t)
874 Return target = (Return) t;
875 // It's null for simple return;
877 target.Expr = Expr.Clone (clonectx);
881 public class Goto : Statement {
883 LabeledStatement label;
886 public override bool Resolve (BlockContext ec)
888 int errors = Report.Errors;
889 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
890 ec.CurrentBranching.CurrentUsageVector.Goto ();
891 return errors == Report.Errors;
894 public Goto (string label, Location l)
900 public string Target {
901 get { return target; }
904 public void SetResolvedTarget (LabeledStatement label)
907 label.AddReference ();
910 protected override void CloneTo (CloneContext clonectx, Statement target)
915 protected override void DoEmit (EmitContext ec)
918 throw new InternalErrorException ("goto emitted before target resolved");
919 Label l = label.LabelTarget (ec);
920 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
923 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
928 public class LabeledStatement : Statement {
935 FlowBranching.UsageVector vectors;
937 public LabeledStatement (string name, Location l)
943 public Label LabelTarget (EmitContext ec)
948 label = ec.ig.DefineLabel ();
958 public bool IsDefined {
959 get { return defined; }
962 public bool HasBeenReferenced {
963 get { return referenced; }
966 public FlowBranching.UsageVector JumpOrigins {
967 get { return vectors; }
970 public void AddUsageVector (FlowBranching.UsageVector vector)
972 vector = vector.Clone ();
973 vector.Next = vectors;
977 protected override void CloneTo (CloneContext clonectx, Statement target)
982 public override bool Resolve (BlockContext ec)
984 // this flow-branching will be terminated when the surrounding block ends
985 ec.StartFlowBranching (this);
989 protected override void DoEmit (EmitContext ec)
991 if (ig != null && ig != ec.ig)
992 throw new InternalErrorException ("cannot happen");
994 ec.ig.MarkLabel (label);
997 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1001 public void AddReference ()
1009 /// `goto default' statement
1011 public class GotoDefault : Statement {
1013 public GotoDefault (Location l)
1018 protected override void CloneTo (CloneContext clonectx, Statement target)
1023 public override bool Resolve (BlockContext ec)
1025 ec.CurrentBranching.CurrentUsageVector.Goto ();
1029 protected override void DoEmit (EmitContext ec)
1031 if (ec.Switch == null){
1032 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1036 if (!ec.Switch.GotDefault){
1037 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1040 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1043 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1049 /// `goto case' statement
1051 public class GotoCase : Statement {
1055 public GotoCase (Expression e, Location l)
1061 public override bool Resolve (BlockContext ec)
1063 if (ec.Switch == null){
1064 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1068 ec.CurrentBranching.CurrentUsageVector.Goto ();
1070 expr = expr.Resolve (ec);
1074 Constant c = expr as Constant;
1076 Error (150, "A constant value is expected");
1080 Type type = ec.Switch.SwitchType;
1081 Constant res = c.TryReduce (ec, type, c.Location);
1083 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1087 if (!Convert.ImplicitStandardConversionExists (c, type))
1088 Report.Warning (469, 2, loc,
1089 "The `goto case' value is not implicitly convertible to type `{0}'",
1090 TypeManager.CSharpName (type));
1092 object val = res.GetValue ();
1094 val = SwitchLabel.NullStringCase;
1096 sl = (SwitchLabel) ec.Switch.Elements [val];
1099 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1100 (c.GetValue () == null ? "null" : val.ToString ()));
1107 protected override void DoEmit (EmitContext ec)
1109 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1112 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1114 expr.MutateHoistedGenericType (storey);
1117 protected override void CloneTo (CloneContext clonectx, Statement t)
1119 GotoCase target = (GotoCase) t;
1121 target.expr = expr.Clone (clonectx);
1125 public class Throw : Statement {
1128 public Throw (Expression expr, Location l)
1134 public override bool Resolve (BlockContext ec)
1137 ec.CurrentBranching.CurrentUsageVector.Goto ();
1138 return ec.CurrentBranching.CheckRethrow (loc);
1141 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1142 ec.CurrentBranching.CurrentUsageVector.Goto ();
1147 if (Convert.ImplicitConversionExists (ec, expr, TypeManager.exception_type))
1148 expr = Convert.ImplicitConversion (ec, expr, TypeManager.exception_type, loc);
1150 Report.Error (155, expr.Location, "The type caught or thrown must be derived from System.Exception");
1155 protected override void DoEmit (EmitContext ec)
1158 ec.ig.Emit (OpCodes.Rethrow);
1162 ec.ig.Emit (OpCodes.Throw);
1166 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1169 expr.MutateHoistedGenericType (storey);
1172 protected override void CloneTo (CloneContext clonectx, Statement t)
1174 Throw target = (Throw) t;
1177 target.expr = expr.Clone (clonectx);
1181 public class Break : Statement {
1183 public Break (Location l)
1188 bool unwind_protect;
1190 public override bool Resolve (BlockContext ec)
1192 int errors = Report.Errors;
1193 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1194 ec.CurrentBranching.CurrentUsageVector.Goto ();
1195 return errors == Report.Errors;
1198 protected override void DoEmit (EmitContext ec)
1200 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1203 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1207 protected override void CloneTo (CloneContext clonectx, Statement t)
1213 public class Continue : Statement {
1215 public Continue (Location l)
1220 bool unwind_protect;
1222 public override bool Resolve (BlockContext ec)
1224 int errors = Report.Errors;
1225 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1226 ec.CurrentBranching.CurrentUsageVector.Goto ();
1227 return errors == Report.Errors;
1230 protected override void DoEmit (EmitContext ec)
1232 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1235 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1239 protected override void CloneTo (CloneContext clonectx, Statement t)
1245 public interface ILocalVariable
1247 void Emit (EmitContext ec);
1248 void EmitAssign (EmitContext ec);
1249 void EmitAddressOf (EmitContext ec);
1252 public interface IKnownVariable {
1253 Block Block { get; }
1254 Location Location { get; }
1258 // The information about a user-perceived local variable
1260 public class LocalInfo : IKnownVariable, ILocalVariable {
1261 public readonly FullNamedExpression Type;
1263 public Type VariableType;
1264 public readonly string Name;
1265 public readonly Location Location;
1266 public readonly Block Block;
1268 public VariableInfo VariableInfo;
1269 public HoistedVariable HoistedVariableReference;
1278 CompilerGenerated = 64,
1282 public enum ReadOnlyContext: byte {
1289 ReadOnlyContext ro_context;
1290 LocalBuilder builder;
1292 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1300 public LocalInfo (DeclSpace ds, Block block, Location l)
1302 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1307 public void ResolveVariable (EmitContext ec)
1309 if (HoistedVariableReference != null)
1312 if (builder == null) {
1315 // This is needed to compile on both .NET 1.x and .NET 2.x
1316 // the later introduced `DeclareLocal (Type t, bool pinned)'
1318 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1320 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1324 public void Emit (EmitContext ec)
1326 ec.ig.Emit (OpCodes.Ldloc, builder);
1329 public void EmitAssign (EmitContext ec)
1331 ec.ig.Emit (OpCodes.Stloc, builder);
1334 public void EmitAddressOf (EmitContext ec)
1336 ec.ig.Emit (OpCodes.Ldloca, builder);
1339 public void EmitSymbolInfo (EmitContext ec)
1341 if (builder != null)
1342 ec.DefineLocalVariable (Name, builder);
1345 public bool IsThisAssigned (BlockContext ec, Block block)
1347 if (VariableInfo == null)
1348 throw new Exception ();
1350 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1353 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, block.StartLocation);
1356 public bool IsAssigned (BlockContext ec)
1358 if (VariableInfo == null)
1359 throw new Exception ();
1361 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1364 public bool Resolve (ResolveContext ec)
1366 if (VariableType != null)
1369 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1373 VariableType = texpr.Type;
1375 if (TypeManager.IsGenericParameter (VariableType))
1378 if (VariableType.IsAbstract && VariableType.IsSealed) {
1379 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1383 if (VariableType.IsPointer && !ec.InUnsafe)
1384 Expression.UnsafeError (Location);
1389 public bool IsConstant {
1390 get { return (flags & Flags.IsConstant) != 0; }
1391 set { flags |= Flags.IsConstant; }
1394 public bool AddressTaken {
1395 get { return (flags & Flags.AddressTaken) != 0; }
1396 set { flags |= Flags.AddressTaken; }
1399 public bool CompilerGenerated {
1400 get { return (flags & Flags.CompilerGenerated) != 0; }
1401 set { flags |= Flags.CompilerGenerated; }
1404 public override string ToString ()
1406 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1407 Name, Type, VariableInfo, Location);
1411 get { return (flags & Flags.Used) != 0; }
1412 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1415 public bool ReadOnly {
1416 get { return (flags & Flags.ReadOnly) != 0; }
1419 public void SetReadOnlyContext (ReadOnlyContext context)
1421 flags |= Flags.ReadOnly;
1422 ro_context = context;
1425 public string GetReadOnlyContext ()
1428 throw new InternalErrorException ("Variable is not readonly");
1430 switch (ro_context) {
1431 case ReadOnlyContext.Fixed:
1432 return "fixed variable";
1433 case ReadOnlyContext.Foreach:
1434 return "foreach iteration variable";
1435 case ReadOnlyContext.Using:
1436 return "using variable";
1438 throw new NotImplementedException ();
1442 // Whether the variable is pinned, if Pinned the variable has been
1443 // allocated in a pinned slot with DeclareLocal.
1445 public bool Pinned {
1446 get { return (flags & Flags.Pinned) != 0; }
1447 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1450 public bool IsThis {
1451 get { return (flags & Flags.IsThis) != 0; }
1452 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1455 Block IKnownVariable.Block {
1456 get { return Block; }
1459 Location IKnownVariable.Location {
1460 get { return Location; }
1463 public LocalInfo Clone (CloneContext clonectx)
1466 // Variables in anonymous block are not resolved yet
1468 if (VariableType == null)
1469 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1472 // Variables in method block are resolved
1474 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1475 li.VariableType = VariableType;
1481 /// Block represents a C# block.
1485 /// This class is used in a number of places: either to represent
1486 /// explicit blocks that the programmer places or implicit blocks.
1488 /// Implicit blocks are used as labels or to introduce variable
1491 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1492 /// they contain extra information that is not necessary on normal blocks.
1494 public class Block : Statement {
1495 public Block Parent;
1496 public Location StartLocation;
1497 public Location EndLocation = Location.Null;
1499 public ExplicitBlock Explicit;
1500 public ToplevelBlock Toplevel; // TODO: Use Explicit
1503 public enum Flags : byte {
1506 VariablesInitialized = 4,
1510 HasCapturedVariable = 64,
1511 HasCapturedThis = 128
1513 protected Flags flags;
1515 public bool Unchecked {
1516 get { return (flags & Flags.Unchecked) != 0; }
1517 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1520 public bool Unsafe {
1521 get { return (flags & Flags.Unsafe) != 0; }
1522 set { flags |= Flags.Unsafe; }
1526 // The statements in this block
1528 protected ArrayList statements;
1531 // An array of Blocks. We keep track of children just
1532 // to generate the local variable declarations.
1534 // Statements and child statements are handled through the
1540 // Labels. (label, block) pairs.
1542 protected HybridDictionary labels;
1545 // Keeps track of (name, type) pairs
1547 IDictionary variables;
1550 // Keeps track of constants
1551 HybridDictionary constants;
1554 // Temporary variables.
1556 ArrayList temporary_variables;
1559 // If this is a switch section, the enclosing switch block.
1563 protected ArrayList scope_initializers;
1565 ArrayList anonymous_children;
1567 protected static int id;
1571 int assignable_slots;
1572 bool unreachable_shown;
1575 public Block (Block parent)
1576 : this (parent, (Flags) 0, Location.Null, Location.Null)
1579 public Block (Block parent, Flags flags)
1580 : this (parent, flags, Location.Null, Location.Null)
1583 public Block (Block parent, Location start, Location end)
1584 : this (parent, (Flags) 0, start, end)
1588 // Useful when TopLevel block is downgraded to normal block
1590 public Block (ToplevelBlock parent, ToplevelBlock source)
1591 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1593 statements = source.statements;
1594 children = source.children;
1595 labels = source.labels;
1596 variables = source.variables;
1597 constants = source.constants;
1598 switch_block = source.switch_block;
1601 public Block (Block parent, Flags flags, Location start, Location end)
1603 if (parent != null) {
1604 parent.AddChild (this);
1606 // the appropriate constructors will fixup these fields
1607 Toplevel = parent.Toplevel;
1608 Explicit = parent.Explicit;
1611 this.Parent = parent;
1613 this.StartLocation = start;
1614 this.EndLocation = end;
1617 statements = new ArrayList (4);
1620 public Block CreateSwitchBlock (Location start)
1622 // FIXME: should this be implicit?
1623 Block new_block = new ExplicitBlock (this, start, start);
1624 new_block.switch_block = this;
1629 get { return this_id; }
1632 public IDictionary Variables {
1634 if (variables == null)
1635 variables = new ListDictionary ();
1640 void AddChild (Block b)
1642 if (children == null)
1643 children = new ArrayList (1);
1648 public void SetEndLocation (Location loc)
1653 protected static void Error_158 (string name, Location loc)
1655 Report.Error (158, loc, "The label `{0}' shadows another label " +
1656 "by the same name in a contained scope", name);
1660 /// Adds a label to the current block.
1664 /// false if the name already exists in this block. true
1668 public bool AddLabel (LabeledStatement target)
1670 if (switch_block != null)
1671 return switch_block.AddLabel (target);
1673 string name = target.Name;
1676 while (cur != null) {
1677 LabeledStatement s = cur.DoLookupLabel (name);
1679 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1680 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1684 if (this == Explicit)
1690 while (cur != null) {
1691 if (cur.DoLookupLabel (name) != null) {
1692 Error_158 (name, target.loc);
1696 if (children != null) {
1697 foreach (Block b in children) {
1698 LabeledStatement s = b.DoLookupLabel (name);
1702 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1703 Error_158 (name, target.loc);
1711 Toplevel.CheckError158 (name, target.loc);
1714 labels = new HybridDictionary();
1716 labels.Add (name, target);
1720 public LabeledStatement LookupLabel (string name)
1722 LabeledStatement s = DoLookupLabel (name);
1726 if (children == null)
1729 foreach (Block child in children) {
1730 if (Explicit != child.Explicit)
1733 s = child.LookupLabel (name);
1741 LabeledStatement DoLookupLabel (string name)
1743 if (switch_block != null)
1744 return switch_block.LookupLabel (name);
1747 if (labels.Contains (name))
1748 return ((LabeledStatement) labels [name]);
1753 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1756 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1757 while (kvi == null) {
1758 b = b.Explicit.Parent;
1761 kvi = b.Explicit.GetKnownVariable (name);
1767 // Is kvi.Block nested inside 'b'
1768 if (b.Explicit != kvi.Block.Explicit) {
1770 // If a variable by the same name it defined in a nested block of this
1771 // block, we violate the invariant meaning in a block.
1774 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1775 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1780 // It's ok if the definition is in a nested subblock of b, but not
1781 // nested inside this block -- a definition in a sibling block
1782 // should not affect us.
1788 // Block 'b' and kvi.Block are the same textual block.
1789 // However, different variables are extant.
1791 // Check if the variable is in scope in both blocks. We use
1792 // an indirect check that depends on AddVariable doing its
1793 // part in maintaining the invariant-meaning-in-block property.
1795 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1798 if (this is ToplevelBlock) {
1799 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1800 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1805 // Even though we detected the error when the name is used, we
1806 // treat it as if the variable declaration was in error.
1808 Report.SymbolRelatedToPreviousError (loc, name);
1809 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1813 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1815 LocalInfo vi = GetLocalInfo (name);
1817 Report.SymbolRelatedToPreviousError (vi.Location, name);
1818 if (Explicit == vi.Block.Explicit) {
1819 Error_AlreadyDeclared (l, name, null);
1821 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1822 "parent or current" : "parent");
1827 if (block != null) {
1828 Expression e = block.GetParameterReference (name, Location.Null);
1830 ParameterReference pr = e as ParameterReference;
1831 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1832 Error_AlreadyDeclared (loc, name);
1834 Error_AlreadyDeclared (loc, name, "parent or current");
1842 public LocalInfo AddVariable (Expression type, string name, Location l)
1844 if (!CheckParentConflictName (Toplevel, name, l))
1847 if (Toplevel.GenericMethod != null) {
1848 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1849 if (tp.Name == name) {
1850 Report.SymbolRelatedToPreviousError (tp);
1851 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1857 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1859 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1860 Error_AlreadyDeclared (l, name, "child");
1864 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1867 if ((flags & Flags.VariablesInitialized) != 0)
1868 throw new InternalErrorException ("block has already been resolved");
1873 protected virtual void AddVariable (LocalInfo li)
1875 Variables.Add (li.Name, li);
1876 Explicit.AddKnownVariable (li.Name, li);
1879 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1881 if (reason == null) {
1882 Error_AlreadyDeclared (loc, var);
1886 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1887 "in this scope because it would give a different meaning " +
1888 "to `{0}', which is already used in a `{1}' scope " +
1889 "to denote something else", var, reason);
1892 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1894 Report.Error (128, loc,
1895 "A local variable named `{0}' is already defined in this scope", name);
1898 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1900 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1904 public bool AddConstant (Expression type, string name, Expression value, Location l)
1906 if (AddVariable (type, name, l) == null)
1909 if (constants == null)
1910 constants = new HybridDictionary();
1912 constants.Add (name, value);
1914 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1919 static int next_temp_id = 0;
1921 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1923 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1925 if (temporary_variables == null)
1926 temporary_variables = new ArrayList ();
1928 int id = ++next_temp_id;
1929 string name = "$s_" + id.ToString ();
1931 LocalInfo li = new LocalInfo (te, name, this, loc);
1932 li.CompilerGenerated = true;
1933 temporary_variables.Add (li);
1937 public LocalInfo GetLocalInfo (string name)
1940 for (Block b = this; b != null; b = b.Parent) {
1941 if (b.variables != null) {
1942 ret = (LocalInfo) b.variables [name];
1951 public Expression GetVariableType (string name)
1953 LocalInfo vi = GetLocalInfo (name);
1954 return vi == null ? null : vi.Type;
1957 public Expression GetConstantExpression (string name)
1959 for (Block b = this; b != null; b = b.Parent) {
1960 if (b.constants != null) {
1961 Expression ret = b.constants [name] as Expression;
1970 // It should be used by expressions which require to
1971 // register a statement during resolve process.
1973 public void AddScopeStatement (Statement s)
1975 if (scope_initializers == null)
1976 scope_initializers = new ArrayList ();
1978 scope_initializers.Add (s);
1981 public void AddStatement (Statement s)
1984 flags |= Flags.BlockUsed;
1988 get { return (flags & Flags.BlockUsed) != 0; }
1993 flags |= Flags.BlockUsed;
1996 public bool HasRet {
1997 get { return (flags & Flags.HasRet) != 0; }
2000 public int AssignableSlots {
2003 // if ((flags & Flags.VariablesInitialized) == 0)
2004 // throw new Exception ("Variables have not been initialized yet");
2005 return assignable_slots;
2009 public ArrayList AnonymousChildren {
2010 get { return anonymous_children; }
2013 public void AddAnonymousChild (ToplevelBlock b)
2015 if (anonymous_children == null)
2016 anonymous_children = new ArrayList ();
2018 anonymous_children.Add (b);
2021 void DoResolveConstants (BlockContext ec)
2023 if (constants == null)
2026 if (variables == null)
2027 throw new InternalErrorException ("cannot happen");
2029 foreach (DictionaryEntry de in variables) {
2030 string name = (string) de.Key;
2031 LocalInfo vi = (LocalInfo) de.Value;
2032 Type variable_type = vi.VariableType;
2034 if (variable_type == null) {
2035 if (vi.Type is VarExpr)
2036 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2041 Expression cv = (Expression) constants [name];
2045 // Don't let 'const int Foo = Foo;' succeed.
2046 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2047 // which in turn causes the 'must be constant' error to be triggered.
2048 constants.Remove (name);
2050 if (!Const.IsConstantTypeValid (variable_type)) {
2051 Const.Error_InvalidConstantType (variable_type, loc);
2055 ec.CurrentBlock = this;
2057 using (ec.With (EmitContext.Options.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2058 e = cv.Resolve (ec);
2063 Constant ce = e as Constant;
2065 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2069 e = ce.ConvertImplicitly (variable_type);
2071 if (TypeManager.IsReferenceType (variable_type))
2072 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2074 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2078 constants.Add (name, e);
2079 vi.IsConstant = true;
2083 protected void ResolveMeta (BlockContext ec, int offset)
2085 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2087 // If some parent block was unsafe, we remain unsafe even if this block
2088 // isn't explicitly marked as such.
2089 using (ec.With (EmitContext.Options.UnsafeScope, ec.InUnsafe | Unsafe)) {
2090 flags |= Flags.VariablesInitialized;
2092 if (variables != null) {
2093 foreach (LocalInfo li in variables.Values) {
2094 if (!li.Resolve (ec))
2096 li.VariableInfo = new VariableInfo (li, offset);
2097 offset += li.VariableInfo.Length;
2100 assignable_slots = offset;
2102 DoResolveConstants (ec);
2104 if (children == null)
2106 foreach (Block b in children)
2107 b.ResolveMeta (ec, offset);
2112 // Emits the local variable declarations for a block
2114 public virtual void EmitMeta (EmitContext ec)
2116 if (variables != null){
2117 foreach (LocalInfo vi in variables.Values)
2118 vi.ResolveVariable (ec);
2121 if (temporary_variables != null) {
2122 for (int i = 0; i < temporary_variables.Count; i++)
2123 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2126 if (children != null) {
2127 for (int i = 0; i < children.Count; i++)
2128 ((Block)children[i]).EmitMeta(ec);
2132 void UsageWarning ()
2134 if (variables == null || Report.WarningLevel < 3)
2137 foreach (DictionaryEntry de in variables) {
2138 LocalInfo vi = (LocalInfo) de.Value;
2141 string name = (string) de.Key;
2143 // vi.VariableInfo can be null for 'catch' variables
2144 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2145 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2147 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2152 static void CheckPossibleMistakenEmptyStatement (Statement s)
2156 // Some statements are wrapped by a Block. Since
2157 // others' internal could be changed, here I treat
2158 // them as possibly wrapped by Block equally.
2159 Block b = s as Block;
2160 if (b != null && b.statements.Count == 1)
2161 s = (Statement) b.statements [0];
2164 body = ((Lock) s).Statement;
2166 body = ((For) s).Statement;
2167 else if (s is Foreach)
2168 body = ((Foreach) s).Statement;
2169 else if (s is While)
2170 body = ((While) s).Statement;
2171 else if (s is Fixed)
2172 body = ((Fixed) s).Statement;
2173 else if (s is Using)
2174 body = ((Using) s).EmbeddedStatement;
2175 else if (s is UsingTemporary)
2176 body = ((UsingTemporary) s).Statement;
2180 if (body == null || body is EmptyStatement)
2181 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2184 public override bool Resolve (BlockContext ec)
2186 Block prev_block = ec.CurrentBlock;
2189 int errors = Report.Errors;
2191 ec.CurrentBlock = this;
2192 ec.StartFlowBranching (this);
2194 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2197 // Compiler generated scope statements
2199 if (scope_initializers != null) {
2200 foreach (Statement s in scope_initializers)
2205 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2206 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2207 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2208 // responsible for handling the situation.
2210 int statement_count = statements.Count;
2211 for (int ix = 0; ix < statement_count; ix++){
2212 Statement s = (Statement) statements [ix];
2213 // Check possible empty statement (CS0642)
2214 if (Report.WarningLevel >= 3 &&
2215 ix + 1 < statement_count &&
2216 statements [ix + 1] is ExplicitBlock)
2217 CheckPossibleMistakenEmptyStatement (s);
2220 // Warn if we detect unreachable code.
2223 if (s is EmptyStatement)
2226 if (!unreachable_shown && !(s is LabeledStatement)) {
2227 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2228 unreachable_shown = true;
2231 Block c_block = s as Block;
2232 if (c_block != null)
2233 c_block.unreachable = c_block.unreachable_shown = true;
2237 // Note that we're not using ResolveUnreachable() for unreachable
2238 // statements here. ResolveUnreachable() creates a temporary
2239 // flow branching and kills it afterwards. This leads to problems
2240 // if you have two unreachable statements where the first one
2241 // assigns a variable and the second one tries to access it.
2244 if (!s.Resolve (ec)) {
2246 if (ec.IsInProbingMode)
2249 statements [ix] = EmptyStatement.Value;
2253 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2254 statements [ix] = EmptyStatement.Value;
2256 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2257 if (unreachable && s is LabeledStatement)
2258 throw new InternalErrorException ("should not happen");
2261 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2262 ec.CurrentBranching, statement_count);
2264 while (ec.CurrentBranching is FlowBranchingLabeled)
2265 ec.EndFlowBranching ();
2267 bool flow_unreachable = ec.EndFlowBranching ();
2269 ec.CurrentBlock = prev_block;
2271 if (flow_unreachable)
2272 flags |= Flags.HasRet;
2274 // If we're a non-static `struct' constructor which doesn't have an
2275 // initializer, then we must initialize all of the struct's fields.
2276 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2279 if ((labels != null) && (Report.WarningLevel >= 2)) {
2280 foreach (LabeledStatement label in labels.Values)
2281 if (!label.HasBeenReferenced)
2282 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2285 if (ok && errors == Report.Errors)
2291 public override bool ResolveUnreachable (BlockContext ec, bool warn)
2293 unreachable_shown = true;
2297 Report.Warning (162, 2, loc, "Unreachable code detected");
2299 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2300 bool ok = Resolve (ec);
2301 ec.KillFlowBranching ();
2306 protected override void DoEmit (EmitContext ec)
2308 for (int ix = 0; ix < statements.Count; ix++){
2309 Statement s = (Statement) statements [ix];
2314 public override void Emit (EmitContext ec)
2316 Block prev_block = ec.CurrentBlock;
2317 ec.CurrentBlock = this;
2319 if (scope_initializers != null)
2320 EmitScopeInitializers (ec);
2322 ec.Mark (StartLocation);
2325 if (SymbolWriter.HasSymbolWriter)
2326 EmitSymbolInfo (ec);
2328 ec.CurrentBlock = prev_block;
2331 protected void EmitScopeInitializers (EmitContext ec)
2333 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2335 using (ec.Set (EmitContext.Options.OmitDebuggingInfo)) {
2336 foreach (Statement s in scope_initializers)
2340 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2343 protected virtual void EmitSymbolInfo (EmitContext ec)
2345 if (variables != null) {
2346 foreach (LocalInfo vi in variables.Values) {
2347 vi.EmitSymbolInfo (ec);
2352 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2354 MutateVariables (storey);
2356 if (scope_initializers != null) {
2357 foreach (Statement s in scope_initializers)
2358 s.MutateHoistedGenericType (storey);
2361 foreach (Statement s in statements)
2362 s.MutateHoistedGenericType (storey);
2365 void MutateVariables (AnonymousMethodStorey storey)
2367 if (variables != null) {
2368 foreach (LocalInfo vi in variables.Values) {
2369 vi.VariableType = storey.MutateType (vi.VariableType);
2373 if (temporary_variables != null) {
2374 foreach (LocalInfo vi in temporary_variables)
2375 vi.VariableType = storey.MutateType (vi.VariableType);
2379 public override string ToString ()
2381 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2384 protected override void CloneTo (CloneContext clonectx, Statement t)
2386 Block target = (Block) t;
2388 clonectx.AddBlockMap (this, target);
2390 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2391 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2393 target.Parent = clonectx.RemapBlockCopy (Parent);
2395 if (variables != null){
2396 target.variables = new Hashtable ();
2398 foreach (DictionaryEntry de in variables){
2399 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2400 target.variables [de.Key] = newlocal;
2401 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2405 target.statements = new ArrayList (statements.Count);
2406 foreach (Statement s in statements)
2407 target.statements.Add (s.Clone (clonectx));
2409 if (target.children != null){
2410 target.children = new ArrayList (children.Count);
2411 foreach (Block b in children){
2412 target.children.Add (clonectx.LookupBlock (b));
2417 // TODO: labels, switch_block, constants (?), anonymous_children
2422 public class ExplicitBlock : Block {
2423 HybridDictionary known_variables;
2424 protected AnonymousMethodStorey am_storey;
2426 public ExplicitBlock (Block parent, Location start, Location end)
2427 : this (parent, (Flags) 0, start, end)
2431 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2432 : base (parent, flags, start, end)
2434 this.Explicit = this;
2438 // Marks a variable with name @name as being used in this or a child block.
2439 // If a variable name has been used in a child block, it's illegal to
2440 // declare a variable with the same name in the current block.
2442 internal void AddKnownVariable (string name, IKnownVariable info)
2444 if (known_variables == null)
2445 known_variables = new HybridDictionary();
2447 known_variables [name] = info;
2450 Parent.Explicit.AddKnownVariable (name, info);
2453 public AnonymousMethodStorey AnonymousMethodStorey {
2454 get { return am_storey; }
2458 // Creates anonymous method storey in current block
2460 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2463 // When referencing a variable in iterator storey from children anonymous method
2465 if (Toplevel.am_storey is IteratorStorey) {
2466 return Toplevel.am_storey;
2470 // An iterator has only 1 storey block
2472 if (ec.CurrentIterator != null)
2473 return ec.CurrentIterator.Storey;
2475 if (am_storey == null) {
2476 MemberBase mc = ec.MemberContext as MemberBase;
2477 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2480 // Creates anonymous method storey for this block
2482 am_storey = new AnonymousMethodStorey (this, ec.CurrentTypeDefinition, mc, gm, "AnonStorey");
2488 public override void Emit (EmitContext ec)
2490 if (am_storey != null)
2491 am_storey.EmitStoreyInstantiation (ec);
2493 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2494 if (emit_debug_info)
2499 if (emit_debug_info)
2503 public override void EmitMeta (EmitContext ec)
2506 // Creates anonymous method storey
2508 if (am_storey != null) {
2509 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2511 // Creates parent storey reference when hoisted this is accessible
2513 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2514 ExplicitBlock parent = Toplevel.Parent.Explicit;
2517 // Hoisted this exists in top-level parent storey only
2519 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2520 parent = parent.Parent.Explicit;
2522 am_storey.AddParentStoreyReference (parent.am_storey);
2525 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2528 am_storey.DefineType ();
2529 am_storey.ResolveType ();
2530 am_storey.Define ();
2531 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2533 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2534 if (ref_blocks != null) {
2535 foreach (ExplicitBlock ref_block in ref_blocks) {
2536 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2537 if (b.am_storey != null) {
2538 b.am_storey.AddParentStoreyReference (am_storey);
2540 // Stop propagation inside same top block
2541 if (b.Toplevel == Toplevel)
2546 b.HasCapturedVariable = true;
2555 internal IKnownVariable GetKnownVariable (string name)
2557 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2560 public bool HasCapturedThis
2562 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2563 get { return (flags & Flags.HasCapturedThis) != 0; }
2566 public bool HasCapturedVariable
2568 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2569 get { return (flags & Flags.HasCapturedVariable) != 0; }
2572 protected override void CloneTo (CloneContext clonectx, Statement t)
2574 ExplicitBlock target = (ExplicitBlock) t;
2575 target.known_variables = null;
2576 base.CloneTo (clonectx, t);
2580 public class ToplevelParameterInfo : IKnownVariable {
2581 public readonly ToplevelBlock Block;
2582 public readonly int Index;
2583 public VariableInfo VariableInfo;
2585 Block IKnownVariable.Block {
2586 get { return Block; }
2588 public Parameter Parameter {
2589 get { return Block.Parameters [Index]; }
2592 public Type ParameterType {
2593 get { return Block.Parameters.Types [Index]; }
2596 public Location Location {
2597 get { return Parameter.Location; }
2600 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2608 // A toplevel block contains extra information, the split is done
2609 // only to separate information that would otherwise bloat the more
2610 // lightweight Block.
2612 // In particular, this was introduced when the support for Anonymous
2613 // Methods was implemented.
2615 public class ToplevelBlock : ExplicitBlock
2618 // Block is converted to an expression
2620 sealed class BlockScopeExpression : Expression
2623 readonly ToplevelBlock block;
2625 public BlockScopeExpression (Expression child, ToplevelBlock block)
2631 public override Expression CreateExpressionTree (ResolveContext ec)
2633 throw new NotSupportedException ();
2636 public override Expression DoResolve (ResolveContext ec)
2641 child = child.Resolve (ec);
2645 eclass = child.eclass;
2650 public override void Emit (EmitContext ec)
2652 block.EmitMeta (ec);
2653 block.EmitScopeInitializers (ec);
2657 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2659 type = storey.MutateType (type);
2660 child.MutateHoistedGenericType (storey);
2661 block.MutateHoistedGenericType (storey);
2665 GenericMethod generic;
2666 protected ParametersCompiled parameters;
2667 ToplevelParameterInfo[] parameter_info;
2668 LocalInfo this_variable;
2672 public HoistedVariable HoistedThisVariable;
2674 public bool Resolved {
2681 // The parameters for the block.
2683 public ParametersCompiled Parameters {
2684 get { return parameters; }
2687 public GenericMethod GenericMethod {
2688 get { return generic; }
2691 public ToplevelBlock Container {
2692 get { return Parent == null ? null : Parent.Toplevel; }
2695 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2696 this (parent, (Flags) 0, parameters, start)
2700 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2701 this (parent, parameters, start)
2703 this.generic = generic;
2706 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2707 this (null, (Flags) 0, parameters, start)
2711 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2712 this (null, flags, parameters, start)
2716 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2717 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2718 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2719 base (null, flags, start, Location.Null)
2721 this.Toplevel = this;
2723 this.parameters = parameters;
2724 this.Parent = parent;
2726 parent.AddAnonymousChild (this);
2728 if (!this.parameters.IsEmpty)
2729 ProcessParameters ();
2732 public ToplevelBlock (Location loc)
2733 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2737 protected override void CloneTo (CloneContext clonectx, Statement t)
2739 ToplevelBlock target = (ToplevelBlock) t;
2740 base.CloneTo (clonectx, t);
2742 if (parameters.Count != 0)
2743 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2744 for (int i = 0; i < parameters.Count; ++i)
2745 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2748 public bool CheckError158 (string name, Location loc)
2750 if (AnonymousChildren != null) {
2751 foreach (ToplevelBlock child in AnonymousChildren) {
2752 if (!child.CheckError158 (name, loc))
2757 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2758 if (!c.DoCheckError158 (name, loc))
2765 void ProcessParameters ()
2767 int n = parameters.Count;
2768 parameter_info = new ToplevelParameterInfo [n];
2769 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2770 for (int i = 0; i < n; ++i) {
2771 parameter_info [i] = new ToplevelParameterInfo (this, i);
2773 Parameter p = parameters [i];
2777 string name = p.Name;
2778 if (CheckParentConflictName (top_parent, name, loc))
2779 AddKnownVariable (name, parameter_info [i]);
2782 // mark this block as "used" so that we create local declarations in a sub-block
2783 // FIXME: This appears to uncover a lot of bugs
2787 bool DoCheckError158 (string name, Location loc)
2789 LabeledStatement s = LookupLabel (name);
2791 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2792 Error_158 (name, loc);
2799 public override Expression CreateExpressionTree (ResolveContext ec)
2801 if (statements.Count == 1) {
2802 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2803 if (scope_initializers != null)
2804 expr = new BlockScopeExpression (expr, this);
2809 return base.CreateExpressionTree (ec);
2813 // Reformats this block to be top-level iterator block
2815 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2819 // Creates block with original statements
2820 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2822 source.statements = new ArrayList (1);
2823 source.AddStatement (new Return (iterator, iterator.Location));
2824 source.IsIterator = false;
2826 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2827 source.am_storey = iterator_storey;
2828 return iterator_storey;
2832 // Returns a parameter reference expression for the given name,
2833 // or null if there is no such parameter
2835 public Expression GetParameterReference (string name, Location loc)
2837 for (ToplevelBlock t = this; t != null; t = t.Container) {
2838 Expression expr = t.GetParameterReferenceExpression (name, loc);
2846 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2848 int idx = parameters.GetParameterIndexByName (name);
2850 null : new ParameterReference (parameter_info [idx], loc);
2854 // Returns the "this" instance variable of this block.
2855 // See AddThisVariable() for more information.
2857 public LocalInfo ThisVariable {
2858 get { return this_variable; }
2862 // This is used by non-static `struct' constructors which do not have an
2863 // initializer - in this case, the constructor must initialize all of the
2864 // struct's fields. To do this, we add a "this" variable and use the flow
2865 // analysis code to ensure that it's been fully initialized before control
2866 // leaves the constructor.
2868 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2870 if (this_variable == null) {
2871 this_variable = new LocalInfo (ds, this, l);
2872 this_variable.Used = true;
2873 this_variable.IsThis = true;
2875 Variables.Add ("this", this_variable);
2878 return this_variable;
2881 public bool IsIterator {
2882 get { return (flags & Flags.IsIterator) != 0; }
2883 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2886 public bool IsThisAssigned (BlockContext ec)
2888 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2891 public bool Resolve (FlowBranching parent, BlockContext rc, ParametersCompiled ip, IMethodData md)
2899 if (!ResolveMeta (rc, ip))
2902 using (rc.With (EmitContext.Options.DoFlowAnalysis, true)) {
2903 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2908 unreachable = top_level.End ();
2910 } catch (Exception) {
2912 if (rc.CurrentBlock != null) {
2913 Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2915 Report.Error (587, "Internal compiler error: Phase Resolve");
2921 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2922 if (rc.CurrentAnonymousMethod == null) {
2923 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2925 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2926 Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2927 rc.CurrentAnonymousMethod.GetSignatureForError ());
2935 bool ResolveMeta (BlockContext ec, ParametersCompiled ip)
2937 int errors = Report.Errors;
2938 int orig_count = parameters.Count;
2943 // Assert: orig_count != parameter.Count => orig_count == 0
2944 if (orig_count != 0 && orig_count != parameters.Count)
2945 throw new InternalErrorException ("parameter information mismatch");
2947 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2949 for (int i = 0; i < orig_count; ++i) {
2950 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2952 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2955 VariableInfo vi = new VariableInfo (ip, i, offset);
2956 parameter_info [i].VariableInfo = vi;
2957 offset += vi.Length;
2960 ResolveMeta (ec, offset);
2962 return Report.Errors == errors;
2966 // Check whether all `out' parameters have been assigned.
2968 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2970 if (vector.IsUnreachable)
2973 int n = parameter_info == null ? 0 : parameter_info.Length;
2975 for (int i = 0; i < n; i++) {
2976 VariableInfo var = parameter_info [i].VariableInfo;
2981 if (vector.IsAssigned (var, false))
2984 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2989 public override void Emit (EmitContext ec)
2991 if (Report.Errors > 0)
2999 if (ec.HasReturnLabel)
3000 ec.ReturnLabel = ec.ig.DefineLabel ();
3004 ec.Mark (EndLocation);
3006 if (ec.HasReturnLabel)
3007 ec.ig.MarkLabel (ec.ReturnLabel);
3009 if (ec.return_value != null) {
3010 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
3011 ec.ig.Emit (OpCodes.Ret);
3014 // If `HasReturnLabel' is set, then we already emitted a
3015 // jump to the end of the method, so we must emit a `ret'
3018 // Unfortunately, System.Reflection.Emit automatically emits
3019 // a leave to the end of a finally block. This is a problem
3020 // if no code is following the try/finally block since we may
3021 // jump to a point after the end of the method.
3022 // As a workaround, we're always creating a return label in
3026 if (ec.HasReturnLabel || !unreachable) {
3027 if (ec.ReturnType != TypeManager.void_type)
3028 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3029 ec.ig.Emit (OpCodes.Ret);
3034 } catch (Exception e){
3035 Console.WriteLine ("Exception caught by the compiler while emitting:");
3036 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3038 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3044 public override void EmitMeta (EmitContext ec)
3046 parameters.ResolveVariable ();
3048 // Avoid declaring an IL variable for this_variable since it is not accessed
3049 // from the generated IL
3050 if (this_variable != null)
3051 Variables.Remove ("this");
3055 protected override void EmitSymbolInfo (EmitContext ec)
3057 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3058 if ((ae != null) && (ae.Storey != null))
3059 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3061 base.EmitSymbolInfo (ec);
3065 public class SwitchLabel {
3072 Label il_label_code;
3073 bool il_label_code_set;
3075 public static readonly object NullStringCase = new object ();
3078 // if expr == null, then it is the default case.
3080 public SwitchLabel (Expression expr, Location l)
3086 public Expression Label {
3092 public Location Location {
3096 public object Converted {
3102 public Label GetILLabel (EmitContext ec)
3105 il_label = ec.ig.DefineLabel ();
3106 il_label_set = true;
3111 public Label GetILLabelCode (EmitContext ec)
3113 if (!il_label_code_set){
3114 il_label_code = ec.ig.DefineLabel ();
3115 il_label_code_set = true;
3117 return il_label_code;
3121 // Resolves the expression, reduces it to a literal if possible
3122 // and then converts it to the requested type.
3124 public bool ResolveAndReduce (ResolveContext ec, Type required_type, bool allow_nullable)
3126 Expression e = label.Resolve (ec);
3131 Constant c = e as Constant;
3133 Report.Error (150, loc, "A constant value is expected");
3137 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3138 converted = NullStringCase;
3142 if (allow_nullable && c.GetValue () == null) {
3143 converted = NullStringCase;
3147 c = c.ImplicitConversionRequired (ec, required_type, loc);
3151 converted = c.GetValue ();
3155 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3158 if (converted == null)
3160 else if (converted == NullStringCase)
3163 label = converted.ToString ();
3165 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3166 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3169 public SwitchLabel Clone (CloneContext clonectx)
3171 return new SwitchLabel (label.Clone (clonectx), loc);
3175 public class SwitchSection {
3176 // An array of SwitchLabels.
3177 public readonly ArrayList Labels;
3178 public readonly Block Block;
3180 public SwitchSection (ArrayList labels, Block block)
3186 public SwitchSection Clone (CloneContext clonectx)
3188 ArrayList cloned_labels = new ArrayList ();
3190 foreach (SwitchLabel sl in cloned_labels)
3191 cloned_labels.Add (sl.Clone (clonectx));
3193 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3197 public class Switch : Statement {
3198 public ArrayList Sections;
3199 public Expression Expr;
3202 /// Maps constants whose type type SwitchType to their SwitchLabels.
3204 public IDictionary Elements;
3207 /// The governing switch type
3209 public Type SwitchType;
3214 Label default_target;
3216 Expression new_expr;
3219 SwitchSection constant_section;
3220 SwitchSection default_section;
3222 ExpressionStatement string_dictionary;
3223 FieldExpr switch_cache_field;
3224 static int unique_counter;
3227 // Nullable Types support
3229 Nullable.Unwrap unwrap;
3231 protected bool HaveUnwrap {
3232 get { return unwrap != null; }
3236 // The types allowed to be implicitly cast from
3237 // on the governing type
3239 static Type [] allowed_types;
3241 public Switch (Expression e, ArrayList sects, Location l)
3248 public bool GotDefault {
3250 return default_section != null;
3254 public Label DefaultTarget {
3256 return default_target;
3261 // Determines the governing type for a switch. The returned
3262 // expression might be the expression from the switch, or an
3263 // expression that includes any potential conversions to the
3264 // integral types or to string.
3266 Expression SwitchGoverningType (ResolveContext ec, Expression expr)
3270 if (t == TypeManager.byte_type ||
3271 t == TypeManager.sbyte_type ||
3272 t == TypeManager.ushort_type ||
3273 t == TypeManager.short_type ||
3274 t == TypeManager.uint32_type ||
3275 t == TypeManager.int32_type ||
3276 t == TypeManager.uint64_type ||
3277 t == TypeManager.int64_type ||
3278 t == TypeManager.char_type ||
3279 t == TypeManager.string_type ||
3280 t == TypeManager.bool_type ||
3281 TypeManager.IsEnumType (t))
3284 if (allowed_types == null){
3285 allowed_types = new Type [] {
3286 TypeManager.sbyte_type,
3287 TypeManager.byte_type,
3288 TypeManager.short_type,
3289 TypeManager.ushort_type,
3290 TypeManager.int32_type,
3291 TypeManager.uint32_type,
3292 TypeManager.int64_type,
3293 TypeManager.uint64_type,
3294 TypeManager.char_type,
3295 TypeManager.string_type
3300 // Try to find a *user* defined implicit conversion.
3302 // If there is no implicit conversion, or if there are multiple
3303 // conversions, we have to report an error
3305 Expression converted = null;
3306 foreach (Type tt in allowed_types){
3309 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3314 // Ignore over-worked ImplicitUserConversions that do
3315 // an implicit conversion in addition to the user conversion.
3317 if (!(e is UserCast))
3320 if (converted != null){
3321 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3331 // Performs the basic sanity checks on the switch statement
3332 // (looks for duplicate keys and non-constant expressions).
3334 // It also returns a hashtable with the keys that we will later
3335 // use to compute the switch tables
3337 bool CheckSwitch (ResolveContext ec)
3340 Elements = Sections.Count > 10 ?
3341 (IDictionary)new Hashtable () :
3342 (IDictionary)new ListDictionary ();
3344 foreach (SwitchSection ss in Sections){
3345 foreach (SwitchLabel sl in ss.Labels){
3346 if (sl.Label == null){
3347 if (default_section != null){
3348 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3351 default_section = ss;
3355 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3360 object key = sl.Converted;
3361 if (key == SwitchLabel.NullStringCase)
3362 has_null_case = true;
3365 Elements.Add (key, sl);
3366 } catch (ArgumentException) {
3367 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3375 void EmitObjectInteger (ILGenerator ig, object k)
3378 IntConstant.EmitInt (ig, (int) k);
3379 else if (k is Constant) {
3380 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3383 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3386 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3388 IntConstant.EmitInt (ig, (int) (long) k);
3389 ig.Emit (OpCodes.Conv_I8);
3392 LongConstant.EmitLong (ig, (long) k);
3394 else if (k is ulong)
3396 ulong ul = (ulong) k;
3399 IntConstant.EmitInt (ig, unchecked ((int) ul));
3400 ig.Emit (OpCodes.Conv_U8);
3404 LongConstant.EmitLong (ig, unchecked ((long) ul));
3408 IntConstant.EmitInt (ig, (int) ((char) k));
3409 else if (k is sbyte)
3410 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3412 IntConstant.EmitInt (ig, (int) ((byte) k));
3413 else if (k is short)
3414 IntConstant.EmitInt (ig, (int) ((short) k));
3415 else if (k is ushort)
3416 IntConstant.EmitInt (ig, (int) ((ushort) k));
3418 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3420 throw new Exception ("Unhandled case");
3423 // structure used to hold blocks of keys while calculating table switch
3424 class KeyBlock : IComparable
3426 public KeyBlock (long _first)
3428 first = last = _first;
3432 public ArrayList element_keys = null;
3433 // how many items are in the bucket
3434 public int Size = 1;
3437 get { return (int) (last - first + 1); }
3439 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3441 return kb_last.last - kb_first.first + 1;
3443 public int CompareTo (object obj)
3445 KeyBlock kb = (KeyBlock) obj;
3446 int nLength = Length;
3447 int nLengthOther = kb.Length;
3448 if (nLengthOther == nLength)
3449 return (int) (kb.first - first);
3450 return nLength - nLengthOther;
3455 /// This method emits code for a lookup-based switch statement (non-string)
3456 /// Basically it groups the cases into blocks that are at least half full,
3457 /// and then spits out individual lookup opcodes for each block.
3458 /// It emits the longest blocks first, and short blocks are just
3459 /// handled with direct compares.
3461 /// <param name="ec"></param>
3462 /// <param name="val"></param>
3463 /// <returns></returns>
3464 void TableSwitchEmit (EmitContext ec, Expression val)
3466 int element_count = Elements.Count;
3467 object [] element_keys = new object [element_count];
3468 Elements.Keys.CopyTo (element_keys, 0);
3469 Array.Sort (element_keys);
3471 // initialize the block list with one element per key
3472 ArrayList key_blocks = new ArrayList (element_count);
3473 foreach (object key in element_keys)
3474 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3476 KeyBlock current_kb;
3477 // iteratively merge the blocks while they are at least half full
3478 // there's probably a really cool way to do this with a tree...
3479 while (key_blocks.Count > 1)
3481 ArrayList key_blocks_new = new ArrayList ();
3482 current_kb = (KeyBlock) key_blocks [0];
3483 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3485 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3486 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3489 current_kb.last = kb.last;
3490 current_kb.Size += kb.Size;
3494 // start a new block
3495 key_blocks_new.Add (current_kb);
3499 key_blocks_new.Add (current_kb);
3500 if (key_blocks.Count == key_blocks_new.Count)
3502 key_blocks = key_blocks_new;
3505 // initialize the key lists
3506 foreach (KeyBlock kb in key_blocks)
3507 kb.element_keys = new ArrayList ();
3509 // fill the key lists
3511 if (key_blocks.Count > 0) {
3512 current_kb = (KeyBlock) key_blocks [0];
3513 foreach (object key in element_keys)
3515 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3516 System.Convert.ToInt64 (key) > current_kb.last;
3518 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3519 current_kb.element_keys.Add (key);
3523 // sort the blocks so we can tackle the largest ones first
3526 // okay now we can start...
3527 ILGenerator ig = ec.ig;
3528 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3529 Label lbl_default = default_target;
3531 Type type_keys = null;
3532 if (element_keys.Length > 0)
3533 type_keys = element_keys [0].GetType (); // used for conversions
3537 if (TypeManager.IsEnumType (SwitchType))
3538 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3540 compare_type = SwitchType;
3542 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3544 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3545 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3548 foreach (object key in kb.element_keys) {
3549 SwitchLabel sl = (SwitchLabel) Elements [key];
3550 if (key is int && (int) key == 0) {
3551 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3554 EmitObjectInteger (ig, key);
3555 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3561 // TODO: if all the keys in the block are the same and there are
3562 // no gaps/defaults then just use a range-check.
3563 if (compare_type == TypeManager.int64_type ||
3564 compare_type == TypeManager.uint64_type)
3566 // TODO: optimize constant/I4 cases
3568 // check block range (could be > 2^31)
3570 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3571 ig.Emit (OpCodes.Blt, lbl_default);
3573 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3574 ig.Emit (OpCodes.Bgt, lbl_default);
3580 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3581 ig.Emit (OpCodes.Sub);
3583 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3589 int first = (int) kb.first;
3592 IntConstant.EmitInt (ig, first);
3593 ig.Emit (OpCodes.Sub);
3597 IntConstant.EmitInt (ig, -first);
3598 ig.Emit (OpCodes.Add);
3602 // first, build the list of labels for the switch
3604 int cJumps = kb.Length;
3605 Label [] switch_labels = new Label [cJumps];
3606 for (int iJump = 0; iJump < cJumps; iJump++)
3608 object key = kb.element_keys [iKey];
3609 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3611 SwitchLabel sl = (SwitchLabel) Elements [key];
3612 switch_labels [iJump] = sl.GetILLabel (ec);
3616 switch_labels [iJump] = lbl_default;
3618 // emit the switch opcode
3619 ig.Emit (OpCodes.Switch, switch_labels);
3622 // mark the default for this block
3624 ig.MarkLabel (lbl_default);
3627 // TODO: find the default case and emit it here,
3628 // to prevent having to do the following jump.
3629 // make sure to mark other labels in the default section
3631 // the last default just goes to the end
3632 if (element_keys.Length > 0)
3633 ig.Emit (OpCodes.Br, lbl_default);
3635 // now emit the code for the sections
3636 bool found_default = false;
3638 foreach (SwitchSection ss in Sections) {
3639 foreach (SwitchLabel sl in ss.Labels) {
3640 if (sl.Converted == SwitchLabel.NullStringCase) {
3641 ig.MarkLabel (null_target);
3642 } else if (sl.Label == null) {
3643 ig.MarkLabel (lbl_default);
3644 found_default = true;
3646 ig.MarkLabel (null_target);
3648 ig.MarkLabel (sl.GetILLabel (ec));
3649 ig.MarkLabel (sl.GetILLabelCode (ec));
3654 if (!found_default) {
3655 ig.MarkLabel (lbl_default);
3656 if (!has_null_case) {
3657 ig.MarkLabel (null_target);
3661 ig.MarkLabel (lbl_end);
3664 SwitchSection FindSection (SwitchLabel label)
3666 foreach (SwitchSection ss in Sections){
3667 foreach (SwitchLabel sl in ss.Labels){
3676 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3678 foreach (SwitchSection ss in Sections)
3679 ss.Block.MutateHoistedGenericType (storey);
3682 public static void Reset ()
3685 allowed_types = null;
3688 public override bool Resolve (BlockContext ec)
3690 Expr = Expr.Resolve (ec);
3694 new_expr = SwitchGoverningType (ec, Expr);
3696 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3697 unwrap = Nullable.Unwrap.Create (Expr, false);
3701 new_expr = SwitchGoverningType (ec, unwrap);
3704 if (new_expr == null){
3705 Report.Error (151, loc,
3706 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3707 TypeManager.CSharpName (Expr.Type));
3712 SwitchType = new_expr.Type;
3714 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3715 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3719 if (!CheckSwitch (ec))
3723 Elements.Remove (SwitchLabel.NullStringCase);
3725 Switch old_switch = ec.Switch;
3727 ec.Switch.SwitchType = SwitchType;
3729 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3730 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3732 is_constant = new_expr is Constant;
3734 object key = ((Constant) new_expr).GetValue ();
3735 SwitchLabel label = (SwitchLabel) Elements [key];
3737 constant_section = FindSection (label);
3738 if (constant_section == null)
3739 constant_section = default_section;
3744 foreach (SwitchSection ss in Sections){
3746 ec.CurrentBranching.CreateSibling (
3747 null, FlowBranching.SiblingType.SwitchSection);
3751 if (is_constant && (ss != constant_section)) {
3752 // If we're a constant switch, we're only emitting
3753 // one single section - mark all the others as
3755 ec.CurrentBranching.CurrentUsageVector.Goto ();
3756 if (!ss.Block.ResolveUnreachable (ec, true)) {
3760 if (!ss.Block.Resolve (ec))
3765 if (default_section == null)
3766 ec.CurrentBranching.CreateSibling (
3767 null, FlowBranching.SiblingType.SwitchSection);
3769 ec.EndFlowBranching ();
3770 ec.Switch = old_switch;
3772 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3777 if (SwitchType == TypeManager.string_type && !is_constant) {
3778 // TODO: Optimize single case, and single+default case
3779 ResolveStringSwitchMap (ec);
3785 void ResolveStringSwitchMap (ResolveContext ec)
3787 FullNamedExpression string_dictionary_type;
3788 if (TypeManager.generic_ienumerable_type != null) {
3789 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3790 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3792 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3794 new TypeExpression (TypeManager.string_type, loc),
3795 new TypeExpression (TypeManager.int32_type, loc)), loc);
3797 MemberAccess system_collections_generic = new MemberAccess (
3798 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3800 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3803 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3804 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3805 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3806 if (!field.Define ())
3808 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3810 ArrayList init = new ArrayList ();
3813 string value = null;
3814 foreach (SwitchSection section in Sections) {
3815 int last_count = init.Count;
3816 foreach (SwitchLabel sl in section.Labels) {
3817 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3820 value = (string) sl.Converted;
3821 ArrayList init_args = new ArrayList (2);
3822 init_args.Add (new StringLiteral (value, sl.Location));
3823 init_args.Add (new IntConstant (counter, loc));
3824 init.Add (new CollectionElementInitializer (init_args, loc));
3828 // Don't add empty sections
3830 if (last_count == init.Count)
3833 Elements.Add (counter, section.Labels [0]);
3837 Arguments args = new Arguments (1);
3838 args.Add (new Argument (new IntConstant (init.Count, loc)));
3839 Expression initializer = new NewInitialize (string_dictionary_type, args,
3840 new CollectionOrObjectInitializers (init, loc), loc);
3842 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3843 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3846 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3848 ILGenerator ig = ec.ig;
3849 Label l_initialized = ig.DefineLabel ();
3852 // Skip initialization when value is null
3854 value.EmitBranchable (ec, null_target, false);
3857 // Check if string dictionary is initialized and initialize
3859 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3860 string_dictionary.EmitStatement (ec);
3861 ig.MarkLabel (l_initialized);
3863 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3865 ResolveContext rc = new ResolveContext (ec.MemberContext);
3867 if (TypeManager.generic_ienumerable_type != null) {
3868 Arguments get_value_args = new Arguments (2);
3869 get_value_args.Add (new Argument (value));
3870 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3871 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (rc);
3872 if (get_item == null)
3876 // A value was not found, go to default case
3878 get_item.EmitBranchable (ec, default_target, false);
3880 Arguments get_value_args = new Arguments (1);
3881 get_value_args.Add (new Argument (value));
3883 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (rc);
3884 if (get_item == null)
3887 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3888 get_item_object.EmitAssign (ec, get_item, true, false);
3889 ec.ig.Emit (OpCodes.Brfalse, default_target);
3891 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3892 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (rc);
3894 get_item_int.EmitStatement (ec);
3895 get_item_object.Release (ec);
3898 TableSwitchEmit (ec, string_switch_variable);
3899 string_switch_variable.Release (ec);
3902 protected override void DoEmit (EmitContext ec)
3904 ILGenerator ig = ec.ig;
3906 default_target = ig.DefineLabel ();
3907 null_target = ig.DefineLabel ();
3909 // Store variable for comparission purposes
3910 // TODO: Don't duplicate non-captured VariableReference
3911 LocalTemporary value;
3913 value = new LocalTemporary (SwitchType);
3914 unwrap.EmitCheck (ec);
3915 ig.Emit (OpCodes.Brfalse, null_target);
3918 } else if (!is_constant) {
3919 value = new LocalTemporary (SwitchType);
3926 // Setup the codegen context
3928 Label old_end = ec.LoopEnd;
3929 Switch old_switch = ec.Switch;
3931 ec.LoopEnd = ig.DefineLabel ();
3936 if (constant_section != null)
3937 constant_section.Block.Emit (ec);
3938 } else if (string_dictionary != null) {
3939 DoEmitStringSwitch (value, ec);
3941 TableSwitchEmit (ec, value);
3947 // Restore context state.
3948 ig.MarkLabel (ec.LoopEnd);
3951 // Restore the previous context
3953 ec.LoopEnd = old_end;
3954 ec.Switch = old_switch;
3957 protected override void CloneTo (CloneContext clonectx, Statement t)
3959 Switch target = (Switch) t;
3961 target.Expr = Expr.Clone (clonectx);
3962 target.Sections = new ArrayList ();
3963 foreach (SwitchSection ss in Sections){
3964 target.Sections.Add (ss.Clone (clonectx));
3969 // A place where execution can restart in an iterator
3970 public abstract class ResumableStatement : Statement
3973 protected Label resume_point;
3975 public Label PrepareForEmit (EmitContext ec)
3979 resume_point = ec.ig.DefineLabel ();
3981 return resume_point;
3984 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3988 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3993 // Base class for statements that are implemented in terms of try...finally
3994 public abstract class ExceptionStatement : ResumableStatement
3998 protected abstract void EmitPreTryBody (EmitContext ec);
3999 protected abstract void EmitTryBody (EmitContext ec);
4000 protected abstract void EmitFinallyBody (EmitContext ec);
4002 protected sealed override void DoEmit (EmitContext ec)
4004 ILGenerator ig = ec.ig;
4006 EmitPreTryBody (ec);
4008 if (resume_points != null) {
4009 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
4010 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
4013 ig.BeginExceptionBlock ();
4015 if (resume_points != null) {
4016 ig.MarkLabel (resume_point);
4018 // For normal control flow, we want to fall-through the Switch
4019 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4020 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
4021 IntConstant.EmitInt (ig, first_resume_pc);
4022 ig.Emit (OpCodes.Sub);
4024 Label [] labels = new Label [resume_points.Count];
4025 for (int i = 0; i < resume_points.Count; ++i)
4026 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
4027 ig.Emit (OpCodes.Switch, labels);
4032 ig.BeginFinallyBlock ();
4034 Label start_finally = ec.ig.DefineLabel ();
4035 if (resume_points != null) {
4036 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
4037 ig.Emit (OpCodes.Brfalse_S, start_finally);
4038 ig.Emit (OpCodes.Endfinally);
4041 ig.MarkLabel (start_finally);
4042 EmitFinallyBody (ec);
4044 ig.EndExceptionBlock ();
4047 public void SomeCodeFollows ()
4049 code_follows = true;
4052 protected void ResolveReachability (BlockContext ec)
4054 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4055 // So, ensure there's some IL code after this statement.
4056 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4057 ec.NeedReturnLabel ();
4061 ArrayList resume_points;
4062 int first_resume_pc;
4063 public void AddResumePoint (ResumableStatement stmt, int pc)
4065 if (resume_points == null) {
4066 resume_points = new ArrayList ();
4067 first_resume_pc = pc;
4070 if (pc != first_resume_pc + resume_points.Count)
4071 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4073 resume_points.Add (stmt);
4076 Label dispose_try_block;
4077 bool prepared_for_dispose, emitted_dispose;
4078 public override Label PrepareForDispose (EmitContext ec, Label end)
4080 if (!prepared_for_dispose) {
4081 prepared_for_dispose = true;
4082 dispose_try_block = ec.ig.DefineLabel ();
4084 return dispose_try_block;
4087 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4089 if (emitted_dispose)
4092 emitted_dispose = true;
4094 ILGenerator ig = ec.ig;
4096 Label end_of_try = ig.DefineLabel ();
4098 // Ensure that the only way we can get into this code is through a dispatcher
4099 if (have_dispatcher)
4100 ig.Emit (OpCodes.Br, end);
4102 ig.BeginExceptionBlock ();
4104 ig.MarkLabel (dispose_try_block);
4106 Label [] labels = null;
4107 for (int i = 0; i < resume_points.Count; ++i) {
4108 ResumableStatement s = (ResumableStatement) resume_points [i];
4109 Label ret = s.PrepareForDispose (ec, end_of_try);
4110 if (ret.Equals (end_of_try) && labels == null)
4112 if (labels == null) {
4113 labels = new Label [resume_points.Count];
4114 for (int j = 0; j < i; ++j)
4115 labels [j] = end_of_try;
4120 if (labels != null) {
4122 for (j = 1; j < labels.Length; ++j)
4123 if (!labels [0].Equals (labels [j]))
4125 bool emit_dispatcher = j < labels.Length;
4127 if (emit_dispatcher) {
4128 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4129 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4130 IntConstant.EmitInt (ig, first_resume_pc);
4131 ig.Emit (OpCodes.Sub);
4132 ig.Emit (OpCodes.Switch, labels);
4133 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4136 foreach (ResumableStatement s in resume_points)
4137 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4140 ig.MarkLabel (end_of_try);
4142 ig.BeginFinallyBlock ();
4144 EmitFinallyBody (ec);
4146 ig.EndExceptionBlock ();
4150 public class Lock : ExceptionStatement {
4152 public Statement Statement;
4153 TemporaryVariable temp;
4155 public Lock (Expression expr, Statement stmt, Location l)
4162 public override bool Resolve (BlockContext ec)
4164 expr = expr.Resolve (ec);
4168 if (!TypeManager.IsReferenceType (expr.Type)){
4169 Report.Error (185, loc,
4170 "`{0}' is not a reference type as required by the lock statement",
4171 TypeManager.CSharpName (expr.Type));
4175 ec.StartFlowBranching (this);
4176 bool ok = Statement.Resolve (ec);
4177 ec.EndFlowBranching ();
4179 ResolveReachability (ec);
4181 // Avoid creating libraries that reference the internal
4184 if (t == TypeManager.null_type)
4185 t = TypeManager.object_type;
4187 temp = new TemporaryVariable (t, loc);
4190 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4191 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4192 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4193 monitor_type, "Enter", loc, TypeManager.object_type);
4194 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4195 monitor_type, "Exit", loc, TypeManager.object_type);
4201 protected override void EmitPreTryBody (EmitContext ec)
4203 ILGenerator ig = ec.ig;
4205 temp.EmitAssign (ec, expr);
4207 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4210 protected override void EmitTryBody (EmitContext ec)
4212 Statement.Emit (ec);
4215 protected override void EmitFinallyBody (EmitContext ec)
4218 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4221 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4223 expr.MutateHoistedGenericType (storey);
4224 temp.MutateHoistedGenericType (storey);
4225 Statement.MutateHoistedGenericType (storey);
4228 protected override void CloneTo (CloneContext clonectx, Statement t)
4230 Lock target = (Lock) t;
4232 target.expr = expr.Clone (clonectx);
4233 target.Statement = Statement.Clone (clonectx);
4237 public class Unchecked : Statement {
4240 public Unchecked (Block b)
4246 public override bool Resolve (BlockContext ec)
4248 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4249 return Block.Resolve (ec);
4252 protected override void DoEmit (EmitContext ec)
4254 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4258 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4260 Block.MutateHoistedGenericType (storey);
4263 protected override void CloneTo (CloneContext clonectx, Statement t)
4265 Unchecked target = (Unchecked) t;
4267 target.Block = clonectx.LookupBlock (Block);
4271 public class Checked : Statement {
4274 public Checked (Block b)
4277 b.Unchecked = false;
4280 public override bool Resolve (BlockContext ec)
4282 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4283 return Block.Resolve (ec);
4286 protected override void DoEmit (EmitContext ec)
4288 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4292 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4294 Block.MutateHoistedGenericType (storey);
4297 protected override void CloneTo (CloneContext clonectx, Statement t)
4299 Checked target = (Checked) t;
4301 target.Block = clonectx.LookupBlock (Block);
4305 public class Unsafe : Statement {
4308 public Unsafe (Block b)
4311 Block.Unsafe = true;
4312 loc = b.StartLocation;
4315 public override bool Resolve (BlockContext ec)
4317 if (ec.CurrentIterator != null)
4318 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4320 using (ec.With (EmitContext.Options.UnsafeScope, true))
4321 return Block.Resolve (ec);
4324 protected override void DoEmit (EmitContext ec)
4326 using (ec.With (EmitContext.Options.UnsafeScope, true))
4330 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4332 Block.MutateHoistedGenericType (storey);
4335 protected override void CloneTo (CloneContext clonectx, Statement t)
4337 Unsafe target = (Unsafe) t;
4339 target.Block = clonectx.LookupBlock (Block);
4346 public class Fixed : Statement {
4348 ArrayList declarators;
4349 Statement statement;
4354 abstract class Emitter
4356 protected LocalInfo vi;
4357 protected Expression converted;
4359 protected Emitter (Expression expr, LocalInfo li)
4365 public abstract void Emit (EmitContext ec);
4366 public abstract void EmitExit (EmitContext ec);
4369 class ExpressionEmitter : Emitter {
4370 public ExpressionEmitter (Expression converted, LocalInfo li) :
4371 base (converted, li)
4375 public override void Emit (EmitContext ec) {
4377 // Store pointer in pinned location
4379 converted.Emit (ec);
4383 public override void EmitExit (EmitContext ec)
4385 ec.ig.Emit (OpCodes.Ldc_I4_0);
4386 ec.ig.Emit (OpCodes.Conv_U);
4391 class StringEmitter : Emitter
4393 LocalInfo pinned_string;
4395 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4398 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4399 pinned_string.Pinned = true;
4402 public StringEmitter Resolve (ResolveContext rc)
4404 pinned_string.Resolve (rc);
4406 if (TypeManager.int_get_offset_to_string_data == null) {
4407 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4408 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4414 public override void Emit (EmitContext ec)
4416 pinned_string.ResolveVariable (ec);
4418 converted.Emit (ec);
4419 pinned_string.EmitAssign (ec);
4421 // TODO: Should use Binary::Add
4422 pinned_string.Emit (ec);
4423 ec.ig.Emit (OpCodes.Conv_I);
4425 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4426 //pe.InstanceExpression = pinned_string;
4427 pe.Resolve (new ResolveContext (ec.MemberContext)).Emit (ec);
4429 ec.ig.Emit (OpCodes.Add);
4433 public override void EmitExit (EmitContext ec)
4435 ec.ig.Emit (OpCodes.Ldnull);
4436 pinned_string.EmitAssign (ec);
4440 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4443 declarators = decls;
4448 public Statement Statement {
4449 get { return statement; }
4452 public override bool Resolve (BlockContext ec)
4455 Expression.UnsafeError (loc);
4459 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4460 if (texpr == null) {
4461 if (type is VarExpr)
4462 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4467 expr_type = texpr.Type;
4469 data = new Emitter [declarators.Count];
4471 if (!expr_type.IsPointer){
4472 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4477 foreach (Pair p in declarators){
4478 LocalInfo vi = (LocalInfo) p.First;
4479 Expression e = (Expression) p.Second;
4481 vi.VariableInfo.SetAssigned (ec);
4482 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4485 // The rules for the possible declarators are pretty wise,
4486 // but the production on the grammar is more concise.
4488 // So we have to enforce these rules here.
4490 // We do not resolve before doing the case 1 test,
4491 // because the grammar is explicit in that the token &
4492 // is present, so we need to test for this particular case.
4496 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4500 using (ec.Set (EmitContext.Options.FixedInitializerScope)) {
4510 if (e.Type.IsArray){
4511 Type array_type = TypeManager.GetElementType (e.Type);
4514 // Provided that array_type is unmanaged,
4516 if (!TypeManager.VerifyUnManaged (array_type, loc))
4520 // and T* is implicitly convertible to the
4521 // pointer type given in the fixed statement.
4523 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4525 Expression converted = Convert.ImplicitConversionRequired (
4526 ec, array_ptr, vi.VariableType, loc);
4527 if (converted == null)
4531 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4533 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4534 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4535 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4536 new NullPointer (loc),
4539 converted = converted.Resolve (ec);
4541 data [i] = new ExpressionEmitter (converted, vi);
4550 if (e.Type == TypeManager.string_type){
4551 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4556 // Case 4: fixed buffer
4557 if (e is FixedBufferPtr) {
4558 data [i++] = new ExpressionEmitter (e, vi);
4563 // Case 1: & object.
4565 Unary u = e as Unary;
4566 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4567 IVariableReference vr = u.Expr as IVariableReference;
4568 if (vr == null || !vr.IsFixed) {
4569 data [i] = new ExpressionEmitter (e, vi);
4573 if (data [i++] == null)
4574 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4576 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4579 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4580 bool ok = statement.Resolve (ec);
4581 bool flow_unreachable = ec.EndFlowBranching ();
4582 has_ret = flow_unreachable;
4587 protected override void DoEmit (EmitContext ec)
4589 for (int i = 0; i < data.Length; i++) {
4593 statement.Emit (ec);
4599 // Clear the pinned variable
4601 for (int i = 0; i < data.Length; i++) {
4602 data [i].EmitExit (ec);
4606 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4608 // Fixed statement cannot be used inside anonymous methods or lambdas
4609 throw new NotSupportedException ();
4612 protected override void CloneTo (CloneContext clonectx, Statement t)
4614 Fixed target = (Fixed) t;
4616 target.type = type.Clone (clonectx);
4617 target.declarators = new ArrayList (declarators.Count);
4618 foreach (Pair p in declarators) {
4619 LocalInfo vi = (LocalInfo) p.First;
4620 Expression e = (Expression) p.Second;
4622 target.declarators.Add (
4623 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4626 target.statement = statement.Clone (clonectx);
4630 public class Catch : Statement {
4631 public readonly string Name;
4633 public Block VarBlock;
4635 Expression type_expr;
4638 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4643 VarBlock = var_block;
4647 public Type CatchType {
4653 public bool IsGeneral {
4655 return type_expr == null;
4659 protected override void DoEmit (EmitContext ec)
4661 ILGenerator ig = ec.ig;
4663 if (CatchType != null)
4664 ig.BeginCatchBlock (CatchType);
4666 ig.BeginCatchBlock (TypeManager.object_type);
4668 if (VarBlock != null)
4672 // TODO: Move to resolve
4673 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4674 lvr.Resolve (new ResolveContext (ec.MemberContext));
4677 // Only to make verifier happy
4678 if (TypeManager.IsGenericParameter (lvr.Type))
4679 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4683 if (lvr.IsHoisted) {
4684 LocalTemporary lt = new LocalTemporary (lvr.Type);
4688 // Variable is at the top of the stack
4689 source = EmptyExpression.Null;
4692 lvr.EmitAssign (ec, source, false, false);
4694 ig.Emit (OpCodes.Pop);
4699 public override bool Resolve (BlockContext ec)
4701 using (ec.With (EmitContext.Options.CatchScope, true)) {
4702 if (type_expr != null) {
4703 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4709 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4710 Error (155, "The type caught or thrown must be derived from System.Exception");
4716 if (!Block.Resolve (ec))
4719 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4720 // emit the "unused variable" warnings.
4721 if (VarBlock != null)
4722 return VarBlock.Resolve (ec);
4728 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4731 type = storey.MutateType (type);
4732 if (VarBlock != null)
4733 VarBlock.MutateHoistedGenericType (storey);
4734 Block.MutateHoistedGenericType (storey);
4737 protected override void CloneTo (CloneContext clonectx, Statement t)
4739 Catch target = (Catch) t;
4741 if (type_expr != null)
4742 target.type_expr = type_expr.Clone (clonectx);
4743 if (VarBlock != null)
4744 target.VarBlock = clonectx.LookupBlock (VarBlock);
4745 target.Block = clonectx.LookupBlock (Block);
4749 public class TryFinally : ExceptionStatement {
4753 public TryFinally (Statement stmt, Block fini, Location l)
4760 public override bool Resolve (BlockContext ec)
4764 ec.StartFlowBranching (this);
4766 if (!stmt.Resolve (ec))
4770 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4771 using (ec.With (EmitContext.Options.FinallyScope, true)) {
4772 if (!fini.Resolve (ec))
4776 ec.EndFlowBranching ();
4778 ResolveReachability (ec);
4783 protected override void EmitPreTryBody (EmitContext ec)
4787 protected override void EmitTryBody (EmitContext ec)
4792 protected override void EmitFinallyBody (EmitContext ec)
4797 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4799 stmt.MutateHoistedGenericType (storey);
4800 fini.MutateHoistedGenericType (storey);
4803 protected override void CloneTo (CloneContext clonectx, Statement t)
4805 TryFinally target = (TryFinally) t;
4807 target.stmt = (Statement) stmt.Clone (clonectx);
4809 target.fini = clonectx.LookupBlock (fini);
4813 public class TryCatch : Statement {
4815 public ArrayList Specific;
4816 public Catch General;
4817 bool inside_try_finally, code_follows;
4819 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4822 this.Specific = catch_clauses;
4823 this.General = null;
4824 this.inside_try_finally = inside_try_finally;
4826 for (int i = 0; i < catch_clauses.Count; ++i) {
4827 Catch c = (Catch) catch_clauses [i];
4829 if (i != catch_clauses.Count - 1)
4830 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4832 catch_clauses.RemoveAt (i);
4840 public override bool Resolve (BlockContext ec)
4844 ec.StartFlowBranching (this);
4846 if (!Block.Resolve (ec))
4849 Type[] prev_catches = new Type [Specific.Count];
4851 foreach (Catch c in Specific){
4852 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4854 if (c.Name != null) {
4855 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4857 throw new Exception ();
4859 vi.VariableInfo = null;
4862 if (!c.Resolve (ec)) {
4867 Type resolved_type = c.CatchType;
4868 for (int ii = 0; ii < last_index; ++ii) {
4869 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4870 Report.Error (160, c.loc,
4871 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4872 TypeManager.CSharpName (prev_catches [ii]));
4877 prev_catches [last_index++] = resolved_type;
4880 if (General != null) {
4881 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4882 foreach (Catch c in Specific){
4883 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4884 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'");
4889 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4891 if (!General.Resolve (ec))
4895 ec.EndFlowBranching ();
4897 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4898 // So, ensure there's some IL code after this statement
4899 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4900 ec.NeedReturnLabel ();
4905 public void SomeCodeFollows ()
4907 code_follows = true;
4910 protected override void DoEmit (EmitContext ec)
4912 ILGenerator ig = ec.ig;
4914 if (!inside_try_finally)
4915 ig.BeginExceptionBlock ();
4919 foreach (Catch c in Specific)
4922 if (General != null)
4925 if (!inside_try_finally)
4926 ig.EndExceptionBlock ();
4929 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4931 Block.MutateHoistedGenericType (storey);
4933 if (General != null)
4934 General.MutateHoistedGenericType (storey);
4935 if (Specific != null) {
4936 foreach (Catch c in Specific)
4937 c.MutateHoistedGenericType (storey);
4941 protected override void CloneTo (CloneContext clonectx, Statement t)
4943 TryCatch target = (TryCatch) t;
4945 target.Block = clonectx.LookupBlock (Block);
4946 if (General != null)
4947 target.General = (Catch) General.Clone (clonectx);
4948 if (Specific != null){
4949 target.Specific = new ArrayList ();
4950 foreach (Catch c in Specific)
4951 target.Specific.Add (c.Clone (clonectx));
4956 // FIXME: Why is it almost exact copy of Using ??
4957 public class UsingTemporary : ExceptionStatement {
4958 TemporaryVariable local_copy;
4959 public Statement Statement;
4963 public UsingTemporary (Expression expr, Statement stmt, Location l)
4970 public override bool Resolve (BlockContext ec)
4972 expr = expr.Resolve (ec);
4976 expr_type = expr.Type;
4978 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4979 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4980 Using.Error_IsNotConvertibleToIDisposable (expr);
4985 local_copy = new TemporaryVariable (expr_type, loc);
4986 local_copy.Resolve (ec);
4988 ec.StartFlowBranching (this);
4990 bool ok = Statement.Resolve (ec);
4992 ec.EndFlowBranching ();
4994 ResolveReachability (ec);
4996 if (TypeManager.void_dispose_void == null) {
4997 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4998 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5004 protected override void EmitPreTryBody (EmitContext ec)
5006 local_copy.EmitAssign (ec, expr);
5009 protected override void EmitTryBody (EmitContext ec)
5011 Statement.Emit (ec);
5014 protected override void EmitFinallyBody (EmitContext ec)
5016 ILGenerator ig = ec.ig;
5017 if (!TypeManager.IsStruct (expr_type)) {
5018 Label skip = ig.DefineLabel ();
5019 local_copy.Emit (ec);
5020 ig.Emit (OpCodes.Brfalse, skip);
5021 local_copy.Emit (ec);
5022 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5023 ig.MarkLabel (skip);
5027 Expression ml = Expression.MemberLookup (
5028 ec.CurrentType, TypeManager.idisposable_type, expr_type,
5029 "Dispose", Location.Null);
5031 if (!(ml is MethodGroupExpr)) {
5032 local_copy.Emit (ec);
5033 ig.Emit (OpCodes.Box, expr_type);
5034 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5038 MethodInfo mi = null;
5040 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5041 if (TypeManager.GetParameterData (mk).Count == 0) {
5048 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5052 local_copy.AddressOf (ec, AddressOp.Load);
5053 ig.Emit (OpCodes.Call, mi);
5056 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5058 expr_type = storey.MutateType (expr_type);
5059 local_copy.MutateHoistedGenericType (storey);
5060 Statement.MutateHoistedGenericType (storey);
5063 protected override void CloneTo (CloneContext clonectx, Statement t)
5065 UsingTemporary target = (UsingTemporary) t;
5067 target.expr = expr.Clone (clonectx);
5068 target.Statement = Statement.Clone (clonectx);
5072 public class Using : ExceptionStatement {
5074 public Statement EmbeddedStatement {
5075 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5081 ExpressionStatement assign;
5083 public Using (Expression var, Expression init, Statement stmt, Location l)
5091 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5093 Report.SymbolRelatedToPreviousError (expr.Type);
5094 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5095 expr.GetSignatureForError ());
5098 protected override void EmitPreTryBody (EmitContext ec)
5100 assign.EmitStatement (ec);
5103 protected override void EmitTryBody (EmitContext ec)
5108 protected override void EmitFinallyBody (EmitContext ec)
5110 ILGenerator ig = ec.ig;
5111 Label skip = ig.DefineLabel ();
5113 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5114 if (emit_null_check) {
5116 ig.Emit (OpCodes.Brfalse, skip);
5119 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5121 if (emit_null_check)
5122 ig.MarkLabel (skip);
5125 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5127 assign.MutateHoistedGenericType (storey);
5128 var.MutateHoistedGenericType (storey);
5129 stmt.MutateHoistedGenericType (storey);
5132 public override bool Resolve (BlockContext ec)
5134 if (!ResolveVariable (ec))
5137 ec.StartFlowBranching (this);
5139 bool ok = stmt.Resolve (ec);
5141 ec.EndFlowBranching ();
5143 ResolveReachability (ec);
5145 if (TypeManager.void_dispose_void == null) {
5146 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5147 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5153 bool ResolveVariable (BlockContext ec)
5155 assign = new SimpleAssign (var, init, loc);
5156 assign = assign.ResolveStatement (ec);
5160 if (assign.Type == TypeManager.idisposable_type ||
5161 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5165 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5167 Error_IsNotConvertibleToIDisposable (var);
5171 throw new NotImplementedException ("covariance?");
5174 protected override void CloneTo (CloneContext clonectx, Statement t)
5176 Using target = (Using) t;
5178 target.var = var.Clone (clonectx);
5179 target.init = init.Clone (clonectx);
5180 target.stmt = stmt.Clone (clonectx);
5185 /// Implementation of the foreach C# statement
5187 public class Foreach : Statement {
5189 sealed class ArrayForeach : Statement
5191 class ArrayCounter : TemporaryVariable
5193 StatementExpression increment;
5195 public ArrayCounter (Location loc)
5196 : base (TypeManager.int32_type, loc)
5200 public void ResolveIncrement (BlockContext ec)
5202 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5203 increment.Resolve (ec);
5206 public void EmitIncrement (EmitContext ec)
5208 increment.Emit (ec);
5212 readonly Foreach for_each;
5213 readonly Statement statement;
5216 TemporaryVariable[] lengths;
5217 Expression [] length_exprs;
5218 ArrayCounter[] counter;
5220 TemporaryVariable copy;
5223 public ArrayForeach (Foreach @foreach, int rank)
5225 for_each = @foreach;
5226 statement = for_each.statement;
5229 counter = new ArrayCounter [rank];
5230 length_exprs = new Expression [rank];
5233 // Only use temporary length variables when dealing with
5234 // multi-dimensional arrays
5237 lengths = new TemporaryVariable [rank];
5240 protected override void CloneTo (CloneContext clonectx, Statement target)
5242 throw new NotImplementedException ();
5245 public override bool Resolve (BlockContext ec)
5247 copy = new TemporaryVariable (for_each.expr.Type, loc);
5250 int rank = length_exprs.Length;
5251 Arguments list = new Arguments (rank);
5252 for (int i = 0; i < rank; i++) {
5253 counter [i] = new ArrayCounter (loc);
5254 counter [i].ResolveIncrement (ec);
5257 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5259 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5260 lengths [i].Resolve (ec);
5262 Arguments args = new Arguments (1);
5263 args.Add (new Argument (new IntConstant (i, loc)));
5264 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5267 list.Add (new Argument (counter [i]));
5270 access = new ElementAccess (copy, list).Resolve (ec);
5274 Expression var_type = for_each.type;
5275 VarExpr ve = var_type as VarExpr;
5277 // Infer implicitly typed local variable from foreach array type
5278 var_type = new TypeExpression (access.Type, ve.Location);
5281 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5282 if (var_type == null)
5285 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5291 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5292 ec.CurrentBranching.CreateSibling ();
5294 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5295 if (for_each.variable == null)
5298 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5299 if (!statement.Resolve (ec))
5301 ec.EndFlowBranching ();
5303 // There's no direct control flow from the end of the embedded statement to the end of the loop
5304 ec.CurrentBranching.CurrentUsageVector.Goto ();
5306 ec.EndFlowBranching ();
5311 protected override void DoEmit (EmitContext ec)
5313 ILGenerator ig = ec.ig;
5315 copy.EmitAssign (ec, for_each.expr);
5317 int rank = length_exprs.Length;
5318 Label[] test = new Label [rank];
5319 Label[] loop = new Label [rank];
5321 for (int i = 0; i < rank; i++) {
5322 test [i] = ig.DefineLabel ();
5323 loop [i] = ig.DefineLabel ();
5325 if (lengths != null)
5326 lengths [i].EmitAssign (ec, length_exprs [i]);
5329 IntConstant zero = new IntConstant (0, loc);
5330 for (int i = 0; i < rank; i++) {
5331 counter [i].EmitAssign (ec, zero);
5333 ig.Emit (OpCodes.Br, test [i]);
5334 ig.MarkLabel (loop [i]);
5337 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5339 statement.Emit (ec);
5341 ig.MarkLabel (ec.LoopBegin);
5343 for (int i = rank - 1; i >= 0; i--){
5344 counter [i].EmitIncrement (ec);
5346 ig.MarkLabel (test [i]);
5347 counter [i].Emit (ec);
5349 if (lengths != null)
5350 lengths [i].Emit (ec);
5352 length_exprs [i].Emit (ec);
5354 ig.Emit (OpCodes.Blt, loop [i]);
5357 ig.MarkLabel (ec.LoopEnd);
5360 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5362 for_each.expr.MutateHoistedGenericType (storey);
5364 copy.MutateHoistedGenericType (storey);
5365 conv.MutateHoistedGenericType (storey);
5366 statement.MutateHoistedGenericType (storey);
5368 for (int i = 0; i < counter.Length; i++) {
5369 counter [i].MutateHoistedGenericType (storey);
5370 if (lengths != null)
5371 lengths [i].MutateHoistedGenericType (storey);
5376 sealed class CollectionForeach : Statement
5378 class CollectionForeachStatement : Statement
5381 Expression variable, current, conv;
5382 Statement statement;
5385 public CollectionForeachStatement (Type type, Expression variable,
5386 Expression current, Statement statement,
5390 this.variable = variable;
5391 this.current = current;
5392 this.statement = statement;
5396 protected override void CloneTo (CloneContext clonectx, Statement target)
5398 throw new NotImplementedException ();
5401 public override bool Resolve (BlockContext ec)
5403 current = current.Resolve (ec);
5404 if (current == null)
5407 conv = Convert.ExplicitConversion (ec, current, type, loc);
5411 assign = new SimpleAssign (variable, conv, loc);
5412 if (assign.Resolve (ec) == null)
5415 if (!statement.Resolve (ec))
5421 protected override void DoEmit (EmitContext ec)
5423 assign.EmitStatement (ec);
5424 statement.Emit (ec);
5427 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5429 assign.MutateHoistedGenericType (storey);
5430 statement.MutateHoistedGenericType (storey);
5434 Expression variable, expr;
5435 Statement statement;
5437 TemporaryVariable enumerator;
5442 MethodGroupExpr get_enumerator;
5443 PropertyExpr get_current;
5444 MethodInfo move_next;
5445 Expression var_type;
5446 Type enumerator_type;
5447 bool enumerator_found;
5449 public CollectionForeach (Expression var_type, Expression var,
5450 Expression expr, Statement stmt, Location l)
5452 this.var_type = var_type;
5453 this.variable = var;
5459 protected override void CloneTo (CloneContext clonectx, Statement target)
5461 throw new NotImplementedException ();
5464 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5466 Type return_type = mi.ReturnType;
5469 // Ok, we can access it, now make sure that we can do something
5470 // with this `GetEnumerator'
5473 if (return_type == TypeManager.ienumerator_type ||
5474 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5476 // If it is not an interface, lets try to find the methods ourselves.
5477 // For example, if we have:
5478 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5479 // We can avoid the iface call. This is a runtime perf boost.
5480 // even bigger if we have a ValueType, because we avoid the cost
5483 // We have to make sure that both methods exist for us to take
5484 // this path. If one of the methods does not exist, we will just
5485 // use the interface. Sadly, this complex if statement is the only
5486 // way I could do this without a goto
5489 if (TypeManager.bool_movenext_void == null) {
5490 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5491 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5494 if (TypeManager.ienumerator_getcurrent == null) {
5495 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5496 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5500 // Prefer a generic enumerator over a non-generic one.
5502 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5503 enumerator_type = return_type;
5504 if (!FetchGetCurrent (ec, return_type))
5505 get_current = new PropertyExpr (
5506 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5507 if (!FetchMoveNext (return_type))
5508 move_next = TypeManager.bool_movenext_void;
5512 if (return_type.IsInterface ||
5513 !FetchMoveNext (return_type) ||
5514 !FetchGetCurrent (ec, return_type)) {
5515 enumerator_type = return_type;
5516 move_next = TypeManager.bool_movenext_void;
5517 get_current = new PropertyExpr (
5518 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5523 // Ok, so they dont return an IEnumerable, we will have to
5524 // find if they support the GetEnumerator pattern.
5527 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5528 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",
5529 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5534 enumerator_type = return_type;
5540 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5542 bool FetchMoveNext (Type t)
5544 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5546 BindingFlags.Public | BindingFlags.Instance,
5549 if (move_next_list == null)
5552 foreach (MemberInfo m in move_next_list){
5553 MethodInfo mi = (MethodInfo) m;
5555 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5556 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5566 // Retrieves a `public T get_Current ()' method from the Type `t'
5568 bool FetchGetCurrent (EmitContext ec, Type t)
5570 PropertyExpr pe = Expression.MemberLookup (
5571 ec.CurrentType, t, "Current", MemberTypes.Property,
5572 Expression.AllBindingFlags, loc) as PropertyExpr;
5580 void Error_Enumerator ()
5582 if (enumerator_found) {
5586 Report.Error (1579, loc,
5587 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5588 TypeManager.CSharpName (expr.Type));
5591 bool IsOverride (MethodInfo m)
5593 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5595 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5597 if (m is MethodBuilder)
5600 MethodInfo base_method = m.GetBaseDefinition ();
5601 return base_method != m;
5604 bool TryType (ResolveContext ec, Type t)
5606 MethodGroupExpr mg = Expression.MemberLookup (
5607 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5608 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5612 MethodInfo result = null;
5613 MethodInfo tmp_move_next = null;
5614 PropertyExpr tmp_get_cur = null;
5615 Type tmp_enumerator_type = enumerator_type;
5616 foreach (MethodInfo mi in mg.Methods) {
5617 if (TypeManager.GetParameterData (mi).Count != 0)
5620 // Check whether GetEnumerator is public
5621 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5624 if (IsOverride (mi))
5627 enumerator_found = true;
5629 if (!GetEnumeratorFilter (ec, mi))
5632 if (result != null) {
5633 if (TypeManager.IsGenericType (result.ReturnType)) {
5634 if (!TypeManager.IsGenericType (mi.ReturnType))
5637 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5638 Report.SymbolRelatedToPreviousError (t);
5639 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5640 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5641 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5645 // Always prefer generics enumerators
5646 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5647 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5648 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5651 Report.SymbolRelatedToPreviousError (result);
5652 Report.SymbolRelatedToPreviousError (mi);
5653 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5654 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5659 tmp_move_next = move_next;
5660 tmp_get_cur = get_current;
5661 tmp_enumerator_type = enumerator_type;
5662 if (mi.DeclaringType == t)
5666 if (result != null) {
5667 move_next = tmp_move_next;
5668 get_current = tmp_get_cur;
5669 enumerator_type = tmp_enumerator_type;
5670 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5671 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5673 if (t != expr.Type) {
5674 expr = Convert.ExplicitConversion (
5677 throw new InternalErrorException ();
5680 get_enumerator.InstanceExpression = expr;
5681 get_enumerator.IsBase = t != expr.Type;
5689 bool ProbeCollectionType (ResolveContext ec, Type t)
5691 int errors = Report.Errors;
5692 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5693 if (TryType (ec, tt))
5698 if (Report.Errors > errors)
5702 // Now try to find the method in the interfaces
5704 Type [] ifaces = TypeManager.GetInterfaces (t);
5705 foreach (Type i in ifaces){
5706 if (TryType (ec, i))
5713 public override bool Resolve (BlockContext ec)
5715 enumerator_type = TypeManager.ienumerator_type;
5717 if (!ProbeCollectionType (ec, expr.Type)) {
5718 Error_Enumerator ();
5722 VarExpr ve = var_type as VarExpr;
5724 // Infer implicitly typed local variable from foreach enumerable type
5725 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5728 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5729 if (var_type == null)
5732 enumerator = new TemporaryVariable (enumerator_type, loc);
5733 enumerator.Resolve (ec);
5735 init = new Invocation (get_enumerator, null);
5736 init = init.Resolve (ec);
5740 Expression move_next_expr;
5742 MemberInfo[] mi = new MemberInfo[] { move_next };
5743 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5744 mg.InstanceExpression = enumerator;
5746 move_next_expr = new Invocation (mg, null);
5749 get_current.InstanceExpression = enumerator;
5751 Statement block = new CollectionForeachStatement (
5752 var_type.Type, variable, get_current, statement, loc);
5754 loop = new While (move_next_expr, block, loc);
5757 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5758 if (implements_idisposable || !enumerator_type.IsSealed) {
5759 wrapper = new DisposableWrapper (this, implements_idisposable);
5761 wrapper = new NonDisposableWrapper (this);
5764 return wrapper.Resolve (ec);
5767 protected override void DoEmit (EmitContext ec)
5772 class NonDisposableWrapper : Statement {
5773 CollectionForeach parent;
5775 internal NonDisposableWrapper (CollectionForeach parent)
5777 this.parent = parent;
5780 protected override void CloneTo (CloneContext clonectx, Statement target)
5782 throw new NotSupportedException ();
5785 public override bool Resolve (BlockContext ec)
5787 return parent.ResolveLoop (ec);
5790 protected override void DoEmit (EmitContext ec)
5792 parent.EmitLoopInit (ec);
5793 parent.EmitLoopBody (ec);
5796 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5798 throw new NotSupportedException ();
5802 sealed class DisposableWrapper : ExceptionStatement
5804 CollectionForeach parent;
5805 bool implements_idisposable;
5807 internal DisposableWrapper (CollectionForeach parent, bool implements)
5809 this.parent = parent;
5810 this.implements_idisposable = implements;
5813 protected override void CloneTo (CloneContext clonectx, Statement target)
5815 throw new NotSupportedException ();
5818 public override bool Resolve (BlockContext ec)
5822 ec.StartFlowBranching (this);
5824 if (!parent.ResolveLoop (ec))
5827 ec.EndFlowBranching ();
5829 ResolveReachability (ec);
5831 if (TypeManager.void_dispose_void == null) {
5832 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5833 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5838 protected override void EmitPreTryBody (EmitContext ec)
5840 parent.EmitLoopInit (ec);
5843 protected override void EmitTryBody (EmitContext ec)
5845 parent.EmitLoopBody (ec);
5848 protected override void EmitFinallyBody (EmitContext ec)
5850 Expression instance = parent.enumerator;
5851 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5852 ILGenerator ig = ec.ig;
5854 parent.enumerator.Emit (ec);
5856 Label call_dispose = ig.DefineLabel ();
5858 if (!implements_idisposable) {
5859 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5860 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5866 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5868 // using 'endfinally' to empty the evaluation stack
5869 ig.Emit (OpCodes.Endfinally);
5870 ig.MarkLabel (call_dispose);
5873 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5876 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5878 throw new NotSupportedException ();
5882 bool ResolveLoop (BlockContext ec)
5884 return loop.Resolve (ec);
5887 void EmitLoopInit (EmitContext ec)
5889 enumerator.EmitAssign (ec, init);
5892 void EmitLoopBody (EmitContext ec)
5897 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5899 enumerator_type = storey.MutateType (enumerator_type);
5900 init.MutateHoistedGenericType (storey);
5901 loop.MutateHoistedGenericType (storey);
5906 Expression variable;
5908 Statement statement;
5910 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5911 Statement stmt, Location l)
5914 this.variable = var;
5920 public Statement Statement {
5921 get { return statement; }
5924 public override bool Resolve (BlockContext ec)
5926 expr = expr.Resolve (ec);
5931 Report.Error (186, loc, "Use of null is not valid in this context");
5935 if (expr.Type == TypeManager.string_type) {
5936 statement = new ArrayForeach (this, 1);
5937 } else if (expr.Type.IsArray) {
5938 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5940 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5941 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5942 expr.ExprClassName);
5946 statement = new CollectionForeach (type, variable, expr, statement, loc);
5949 return statement.Resolve (ec);
5952 protected override void DoEmit (EmitContext ec)
5954 ILGenerator ig = ec.ig;
5956 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5957 ec.LoopBegin = ig.DefineLabel ();
5958 ec.LoopEnd = ig.DefineLabel ();
5960 statement.Emit (ec);
5962 ec.LoopBegin = old_begin;
5963 ec.LoopEnd = old_end;
5966 protected override void CloneTo (CloneContext clonectx, Statement t)
5968 Foreach target = (Foreach) t;
5970 target.type = type.Clone (clonectx);
5971 target.variable = variable.Clone (clonectx);
5972 target.expr = expr.Clone (clonectx);
5973 target.statement = statement.Clone (clonectx);
5976 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5978 statement.MutateHoistedGenericType (storey);