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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext ec)
191 public override bool ResolveUnreachable (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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.ResolveContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext ec)
1358 if (VariableInfo == null)
1359 throw new Exception ();
1361 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1364 public bool Resolve (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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 (EmitContext 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.ResolveContext 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 (EmitContext ec)
2633 throw new NotSupportedException ();
2636 public override Expression DoResolve (EmitContext 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;
2675 // The parameters for the block.
2677 public ParametersCompiled Parameters {
2678 get { return parameters; }
2681 public GenericMethod GenericMethod {
2682 get { return generic; }
2685 public ToplevelBlock Container {
2686 get { return Parent == null ? null : Parent.Toplevel; }
2689 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2690 this (parent, (Flags) 0, parameters, start)
2694 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2695 this (parent, parameters, start)
2697 this.generic = generic;
2700 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2701 this (null, (Flags) 0, parameters, start)
2705 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2706 this (null, flags, parameters, start)
2710 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2711 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2712 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2713 base (null, flags, start, Location.Null)
2715 this.Toplevel = this;
2717 this.parameters = parameters;
2718 this.Parent = parent;
2720 parent.AddAnonymousChild (this);
2722 if (!this.parameters.IsEmpty)
2723 ProcessParameters ();
2726 public ToplevelBlock (Location loc)
2727 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2731 protected override void CloneTo (CloneContext clonectx, Statement t)
2733 ToplevelBlock target = (ToplevelBlock) t;
2734 base.CloneTo (clonectx, t);
2736 if (parameters.Count != 0)
2737 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2738 for (int i = 0; i < parameters.Count; ++i)
2739 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2742 public bool CheckError158 (string name, Location loc)
2744 if (AnonymousChildren != null) {
2745 foreach (ToplevelBlock child in AnonymousChildren) {
2746 if (!child.CheckError158 (name, loc))
2751 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2752 if (!c.DoCheckError158 (name, loc))
2759 void ProcessParameters ()
2761 int n = parameters.Count;
2762 parameter_info = new ToplevelParameterInfo [n];
2763 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2764 for (int i = 0; i < n; ++i) {
2765 parameter_info [i] = new ToplevelParameterInfo (this, i);
2767 Parameter p = parameters [i];
2771 string name = p.Name;
2772 if (CheckParentConflictName (top_parent, name, loc))
2773 AddKnownVariable (name, parameter_info [i]);
2776 // mark this block as "used" so that we create local declarations in a sub-block
2777 // FIXME: This appears to uncover a lot of bugs
2781 bool DoCheckError158 (string name, Location loc)
2783 LabeledStatement s = LookupLabel (name);
2785 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2786 Error_158 (name, loc);
2793 public override Expression CreateExpressionTree (EmitContext ec)
2795 if (statements.Count == 1) {
2796 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2797 if (scope_initializers != null)
2798 expr = new BlockScopeExpression (expr, this);
2803 return base.CreateExpressionTree (ec);
2807 // Reformats this block to be top-level iterator block
2809 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2813 // Creates block with original statements
2814 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2816 source.statements = new ArrayList (1);
2817 source.AddStatement (new Return (iterator, iterator.Location));
2818 source.IsIterator = false;
2820 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2821 source.am_storey = iterator_storey;
2822 return iterator_storey;
2826 // Returns a parameter reference expression for the given name,
2827 // or null if there is no such parameter
2829 public Expression GetParameterReference (string name, Location loc)
2831 for (ToplevelBlock t = this; t != null; t = t.Container) {
2832 Expression expr = t.GetParameterReferenceExpression (name, loc);
2840 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2842 int idx = parameters.GetParameterIndexByName (name);
2844 null : new ParameterReference (parameter_info [idx], loc);
2848 // Returns the "this" instance variable of this block.
2849 // See AddThisVariable() for more information.
2851 public LocalInfo ThisVariable {
2852 get { return this_variable; }
2856 // This is used by non-static `struct' constructors which do not have an
2857 // initializer - in this case, the constructor must initialize all of the
2858 // struct's fields. To do this, we add a "this" variable and use the flow
2859 // analysis code to ensure that it's been fully initialized before control
2860 // leaves the constructor.
2862 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2864 if (this_variable == null) {
2865 this_variable = new LocalInfo (ds, this, l);
2866 this_variable.Used = true;
2867 this_variable.IsThis = true;
2869 Variables.Add ("this", this_variable);
2872 return this_variable;
2875 public bool IsIterator {
2876 get { return (flags & Flags.IsIterator) != 0; }
2877 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2880 public bool IsThisAssigned (EmitContext ec)
2882 return this_variable == null || this_variable.IsThisAssigned (ec, this);
2885 public bool Resolve (FlowBranching parent, EmitContext rc, ParametersCompiled ip, IMethodData md)
2893 if (!ResolveMeta (rc, ip))
2896 using (rc.With (EmitContext.Options.DoFlowAnalysis, true)) {
2897 FlowBranchingToplevel top_level = rc.StartFlowBranching (this, parent);
2902 unreachable = top_level.End ();
2904 } catch (Exception) {
2906 if (rc.CurrentBlock != null) {
2907 Report.Error (584, rc.CurrentBlock.StartLocation, "Internal compiler error: Phase Resolve");
2909 Report.Error (587, "Internal compiler error: Phase Resolve");
2915 if (rc.ReturnType != TypeManager.void_type && !unreachable) {
2916 if (rc.CurrentAnonymousMethod == null) {
2917 Report.Error (161, md.Location, "`{0}': not all code paths return a value", md.GetSignatureForError ());
2919 } else if (!rc.CurrentAnonymousMethod.IsIterator) {
2920 Report.Error (1643, rc.CurrentAnonymousMethod.Location, "Not all code paths return a value in anonymous method of type `{0}'",
2921 rc.CurrentAnonymousMethod.GetSignatureForError ());
2929 bool ResolveMeta (EmitContext ec, ParametersCompiled ip)
2931 int errors = Report.Errors;
2932 int orig_count = parameters.Count;
2937 // Assert: orig_count != parameter.Count => orig_count == 0
2938 if (orig_count != 0 && orig_count != parameters.Count)
2939 throw new InternalErrorException ("parameter information mismatch");
2941 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2943 for (int i = 0; i < orig_count; ++i) {
2944 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2946 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2949 VariableInfo vi = new VariableInfo (ip, i, offset);
2950 parameter_info [i].VariableInfo = vi;
2951 offset += vi.Length;
2954 ResolveMeta (ec, offset);
2956 return Report.Errors == errors;
2960 // Check whether all `out' parameters have been assigned.
2962 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2964 if (vector.IsUnreachable)
2967 int n = parameter_info == null ? 0 : parameter_info.Length;
2969 for (int i = 0; i < n; i++) {
2970 VariableInfo var = parameter_info [i].VariableInfo;
2975 if (vector.IsAssigned (var, false))
2978 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2983 public override void Emit (EmitContext ec)
2985 if (Report.Errors > 0)
2993 if (ec.HasReturnLabel)
2994 ec.ReturnLabel = ec.ig.DefineLabel ();
2998 ec.Mark (EndLocation);
3000 if (ec.HasReturnLabel)
3001 ec.ig.MarkLabel (ec.ReturnLabel);
3003 if (ec.return_value != null) {
3004 ec.ig.Emit (OpCodes.Ldloc, ec.return_value);
3005 ec.ig.Emit (OpCodes.Ret);
3008 // If `HasReturnLabel' is set, then we already emitted a
3009 // jump to the end of the method, so we must emit a `ret'
3012 // Unfortunately, System.Reflection.Emit automatically emits
3013 // a leave to the end of a finally block. This is a problem
3014 // if no code is following the try/finally block since we may
3015 // jump to a point after the end of the method.
3016 // As a workaround, we're always creating a return label in
3020 if (ec.HasReturnLabel || !unreachable) {
3021 if (ec.ReturnType != TypeManager.void_type)
3022 ec.ig.Emit (OpCodes.Ldloc, ec.TemporaryReturn ());
3023 ec.ig.Emit (OpCodes.Ret);
3028 } catch (Exception e){
3029 Console.WriteLine ("Exception caught by the compiler while emitting:");
3030 Console.WriteLine (" Block that caused the problem begin at: " + block.loc);
3032 Console.WriteLine (e.GetType ().FullName + ": " + e.Message);
3038 public override void EmitMeta (EmitContext ec)
3040 parameters.ResolveVariable ();
3042 // Avoid declaring an IL variable for this_variable since it is not accessed
3043 // from the generated IL
3044 if (this_variable != null)
3045 Variables.Remove ("this");
3049 protected override void EmitSymbolInfo (EmitContext ec)
3051 AnonymousExpression ae = ec.CurrentAnonymousMethod;
3052 if ((ae != null) && (ae.Storey != null))
3053 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
3055 base.EmitSymbolInfo (ec);
3059 public class SwitchLabel {
3066 Label il_label_code;
3067 bool il_label_code_set;
3069 public static readonly object NullStringCase = new object ();
3072 // if expr == null, then it is the default case.
3074 public SwitchLabel (Expression expr, Location l)
3080 public Expression Label {
3086 public Location Location {
3090 public object Converted {
3096 public Label GetILLabel (EmitContext ec)
3099 il_label = ec.ig.DefineLabel ();
3100 il_label_set = true;
3105 public Label GetILLabelCode (EmitContext ec)
3107 if (!il_label_code_set){
3108 il_label_code = ec.ig.DefineLabel ();
3109 il_label_code_set = true;
3111 return il_label_code;
3115 // Resolves the expression, reduces it to a literal if possible
3116 // and then converts it to the requested type.
3118 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
3120 Expression e = label.Resolve (ec);
3125 Constant c = e as Constant;
3127 Report.Error (150, loc, "A constant value is expected");
3131 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3132 converted = NullStringCase;
3136 if (allow_nullable && c.GetValue () == null) {
3137 converted = NullStringCase;
3141 c = c.ImplicitConversionRequired (ec, required_type, loc);
3145 converted = c.GetValue ();
3149 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3152 if (converted == null)
3154 else if (converted == NullStringCase)
3157 label = converted.ToString ();
3159 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3160 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3163 public SwitchLabel Clone (CloneContext clonectx)
3165 return new SwitchLabel (label.Clone (clonectx), loc);
3169 public class SwitchSection {
3170 // An array of SwitchLabels.
3171 public readonly ArrayList Labels;
3172 public readonly Block Block;
3174 public SwitchSection (ArrayList labels, Block block)
3180 public SwitchSection Clone (CloneContext clonectx)
3182 ArrayList cloned_labels = new ArrayList ();
3184 foreach (SwitchLabel sl in cloned_labels)
3185 cloned_labels.Add (sl.Clone (clonectx));
3187 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3191 public class Switch : Statement {
3192 public ArrayList Sections;
3193 public Expression Expr;
3196 /// Maps constants whose type type SwitchType to their SwitchLabels.
3198 public IDictionary Elements;
3201 /// The governing switch type
3203 public Type SwitchType;
3208 Label default_target;
3210 Expression new_expr;
3213 SwitchSection constant_section;
3214 SwitchSection default_section;
3216 ExpressionStatement string_dictionary;
3217 FieldExpr switch_cache_field;
3218 static int unique_counter;
3221 // Nullable Types support
3223 Nullable.Unwrap unwrap;
3225 protected bool HaveUnwrap {
3226 get { return unwrap != null; }
3230 // The types allowed to be implicitly cast from
3231 // on the governing type
3233 static Type [] allowed_types;
3235 public Switch (Expression e, ArrayList sects, Location l)
3242 public bool GotDefault {
3244 return default_section != null;
3248 public Label DefaultTarget {
3250 return default_target;
3255 // Determines the governing type for a switch. The returned
3256 // expression might be the expression from the switch, or an
3257 // expression that includes any potential conversions to the
3258 // integral types or to string.
3260 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3264 if (t == TypeManager.byte_type ||
3265 t == TypeManager.sbyte_type ||
3266 t == TypeManager.ushort_type ||
3267 t == TypeManager.short_type ||
3268 t == TypeManager.uint32_type ||
3269 t == TypeManager.int32_type ||
3270 t == TypeManager.uint64_type ||
3271 t == TypeManager.int64_type ||
3272 t == TypeManager.char_type ||
3273 t == TypeManager.string_type ||
3274 t == TypeManager.bool_type ||
3275 TypeManager.IsEnumType (t))
3278 if (allowed_types == null){
3279 allowed_types = new Type [] {
3280 TypeManager.sbyte_type,
3281 TypeManager.byte_type,
3282 TypeManager.short_type,
3283 TypeManager.ushort_type,
3284 TypeManager.int32_type,
3285 TypeManager.uint32_type,
3286 TypeManager.int64_type,
3287 TypeManager.uint64_type,
3288 TypeManager.char_type,
3289 TypeManager.string_type
3294 // Try to find a *user* defined implicit conversion.
3296 // If there is no implicit conversion, or if there are multiple
3297 // conversions, we have to report an error
3299 Expression converted = null;
3300 foreach (Type tt in allowed_types){
3303 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3308 // Ignore over-worked ImplicitUserConversions that do
3309 // an implicit conversion in addition to the user conversion.
3311 if (!(e is UserCast))
3314 if (converted != null){
3315 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3325 // Performs the basic sanity checks on the switch statement
3326 // (looks for duplicate keys and non-constant expressions).
3328 // It also returns a hashtable with the keys that we will later
3329 // use to compute the switch tables
3331 bool CheckSwitch (EmitContext ec)
3334 Elements = Sections.Count > 10 ?
3335 (IDictionary)new Hashtable () :
3336 (IDictionary)new ListDictionary ();
3338 foreach (SwitchSection ss in Sections){
3339 foreach (SwitchLabel sl in ss.Labels){
3340 if (sl.Label == null){
3341 if (default_section != null){
3342 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3345 default_section = ss;
3349 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3354 object key = sl.Converted;
3355 if (key == SwitchLabel.NullStringCase)
3356 has_null_case = true;
3359 Elements.Add (key, sl);
3360 } catch (ArgumentException) {
3361 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3369 void EmitObjectInteger (ILGenerator ig, object k)
3372 IntConstant.EmitInt (ig, (int) k);
3373 else if (k is Constant) {
3374 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3377 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3380 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3382 IntConstant.EmitInt (ig, (int) (long) k);
3383 ig.Emit (OpCodes.Conv_I8);
3386 LongConstant.EmitLong (ig, (long) k);
3388 else if (k is ulong)
3390 ulong ul = (ulong) k;
3393 IntConstant.EmitInt (ig, unchecked ((int) ul));
3394 ig.Emit (OpCodes.Conv_U8);
3398 LongConstant.EmitLong (ig, unchecked ((long) ul));
3402 IntConstant.EmitInt (ig, (int) ((char) k));
3403 else if (k is sbyte)
3404 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3406 IntConstant.EmitInt (ig, (int) ((byte) k));
3407 else if (k is short)
3408 IntConstant.EmitInt (ig, (int) ((short) k));
3409 else if (k is ushort)
3410 IntConstant.EmitInt (ig, (int) ((ushort) k));
3412 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3414 throw new Exception ("Unhandled case");
3417 // structure used to hold blocks of keys while calculating table switch
3418 class KeyBlock : IComparable
3420 public KeyBlock (long _first)
3422 first = last = _first;
3426 public ArrayList element_keys = null;
3427 // how many items are in the bucket
3428 public int Size = 1;
3431 get { return (int) (last - first + 1); }
3433 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3435 return kb_last.last - kb_first.first + 1;
3437 public int CompareTo (object obj)
3439 KeyBlock kb = (KeyBlock) obj;
3440 int nLength = Length;
3441 int nLengthOther = kb.Length;
3442 if (nLengthOther == nLength)
3443 return (int) (kb.first - first);
3444 return nLength - nLengthOther;
3449 /// This method emits code for a lookup-based switch statement (non-string)
3450 /// Basically it groups the cases into blocks that are at least half full,
3451 /// and then spits out individual lookup opcodes for each block.
3452 /// It emits the longest blocks first, and short blocks are just
3453 /// handled with direct compares.
3455 /// <param name="ec"></param>
3456 /// <param name="val"></param>
3457 /// <returns></returns>
3458 void TableSwitchEmit (EmitContext ec, Expression val)
3460 int element_count = Elements.Count;
3461 object [] element_keys = new object [element_count];
3462 Elements.Keys.CopyTo (element_keys, 0);
3463 Array.Sort (element_keys);
3465 // initialize the block list with one element per key
3466 ArrayList key_blocks = new ArrayList (element_count);
3467 foreach (object key in element_keys)
3468 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3470 KeyBlock current_kb;
3471 // iteratively merge the blocks while they are at least half full
3472 // there's probably a really cool way to do this with a tree...
3473 while (key_blocks.Count > 1)
3475 ArrayList key_blocks_new = new ArrayList ();
3476 current_kb = (KeyBlock) key_blocks [0];
3477 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3479 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3480 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3483 current_kb.last = kb.last;
3484 current_kb.Size += kb.Size;
3488 // start a new block
3489 key_blocks_new.Add (current_kb);
3493 key_blocks_new.Add (current_kb);
3494 if (key_blocks.Count == key_blocks_new.Count)
3496 key_blocks = key_blocks_new;
3499 // initialize the key lists
3500 foreach (KeyBlock kb in key_blocks)
3501 kb.element_keys = new ArrayList ();
3503 // fill the key lists
3505 if (key_blocks.Count > 0) {
3506 current_kb = (KeyBlock) key_blocks [0];
3507 foreach (object key in element_keys)
3509 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3510 System.Convert.ToInt64 (key) > current_kb.last;
3512 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3513 current_kb.element_keys.Add (key);
3517 // sort the blocks so we can tackle the largest ones first
3520 // okay now we can start...
3521 ILGenerator ig = ec.ig;
3522 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3523 Label lbl_default = default_target;
3525 Type type_keys = null;
3526 if (element_keys.Length > 0)
3527 type_keys = element_keys [0].GetType (); // used for conversions
3531 if (TypeManager.IsEnumType (SwitchType))
3532 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3534 compare_type = SwitchType;
3536 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3538 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3539 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3542 foreach (object key in kb.element_keys) {
3543 SwitchLabel sl = (SwitchLabel) Elements [key];
3544 if (key is int && (int) key == 0) {
3545 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3548 EmitObjectInteger (ig, key);
3549 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3555 // TODO: if all the keys in the block are the same and there are
3556 // no gaps/defaults then just use a range-check.
3557 if (compare_type == TypeManager.int64_type ||
3558 compare_type == TypeManager.uint64_type)
3560 // TODO: optimize constant/I4 cases
3562 // check block range (could be > 2^31)
3564 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3565 ig.Emit (OpCodes.Blt, lbl_default);
3567 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3568 ig.Emit (OpCodes.Bgt, lbl_default);
3574 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3575 ig.Emit (OpCodes.Sub);
3577 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3583 int first = (int) kb.first;
3586 IntConstant.EmitInt (ig, first);
3587 ig.Emit (OpCodes.Sub);
3591 IntConstant.EmitInt (ig, -first);
3592 ig.Emit (OpCodes.Add);
3596 // first, build the list of labels for the switch
3598 int cJumps = kb.Length;
3599 Label [] switch_labels = new Label [cJumps];
3600 for (int iJump = 0; iJump < cJumps; iJump++)
3602 object key = kb.element_keys [iKey];
3603 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3605 SwitchLabel sl = (SwitchLabel) Elements [key];
3606 switch_labels [iJump] = sl.GetILLabel (ec);
3610 switch_labels [iJump] = lbl_default;
3612 // emit the switch opcode
3613 ig.Emit (OpCodes.Switch, switch_labels);
3616 // mark the default for this block
3618 ig.MarkLabel (lbl_default);
3621 // TODO: find the default case and emit it here,
3622 // to prevent having to do the following jump.
3623 // make sure to mark other labels in the default section
3625 // the last default just goes to the end
3626 if (element_keys.Length > 0)
3627 ig.Emit (OpCodes.Br, lbl_default);
3629 // now emit the code for the sections
3630 bool found_default = false;
3632 foreach (SwitchSection ss in Sections) {
3633 foreach (SwitchLabel sl in ss.Labels) {
3634 if (sl.Converted == SwitchLabel.NullStringCase) {
3635 ig.MarkLabel (null_target);
3636 } else if (sl.Label == null) {
3637 ig.MarkLabel (lbl_default);
3638 found_default = true;
3640 ig.MarkLabel (null_target);
3642 ig.MarkLabel (sl.GetILLabel (ec));
3643 ig.MarkLabel (sl.GetILLabelCode (ec));
3648 if (!found_default) {
3649 ig.MarkLabel (lbl_default);
3650 if (!has_null_case) {
3651 ig.MarkLabel (null_target);
3655 ig.MarkLabel (lbl_end);
3658 SwitchSection FindSection (SwitchLabel label)
3660 foreach (SwitchSection ss in Sections){
3661 foreach (SwitchLabel sl in ss.Labels){
3670 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3672 foreach (SwitchSection ss in Sections)
3673 ss.Block.MutateHoistedGenericType (storey);
3676 public static void Reset ()
3679 allowed_types = null;
3682 public override bool Resolve (EmitContext ec)
3684 Expr = Expr.Resolve (ec);
3688 new_expr = SwitchGoverningType (ec, Expr);
3690 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3691 unwrap = Nullable.Unwrap.Create (Expr, false);
3695 new_expr = SwitchGoverningType (ec, unwrap);
3698 if (new_expr == null){
3699 Report.Error (151, loc,
3700 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3701 TypeManager.CSharpName (Expr.Type));
3706 SwitchType = new_expr.Type;
3708 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3709 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3713 if (!CheckSwitch (ec))
3717 Elements.Remove (SwitchLabel.NullStringCase);
3719 Switch old_switch = ec.Switch;
3721 ec.Switch.SwitchType = SwitchType;
3723 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3724 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3726 is_constant = new_expr is Constant;
3728 object key = ((Constant) new_expr).GetValue ();
3729 SwitchLabel label = (SwitchLabel) Elements [key];
3731 constant_section = FindSection (label);
3732 if (constant_section == null)
3733 constant_section = default_section;
3738 foreach (SwitchSection ss in Sections){
3740 ec.CurrentBranching.CreateSibling (
3741 null, FlowBranching.SiblingType.SwitchSection);
3745 if (is_constant && (ss != constant_section)) {
3746 // If we're a constant switch, we're only emitting
3747 // one single section - mark all the others as
3749 ec.CurrentBranching.CurrentUsageVector.Goto ();
3750 if (!ss.Block.ResolveUnreachable (ec, true)) {
3754 if (!ss.Block.Resolve (ec))
3759 if (default_section == null)
3760 ec.CurrentBranching.CreateSibling (
3761 null, FlowBranching.SiblingType.SwitchSection);
3763 ec.EndFlowBranching ();
3764 ec.Switch = old_switch;
3766 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3771 if (SwitchType == TypeManager.string_type && !is_constant) {
3772 // TODO: Optimize single case, and single+default case
3773 ResolveStringSwitchMap (ec);
3779 void ResolveStringSwitchMap (EmitContext ec)
3781 FullNamedExpression string_dictionary_type;
3782 if (TypeManager.generic_ienumerable_type != null) {
3783 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3784 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3786 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3788 new TypeExpression (TypeManager.string_type, loc),
3789 new TypeExpression (TypeManager.int32_type, loc)), loc);
3791 MemberAccess system_collections_generic = new MemberAccess (
3792 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3794 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3797 Field field = new Field (ec.CurrentTypeDefinition, string_dictionary_type,
3798 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3799 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3800 if (!field.Define ())
3802 ec.CurrentTypeDefinition.PartialContainer.AddField (field);
3804 ArrayList init = new ArrayList ();
3807 string value = null;
3808 foreach (SwitchSection section in Sections) {
3809 int last_count = init.Count;
3810 foreach (SwitchLabel sl in section.Labels) {
3811 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3814 value = (string) sl.Converted;
3815 ArrayList init_args = new ArrayList (2);
3816 init_args.Add (new StringLiteral (value, sl.Location));
3817 init_args.Add (new IntConstant (counter, loc));
3818 init.Add (new CollectionElementInitializer (init_args, loc));
3822 // Don't add empty sections
3824 if (last_count == init.Count)
3827 Elements.Add (counter, section.Labels [0]);
3831 Arguments args = new Arguments (1);
3832 args.Add (new Argument (new IntConstant (init.Count, loc)));
3833 Expression initializer = new NewInitialize (string_dictionary_type, args,
3834 new CollectionOrObjectInitializers (init, loc), loc);
3836 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3837 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3840 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3842 ILGenerator ig = ec.ig;
3843 Label l_initialized = ig.DefineLabel ();
3846 // Skip initialization when value is null
3848 value.EmitBranchable (ec, null_target, false);
3851 // Check if string dictionary is initialized and initialize
3853 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3854 string_dictionary.EmitStatement (ec);
3855 ig.MarkLabel (l_initialized);
3857 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3859 if (TypeManager.generic_ienumerable_type != null) {
3860 Arguments get_value_args = new Arguments (2);
3861 get_value_args.Add (new Argument (value));
3862 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3863 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3864 if (get_item == null)
3868 // A value was not found, go to default case
3870 get_item.EmitBranchable (ec, default_target, false);
3872 Arguments get_value_args = new Arguments (1);
3873 get_value_args.Add (new Argument (value));
3875 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3876 if (get_item == null)
3879 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3880 get_item_object.EmitAssign (ec, get_item, true, false);
3881 ec.ig.Emit (OpCodes.Brfalse, default_target);
3883 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3884 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3886 get_item_int.EmitStatement (ec);
3887 get_item_object.Release (ec);
3890 TableSwitchEmit (ec, string_switch_variable);
3891 string_switch_variable.Release (ec);
3894 protected override void DoEmit (EmitContext ec)
3896 ILGenerator ig = ec.ig;
3898 default_target = ig.DefineLabel ();
3899 null_target = ig.DefineLabel ();
3901 // Store variable for comparission purposes
3902 // TODO: Don't duplicate non-captured VariableReference
3903 LocalTemporary value;
3905 value = new LocalTemporary (SwitchType);
3906 unwrap.EmitCheck (ec);
3907 ig.Emit (OpCodes.Brfalse, null_target);
3910 } else if (!is_constant) {
3911 value = new LocalTemporary (SwitchType);
3918 // Setup the codegen context
3920 Label old_end = ec.LoopEnd;
3921 Switch old_switch = ec.Switch;
3923 ec.LoopEnd = ig.DefineLabel ();
3928 if (constant_section != null)
3929 constant_section.Block.Emit (ec);
3930 } else if (string_dictionary != null) {
3931 DoEmitStringSwitch (value, ec);
3933 TableSwitchEmit (ec, value);
3939 // Restore context state.
3940 ig.MarkLabel (ec.LoopEnd);
3943 // Restore the previous context
3945 ec.LoopEnd = old_end;
3946 ec.Switch = old_switch;
3949 protected override void CloneTo (CloneContext clonectx, Statement t)
3951 Switch target = (Switch) t;
3953 target.Expr = Expr.Clone (clonectx);
3954 target.Sections = new ArrayList ();
3955 foreach (SwitchSection ss in Sections){
3956 target.Sections.Add (ss.Clone (clonectx));
3961 // A place where execution can restart in an iterator
3962 public abstract class ResumableStatement : Statement
3965 protected Label resume_point;
3967 public Label PrepareForEmit (EmitContext ec)
3971 resume_point = ec.ig.DefineLabel ();
3973 return resume_point;
3976 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3980 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3985 // Base class for statements that are implemented in terms of try...finally
3986 public abstract class ExceptionStatement : ResumableStatement
3990 protected abstract void EmitPreTryBody (EmitContext ec);
3991 protected abstract void EmitTryBody (EmitContext ec);
3992 protected abstract void EmitFinallyBody (EmitContext ec);
3994 protected sealed override void DoEmit (EmitContext ec)
3996 ILGenerator ig = ec.ig;
3998 EmitPreTryBody (ec);
4000 if (resume_points != null) {
4001 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
4002 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
4005 ig.BeginExceptionBlock ();
4007 if (resume_points != null) {
4008 ig.MarkLabel (resume_point);
4010 // For normal control flow, we want to fall-through the Switch
4011 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
4012 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
4013 IntConstant.EmitInt (ig, first_resume_pc);
4014 ig.Emit (OpCodes.Sub);
4016 Label [] labels = new Label [resume_points.Count];
4017 for (int i = 0; i < resume_points.Count; ++i)
4018 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
4019 ig.Emit (OpCodes.Switch, labels);
4024 ig.BeginFinallyBlock ();
4026 Label start_finally = ec.ig.DefineLabel ();
4027 if (resume_points != null) {
4028 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
4029 ig.Emit (OpCodes.Brfalse_S, start_finally);
4030 ig.Emit (OpCodes.Endfinally);
4033 ig.MarkLabel (start_finally);
4034 EmitFinallyBody (ec);
4036 ig.EndExceptionBlock ();
4039 public void SomeCodeFollows ()
4041 code_follows = true;
4044 protected void ResolveReachability (EmitContext ec)
4046 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
4047 // So, ensure there's some IL code after this statement.
4048 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4049 ec.NeedReturnLabel ();
4053 ArrayList resume_points;
4054 int first_resume_pc;
4055 public void AddResumePoint (ResumableStatement stmt, int pc)
4057 if (resume_points == null) {
4058 resume_points = new ArrayList ();
4059 first_resume_pc = pc;
4062 if (pc != first_resume_pc + resume_points.Count)
4063 throw new InternalErrorException ("missed an intervening AddResumePoint?");
4065 resume_points.Add (stmt);
4068 Label dispose_try_block;
4069 bool prepared_for_dispose, emitted_dispose;
4070 public override Label PrepareForDispose (EmitContext ec, Label end)
4072 if (!prepared_for_dispose) {
4073 prepared_for_dispose = true;
4074 dispose_try_block = ec.ig.DefineLabel ();
4076 return dispose_try_block;
4079 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
4081 if (emitted_dispose)
4084 emitted_dispose = true;
4086 ILGenerator ig = ec.ig;
4088 Label end_of_try = ig.DefineLabel ();
4090 // Ensure that the only way we can get into this code is through a dispatcher
4091 if (have_dispatcher)
4092 ig.Emit (OpCodes.Br, end);
4094 ig.BeginExceptionBlock ();
4096 ig.MarkLabel (dispose_try_block);
4098 Label [] labels = null;
4099 for (int i = 0; i < resume_points.Count; ++i) {
4100 ResumableStatement s = (ResumableStatement) resume_points [i];
4101 Label ret = s.PrepareForDispose (ec, end_of_try);
4102 if (ret.Equals (end_of_try) && labels == null)
4104 if (labels == null) {
4105 labels = new Label [resume_points.Count];
4106 for (int j = 0; j < i; ++j)
4107 labels [j] = end_of_try;
4112 if (labels != null) {
4114 for (j = 1; j < labels.Length; ++j)
4115 if (!labels [0].Equals (labels [j]))
4117 bool emit_dispatcher = j < labels.Length;
4119 if (emit_dispatcher) {
4120 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4121 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4122 IntConstant.EmitInt (ig, first_resume_pc);
4123 ig.Emit (OpCodes.Sub);
4124 ig.Emit (OpCodes.Switch, labels);
4125 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4128 foreach (ResumableStatement s in resume_points)
4129 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4132 ig.MarkLabel (end_of_try);
4134 ig.BeginFinallyBlock ();
4136 EmitFinallyBody (ec);
4138 ig.EndExceptionBlock ();
4142 public class Lock : ExceptionStatement {
4144 public Statement Statement;
4145 TemporaryVariable temp;
4147 public Lock (Expression expr, Statement stmt, Location l)
4154 public override bool Resolve (EmitContext ec)
4156 expr = expr.Resolve (ec);
4160 if (!TypeManager.IsReferenceType (expr.Type)){
4161 Report.Error (185, loc,
4162 "`{0}' is not a reference type as required by the lock statement",
4163 TypeManager.CSharpName (expr.Type));
4167 ec.StartFlowBranching (this);
4168 bool ok = Statement.Resolve (ec);
4169 ec.EndFlowBranching ();
4171 ResolveReachability (ec);
4173 // Avoid creating libraries that reference the internal
4176 if (t == TypeManager.null_type)
4177 t = TypeManager.object_type;
4179 temp = new TemporaryVariable (t, loc);
4182 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4183 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4184 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4185 monitor_type, "Enter", loc, TypeManager.object_type);
4186 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4187 monitor_type, "Exit", loc, TypeManager.object_type);
4193 protected override void EmitPreTryBody (EmitContext ec)
4195 ILGenerator ig = ec.ig;
4197 temp.EmitAssign (ec, expr);
4199 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4202 protected override void EmitTryBody (EmitContext ec)
4204 Statement.Emit (ec);
4207 protected override void EmitFinallyBody (EmitContext ec)
4210 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4213 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4215 expr.MutateHoistedGenericType (storey);
4216 temp.MutateHoistedGenericType (storey);
4217 Statement.MutateHoistedGenericType (storey);
4220 protected override void CloneTo (CloneContext clonectx, Statement t)
4222 Lock target = (Lock) t;
4224 target.expr = expr.Clone (clonectx);
4225 target.Statement = Statement.Clone (clonectx);
4229 public class Unchecked : Statement {
4232 public Unchecked (Block b)
4238 public override bool Resolve (EmitContext ec)
4240 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4241 return Block.Resolve (ec);
4244 protected override void DoEmit (EmitContext ec)
4246 using (ec.With (EmitContext.Options.AllCheckStateFlags, false))
4250 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4252 Block.MutateHoistedGenericType (storey);
4255 protected override void CloneTo (CloneContext clonectx, Statement t)
4257 Unchecked target = (Unchecked) t;
4259 target.Block = clonectx.LookupBlock (Block);
4263 public class Checked : Statement {
4266 public Checked (Block b)
4269 b.Unchecked = false;
4272 public override bool Resolve (EmitContext ec)
4274 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4275 return Block.Resolve (ec);
4278 protected override void DoEmit (EmitContext ec)
4280 using (ec.With (EmitContext.Options.AllCheckStateFlags, true))
4284 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4286 Block.MutateHoistedGenericType (storey);
4289 protected override void CloneTo (CloneContext clonectx, Statement t)
4291 Checked target = (Checked) t;
4293 target.Block = clonectx.LookupBlock (Block);
4297 public class Unsafe : Statement {
4300 public Unsafe (Block b)
4303 Block.Unsafe = true;
4304 loc = b.StartLocation;
4307 public override bool Resolve (EmitContext ec)
4309 if (ec.CurrentIterator != null)
4310 Report.Error (1629, loc, "Unsafe code may not appear in iterators");
4312 using (ec.With (EmitContext.Options.UnsafeScope, true))
4313 return Block.Resolve (ec);
4316 protected override void DoEmit (EmitContext ec)
4318 using (ec.With (EmitContext.Options.UnsafeScope, true))
4322 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4324 Block.MutateHoistedGenericType (storey);
4327 protected override void CloneTo (CloneContext clonectx, Statement t)
4329 Unsafe target = (Unsafe) t;
4331 target.Block = clonectx.LookupBlock (Block);
4338 public class Fixed : Statement {
4340 ArrayList declarators;
4341 Statement statement;
4346 abstract class Emitter
4348 protected LocalInfo vi;
4349 protected Expression converted;
4351 protected Emitter (Expression expr, LocalInfo li)
4357 public abstract void Emit (EmitContext ec);
4358 public abstract void EmitExit (EmitContext ec);
4361 class ExpressionEmitter : Emitter {
4362 public ExpressionEmitter (Expression converted, LocalInfo li) :
4363 base (converted, li)
4367 public override void Emit (EmitContext ec) {
4369 // Store pointer in pinned location
4371 converted.Emit (ec);
4375 public override void EmitExit (EmitContext ec)
4377 ec.ig.Emit (OpCodes.Ldc_I4_0);
4378 ec.ig.Emit (OpCodes.Conv_U);
4383 class StringEmitter : Emitter
4385 LocalInfo pinned_string;
4387 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4390 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4391 pinned_string.Pinned = true;
4394 public StringEmitter Resolve (EmitContext rc)
4396 pinned_string.Resolve (rc);
4398 if (TypeManager.int_get_offset_to_string_data == null) {
4399 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4400 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4406 public override void Emit (EmitContext ec)
4408 pinned_string.ResolveVariable (ec);
4410 converted.Emit (ec);
4411 pinned_string.EmitAssign (ec);
4413 // TODO: Should use Binary::Add
4414 pinned_string.Emit (ec);
4415 ec.ig.Emit (OpCodes.Conv_I);
4417 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4418 //pe.InstanceExpression = pinned_string;
4419 pe.Resolve (ec).Emit (ec);
4421 ec.ig.Emit (OpCodes.Add);
4425 public override void EmitExit (EmitContext ec)
4427 ec.ig.Emit (OpCodes.Ldnull);
4428 pinned_string.EmitAssign (ec);
4432 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4435 declarators = decls;
4440 public Statement Statement {
4441 get { return statement; }
4444 public override bool Resolve (EmitContext ec)
4447 Expression.UnsafeError (loc);
4451 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4452 if (texpr == null) {
4453 if (type is VarExpr)
4454 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4459 expr_type = texpr.Type;
4461 data = new Emitter [declarators.Count];
4463 if (!expr_type.IsPointer){
4464 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4469 foreach (Pair p in declarators){
4470 LocalInfo vi = (LocalInfo) p.First;
4471 Expression e = (Expression) p.Second;
4473 vi.VariableInfo.SetAssigned (ec);
4474 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4477 // The rules for the possible declarators are pretty wise,
4478 // but the production on the grammar is more concise.
4480 // So we have to enforce these rules here.
4482 // We do not resolve before doing the case 1 test,
4483 // because the grammar is explicit in that the token &
4484 // is present, so we need to test for this particular case.
4488 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4492 using (ec.Set (EmitContext.Options.FixedInitializerScope)) {
4502 if (e.Type.IsArray){
4503 Type array_type = TypeManager.GetElementType (e.Type);
4506 // Provided that array_type is unmanaged,
4508 if (!TypeManager.VerifyUnManaged (array_type, loc))
4512 // and T* is implicitly convertible to the
4513 // pointer type given in the fixed statement.
4515 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4517 Expression converted = Convert.ImplicitConversionRequired (
4518 ec, array_ptr, vi.VariableType, loc);
4519 if (converted == null)
4523 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4525 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4526 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4527 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4528 new NullPointer (loc),
4531 converted = converted.Resolve (ec);
4533 data [i] = new ExpressionEmitter (converted, vi);
4542 if (e.Type == TypeManager.string_type){
4543 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4548 // Case 4: fixed buffer
4549 if (e is FixedBufferPtr) {
4550 data [i++] = new ExpressionEmitter (e, vi);
4555 // Case 1: & object.
4557 Unary u = e as Unary;
4558 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4559 IVariableReference vr = u.Expr as IVariableReference;
4560 if (vr == null || !vr.IsFixed) {
4561 data [i] = new ExpressionEmitter (e, vi);
4565 if (data [i++] == null)
4566 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4568 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4571 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4572 bool ok = statement.Resolve (ec);
4573 bool flow_unreachable = ec.EndFlowBranching ();
4574 has_ret = flow_unreachable;
4579 protected override void DoEmit (EmitContext ec)
4581 for (int i = 0; i < data.Length; i++) {
4585 statement.Emit (ec);
4591 // Clear the pinned variable
4593 for (int i = 0; i < data.Length; i++) {
4594 data [i].EmitExit (ec);
4598 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4600 // Fixed statement cannot be used inside anonymous methods or lambdas
4601 throw new NotSupportedException ();
4604 protected override void CloneTo (CloneContext clonectx, Statement t)
4606 Fixed target = (Fixed) t;
4608 target.type = type.Clone (clonectx);
4609 target.declarators = new ArrayList (declarators.Count);
4610 foreach (Pair p in declarators) {
4611 LocalInfo vi = (LocalInfo) p.First;
4612 Expression e = (Expression) p.Second;
4614 target.declarators.Add (
4615 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4618 target.statement = statement.Clone (clonectx);
4622 public class Catch : Statement {
4623 public readonly string Name;
4625 public Block VarBlock;
4627 Expression type_expr;
4630 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4635 VarBlock = var_block;
4639 public Type CatchType {
4645 public bool IsGeneral {
4647 return type_expr == null;
4651 protected override void DoEmit (EmitContext ec)
4653 ILGenerator ig = ec.ig;
4655 if (CatchType != null)
4656 ig.BeginCatchBlock (CatchType);
4658 ig.BeginCatchBlock (TypeManager.object_type);
4660 if (VarBlock != null)
4664 // TODO: Move to resolve
4665 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4669 // Only to make verifier happy
4670 if (TypeManager.IsGenericParameter (lvr.Type))
4671 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4675 if (lvr.IsHoisted) {
4676 LocalTemporary lt = new LocalTemporary (lvr.Type);
4680 // Variable is at the top of the stack
4681 source = EmptyExpression.Null;
4684 lvr.EmitAssign (ec, source, false, false);
4686 ig.Emit (OpCodes.Pop);
4691 public override bool Resolve (EmitContext ec)
4693 using (ec.With (EmitContext.Options.CatchScope, true)) {
4694 if (type_expr != null) {
4695 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4701 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4702 Error (155, "The type caught or thrown must be derived from System.Exception");
4708 if (!Block.Resolve (ec))
4711 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4712 // emit the "unused variable" warnings.
4713 if (VarBlock != null)
4714 return VarBlock.Resolve (ec);
4720 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4723 type = storey.MutateType (type);
4724 if (VarBlock != null)
4725 VarBlock.MutateHoistedGenericType (storey);
4726 Block.MutateHoistedGenericType (storey);
4729 protected override void CloneTo (CloneContext clonectx, Statement t)
4731 Catch target = (Catch) t;
4733 if (type_expr != null)
4734 target.type_expr = type_expr.Clone (clonectx);
4735 if (VarBlock != null)
4736 target.VarBlock = clonectx.LookupBlock (VarBlock);
4737 target.Block = clonectx.LookupBlock (Block);
4741 public class TryFinally : ExceptionStatement {
4745 public TryFinally (Statement stmt, Block fini, Location l)
4752 public override bool Resolve (EmitContext ec)
4756 ec.StartFlowBranching (this);
4758 if (!stmt.Resolve (ec))
4762 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4763 using (ec.With (EmitContext.Options.FinallyScope, true)) {
4764 if (!fini.Resolve (ec))
4768 ec.EndFlowBranching ();
4770 ResolveReachability (ec);
4775 protected override void EmitPreTryBody (EmitContext ec)
4779 protected override void EmitTryBody (EmitContext ec)
4784 protected override void EmitFinallyBody (EmitContext ec)
4789 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4791 stmt.MutateHoistedGenericType (storey);
4792 fini.MutateHoistedGenericType (storey);
4795 protected override void CloneTo (CloneContext clonectx, Statement t)
4797 TryFinally target = (TryFinally) t;
4799 target.stmt = (Statement) stmt.Clone (clonectx);
4801 target.fini = clonectx.LookupBlock (fini);
4805 public class TryCatch : Statement {
4807 public ArrayList Specific;
4808 public Catch General;
4809 bool inside_try_finally, code_follows;
4811 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4814 this.Specific = catch_clauses;
4815 this.General = null;
4816 this.inside_try_finally = inside_try_finally;
4818 for (int i = 0; i < catch_clauses.Count; ++i) {
4819 Catch c = (Catch) catch_clauses [i];
4821 if (i != catch_clauses.Count - 1)
4822 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4824 catch_clauses.RemoveAt (i);
4832 public override bool Resolve (EmitContext ec)
4836 ec.StartFlowBranching (this);
4838 if (!Block.Resolve (ec))
4841 Type[] prev_catches = new Type [Specific.Count];
4843 foreach (Catch c in Specific){
4844 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4846 if (c.Name != null) {
4847 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4849 throw new Exception ();
4851 vi.VariableInfo = null;
4854 if (!c.Resolve (ec)) {
4859 Type resolved_type = c.CatchType;
4860 for (int ii = 0; ii < last_index; ++ii) {
4861 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4862 Report.Error (160, c.loc,
4863 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4864 TypeManager.CSharpName (prev_catches [ii]));
4869 prev_catches [last_index++] = resolved_type;
4872 if (General != null) {
4873 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4874 foreach (Catch c in Specific){
4875 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4876 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'");
4881 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4883 if (!General.Resolve (ec))
4887 ec.EndFlowBranching ();
4889 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4890 // So, ensure there's some IL code after this statement
4891 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4892 ec.NeedReturnLabel ();
4897 public void SomeCodeFollows ()
4899 code_follows = true;
4902 protected override void DoEmit (EmitContext ec)
4904 ILGenerator ig = ec.ig;
4906 if (!inside_try_finally)
4907 ig.BeginExceptionBlock ();
4911 foreach (Catch c in Specific)
4914 if (General != null)
4917 if (!inside_try_finally)
4918 ig.EndExceptionBlock ();
4921 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4923 Block.MutateHoistedGenericType (storey);
4925 if (General != null)
4926 General.MutateHoistedGenericType (storey);
4927 if (Specific != null) {
4928 foreach (Catch c in Specific)
4929 c.MutateHoistedGenericType (storey);
4933 protected override void CloneTo (CloneContext clonectx, Statement t)
4935 TryCatch target = (TryCatch) t;
4937 target.Block = clonectx.LookupBlock (Block);
4938 if (General != null)
4939 target.General = (Catch) General.Clone (clonectx);
4940 if (Specific != null){
4941 target.Specific = new ArrayList ();
4942 foreach (Catch c in Specific)
4943 target.Specific.Add (c.Clone (clonectx));
4948 // FIXME: Why is it almost exact copy of Using ??
4949 public class UsingTemporary : ExceptionStatement {
4950 TemporaryVariable local_copy;
4951 public Statement Statement;
4955 public UsingTemporary (Expression expr, Statement stmt, Location l)
4962 public override bool Resolve (EmitContext ec)
4964 expr = expr.Resolve (ec);
4968 expr_type = expr.Type;
4970 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4971 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4972 Using.Error_IsNotConvertibleToIDisposable (expr);
4977 local_copy = new TemporaryVariable (expr_type, loc);
4978 local_copy.Resolve (ec);
4980 ec.StartFlowBranching (this);
4982 bool ok = Statement.Resolve (ec);
4984 ec.EndFlowBranching ();
4986 ResolveReachability (ec);
4988 if (TypeManager.void_dispose_void == null) {
4989 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4990 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4996 protected override void EmitPreTryBody (EmitContext ec)
4998 local_copy.EmitAssign (ec, expr);
5001 protected override void EmitTryBody (EmitContext ec)
5003 Statement.Emit (ec);
5006 protected override void EmitFinallyBody (EmitContext ec)
5008 ILGenerator ig = ec.ig;
5009 if (!TypeManager.IsStruct (expr_type)) {
5010 Label skip = ig.DefineLabel ();
5011 local_copy.Emit (ec);
5012 ig.Emit (OpCodes.Brfalse, skip);
5013 local_copy.Emit (ec);
5014 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5015 ig.MarkLabel (skip);
5019 Expression ml = Expression.MemberLookup (
5020 ec.CurrentType, TypeManager.idisposable_type, expr_type,
5021 "Dispose", Location.Null);
5023 if (!(ml is MethodGroupExpr)) {
5024 local_copy.Emit (ec);
5025 ig.Emit (OpCodes.Box, expr_type);
5026 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5030 MethodInfo mi = null;
5032 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5033 if (TypeManager.GetParameterData (mk).Count == 0) {
5040 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5044 local_copy.AddressOf (ec, AddressOp.Load);
5045 ig.Emit (OpCodes.Call, mi);
5048 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5050 expr_type = storey.MutateType (expr_type);
5051 local_copy.MutateHoistedGenericType (storey);
5052 Statement.MutateHoistedGenericType (storey);
5055 protected override void CloneTo (CloneContext clonectx, Statement t)
5057 UsingTemporary target = (UsingTemporary) t;
5059 target.expr = expr.Clone (clonectx);
5060 target.Statement = Statement.Clone (clonectx);
5064 public class Using : ExceptionStatement {
5066 public Statement EmbeddedStatement {
5067 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5073 ExpressionStatement assign;
5075 public Using (Expression var, Expression init, Statement stmt, Location l)
5083 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5085 Report.SymbolRelatedToPreviousError (expr.Type);
5086 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5087 expr.GetSignatureForError ());
5090 protected override void EmitPreTryBody (EmitContext ec)
5092 assign.EmitStatement (ec);
5095 protected override void EmitTryBody (EmitContext ec)
5100 protected override void EmitFinallyBody (EmitContext ec)
5102 ILGenerator ig = ec.ig;
5103 Label skip = ig.DefineLabel ();
5105 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5106 if (emit_null_check) {
5108 ig.Emit (OpCodes.Brfalse, skip);
5111 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5113 if (emit_null_check)
5114 ig.MarkLabel (skip);
5117 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5119 assign.MutateHoistedGenericType (storey);
5120 var.MutateHoistedGenericType (storey);
5121 stmt.MutateHoistedGenericType (storey);
5124 public override bool Resolve (EmitContext ec)
5126 if (!ResolveVariable (ec))
5129 ec.StartFlowBranching (this);
5131 bool ok = stmt.Resolve (ec);
5133 ec.EndFlowBranching ();
5135 ResolveReachability (ec);
5137 if (TypeManager.void_dispose_void == null) {
5138 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5139 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5145 bool ResolveVariable (EmitContext ec)
5147 assign = new SimpleAssign (var, init, loc);
5148 assign = assign.ResolveStatement (ec);
5152 if (assign.Type == TypeManager.idisposable_type ||
5153 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5157 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5159 Error_IsNotConvertibleToIDisposable (var);
5163 throw new NotImplementedException ("covariance?");
5166 protected override void CloneTo (CloneContext clonectx, Statement t)
5168 Using target = (Using) t;
5170 target.var = var.Clone (clonectx);
5171 target.init = init.Clone (clonectx);
5172 target.stmt = stmt.Clone (clonectx);
5177 /// Implementation of the foreach C# statement
5179 public class Foreach : Statement {
5181 sealed class ArrayForeach : Statement
5183 class ArrayCounter : TemporaryVariable
5185 StatementExpression increment;
5187 public ArrayCounter (Location loc)
5188 : base (TypeManager.int32_type, loc)
5192 public void ResolveIncrement (EmitContext ec)
5194 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5195 increment.Resolve (ec);
5198 public void EmitIncrement (EmitContext ec)
5200 increment.Emit (ec);
5204 readonly Foreach for_each;
5205 readonly Statement statement;
5208 TemporaryVariable[] lengths;
5209 Expression [] length_exprs;
5210 ArrayCounter[] counter;
5212 TemporaryVariable copy;
5215 public ArrayForeach (Foreach @foreach, int rank)
5217 for_each = @foreach;
5218 statement = for_each.statement;
5221 counter = new ArrayCounter [rank];
5222 length_exprs = new Expression [rank];
5225 // Only use temporary length variables when dealing with
5226 // multi-dimensional arrays
5229 lengths = new TemporaryVariable [rank];
5232 protected override void CloneTo (CloneContext clonectx, Statement target)
5234 throw new NotImplementedException ();
5237 public override bool Resolve (EmitContext ec)
5239 copy = new TemporaryVariable (for_each.expr.Type, loc);
5242 int rank = length_exprs.Length;
5243 Arguments list = new Arguments (rank);
5244 for (int i = 0; i < rank; i++) {
5245 counter [i] = new ArrayCounter (loc);
5246 counter [i].ResolveIncrement (ec);
5249 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5251 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5252 lengths [i].Resolve (ec);
5254 Arguments args = new Arguments (1);
5255 args.Add (new Argument (new IntConstant (i, loc)));
5256 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5259 list.Add (new Argument (counter [i]));
5262 access = new ElementAccess (copy, list).Resolve (ec);
5266 Expression var_type = for_each.type;
5267 VarExpr ve = var_type as VarExpr;
5269 // Infer implicitly typed local variable from foreach array type
5270 var_type = new TypeExpression (access.Type, ve.Location);
5273 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5274 if (var_type == null)
5277 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5283 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5284 ec.CurrentBranching.CreateSibling ();
5286 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5287 if (for_each.variable == null)
5290 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5291 if (!statement.Resolve (ec))
5293 ec.EndFlowBranching ();
5295 // There's no direct control flow from the end of the embedded statement to the end of the loop
5296 ec.CurrentBranching.CurrentUsageVector.Goto ();
5298 ec.EndFlowBranching ();
5303 protected override void DoEmit (EmitContext ec)
5305 ILGenerator ig = ec.ig;
5307 copy.EmitAssign (ec, for_each.expr);
5309 int rank = length_exprs.Length;
5310 Label[] test = new Label [rank];
5311 Label[] loop = new Label [rank];
5313 for (int i = 0; i < rank; i++) {
5314 test [i] = ig.DefineLabel ();
5315 loop [i] = ig.DefineLabel ();
5317 if (lengths != null)
5318 lengths [i].EmitAssign (ec, length_exprs [i]);
5321 IntConstant zero = new IntConstant (0, loc);
5322 for (int i = 0; i < rank; i++) {
5323 counter [i].EmitAssign (ec, zero);
5325 ig.Emit (OpCodes.Br, test [i]);
5326 ig.MarkLabel (loop [i]);
5329 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5331 statement.Emit (ec);
5333 ig.MarkLabel (ec.LoopBegin);
5335 for (int i = rank - 1; i >= 0; i--){
5336 counter [i].EmitIncrement (ec);
5338 ig.MarkLabel (test [i]);
5339 counter [i].Emit (ec);
5341 if (lengths != null)
5342 lengths [i].Emit (ec);
5344 length_exprs [i].Emit (ec);
5346 ig.Emit (OpCodes.Blt, loop [i]);
5349 ig.MarkLabel (ec.LoopEnd);
5352 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5354 for_each.expr.MutateHoistedGenericType (storey);
5356 copy.MutateHoistedGenericType (storey);
5357 conv.MutateHoistedGenericType (storey);
5358 statement.MutateHoistedGenericType (storey);
5360 for (int i = 0; i < counter.Length; i++) {
5361 counter [i].MutateHoistedGenericType (storey);
5362 if (lengths != null)
5363 lengths [i].MutateHoistedGenericType (storey);
5368 sealed class CollectionForeach : Statement
5370 class CollectionForeachStatement : Statement
5373 Expression variable, current, conv;
5374 Statement statement;
5377 public CollectionForeachStatement (Type type, Expression variable,
5378 Expression current, Statement statement,
5382 this.variable = variable;
5383 this.current = current;
5384 this.statement = statement;
5388 protected override void CloneTo (CloneContext clonectx, Statement target)
5390 throw new NotImplementedException ();
5393 public override bool Resolve (EmitContext ec)
5395 current = current.Resolve (ec);
5396 if (current == null)
5399 conv = Convert.ExplicitConversion (ec, current, type, loc);
5403 assign = new SimpleAssign (variable, conv, loc);
5404 if (assign.Resolve (ec) == null)
5407 if (!statement.Resolve (ec))
5413 protected override void DoEmit (EmitContext ec)
5415 assign.EmitStatement (ec);
5416 statement.Emit (ec);
5419 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5421 assign.MutateHoistedGenericType (storey);
5422 statement.MutateHoistedGenericType (storey);
5426 Expression variable, expr;
5427 Statement statement;
5429 TemporaryVariable enumerator;
5434 MethodGroupExpr get_enumerator;
5435 PropertyExpr get_current;
5436 MethodInfo move_next;
5437 Expression var_type;
5438 Type enumerator_type;
5439 bool enumerator_found;
5441 public CollectionForeach (Expression var_type, Expression var,
5442 Expression expr, Statement stmt, Location l)
5444 this.var_type = var_type;
5445 this.variable = var;
5451 protected override void CloneTo (CloneContext clonectx, Statement target)
5453 throw new NotImplementedException ();
5456 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5458 Type return_type = mi.ReturnType;
5461 // Ok, we can access it, now make sure that we can do something
5462 // with this `GetEnumerator'
5465 if (return_type == TypeManager.ienumerator_type ||
5466 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5468 // If it is not an interface, lets try to find the methods ourselves.
5469 // For example, if we have:
5470 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5471 // We can avoid the iface call. This is a runtime perf boost.
5472 // even bigger if we have a ValueType, because we avoid the cost
5475 // We have to make sure that both methods exist for us to take
5476 // this path. If one of the methods does not exist, we will just
5477 // use the interface. Sadly, this complex if statement is the only
5478 // way I could do this without a goto
5481 if (TypeManager.bool_movenext_void == null) {
5482 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5483 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5486 if (TypeManager.ienumerator_getcurrent == null) {
5487 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5488 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5492 // Prefer a generic enumerator over a non-generic one.
5494 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5495 enumerator_type = return_type;
5496 if (!FetchGetCurrent (ec, return_type))
5497 get_current = new PropertyExpr (
5498 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5499 if (!FetchMoveNext (return_type))
5500 move_next = TypeManager.bool_movenext_void;
5504 if (return_type.IsInterface ||
5505 !FetchMoveNext (return_type) ||
5506 !FetchGetCurrent (ec, return_type)) {
5507 enumerator_type = return_type;
5508 move_next = TypeManager.bool_movenext_void;
5509 get_current = new PropertyExpr (
5510 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5515 // Ok, so they dont return an IEnumerable, we will have to
5516 // find if they support the GetEnumerator pattern.
5519 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5520 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",
5521 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5526 enumerator_type = return_type;
5532 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5534 bool FetchMoveNext (Type t)
5536 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5538 BindingFlags.Public | BindingFlags.Instance,
5541 if (move_next_list == null)
5544 foreach (MemberInfo m in move_next_list){
5545 MethodInfo mi = (MethodInfo) m;
5547 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5548 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5558 // Retrieves a `public T get_Current ()' method from the Type `t'
5560 bool FetchGetCurrent (EmitContext ec, Type t)
5562 PropertyExpr pe = Expression.MemberLookup (
5563 ec.CurrentType, t, "Current", MemberTypes.Property,
5564 Expression.AllBindingFlags, loc) as PropertyExpr;
5572 void Error_Enumerator ()
5574 if (enumerator_found) {
5578 Report.Error (1579, loc,
5579 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5580 TypeManager.CSharpName (expr.Type));
5583 bool IsOverride (MethodInfo m)
5585 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5587 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5589 if (m is MethodBuilder)
5592 MethodInfo base_method = m.GetBaseDefinition ();
5593 return base_method != m;
5596 bool TryType (EmitContext ec, Type t)
5598 MethodGroupExpr mg = Expression.MemberLookup (
5599 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5600 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5604 MethodInfo result = null;
5605 MethodInfo tmp_move_next = null;
5606 PropertyExpr tmp_get_cur = null;
5607 Type tmp_enumerator_type = enumerator_type;
5608 foreach (MethodInfo mi in mg.Methods) {
5609 if (TypeManager.GetParameterData (mi).Count != 0)
5612 // Check whether GetEnumerator is public
5613 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5616 if (IsOverride (mi))
5619 enumerator_found = true;
5621 if (!GetEnumeratorFilter (ec, mi))
5624 if (result != null) {
5625 if (TypeManager.IsGenericType (result.ReturnType)) {
5626 if (!TypeManager.IsGenericType (mi.ReturnType))
5629 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5630 Report.SymbolRelatedToPreviousError (t);
5631 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5632 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5633 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5637 // Always prefer generics enumerators
5638 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5639 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5640 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5643 Report.SymbolRelatedToPreviousError (result);
5644 Report.SymbolRelatedToPreviousError (mi);
5645 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5646 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5651 tmp_move_next = move_next;
5652 tmp_get_cur = get_current;
5653 tmp_enumerator_type = enumerator_type;
5654 if (mi.DeclaringType == t)
5658 if (result != null) {
5659 move_next = tmp_move_next;
5660 get_current = tmp_get_cur;
5661 enumerator_type = tmp_enumerator_type;
5662 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5663 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5665 if (t != expr.Type) {
5666 expr = Convert.ExplicitConversion (
5669 throw new InternalErrorException ();
5672 get_enumerator.InstanceExpression = expr;
5673 get_enumerator.IsBase = t != expr.Type;
5681 bool ProbeCollectionType (EmitContext ec, Type t)
5683 int errors = Report.Errors;
5684 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5685 if (TryType (ec, tt))
5690 if (Report.Errors > errors)
5694 // Now try to find the method in the interfaces
5696 Type [] ifaces = TypeManager.GetInterfaces (t);
5697 foreach (Type i in ifaces){
5698 if (TryType (ec, i))
5705 public override bool Resolve (EmitContext ec)
5707 enumerator_type = TypeManager.ienumerator_type;
5709 if (!ProbeCollectionType (ec, expr.Type)) {
5710 Error_Enumerator ();
5714 VarExpr ve = var_type as VarExpr;
5716 // Infer implicitly typed local variable from foreach enumerable type
5717 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5720 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5721 if (var_type == null)
5724 enumerator = new TemporaryVariable (enumerator_type, loc);
5725 enumerator.Resolve (ec);
5727 init = new Invocation (get_enumerator, null);
5728 init = init.Resolve (ec);
5732 Expression move_next_expr;
5734 MemberInfo[] mi = new MemberInfo[] { move_next };
5735 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5736 mg.InstanceExpression = enumerator;
5738 move_next_expr = new Invocation (mg, null);
5741 get_current.InstanceExpression = enumerator;
5743 Statement block = new CollectionForeachStatement (
5744 var_type.Type, variable, get_current, statement, loc);
5746 loop = new While (move_next_expr, block, loc);
5749 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5750 if (implements_idisposable || !enumerator_type.IsSealed) {
5751 wrapper = new DisposableWrapper (this, implements_idisposable);
5753 wrapper = new NonDisposableWrapper (this);
5756 return wrapper.Resolve (ec);
5759 protected override void DoEmit (EmitContext ec)
5764 class NonDisposableWrapper : Statement {
5765 CollectionForeach parent;
5767 internal NonDisposableWrapper (CollectionForeach parent)
5769 this.parent = parent;
5772 protected override void CloneTo (CloneContext clonectx, Statement target)
5774 throw new NotSupportedException ();
5777 public override bool Resolve (EmitContext ec)
5779 return parent.ResolveLoop (ec);
5782 protected override void DoEmit (EmitContext ec)
5784 parent.EmitLoopInit (ec);
5785 parent.EmitLoopBody (ec);
5788 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5790 throw new NotSupportedException ();
5794 sealed class DisposableWrapper : ExceptionStatement
5796 CollectionForeach parent;
5797 bool implements_idisposable;
5799 internal DisposableWrapper (CollectionForeach parent, bool implements)
5801 this.parent = parent;
5802 this.implements_idisposable = implements;
5805 protected override void CloneTo (CloneContext clonectx, Statement target)
5807 throw new NotSupportedException ();
5810 public override bool Resolve (EmitContext ec)
5814 ec.StartFlowBranching (this);
5816 if (!parent.ResolveLoop (ec))
5819 ec.EndFlowBranching ();
5821 ResolveReachability (ec);
5823 if (TypeManager.void_dispose_void == null) {
5824 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5825 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5830 protected override void EmitPreTryBody (EmitContext ec)
5832 parent.EmitLoopInit (ec);
5835 protected override void EmitTryBody (EmitContext ec)
5837 parent.EmitLoopBody (ec);
5840 protected override void EmitFinallyBody (EmitContext ec)
5842 Expression instance = parent.enumerator;
5843 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5844 ILGenerator ig = ec.ig;
5846 parent.enumerator.Emit (ec);
5848 Label call_dispose = ig.DefineLabel ();
5850 if (!implements_idisposable) {
5851 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5852 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5858 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5860 // using 'endfinally' to empty the evaluation stack
5861 ig.Emit (OpCodes.Endfinally);
5862 ig.MarkLabel (call_dispose);
5865 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5868 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5870 throw new NotSupportedException ();
5874 bool ResolveLoop (EmitContext ec)
5876 return loop.Resolve (ec);
5879 void EmitLoopInit (EmitContext ec)
5881 enumerator.EmitAssign (ec, init);
5884 void EmitLoopBody (EmitContext ec)
5889 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5891 enumerator_type = storey.MutateType (enumerator_type);
5892 init.MutateHoistedGenericType (storey);
5893 loop.MutateHoistedGenericType (storey);
5898 Expression variable;
5900 Statement statement;
5902 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5903 Statement stmt, Location l)
5906 this.variable = var;
5912 public Statement Statement {
5913 get { return statement; }
5916 public override bool Resolve (EmitContext ec)
5918 expr = expr.Resolve (ec);
5923 Report.Error (186, loc, "Use of null is not valid in this context");
5927 if (expr.Type == TypeManager.string_type) {
5928 statement = new ArrayForeach (this, 1);
5929 } else if (expr.Type.IsArray) {
5930 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5932 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5933 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5934 expr.ExprClassName);
5938 statement = new CollectionForeach (type, variable, expr, statement, loc);
5941 return statement.Resolve (ec);
5944 protected override void DoEmit (EmitContext ec)
5946 ILGenerator ig = ec.ig;
5948 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5949 ec.LoopBegin = ig.DefineLabel ();
5950 ec.LoopEnd = ig.DefineLabel ();
5952 statement.Emit (ec);
5954 ec.LoopBegin = old_begin;
5955 ec.LoopEnd = old_end;
5958 protected override void CloneTo (CloneContext clonectx, Statement t)
5960 Foreach target = (Foreach) t;
5962 target.type = type.Clone (clonectx);
5963 target.variable = variable.Clone (clonectx);
5964 target.expr = expr.Clone (clonectx);
5965 target.statement = statement.Clone (clonectx);
5968 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5970 statement.MutateHoistedGenericType (storey);