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;
4306 public override bool Resolve (EmitContext ec)
4308 using (ec.With (EmitContext.Options.UnsafeScope, true))
4309 return Block.Resolve (ec);
4312 protected override void DoEmit (EmitContext ec)
4314 using (ec.With (EmitContext.Options.UnsafeScope, true))
4318 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4320 Block.MutateHoistedGenericType (storey);
4323 protected override void CloneTo (CloneContext clonectx, Statement t)
4325 Unsafe target = (Unsafe) t;
4327 target.Block = clonectx.LookupBlock (Block);
4334 public class Fixed : Statement {
4336 ArrayList declarators;
4337 Statement statement;
4342 abstract class Emitter
4344 protected LocalInfo vi;
4345 protected Expression converted;
4347 protected Emitter (Expression expr, LocalInfo li)
4353 public abstract void Emit (EmitContext ec);
4354 public abstract void EmitExit (EmitContext ec);
4357 class ExpressionEmitter : Emitter {
4358 public ExpressionEmitter (Expression converted, LocalInfo li) :
4359 base (converted, li)
4363 public override void Emit (EmitContext ec) {
4365 // Store pointer in pinned location
4367 converted.Emit (ec);
4371 public override void EmitExit (EmitContext ec)
4373 ec.ig.Emit (OpCodes.Ldc_I4_0);
4374 ec.ig.Emit (OpCodes.Conv_U);
4379 class StringEmitter : Emitter
4381 LocalInfo pinned_string;
4383 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4386 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4387 pinned_string.Pinned = true;
4390 public StringEmitter Resolve (EmitContext rc)
4392 pinned_string.Resolve (rc);
4394 if (TypeManager.int_get_offset_to_string_data == null) {
4395 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4396 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4402 public override void Emit (EmitContext ec)
4404 pinned_string.ResolveVariable (ec);
4406 converted.Emit (ec);
4407 pinned_string.EmitAssign (ec);
4409 // TODO: Should use Binary::Add
4410 pinned_string.Emit (ec);
4411 ec.ig.Emit (OpCodes.Conv_I);
4413 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, TypeManager.int_get_offset_to_string_data, pinned_string.Location);
4414 //pe.InstanceExpression = pinned_string;
4415 pe.Resolve (ec).Emit (ec);
4417 ec.ig.Emit (OpCodes.Add);
4421 public override void EmitExit (EmitContext ec)
4423 ec.ig.Emit (OpCodes.Ldnull);
4424 pinned_string.EmitAssign (ec);
4428 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4431 declarators = decls;
4436 public Statement Statement {
4437 get { return statement; }
4440 public override bool Resolve (EmitContext ec)
4443 Expression.UnsafeError (loc);
4447 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4448 if (texpr == null) {
4449 if (type is VarExpr)
4450 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4455 expr_type = texpr.Type;
4457 data = new Emitter [declarators.Count];
4459 if (!expr_type.IsPointer){
4460 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4465 foreach (Pair p in declarators){
4466 LocalInfo vi = (LocalInfo) p.First;
4467 Expression e = (Expression) p.Second;
4469 vi.VariableInfo.SetAssigned (ec);
4470 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4473 // The rules for the possible declarators are pretty wise,
4474 // but the production on the grammar is more concise.
4476 // So we have to enforce these rules here.
4478 // We do not resolve before doing the case 1 test,
4479 // because the grammar is explicit in that the token &
4480 // is present, so we need to test for this particular case.
4484 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4488 using (ec.Set (EmitContext.Options.FixedInitializerScope)) {
4498 if (e.Type.IsArray){
4499 Type array_type = TypeManager.GetElementType (e.Type);
4502 // Provided that array_type is unmanaged,
4504 if (!TypeManager.VerifyUnManaged (array_type, loc))
4508 // and T* is implicitly convertible to the
4509 // pointer type given in the fixed statement.
4511 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4513 Expression converted = Convert.ImplicitConversionRequired (
4514 ec, array_ptr, vi.VariableType, loc);
4515 if (converted == null)
4519 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4521 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4522 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4523 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4524 new NullPointer (loc),
4527 converted = converted.Resolve (ec);
4529 data [i] = new ExpressionEmitter (converted, vi);
4538 if (e.Type == TypeManager.string_type){
4539 data [i] = new StringEmitter (e, vi, loc).Resolve (ec);
4544 // Case 4: fixed buffer
4545 if (e is FixedBufferPtr) {
4546 data [i++] = new ExpressionEmitter (e, vi);
4551 // Case 1: & object.
4553 Unary u = e as Unary;
4554 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4555 IVariableReference vr = u.Expr as IVariableReference;
4556 if (vr == null || !vr.IsFixed) {
4557 data [i] = new ExpressionEmitter (e, vi);
4561 if (data [i++] == null)
4562 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4564 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4567 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4568 bool ok = statement.Resolve (ec);
4569 bool flow_unreachable = ec.EndFlowBranching ();
4570 has_ret = flow_unreachable;
4575 protected override void DoEmit (EmitContext ec)
4577 for (int i = 0; i < data.Length; i++) {
4581 statement.Emit (ec);
4587 // Clear the pinned variable
4589 for (int i = 0; i < data.Length; i++) {
4590 data [i].EmitExit (ec);
4594 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4596 // Fixed statement cannot be used inside anonymous methods or lambdas
4597 throw new NotSupportedException ();
4600 protected override void CloneTo (CloneContext clonectx, Statement t)
4602 Fixed target = (Fixed) t;
4604 target.type = type.Clone (clonectx);
4605 target.declarators = new ArrayList (declarators.Count);
4606 foreach (Pair p in declarators) {
4607 LocalInfo vi = (LocalInfo) p.First;
4608 Expression e = (Expression) p.Second;
4610 target.declarators.Add (
4611 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4614 target.statement = statement.Clone (clonectx);
4618 public class Catch : Statement {
4619 public readonly string Name;
4621 public Block VarBlock;
4623 Expression type_expr;
4626 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4631 VarBlock = var_block;
4635 public Type CatchType {
4641 public bool IsGeneral {
4643 return type_expr == null;
4647 protected override void DoEmit (EmitContext ec)
4649 ILGenerator ig = ec.ig;
4651 if (CatchType != null)
4652 ig.BeginCatchBlock (CatchType);
4654 ig.BeginCatchBlock (TypeManager.object_type);
4656 if (VarBlock != null)
4660 // TODO: Move to resolve
4661 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4665 // Only to make verifier happy
4666 if (TypeManager.IsGenericParameter (lvr.Type))
4667 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4671 if (lvr.IsHoisted) {
4672 LocalTemporary lt = new LocalTemporary (lvr.Type);
4676 // Variable is at the top of the stack
4677 source = EmptyExpression.Null;
4680 lvr.EmitAssign (ec, source, false, false);
4682 ig.Emit (OpCodes.Pop);
4687 public override bool Resolve (EmitContext ec)
4689 using (ec.With (EmitContext.Options.CatchScope, true)) {
4690 if (type_expr != null) {
4691 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4697 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4698 Error (155, "The type caught or thrown must be derived from System.Exception");
4704 if (!Block.Resolve (ec))
4707 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4708 // emit the "unused variable" warnings.
4709 if (VarBlock != null)
4710 return VarBlock.Resolve (ec);
4716 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4719 type = storey.MutateType (type);
4720 if (VarBlock != null)
4721 VarBlock.MutateHoistedGenericType (storey);
4722 Block.MutateHoistedGenericType (storey);
4725 protected override void CloneTo (CloneContext clonectx, Statement t)
4727 Catch target = (Catch) t;
4729 if (type_expr != null)
4730 target.type_expr = type_expr.Clone (clonectx);
4731 if (VarBlock != null)
4732 target.VarBlock = clonectx.LookupBlock (VarBlock);
4733 target.Block = clonectx.LookupBlock (Block);
4737 public class TryFinally : ExceptionStatement {
4741 public TryFinally (Statement stmt, Block fini, Location l)
4748 public override bool Resolve (EmitContext ec)
4752 ec.StartFlowBranching (this);
4754 if (!stmt.Resolve (ec))
4758 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4759 using (ec.With (EmitContext.Options.FinallyScope, true)) {
4760 if (!fini.Resolve (ec))
4764 ec.EndFlowBranching ();
4766 ResolveReachability (ec);
4771 protected override void EmitPreTryBody (EmitContext ec)
4775 protected override void EmitTryBody (EmitContext ec)
4780 protected override void EmitFinallyBody (EmitContext ec)
4785 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4787 stmt.MutateHoistedGenericType (storey);
4788 fini.MutateHoistedGenericType (storey);
4791 protected override void CloneTo (CloneContext clonectx, Statement t)
4793 TryFinally target = (TryFinally) t;
4795 target.stmt = (Statement) stmt.Clone (clonectx);
4797 target.fini = clonectx.LookupBlock (fini);
4801 public class TryCatch : Statement {
4803 public ArrayList Specific;
4804 public Catch General;
4805 bool inside_try_finally, code_follows;
4807 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4810 this.Specific = catch_clauses;
4811 this.General = null;
4812 this.inside_try_finally = inside_try_finally;
4814 for (int i = 0; i < catch_clauses.Count; ++i) {
4815 Catch c = (Catch) catch_clauses [i];
4817 if (i != catch_clauses.Count - 1)
4818 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4820 catch_clauses.RemoveAt (i);
4828 public override bool Resolve (EmitContext ec)
4832 ec.StartFlowBranching (this);
4834 if (!Block.Resolve (ec))
4837 Type[] prev_catches = new Type [Specific.Count];
4839 foreach (Catch c in Specific){
4840 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4842 if (c.Name != null) {
4843 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4845 throw new Exception ();
4847 vi.VariableInfo = null;
4850 if (!c.Resolve (ec)) {
4855 Type resolved_type = c.CatchType;
4856 for (int ii = 0; ii < last_index; ++ii) {
4857 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4858 Report.Error (160, c.loc,
4859 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4860 TypeManager.CSharpName (prev_catches [ii]));
4865 prev_catches [last_index++] = resolved_type;
4868 if (General != null) {
4869 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4870 foreach (Catch c in Specific){
4871 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4872 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'");
4877 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4879 if (!General.Resolve (ec))
4883 ec.EndFlowBranching ();
4885 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4886 // So, ensure there's some IL code after this statement
4887 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4888 ec.NeedReturnLabel ();
4893 public void SomeCodeFollows ()
4895 code_follows = true;
4898 protected override void DoEmit (EmitContext ec)
4900 ILGenerator ig = ec.ig;
4902 if (!inside_try_finally)
4903 ig.BeginExceptionBlock ();
4907 foreach (Catch c in Specific)
4910 if (General != null)
4913 if (!inside_try_finally)
4914 ig.EndExceptionBlock ();
4917 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4919 Block.MutateHoistedGenericType (storey);
4921 if (General != null)
4922 General.MutateHoistedGenericType (storey);
4923 if (Specific != null) {
4924 foreach (Catch c in Specific)
4925 c.MutateHoistedGenericType (storey);
4929 protected override void CloneTo (CloneContext clonectx, Statement t)
4931 TryCatch target = (TryCatch) t;
4933 target.Block = clonectx.LookupBlock (Block);
4934 if (General != null)
4935 target.General = (Catch) General.Clone (clonectx);
4936 if (Specific != null){
4937 target.Specific = new ArrayList ();
4938 foreach (Catch c in Specific)
4939 target.Specific.Add (c.Clone (clonectx));
4944 // FIXME: Why is it almost exact copy of Using ??
4945 public class UsingTemporary : ExceptionStatement {
4946 TemporaryVariable local_copy;
4947 public Statement Statement;
4951 public UsingTemporary (Expression expr, Statement stmt, Location l)
4958 public override bool Resolve (EmitContext ec)
4960 expr = expr.Resolve (ec);
4964 expr_type = expr.Type;
4966 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4967 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4968 Using.Error_IsNotConvertibleToIDisposable (expr);
4973 local_copy = new TemporaryVariable (expr_type, loc);
4974 local_copy.Resolve (ec);
4976 ec.StartFlowBranching (this);
4978 bool ok = Statement.Resolve (ec);
4980 ec.EndFlowBranching ();
4982 ResolveReachability (ec);
4984 if (TypeManager.void_dispose_void == null) {
4985 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4986 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4992 protected override void EmitPreTryBody (EmitContext ec)
4994 local_copy.EmitAssign (ec, expr);
4997 protected override void EmitTryBody (EmitContext ec)
4999 Statement.Emit (ec);
5002 protected override void EmitFinallyBody (EmitContext ec)
5004 ILGenerator ig = ec.ig;
5005 if (!TypeManager.IsStruct (expr_type)) {
5006 Label skip = ig.DefineLabel ();
5007 local_copy.Emit (ec);
5008 ig.Emit (OpCodes.Brfalse, skip);
5009 local_copy.Emit (ec);
5010 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5011 ig.MarkLabel (skip);
5015 Expression ml = Expression.MemberLookup (
5016 ec.CurrentType, TypeManager.idisposable_type, expr_type,
5017 "Dispose", Location.Null);
5019 if (!(ml is MethodGroupExpr)) {
5020 local_copy.Emit (ec);
5021 ig.Emit (OpCodes.Box, expr_type);
5022 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5026 MethodInfo mi = null;
5028 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5029 if (TypeManager.GetParameterData (mk).Count == 0) {
5036 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5040 local_copy.AddressOf (ec, AddressOp.Load);
5041 ig.Emit (OpCodes.Call, mi);
5044 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5046 expr_type = storey.MutateType (expr_type);
5047 local_copy.MutateHoistedGenericType (storey);
5048 Statement.MutateHoistedGenericType (storey);
5051 protected override void CloneTo (CloneContext clonectx, Statement t)
5053 UsingTemporary target = (UsingTemporary) t;
5055 target.expr = expr.Clone (clonectx);
5056 target.Statement = Statement.Clone (clonectx);
5060 public class Using : ExceptionStatement {
5062 public Statement EmbeddedStatement {
5063 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
5069 ExpressionStatement assign;
5071 public Using (Expression var, Expression init, Statement stmt, Location l)
5079 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
5081 Report.SymbolRelatedToPreviousError (expr.Type);
5082 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
5083 expr.GetSignatureForError ());
5086 protected override void EmitPreTryBody (EmitContext ec)
5088 assign.EmitStatement (ec);
5091 protected override void EmitTryBody (EmitContext ec)
5096 protected override void EmitFinallyBody (EmitContext ec)
5098 ILGenerator ig = ec.ig;
5099 Label skip = ig.DefineLabel ();
5101 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5102 if (emit_null_check) {
5104 ig.Emit (OpCodes.Brfalse, skip);
5107 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5109 if (emit_null_check)
5110 ig.MarkLabel (skip);
5113 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5115 assign.MutateHoistedGenericType (storey);
5116 var.MutateHoistedGenericType (storey);
5117 stmt.MutateHoistedGenericType (storey);
5120 public override bool Resolve (EmitContext ec)
5122 if (!ResolveVariable (ec))
5125 ec.StartFlowBranching (this);
5127 bool ok = stmt.Resolve (ec);
5129 ec.EndFlowBranching ();
5131 ResolveReachability (ec);
5133 if (TypeManager.void_dispose_void == null) {
5134 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5135 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5141 bool ResolveVariable (EmitContext ec)
5143 assign = new SimpleAssign (var, init, loc);
5144 assign = assign.ResolveStatement (ec);
5148 if (assign.Type == TypeManager.idisposable_type ||
5149 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5153 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5155 Error_IsNotConvertibleToIDisposable (var);
5159 throw new NotImplementedException ("covariance?");
5162 protected override void CloneTo (CloneContext clonectx, Statement t)
5164 Using target = (Using) t;
5166 target.var = var.Clone (clonectx);
5167 target.init = init.Clone (clonectx);
5168 target.stmt = stmt.Clone (clonectx);
5173 /// Implementation of the foreach C# statement
5175 public class Foreach : Statement {
5177 sealed class ArrayForeach : Statement
5179 class ArrayCounter : TemporaryVariable
5181 StatementExpression increment;
5183 public ArrayCounter (Location loc)
5184 : base (TypeManager.int32_type, loc)
5188 public void ResolveIncrement (EmitContext ec)
5190 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5191 increment.Resolve (ec);
5194 public void EmitIncrement (EmitContext ec)
5196 increment.Emit (ec);
5200 readonly Foreach for_each;
5201 readonly Statement statement;
5204 TemporaryVariable[] lengths;
5205 Expression [] length_exprs;
5206 ArrayCounter[] counter;
5208 TemporaryVariable copy;
5211 public ArrayForeach (Foreach @foreach, int rank)
5213 for_each = @foreach;
5214 statement = for_each.statement;
5217 counter = new ArrayCounter [rank];
5218 length_exprs = new Expression [rank];
5221 // Only use temporary length variables when dealing with
5222 // multi-dimensional arrays
5225 lengths = new TemporaryVariable [rank];
5228 protected override void CloneTo (CloneContext clonectx, Statement target)
5230 throw new NotImplementedException ();
5233 public override bool Resolve (EmitContext ec)
5235 copy = new TemporaryVariable (for_each.expr.Type, loc);
5238 int rank = length_exprs.Length;
5239 Arguments list = new Arguments (rank);
5240 for (int i = 0; i < rank; i++) {
5241 counter [i] = new ArrayCounter (loc);
5242 counter [i].ResolveIncrement (ec);
5245 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5247 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5248 lengths [i].Resolve (ec);
5250 Arguments args = new Arguments (1);
5251 args.Add (new Argument (new IntConstant (i, loc)));
5252 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5255 list.Add (new Argument (counter [i]));
5258 access = new ElementAccess (copy, list).Resolve (ec);
5262 Expression var_type = for_each.type;
5263 VarExpr ve = var_type as VarExpr;
5265 // Infer implicitly typed local variable from foreach array type
5266 var_type = new TypeExpression (access.Type, ve.Location);
5269 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5270 if (var_type == null)
5273 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5279 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5280 ec.CurrentBranching.CreateSibling ();
5282 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5283 if (for_each.variable == null)
5286 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5287 if (!statement.Resolve (ec))
5289 ec.EndFlowBranching ();
5291 // There's no direct control flow from the end of the embedded statement to the end of the loop
5292 ec.CurrentBranching.CurrentUsageVector.Goto ();
5294 ec.EndFlowBranching ();
5299 protected override void DoEmit (EmitContext ec)
5301 ILGenerator ig = ec.ig;
5303 copy.EmitAssign (ec, for_each.expr);
5305 int rank = length_exprs.Length;
5306 Label[] test = new Label [rank];
5307 Label[] loop = new Label [rank];
5309 for (int i = 0; i < rank; i++) {
5310 test [i] = ig.DefineLabel ();
5311 loop [i] = ig.DefineLabel ();
5313 if (lengths != null)
5314 lengths [i].EmitAssign (ec, length_exprs [i]);
5317 IntConstant zero = new IntConstant (0, loc);
5318 for (int i = 0; i < rank; i++) {
5319 counter [i].EmitAssign (ec, zero);
5321 ig.Emit (OpCodes.Br, test [i]);
5322 ig.MarkLabel (loop [i]);
5325 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5327 statement.Emit (ec);
5329 ig.MarkLabel (ec.LoopBegin);
5331 for (int i = rank - 1; i >= 0; i--){
5332 counter [i].EmitIncrement (ec);
5334 ig.MarkLabel (test [i]);
5335 counter [i].Emit (ec);
5337 if (lengths != null)
5338 lengths [i].Emit (ec);
5340 length_exprs [i].Emit (ec);
5342 ig.Emit (OpCodes.Blt, loop [i]);
5345 ig.MarkLabel (ec.LoopEnd);
5348 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5350 for_each.expr.MutateHoistedGenericType (storey);
5352 copy.MutateHoistedGenericType (storey);
5353 conv.MutateHoistedGenericType (storey);
5354 statement.MutateHoistedGenericType (storey);
5356 for (int i = 0; i < counter.Length; i++) {
5357 counter [i].MutateHoistedGenericType (storey);
5358 if (lengths != null)
5359 lengths [i].MutateHoistedGenericType (storey);
5364 sealed class CollectionForeach : Statement
5366 class CollectionForeachStatement : Statement
5369 Expression variable, current, conv;
5370 Statement statement;
5373 public CollectionForeachStatement (Type type, Expression variable,
5374 Expression current, Statement statement,
5378 this.variable = variable;
5379 this.current = current;
5380 this.statement = statement;
5384 protected override void CloneTo (CloneContext clonectx, Statement target)
5386 throw new NotImplementedException ();
5389 public override bool Resolve (EmitContext ec)
5391 current = current.Resolve (ec);
5392 if (current == null)
5395 conv = Convert.ExplicitConversion (ec, current, type, loc);
5399 assign = new SimpleAssign (variable, conv, loc);
5400 if (assign.Resolve (ec) == null)
5403 if (!statement.Resolve (ec))
5409 protected override void DoEmit (EmitContext ec)
5411 assign.EmitStatement (ec);
5412 statement.Emit (ec);
5415 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5417 assign.MutateHoistedGenericType (storey);
5418 statement.MutateHoistedGenericType (storey);
5422 Expression variable, expr;
5423 Statement statement;
5425 TemporaryVariable enumerator;
5430 MethodGroupExpr get_enumerator;
5431 PropertyExpr get_current;
5432 MethodInfo move_next;
5433 Expression var_type;
5434 Type enumerator_type;
5435 bool enumerator_found;
5437 public CollectionForeach (Expression var_type, Expression var,
5438 Expression expr, Statement stmt, Location l)
5440 this.var_type = var_type;
5441 this.variable = var;
5447 protected override void CloneTo (CloneContext clonectx, Statement target)
5449 throw new NotImplementedException ();
5452 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5454 Type return_type = mi.ReturnType;
5457 // Ok, we can access it, now make sure that we can do something
5458 // with this `GetEnumerator'
5461 if (return_type == TypeManager.ienumerator_type ||
5462 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5464 // If it is not an interface, lets try to find the methods ourselves.
5465 // For example, if we have:
5466 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5467 // We can avoid the iface call. This is a runtime perf boost.
5468 // even bigger if we have a ValueType, because we avoid the cost
5471 // We have to make sure that both methods exist for us to take
5472 // this path. If one of the methods does not exist, we will just
5473 // use the interface. Sadly, this complex if statement is the only
5474 // way I could do this without a goto
5477 if (TypeManager.bool_movenext_void == null) {
5478 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5479 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5482 if (TypeManager.ienumerator_getcurrent == null) {
5483 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5484 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5488 // Prefer a generic enumerator over a non-generic one.
5490 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5491 enumerator_type = return_type;
5492 if (!FetchGetCurrent (ec, return_type))
5493 get_current = new PropertyExpr (
5494 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5495 if (!FetchMoveNext (return_type))
5496 move_next = TypeManager.bool_movenext_void;
5500 if (return_type.IsInterface ||
5501 !FetchMoveNext (return_type) ||
5502 !FetchGetCurrent (ec, return_type)) {
5503 enumerator_type = return_type;
5504 move_next = TypeManager.bool_movenext_void;
5505 get_current = new PropertyExpr (
5506 ec.CurrentType, TypeManager.ienumerator_getcurrent, loc);
5511 // Ok, so they dont return an IEnumerable, we will have to
5512 // find if they support the GetEnumerator pattern.
5515 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5516 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",
5517 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5522 enumerator_type = return_type;
5528 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5530 bool FetchMoveNext (Type t)
5532 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5534 BindingFlags.Public | BindingFlags.Instance,
5537 foreach (MemberInfo m in move_next_list){
5538 MethodInfo mi = (MethodInfo) m;
5540 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5541 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5551 // Retrieves a `public T get_Current ()' method from the Type `t'
5553 bool FetchGetCurrent (EmitContext ec, Type t)
5555 PropertyExpr pe = Expression.MemberLookup (
5556 ec.CurrentType, t, "Current", MemberTypes.Property,
5557 Expression.AllBindingFlags, loc) as PropertyExpr;
5565 void Error_Enumerator ()
5567 if (enumerator_found) {
5571 Report.Error (1579, loc,
5572 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5573 TypeManager.CSharpName (expr.Type));
5576 bool IsOverride (MethodInfo m)
5578 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5580 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5582 if (m is MethodBuilder)
5585 MethodInfo base_method = m.GetBaseDefinition ();
5586 return base_method != m;
5589 bool TryType (EmitContext ec, Type t)
5591 MethodGroupExpr mg = Expression.MemberLookup (
5592 ec.CurrentType, t, "GetEnumerator", MemberTypes.Method,
5593 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5597 MethodInfo result = null;
5598 MethodInfo tmp_move_next = null;
5599 PropertyExpr tmp_get_cur = null;
5600 Type tmp_enumerator_type = enumerator_type;
5601 foreach (MethodInfo mi in mg.Methods) {
5602 if (TypeManager.GetParameterData (mi).Count != 0)
5605 // Check whether GetEnumerator is public
5606 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5609 if (IsOverride (mi))
5612 enumerator_found = true;
5614 if (!GetEnumeratorFilter (ec, mi))
5617 if (result != null) {
5618 if (TypeManager.IsGenericType (result.ReturnType)) {
5619 if (!TypeManager.IsGenericType (mi.ReturnType))
5622 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5623 Report.SymbolRelatedToPreviousError (t);
5624 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5625 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5626 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5630 // Always prefer generics enumerators
5631 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5632 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5633 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5636 Report.SymbolRelatedToPreviousError (result);
5637 Report.SymbolRelatedToPreviousError (mi);
5638 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5639 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5644 tmp_move_next = move_next;
5645 tmp_get_cur = get_current;
5646 tmp_enumerator_type = enumerator_type;
5647 if (mi.DeclaringType == t)
5651 if (result != null) {
5652 move_next = tmp_move_next;
5653 get_current = tmp_get_cur;
5654 enumerator_type = tmp_enumerator_type;
5655 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5656 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5658 if (t != expr.Type) {
5659 expr = Convert.ExplicitConversion (
5662 throw new InternalErrorException ();
5665 get_enumerator.InstanceExpression = expr;
5666 get_enumerator.IsBase = t != expr.Type;
5674 bool ProbeCollectionType (EmitContext ec, Type t)
5676 int errors = Report.Errors;
5677 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5678 if (TryType (ec, tt))
5683 if (Report.Errors > errors)
5687 // Now try to find the method in the interfaces
5689 Type [] ifaces = TypeManager.GetInterfaces (t);
5690 foreach (Type i in ifaces){
5691 if (TryType (ec, i))
5698 public override bool Resolve (EmitContext ec)
5700 enumerator_type = TypeManager.ienumerator_type;
5702 if (!ProbeCollectionType (ec, expr.Type)) {
5703 Error_Enumerator ();
5707 VarExpr ve = var_type as VarExpr;
5709 // Infer implicitly typed local variable from foreach enumerable type
5710 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5713 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5714 if (var_type == null)
5717 enumerator = new TemporaryVariable (enumerator_type, loc);
5718 enumerator.Resolve (ec);
5720 init = new Invocation (get_enumerator, null);
5721 init = init.Resolve (ec);
5725 Expression move_next_expr;
5727 MemberInfo[] mi = new MemberInfo[] { move_next };
5728 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5729 mg.InstanceExpression = enumerator;
5731 move_next_expr = new Invocation (mg, null);
5734 get_current.InstanceExpression = enumerator;
5736 Statement block = new CollectionForeachStatement (
5737 var_type.Type, variable, get_current, statement, loc);
5739 loop = new While (move_next_expr, block, loc);
5742 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5743 if (implements_idisposable || !enumerator_type.IsSealed) {
5744 wrapper = new DisposableWrapper (this, implements_idisposable);
5746 wrapper = new NonDisposableWrapper (this);
5749 return wrapper.Resolve (ec);
5752 protected override void DoEmit (EmitContext ec)
5757 class NonDisposableWrapper : Statement {
5758 CollectionForeach parent;
5760 internal NonDisposableWrapper (CollectionForeach parent)
5762 this.parent = parent;
5765 protected override void CloneTo (CloneContext clonectx, Statement target)
5767 throw new NotSupportedException ();
5770 public override bool Resolve (EmitContext ec)
5772 return parent.ResolveLoop (ec);
5775 protected override void DoEmit (EmitContext ec)
5777 parent.EmitLoopInit (ec);
5778 parent.EmitLoopBody (ec);
5781 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5783 throw new NotSupportedException ();
5787 sealed class DisposableWrapper : ExceptionStatement
5789 CollectionForeach parent;
5790 bool implements_idisposable;
5792 internal DisposableWrapper (CollectionForeach parent, bool implements)
5794 this.parent = parent;
5795 this.implements_idisposable = implements;
5798 protected override void CloneTo (CloneContext clonectx, Statement target)
5800 throw new NotSupportedException ();
5803 public override bool Resolve (EmitContext ec)
5807 ec.StartFlowBranching (this);
5809 if (!parent.ResolveLoop (ec))
5812 ec.EndFlowBranching ();
5814 ResolveReachability (ec);
5816 if (TypeManager.void_dispose_void == null) {
5817 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5818 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5823 protected override void EmitPreTryBody (EmitContext ec)
5825 parent.EmitLoopInit (ec);
5828 protected override void EmitTryBody (EmitContext ec)
5830 parent.EmitLoopBody (ec);
5833 protected override void EmitFinallyBody (EmitContext ec)
5835 Expression instance = parent.enumerator;
5836 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5837 ILGenerator ig = ec.ig;
5839 parent.enumerator.Emit (ec);
5841 Label call_dispose = ig.DefineLabel ();
5843 if (!implements_idisposable) {
5844 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5845 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5851 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5853 // using 'endfinally' to empty the evaluation stack
5854 ig.Emit (OpCodes.Endfinally);
5855 ig.MarkLabel (call_dispose);
5858 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5861 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5863 throw new NotSupportedException ();
5867 bool ResolveLoop (EmitContext ec)
5869 return loop.Resolve (ec);
5872 void EmitLoopInit (EmitContext ec)
5874 enumerator.EmitAssign (ec, init);
5877 void EmitLoopBody (EmitContext ec)
5882 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5884 enumerator_type = storey.MutateType (enumerator_type);
5885 init.MutateHoistedGenericType (storey);
5886 loop.MutateHoistedGenericType (storey);
5891 Expression variable;
5893 Statement statement;
5895 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5896 Statement stmt, Location l)
5899 this.variable = var;
5905 public Statement Statement {
5906 get { return statement; }
5909 public override bool Resolve (EmitContext ec)
5911 expr = expr.Resolve (ec);
5916 Report.Error (186, loc, "Use of null is not valid in this context");
5920 if (expr.Type == TypeManager.string_type) {
5921 statement = new ArrayForeach (this, 1);
5922 } else if (expr.Type.IsArray) {
5923 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5925 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5926 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5927 expr.ExprClassName);
5931 statement = new CollectionForeach (type, variable, expr, statement, loc);
5934 return statement.Resolve (ec);
5937 protected override void DoEmit (EmitContext ec)
5939 ILGenerator ig = ec.ig;
5941 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5942 ec.LoopBegin = ig.DefineLabel ();
5943 ec.LoopEnd = ig.DefineLabel ();
5945 statement.Emit (ec);
5947 ec.LoopBegin = old_begin;
5948 ec.LoopEnd = old_end;
5951 protected override void CloneTo (CloneContext clonectx, Statement t)
5953 Foreach target = (Foreach) t;
5955 target.type = type.Clone (clonectx);
5956 target.variable = variable.Clone (clonectx);
5957 target.expr = expr.Clone (clonectx);
5958 target.statement = statement.Clone (clonectx);
5961 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5963 statement.MutateHoistedGenericType (storey);