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)
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 (Expr.Type != ec.ReturnType) {
831 if (ec.InferReturnType) {
833 // void cannot be used in contextual return
835 if (Expr.Type == TypeManager.void_type)
838 ec.ReturnType = Expr.Type;
840 Expr = Convert.ImplicitConversionRequired (
841 ec, Expr, ec.ReturnType, loc);
845 Report.Error (1662, loc,
846 "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",
847 am.ContainerType, am.GetSignatureForError ());
857 protected override void DoEmit (EmitContext ec)
863 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
867 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
869 ec.ig.Emit (OpCodes.Ret);
872 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
875 Expr.MutateHoistedGenericType (storey);
878 protected override void CloneTo (CloneContext clonectx, Statement t)
880 Return target = (Return) t;
881 // It's null for simple return;
883 target.Expr = Expr.Clone (clonectx);
887 public class Goto : Statement {
889 LabeledStatement label;
892 public override bool Resolve (EmitContext ec)
894 int errors = Report.Errors;
895 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
896 ec.CurrentBranching.CurrentUsageVector.Goto ();
897 return errors == Report.Errors;
900 public Goto (string label, Location l)
906 public string Target {
907 get { return target; }
910 public void SetResolvedTarget (LabeledStatement label)
913 label.AddReference ();
916 protected override void CloneTo (CloneContext clonectx, Statement target)
921 protected override void DoEmit (EmitContext ec)
924 throw new InternalErrorException ("goto emitted before target resolved");
925 Label l = label.LabelTarget (ec);
926 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
929 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
934 public class LabeledStatement : Statement {
941 FlowBranching.UsageVector vectors;
943 public LabeledStatement (string name, Location l)
949 public Label LabelTarget (EmitContext ec)
954 label = ec.ig.DefineLabel ();
964 public bool IsDefined {
965 get { return defined; }
968 public bool HasBeenReferenced {
969 get { return referenced; }
972 public FlowBranching.UsageVector JumpOrigins {
973 get { return vectors; }
976 public void AddUsageVector (FlowBranching.UsageVector vector)
978 vector = vector.Clone ();
979 vector.Next = vectors;
983 protected override void CloneTo (CloneContext clonectx, Statement target)
988 public override bool Resolve (EmitContext ec)
990 // this flow-branching will be terminated when the surrounding block ends
991 ec.StartFlowBranching (this);
995 protected override void DoEmit (EmitContext ec)
997 if (ig != null && ig != ec.ig)
998 throw new InternalErrorException ("cannot happen");
1000 ec.ig.MarkLabel (label);
1003 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1007 public void AddReference ()
1015 /// `goto default' statement
1017 public class GotoDefault : Statement {
1019 public GotoDefault (Location l)
1024 protected override void CloneTo (CloneContext clonectx, Statement target)
1029 public override bool Resolve (EmitContext ec)
1031 ec.CurrentBranching.CurrentUsageVector.Goto ();
1035 protected override void DoEmit (EmitContext ec)
1037 if (ec.Switch == null){
1038 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1042 if (!ec.Switch.GotDefault){
1043 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1046 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1049 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1055 /// `goto case' statement
1057 public class GotoCase : Statement {
1061 public GotoCase (Expression e, Location l)
1067 public override bool Resolve (EmitContext ec)
1069 if (ec.Switch == null){
1070 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1074 ec.CurrentBranching.CurrentUsageVector.Goto ();
1076 expr = expr.Resolve (ec);
1080 Constant c = expr as Constant;
1082 Error (150, "A constant value is expected");
1086 Type type = ec.Switch.SwitchType;
1087 Constant res = c.TryReduce (ec, type, c.Location);
1089 c.Error_ValueCannotBeConverted (ec, loc, type, true);
1093 if (!Convert.ImplicitStandardConversionExists (c, type))
1094 Report.Warning (469, 2, loc,
1095 "The `goto case' value is not implicitly convertible to type `{0}'",
1096 TypeManager.CSharpName (type));
1098 object val = res.GetValue ();
1100 val = SwitchLabel.NullStringCase;
1102 sl = (SwitchLabel) ec.Switch.Elements [val];
1105 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1106 (c.GetValue () == null ? "null" : val.ToString ()));
1113 protected override void DoEmit (EmitContext ec)
1115 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1118 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1120 expr.MutateHoistedGenericType (storey);
1123 protected override void CloneTo (CloneContext clonectx, Statement t)
1125 GotoCase target = (GotoCase) t;
1127 target.expr = expr.Clone (clonectx);
1131 public class Throw : Statement {
1134 public Throw (Expression expr, Location l)
1140 public override bool Resolve (EmitContext ec)
1143 ec.CurrentBranching.CurrentUsageVector.Goto ();
1144 return ec.CurrentBranching.CheckRethrow (loc);
1147 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1148 ec.CurrentBranching.CurrentUsageVector.Goto ();
1155 if ((t != TypeManager.exception_type) &&
1156 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1157 t != TypeManager.null_type) {
1158 Error (155, "The type caught or thrown must be derived from System.Exception");
1164 protected override void DoEmit (EmitContext ec)
1167 ec.ig.Emit (OpCodes.Rethrow);
1171 ec.ig.Emit (OpCodes.Throw);
1175 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1178 expr.MutateHoistedGenericType (storey);
1181 protected override void CloneTo (CloneContext clonectx, Statement t)
1183 Throw target = (Throw) t;
1186 target.expr = expr.Clone (clonectx);
1190 public class Break : Statement {
1192 public Break (Location l)
1197 bool unwind_protect;
1199 public override bool Resolve (EmitContext ec)
1201 int errors = Report.Errors;
1202 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1203 ec.CurrentBranching.CurrentUsageVector.Goto ();
1204 return errors == Report.Errors;
1207 protected override void DoEmit (EmitContext ec)
1209 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1212 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1216 protected override void CloneTo (CloneContext clonectx, Statement t)
1222 public class Continue : Statement {
1224 public Continue (Location l)
1229 bool unwind_protect;
1231 public override bool Resolve (EmitContext ec)
1233 int errors = Report.Errors;
1234 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1235 ec.CurrentBranching.CurrentUsageVector.Goto ();
1236 return errors == Report.Errors;
1239 protected override void DoEmit (EmitContext ec)
1241 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1244 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1248 protected override void CloneTo (CloneContext clonectx, Statement t)
1254 public interface ILocalVariable
1256 void Emit (EmitContext ec);
1257 void EmitAssign (EmitContext ec);
1258 void EmitAddressOf (EmitContext ec);
1261 public interface IKnownVariable {
1262 Block Block { get; }
1263 Location Location { get; }
1267 // The information about a user-perceived local variable
1269 public class LocalInfo : IKnownVariable, ILocalVariable {
1270 public readonly FullNamedExpression Type;
1272 public Type VariableType;
1273 public readonly string Name;
1274 public readonly Location Location;
1275 public readonly Block Block;
1277 public VariableInfo VariableInfo;
1278 public HoistedVariable HoistedVariableReference;
1287 CompilerGenerated = 64,
1291 public enum ReadOnlyContext: byte {
1298 ReadOnlyContext ro_context;
1299 LocalBuilder builder;
1301 public LocalInfo (FullNamedExpression type, string name, Block block, Location l)
1309 public LocalInfo (DeclSpace ds, Block block, Location l)
1311 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1316 public void ResolveVariable (EmitContext ec)
1318 if (HoistedVariableReference != null)
1321 if (builder == null) {
1324 // This is needed to compile on both .NET 1.x and .NET 2.x
1325 // the later introduced `DeclareLocal (Type t, bool pinned)'
1327 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1329 builder = ec.ig.DeclareLocal (TypeManager.TypeToReflectionType (VariableType));
1333 public void Emit (EmitContext ec)
1335 ec.ig.Emit (OpCodes.Ldloc, builder);
1338 public void EmitAssign (EmitContext ec)
1340 ec.ig.Emit (OpCodes.Stloc, builder);
1343 public void EmitAddressOf (EmitContext ec)
1345 ec.ig.Emit (OpCodes.Ldloca, builder);
1348 public void EmitSymbolInfo (EmitContext ec)
1350 if (builder != null)
1351 ec.DefineLocalVariable (Name, builder);
1354 public bool IsThisAssigned (EmitContext ec)
1356 if (VariableInfo == null)
1357 throw new Exception ();
1359 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1362 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1365 public bool IsAssigned (EmitContext ec)
1367 if (VariableInfo == null)
1368 throw new Exception ();
1370 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1373 public bool Resolve (EmitContext ec)
1375 if (VariableType != null)
1378 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1382 VariableType = texpr.Type;
1384 if (TypeManager.IsGenericParameter (VariableType))
1387 if (VariableType.IsAbstract && VariableType.IsSealed) {
1388 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1392 if (VariableType.IsPointer && !ec.InUnsafe)
1393 Expression.UnsafeError (Location);
1398 public bool IsConstant {
1399 get { return (flags & Flags.IsConstant) != 0; }
1400 set { flags |= Flags.IsConstant; }
1403 public bool AddressTaken {
1404 get { return (flags & Flags.AddressTaken) != 0; }
1405 set { flags |= Flags.AddressTaken; }
1408 public bool CompilerGenerated {
1409 get { return (flags & Flags.CompilerGenerated) != 0; }
1410 set { flags |= Flags.CompilerGenerated; }
1413 public override string ToString ()
1415 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1416 Name, Type, VariableInfo, Location);
1420 get { return (flags & Flags.Used) != 0; }
1421 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1424 public bool ReadOnly {
1425 get { return (flags & Flags.ReadOnly) != 0; }
1428 public void SetReadOnlyContext (ReadOnlyContext context)
1430 flags |= Flags.ReadOnly;
1431 ro_context = context;
1434 public string GetReadOnlyContext ()
1437 throw new InternalErrorException ("Variable is not readonly");
1439 switch (ro_context) {
1440 case ReadOnlyContext.Fixed:
1441 return "fixed variable";
1442 case ReadOnlyContext.Foreach:
1443 return "foreach iteration variable";
1444 case ReadOnlyContext.Using:
1445 return "using variable";
1447 throw new NotImplementedException ();
1451 // Whether the variable is pinned, if Pinned the variable has been
1452 // allocated in a pinned slot with DeclareLocal.
1454 public bool Pinned {
1455 get { return (flags & Flags.Pinned) != 0; }
1456 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1459 public bool IsThis {
1460 get { return (flags & Flags.IsThis) != 0; }
1461 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1464 Block IKnownVariable.Block {
1465 get { return Block; }
1468 Location IKnownVariable.Location {
1469 get { return Location; }
1472 public LocalInfo Clone (CloneContext clonectx)
1475 // Variables in anonymous block are not resolved yet
1477 if (VariableType == null)
1478 return new LocalInfo ((FullNamedExpression) Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1481 // Variables in method block are resolved
1483 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1484 li.VariableType = VariableType;
1490 /// Block represents a C# block.
1494 /// This class is used in a number of places: either to represent
1495 /// explicit blocks that the programmer places or implicit blocks.
1497 /// Implicit blocks are used as labels or to introduce variable
1500 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1501 /// they contain extra information that is not necessary on normal blocks.
1503 public class Block : Statement {
1504 public Block Parent;
1505 public Location StartLocation;
1506 public Location EndLocation = Location.Null;
1508 public ExplicitBlock Explicit;
1509 public ToplevelBlock Toplevel; // TODO: Use Explicit
1512 public enum Flags : byte {
1515 VariablesInitialized = 4,
1519 HasCapturedVariable = 64,
1520 HasCapturedThis = 128
1522 protected Flags flags;
1524 public bool Unchecked {
1525 get { return (flags & Flags.Unchecked) != 0; }
1526 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1529 public bool Unsafe {
1530 get { return (flags & Flags.Unsafe) != 0; }
1531 set { flags |= Flags.Unsafe; }
1535 // The statements in this block
1537 protected ArrayList statements;
1540 // An array of Blocks. We keep track of children just
1541 // to generate the local variable declarations.
1543 // Statements and child statements are handled through the
1549 // Labels. (label, block) pairs.
1551 protected HybridDictionary labels;
1554 // Keeps track of (name, type) pairs
1556 IDictionary variables;
1559 // Keeps track of constants
1560 HybridDictionary constants;
1563 // Temporary variables.
1565 ArrayList temporary_variables;
1568 // If this is a switch section, the enclosing switch block.
1572 protected ArrayList scope_initializers;
1574 ArrayList anonymous_children;
1576 protected static int id;
1580 int assignable_slots;
1581 bool unreachable_shown;
1584 public Block (Block parent)
1585 : this (parent, (Flags) 0, Location.Null, Location.Null)
1588 public Block (Block parent, Flags flags)
1589 : this (parent, flags, Location.Null, Location.Null)
1592 public Block (Block parent, Location start, Location end)
1593 : this (parent, (Flags) 0, start, end)
1597 // Useful when TopLevel block is downgraded to normal block
1599 public Block (ToplevelBlock parent, ToplevelBlock source)
1600 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1602 statements = source.statements;
1603 children = source.children;
1604 labels = source.labels;
1605 variables = source.variables;
1606 constants = source.constants;
1607 switch_block = source.switch_block;
1610 public Block (Block parent, Flags flags, Location start, Location end)
1612 if (parent != null) {
1613 parent.AddChild (this);
1615 // the appropriate constructors will fixup these fields
1616 Toplevel = parent.Toplevel;
1617 Explicit = parent.Explicit;
1620 this.Parent = parent;
1622 this.StartLocation = start;
1623 this.EndLocation = end;
1626 statements = new ArrayList (4);
1629 public Block CreateSwitchBlock (Location start)
1631 // FIXME: should this be implicit?
1632 Block new_block = new ExplicitBlock (this, start, start);
1633 new_block.switch_block = this;
1638 get { return this_id; }
1641 public IDictionary Variables {
1643 if (variables == null)
1644 variables = new ListDictionary ();
1649 void AddChild (Block b)
1651 if (children == null)
1652 children = new ArrayList (1);
1657 public void SetEndLocation (Location loc)
1662 protected static void Error_158 (string name, Location loc)
1664 Report.Error (158, loc, "The label `{0}' shadows another label " +
1665 "by the same name in a contained scope", name);
1669 /// Adds a label to the current block.
1673 /// false if the name already exists in this block. true
1677 public bool AddLabel (LabeledStatement target)
1679 if (switch_block != null)
1680 return switch_block.AddLabel (target);
1682 string name = target.Name;
1685 while (cur != null) {
1686 LabeledStatement s = cur.DoLookupLabel (name);
1688 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1689 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1693 if (this == Explicit)
1699 while (cur != null) {
1700 if (cur.DoLookupLabel (name) != null) {
1701 Error_158 (name, target.loc);
1705 if (children != null) {
1706 foreach (Block b in children) {
1707 LabeledStatement s = b.DoLookupLabel (name);
1711 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1712 Error_158 (name, target.loc);
1720 Toplevel.CheckError158 (name, target.loc);
1723 labels = new HybridDictionary();
1725 labels.Add (name, target);
1729 public LabeledStatement LookupLabel (string name)
1731 LabeledStatement s = DoLookupLabel (name);
1735 if (children == null)
1738 foreach (Block child in children) {
1739 if (Explicit != child.Explicit)
1742 s = child.LookupLabel (name);
1750 LabeledStatement DoLookupLabel (string name)
1752 if (switch_block != null)
1753 return switch_block.LookupLabel (name);
1756 if (labels.Contains (name))
1757 return ((LabeledStatement) labels [name]);
1762 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1765 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1766 while (kvi == null) {
1767 b = b.Explicit.Parent;
1770 kvi = b.Explicit.GetKnownVariable (name);
1776 // Is kvi.Block nested inside 'b'
1777 if (b.Explicit != kvi.Block.Explicit) {
1779 // If a variable by the same name it defined in a nested block of this
1780 // block, we violate the invariant meaning in a block.
1783 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1784 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1789 // It's ok if the definition is in a nested subblock of b, but not
1790 // nested inside this block -- a definition in a sibling block
1791 // should not affect us.
1797 // Block 'b' and kvi.Block are the same textual block.
1798 // However, different variables are extant.
1800 // Check if the variable is in scope in both blocks. We use
1801 // an indirect check that depends on AddVariable doing its
1802 // part in maintaining the invariant-meaning-in-block property.
1804 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1807 if (this is ToplevelBlock) {
1808 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1809 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1814 // Even though we detected the error when the name is used, we
1815 // treat it as if the variable declaration was in error.
1817 Report.SymbolRelatedToPreviousError (loc, name);
1818 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1822 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1824 LocalInfo vi = GetLocalInfo (name);
1826 Report.SymbolRelatedToPreviousError (vi.Location, name);
1827 if (Explicit == vi.Block.Explicit) {
1828 Error_AlreadyDeclared (l, name, null);
1830 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1831 "parent or current" : "parent");
1836 if (block != null) {
1837 Expression e = block.GetParameterReference (name, Location.Null);
1839 ParameterReference pr = e as ParameterReference;
1840 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1841 Error_AlreadyDeclared (loc, name);
1843 Error_AlreadyDeclared (loc, name, "parent or current");
1851 public LocalInfo AddVariable (Expression type, string name, Location l)
1853 if (!CheckParentConflictName (Toplevel, name, l))
1856 if (Toplevel.GenericMethod != null) {
1857 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1858 if (tp.Name == name) {
1859 Report.SymbolRelatedToPreviousError (tp);
1860 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1866 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1868 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1869 Error_AlreadyDeclared (l, name, "child");
1873 LocalInfo vi = new LocalInfo ((FullNamedExpression) type, name, this, l);
1876 if ((flags & Flags.VariablesInitialized) != 0)
1877 throw new InternalErrorException ("block has already been resolved");
1882 protected virtual void AddVariable (LocalInfo li)
1884 Variables.Add (li.Name, li);
1885 Explicit.AddKnownVariable (li.Name, li);
1888 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1890 if (reason == null) {
1891 Error_AlreadyDeclared (loc, var);
1895 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1896 "in this scope because it would give a different meaning " +
1897 "to `{0}', which is already used in a `{1}' scope " +
1898 "to denote something else", var, reason);
1901 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1903 Report.Error (128, loc,
1904 "A local variable named `{0}' is already defined in this scope", name);
1907 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1909 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1913 public bool AddConstant (Expression type, string name, Expression value, Location l)
1915 if (AddVariable (type, name, l) == null)
1918 if (constants == null)
1919 constants = new HybridDictionary();
1921 constants.Add (name, value);
1923 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1928 static int next_temp_id = 0;
1930 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1932 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1934 if (temporary_variables == null)
1935 temporary_variables = new ArrayList ();
1937 int id = ++next_temp_id;
1938 string name = "$s_" + id.ToString ();
1940 LocalInfo li = new LocalInfo (te, name, this, loc);
1941 li.CompilerGenerated = true;
1942 temporary_variables.Add (li);
1946 public LocalInfo GetLocalInfo (string name)
1949 for (Block b = this; b != null; b = b.Parent) {
1950 if (b.variables != null) {
1951 ret = (LocalInfo) b.variables [name];
1960 public Expression GetVariableType (string name)
1962 LocalInfo vi = GetLocalInfo (name);
1963 return vi == null ? null : vi.Type;
1966 public Expression GetConstantExpression (string name)
1968 for (Block b = this; b != null; b = b.Parent) {
1969 if (b.constants != null) {
1970 Expression ret = b.constants [name] as Expression;
1979 // It should be used by expressions which require to
1980 // register a statement during resolve process.
1982 public void AddScopeStatement (Statement s)
1984 if (scope_initializers == null)
1985 scope_initializers = new ArrayList ();
1987 scope_initializers.Add (s);
1990 public void AddStatement (Statement s)
1993 flags |= Flags.BlockUsed;
1997 get { return (flags & Flags.BlockUsed) != 0; }
2002 flags |= Flags.BlockUsed;
2005 public bool HasRet {
2006 get { return (flags & Flags.HasRet) != 0; }
2009 public int AssignableSlots {
2012 // if ((flags & Flags.VariablesInitialized) == 0)
2013 // throw new Exception ("Variables have not been initialized yet");
2014 return assignable_slots;
2018 public ArrayList AnonymousChildren {
2019 get { return anonymous_children; }
2022 public void AddAnonymousChild (ToplevelBlock b)
2024 if (anonymous_children == null)
2025 anonymous_children = new ArrayList ();
2027 anonymous_children.Add (b);
2030 void DoResolveConstants (EmitContext ec)
2032 if (constants == null)
2035 if (variables == null)
2036 throw new InternalErrorException ("cannot happen");
2038 foreach (DictionaryEntry de in variables) {
2039 string name = (string) de.Key;
2040 LocalInfo vi = (LocalInfo) de.Value;
2041 Type variable_type = vi.VariableType;
2043 if (variable_type == null) {
2044 if (vi.Type is VarExpr)
2045 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2050 Expression cv = (Expression) constants [name];
2054 // Don't let 'const int Foo = Foo;' succeed.
2055 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2056 // which in turn causes the 'must be constant' error to be triggered.
2057 constants.Remove (name);
2059 if (!Const.IsConstantTypeValid (variable_type)) {
2060 Const.Error_InvalidConstantType (variable_type, loc);
2064 ec.CurrentBlock = this;
2066 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2067 e = cv.Resolve (ec);
2072 Constant ce = e as Constant;
2074 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2078 e = ce.ConvertImplicitly (variable_type);
2080 if (TypeManager.IsReferenceType (variable_type))
2081 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2083 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2087 constants.Add (name, e);
2088 vi.IsConstant = true;
2092 protected void ResolveMeta (EmitContext ec, int offset)
2094 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2096 // If some parent block was unsafe, we remain unsafe even if this block
2097 // isn't explicitly marked as such.
2098 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2099 flags |= Flags.VariablesInitialized;
2101 if (variables != null) {
2102 foreach (LocalInfo li in variables.Values) {
2103 if (!li.Resolve (ec))
2105 li.VariableInfo = new VariableInfo (li, offset);
2106 offset += li.VariableInfo.Length;
2109 assignable_slots = offset;
2111 DoResolveConstants (ec);
2113 if (children == null)
2115 foreach (Block b in children)
2116 b.ResolveMeta (ec, offset);
2121 // Emits the local variable declarations for a block
2123 public virtual void EmitMeta (EmitContext ec)
2125 if (variables != null){
2126 foreach (LocalInfo vi in variables.Values)
2127 vi.ResolveVariable (ec);
2130 if (temporary_variables != null) {
2131 for (int i = 0; i < temporary_variables.Count; i++)
2132 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2135 if (children != null) {
2136 for (int i = 0; i < children.Count; i++)
2137 ((Block)children[i]).EmitMeta(ec);
2141 void UsageWarning ()
2143 if (variables == null || Report.WarningLevel < 3)
2146 foreach (DictionaryEntry de in variables) {
2147 LocalInfo vi = (LocalInfo) de.Value;
2150 string name = (string) de.Key;
2152 // vi.VariableInfo can be null for 'catch' variables
2153 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2154 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2156 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2161 static void CheckPossibleMistakenEmptyStatement (Statement s)
2165 // Some statements are wrapped by a Block. Since
2166 // others' internal could be changed, here I treat
2167 // them as possibly wrapped by Block equally.
2168 Block b = s as Block;
2169 if (b != null && b.statements.Count == 1)
2170 s = (Statement) b.statements [0];
2173 body = ((Lock) s).Statement;
2175 body = ((For) s).Statement;
2176 else if (s is Foreach)
2177 body = ((Foreach) s).Statement;
2178 else if (s is While)
2179 body = ((While) s).Statement;
2180 else if (s is Fixed)
2181 body = ((Fixed) s).Statement;
2182 else if (s is Using)
2183 body = ((Using) s).EmbeddedStatement;
2184 else if (s is UsingTemporary)
2185 body = ((UsingTemporary) s).Statement;
2189 if (body == null || body is EmptyStatement)
2190 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2193 public override bool Resolve (EmitContext ec)
2195 Block prev_block = ec.CurrentBlock;
2198 int errors = Report.Errors;
2200 ec.CurrentBlock = this;
2201 ec.StartFlowBranching (this);
2203 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2206 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2207 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2208 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2209 // responsible for handling the situation.
2211 int statement_count = statements.Count;
2212 for (int ix = 0; ix < statement_count; ix++){
2213 Statement s = (Statement) statements [ix];
2214 // Check possible empty statement (CS0642)
2215 if (Report.WarningLevel >= 3 &&
2216 ix + 1 < statement_count &&
2217 statements [ix + 1] is ExplicitBlock)
2218 CheckPossibleMistakenEmptyStatement (s);
2221 // Warn if we detect unreachable code.
2224 if (s is EmptyStatement)
2227 if (!unreachable_shown && !(s is LabeledStatement)) {
2228 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2229 unreachable_shown = true;
2232 Block c_block = s as Block;
2233 if (c_block != null)
2234 c_block.unreachable = c_block.unreachable_shown = true;
2238 // Note that we're not using ResolveUnreachable() for unreachable
2239 // statements here. ResolveUnreachable() creates a temporary
2240 // flow branching and kills it afterwards. This leads to problems
2241 // if you have two unreachable statements where the first one
2242 // assigns a variable and the second one tries to access it.
2245 if (!s.Resolve (ec)) {
2247 if (ec.IsInProbingMode)
2250 statements [ix] = EmptyStatement.Value;
2254 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2255 statements [ix] = EmptyStatement.Value;
2257 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2258 if (unreachable && s is LabeledStatement)
2259 throw new InternalErrorException ("should not happen");
2262 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2263 ec.CurrentBranching, statement_count);
2265 while (ec.CurrentBranching is FlowBranchingLabeled)
2266 ec.EndFlowBranching ();
2268 bool flow_unreachable = ec.EndFlowBranching ();
2270 ec.CurrentBlock = prev_block;
2272 if (flow_unreachable)
2273 flags |= Flags.HasRet;
2275 // If we're a non-static `struct' constructor which doesn't have an
2276 // initializer, then we must initialize all of the struct's fields.
2277 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2280 if ((labels != null) && (Report.WarningLevel >= 2)) {
2281 foreach (LabeledStatement label in labels.Values)
2282 if (!label.HasBeenReferenced)
2283 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2286 if (ok && errors == Report.Errors)
2292 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2294 unreachable_shown = true;
2298 Report.Warning (162, 2, loc, "Unreachable code detected");
2300 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2301 bool ok = Resolve (ec);
2302 ec.KillFlowBranching ();
2307 protected override void DoEmit (EmitContext ec)
2309 for (int ix = 0; ix < statements.Count; ix++){
2310 Statement s = (Statement) statements [ix];
2315 public override void Emit (EmitContext ec)
2317 Block prev_block = ec.CurrentBlock;
2318 ec.CurrentBlock = this;
2320 if (scope_initializers != null)
2321 EmitScopeInitializers (ec);
2323 ec.Mark (StartLocation);
2326 if (SymbolWriter.HasSymbolWriter)
2327 EmitSymbolInfo (ec);
2329 ec.CurrentBlock = prev_block;
2332 protected void EmitScopeInitializers (EmitContext ec)
2334 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2336 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2337 foreach (Statement s in scope_initializers)
2341 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2344 protected virtual void EmitSymbolInfo (EmitContext ec)
2346 if (variables != null) {
2347 foreach (LocalInfo vi in variables.Values) {
2348 vi.EmitSymbolInfo (ec);
2353 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2355 MutateVariables (storey);
2357 if (scope_initializers != null) {
2358 foreach (Statement s in scope_initializers)
2359 s.MutateHoistedGenericType (storey);
2362 foreach (Statement s in statements)
2363 s.MutateHoistedGenericType (storey);
2366 void MutateVariables (AnonymousMethodStorey storey)
2368 if (variables != null) {
2369 foreach (LocalInfo vi in variables.Values) {
2370 vi.VariableType = storey.MutateType (vi.VariableType);
2374 if (temporary_variables != null) {
2375 foreach (LocalInfo vi in temporary_variables)
2376 vi.VariableType = storey.MutateType (vi.VariableType);
2380 public override string ToString ()
2382 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2385 protected override void CloneTo (CloneContext clonectx, Statement t)
2387 Block target = (Block) t;
2389 clonectx.AddBlockMap (this, target);
2391 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2392 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2394 target.Parent = clonectx.RemapBlockCopy (Parent);
2396 if (variables != null){
2397 target.variables = new Hashtable ();
2399 foreach (DictionaryEntry de in variables){
2400 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2401 target.variables [de.Key] = newlocal;
2402 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2406 target.statements = new ArrayList (statements.Count);
2407 foreach (Statement s in statements)
2408 target.statements.Add (s.Clone (clonectx));
2410 if (target.children != null){
2411 target.children = new ArrayList (children.Count);
2412 foreach (Block b in children){
2413 target.children.Add (clonectx.LookupBlock (b));
2418 // TODO: labels, switch_block, constants (?), anonymous_children
2423 public class ExplicitBlock : Block {
2424 HybridDictionary known_variables;
2425 protected AnonymousMethodStorey am_storey;
2427 public ExplicitBlock (Block parent, Location start, Location end)
2428 : this (parent, (Flags) 0, start, end)
2432 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2433 : base (parent, flags, start, end)
2435 this.Explicit = this;
2439 // Marks a variable with name @name as being used in this or a child block.
2440 // If a variable name has been used in a child block, it's illegal to
2441 // declare a variable with the same name in the current block.
2443 internal void AddKnownVariable (string name, IKnownVariable info)
2445 if (known_variables == null)
2446 known_variables = new HybridDictionary();
2448 known_variables [name] = info;
2451 Parent.Explicit.AddKnownVariable (name, info);
2454 public AnonymousMethodStorey AnonymousMethodStorey {
2455 get { return am_storey; }
2459 // Creates anonymous method storey in current block
2461 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2464 // When referencing a variable in iterator storey from children anonymous method
2466 if (Toplevel.am_storey is IteratorStorey) {
2467 return Toplevel.am_storey;
2471 // An iterator has only 1 storey block
2473 if (ec.CurrentIterator != null)
2474 return ec.CurrentIterator.Storey;
2476 if (am_storey == null) {
2477 MemberBase mc = ec.ResolveContext as MemberBase;
2478 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2481 // Creates anonymous method storey for this block
2483 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2489 public override void Emit (EmitContext ec)
2491 if (am_storey != null)
2492 am_storey.EmitStoreyInstantiation (ec);
2494 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2495 if (emit_debug_info)
2500 if (emit_debug_info)
2504 public override void EmitMeta (EmitContext ec)
2507 // Creates anonymous method storey
2509 if (am_storey != null) {
2510 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2512 // Creates parent storey reference when hoisted this is accessible
2514 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2515 ExplicitBlock parent = Toplevel.Parent.Explicit;
2518 // Hoisted this exists in top-level parent storey only
2520 while (parent.am_storey == null || parent.am_storey.Parent is AnonymousMethodStorey)
2521 parent = parent.Parent.Explicit;
2523 am_storey.AddParentStoreyReference (parent.am_storey);
2526 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2529 am_storey.DefineType ();
2530 am_storey.ResolveType ();
2531 am_storey.Define ();
2532 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2534 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2535 if (ref_blocks != null) {
2536 foreach (ExplicitBlock ref_block in ref_blocks) {
2537 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2538 if (b.am_storey != null) {
2539 b.am_storey.AddParentStoreyReference (am_storey);
2541 // Stop propagation inside same top block
2542 if (b.Toplevel == Toplevel)
2547 b.HasCapturedVariable = true;
2556 internal IKnownVariable GetKnownVariable (string name)
2558 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2561 public bool HasCapturedThis
2563 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2564 get { return (flags & Flags.HasCapturedThis) != 0; }
2567 public bool HasCapturedVariable
2569 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2570 get { return (flags & Flags.HasCapturedVariable) != 0; }
2573 protected override void CloneTo (CloneContext clonectx, Statement t)
2575 ExplicitBlock target = (ExplicitBlock) t;
2576 target.known_variables = null;
2577 base.CloneTo (clonectx, t);
2581 public class ToplevelParameterInfo : IKnownVariable {
2582 public readonly ToplevelBlock Block;
2583 public readonly int Index;
2584 public VariableInfo VariableInfo;
2586 Block IKnownVariable.Block {
2587 get { return Block; }
2589 public Parameter Parameter {
2590 get { return Block.Parameters [Index]; }
2593 public Type ParameterType {
2594 get { return Block.Parameters.Types [Index]; }
2597 public Location Location {
2598 get { return Parameter.Location; }
2601 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2609 // A toplevel block contains extra information, the split is done
2610 // only to separate information that would otherwise bloat the more
2611 // lightweight Block.
2613 // In particular, this was introduced when the support for Anonymous
2614 // Methods was implemented.
2616 public class ToplevelBlock : ExplicitBlock
2619 // Block is converted to an expression
2621 sealed class BlockScopeExpression : Expression
2624 readonly ToplevelBlock block;
2626 public BlockScopeExpression (Expression child, ToplevelBlock block)
2632 public override Expression CreateExpressionTree (EmitContext ec)
2634 throw new NotSupportedException ();
2637 public override Expression DoResolve (EmitContext ec)
2642 block.ResolveMeta (ec, ParametersCompiled.EmptyReadOnlyParameters);
2643 child = child.Resolve (ec);
2647 eclass = child.eclass;
2652 public override void Emit (EmitContext ec)
2654 block.EmitMeta (ec);
2655 block.EmitScopeInitializers (ec);
2659 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2661 type = storey.MutateType (type);
2662 child.MutateHoistedGenericType (storey);
2663 block.MutateHoistedGenericType (storey);
2667 GenericMethod generic;
2668 FlowBranchingToplevel top_level_branching;
2669 protected ParametersCompiled parameters;
2670 ToplevelParameterInfo[] parameter_info;
2671 LocalInfo this_variable;
2673 public HoistedVariable HoistedThisVariable;
2676 // The parameters for the block.
2678 public ParametersCompiled Parameters {
2679 get { return parameters; }
2682 public GenericMethod GenericMethod {
2683 get { return generic; }
2686 public ToplevelBlock Container {
2687 get { return Parent == null ? null : Parent.Toplevel; }
2690 public ToplevelBlock (Block parent, ParametersCompiled parameters, Location start) :
2691 this (parent, (Flags) 0, parameters, start)
2695 public ToplevelBlock (Block parent, ParametersCompiled parameters, GenericMethod generic, Location start) :
2696 this (parent, parameters, start)
2698 this.generic = generic;
2701 public ToplevelBlock (ParametersCompiled parameters, Location start) :
2702 this (null, (Flags) 0, parameters, start)
2706 ToplevelBlock (Flags flags, ParametersCompiled parameters, Location start) :
2707 this (null, flags, parameters, start)
2711 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2712 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2713 public ToplevelBlock (Block parent, Flags flags, ParametersCompiled parameters, Location start) :
2714 base (null, flags, start, Location.Null)
2716 this.Toplevel = this;
2718 this.parameters = parameters;
2719 this.Parent = parent;
2721 parent.AddAnonymousChild (this);
2723 if (!this.parameters.IsEmpty)
2724 ProcessParameters ();
2727 public ToplevelBlock (Location loc)
2728 : this (null, (Flags) 0, ParametersCompiled.EmptyReadOnlyParameters, loc)
2732 protected override void CloneTo (CloneContext clonectx, Statement t)
2734 ToplevelBlock target = (ToplevelBlock) t;
2735 base.CloneTo (clonectx, t);
2737 if (parameters.Count != 0)
2738 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2739 for (int i = 0; i < parameters.Count; ++i)
2740 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2743 public bool CheckError158 (string name, Location loc)
2745 if (AnonymousChildren != null) {
2746 foreach (ToplevelBlock child in AnonymousChildren) {
2747 if (!child.CheckError158 (name, loc))
2752 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2753 if (!c.DoCheckError158 (name, loc))
2760 void ProcessParameters ()
2762 int n = parameters.Count;
2763 parameter_info = new ToplevelParameterInfo [n];
2764 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2765 for (int i = 0; i < n; ++i) {
2766 parameter_info [i] = new ToplevelParameterInfo (this, i);
2768 Parameter p = parameters [i];
2772 string name = p.Name;
2773 if (CheckParentConflictName (top_parent, name, loc))
2774 AddKnownVariable (name, parameter_info [i]);
2777 // mark this block as "used" so that we create local declarations in a sub-block
2778 // FIXME: This appears to uncover a lot of bugs
2782 bool DoCheckError158 (string name, Location loc)
2784 LabeledStatement s = LookupLabel (name);
2786 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2787 Error_158 (name, loc);
2794 public override Expression CreateExpressionTree (EmitContext ec)
2796 if (statements.Count == 1) {
2797 Expression expr = ((Statement) statements[0]).CreateExpressionTree (ec);
2798 if (scope_initializers != null)
2799 expr = new BlockScopeExpression (expr, this);
2804 return base.CreateExpressionTree (ec);
2808 // Reformats this block to be top-level iterator block
2810 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2814 // Creates block with original statements
2815 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2817 source.statements = new ArrayList (1);
2818 source.AddStatement (new Return (iterator, iterator.Location));
2819 source.IsIterator = false;
2821 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2822 source.am_storey = iterator_storey;
2823 return iterator_storey;
2826 public FlowBranchingToplevel TopLevelBranching {
2827 get { return top_level_branching; }
2831 // Returns a parameter reference expression for the given name,
2832 // or null if there is no such parameter
2834 public Expression GetParameterReference (string name, Location loc)
2836 for (ToplevelBlock t = this; t != null; t = t.Container) {
2837 Expression expr = t.GetParameterReferenceExpression (name, loc);
2845 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2847 int idx = parameters.GetParameterIndexByName (name);
2849 null : new ParameterReference (parameter_info [idx], loc);
2853 // Returns the "this" instance variable of this block.
2854 // See AddThisVariable() for more information.
2856 public LocalInfo ThisVariable {
2857 get { return this_variable; }
2861 // This is used by non-static `struct' constructors which do not have an
2862 // initializer - in this case, the constructor must initialize all of the
2863 // struct's fields. To do this, we add a "this" variable and use the flow
2864 // analysis code to ensure that it's been fully initialized before control
2865 // leaves the constructor.
2867 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2869 if (this_variable == null) {
2870 this_variable = new LocalInfo (ds, this, l);
2871 this_variable.Used = true;
2872 this_variable.IsThis = true;
2874 Variables.Add ("this", this_variable);
2877 return this_variable;
2880 public bool IsIterator {
2881 get { return (flags & Flags.IsIterator) != 0; }
2882 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2885 public bool IsThisAssigned (EmitContext ec)
2887 return this_variable == null || this_variable.IsThisAssigned (ec);
2890 public bool ResolveMeta (EmitContext ec, ParametersCompiled ip)
2892 int errors = Report.Errors;
2893 int orig_count = parameters.Count;
2895 if (top_level_branching != null)
2901 // Assert: orig_count != parameter.Count => orig_count == 0
2902 if (orig_count != 0 && orig_count != parameters.Count)
2903 throw new InternalErrorException ("parameter information mismatch");
2905 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2907 for (int i = 0; i < orig_count; ++i) {
2908 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2910 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2913 VariableInfo vi = new VariableInfo (ip, i, offset);
2914 parameter_info [i].VariableInfo = vi;
2915 offset += vi.Length;
2918 ResolveMeta (ec, offset);
2920 top_level_branching = ec.StartFlowBranching (this);
2922 return Report.Errors == errors;
2926 // Check whether all `out' parameters have been assigned.
2928 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2930 if (vector.IsUnreachable)
2933 int n = parameter_info == null ? 0 : parameter_info.Length;
2935 for (int i = 0; i < n; i++) {
2936 VariableInfo var = parameter_info [i].VariableInfo;
2941 if (vector.IsAssigned (var, false))
2944 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2949 public override void EmitMeta (EmitContext ec)
2951 parameters.ResolveVariable ();
2953 // Avoid declaring an IL variable for this_variable since it is not accessed
2954 // from the generated IL
2955 if (this_variable != null)
2956 Variables.Remove ("this");
2960 protected override void EmitSymbolInfo (EmitContext ec)
2962 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2963 if ((ae != null) && (ae.Storey != null))
2964 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2966 base.EmitSymbolInfo (ec);
2969 public override void Emit (EmitContext ec)
2972 ec.Mark (EndLocation);
2976 public class SwitchLabel {
2983 Label il_label_code;
2984 bool il_label_code_set;
2986 public static readonly object NullStringCase = new object ();
2989 // if expr == null, then it is the default case.
2991 public SwitchLabel (Expression expr, Location l)
2997 public Expression Label {
3003 public Location Location {
3007 public object Converted {
3013 public Label GetILLabel (EmitContext ec)
3016 il_label = ec.ig.DefineLabel ();
3017 il_label_set = true;
3022 public Label GetILLabelCode (EmitContext ec)
3024 if (!il_label_code_set){
3025 il_label_code = ec.ig.DefineLabel ();
3026 il_label_code_set = true;
3028 return il_label_code;
3032 // Resolves the expression, reduces it to a literal if possible
3033 // and then converts it to the requested type.
3035 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
3037 Expression e = label.Resolve (ec);
3042 Constant c = e as Constant;
3044 Report.Error (150, loc, "A constant value is expected");
3048 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3049 converted = NullStringCase;
3053 if (allow_nullable && c.GetValue () == null) {
3054 converted = NullStringCase;
3058 c = c.ImplicitConversionRequired (ec, required_type, loc);
3062 converted = c.GetValue ();
3066 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3069 if (converted == null)
3071 else if (converted == NullStringCase)
3074 label = converted.ToString ();
3076 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3077 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3080 public SwitchLabel Clone (CloneContext clonectx)
3082 return new SwitchLabel (label.Clone (clonectx), loc);
3086 public class SwitchSection {
3087 // An array of SwitchLabels.
3088 public readonly ArrayList Labels;
3089 public readonly Block Block;
3091 public SwitchSection (ArrayList labels, Block block)
3097 public SwitchSection Clone (CloneContext clonectx)
3099 ArrayList cloned_labels = new ArrayList ();
3101 foreach (SwitchLabel sl in cloned_labels)
3102 cloned_labels.Add (sl.Clone (clonectx));
3104 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3108 public class Switch : Statement {
3109 public ArrayList Sections;
3110 public Expression Expr;
3113 /// Maps constants whose type type SwitchType to their SwitchLabels.
3115 public IDictionary Elements;
3118 /// The governing switch type
3120 public Type SwitchType;
3125 Label default_target;
3127 Expression new_expr;
3130 SwitchSection constant_section;
3131 SwitchSection default_section;
3133 ExpressionStatement string_dictionary;
3134 FieldExpr switch_cache_field;
3135 static int unique_counter;
3138 // Nullable Types support
3140 Nullable.Unwrap unwrap;
3142 protected bool HaveUnwrap {
3143 get { return unwrap != null; }
3147 // The types allowed to be implicitly cast from
3148 // on the governing type
3150 static Type [] allowed_types;
3152 public Switch (Expression e, ArrayList sects, Location l)
3159 public bool GotDefault {
3161 return default_section != null;
3165 public Label DefaultTarget {
3167 return default_target;
3172 // Determines the governing type for a switch. The returned
3173 // expression might be the expression from the switch, or an
3174 // expression that includes any potential conversions to the
3175 // integral types or to string.
3177 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3181 if (t == TypeManager.byte_type ||
3182 t == TypeManager.sbyte_type ||
3183 t == TypeManager.ushort_type ||
3184 t == TypeManager.short_type ||
3185 t == TypeManager.uint32_type ||
3186 t == TypeManager.int32_type ||
3187 t == TypeManager.uint64_type ||
3188 t == TypeManager.int64_type ||
3189 t == TypeManager.char_type ||
3190 t == TypeManager.string_type ||
3191 t == TypeManager.bool_type ||
3192 TypeManager.IsEnumType (t))
3195 if (allowed_types == null){
3196 allowed_types = new Type [] {
3197 TypeManager.sbyte_type,
3198 TypeManager.byte_type,
3199 TypeManager.short_type,
3200 TypeManager.ushort_type,
3201 TypeManager.int32_type,
3202 TypeManager.uint32_type,
3203 TypeManager.int64_type,
3204 TypeManager.uint64_type,
3205 TypeManager.char_type,
3206 TypeManager.string_type
3211 // Try to find a *user* defined implicit conversion.
3213 // If there is no implicit conversion, or if there are multiple
3214 // conversions, we have to report an error
3216 Expression converted = null;
3217 foreach (Type tt in allowed_types){
3220 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3225 // Ignore over-worked ImplicitUserConversions that do
3226 // an implicit conversion in addition to the user conversion.
3228 if (!(e is UserCast))
3231 if (converted != null){
3232 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3242 // Performs the basic sanity checks on the switch statement
3243 // (looks for duplicate keys and non-constant expressions).
3245 // It also returns a hashtable with the keys that we will later
3246 // use to compute the switch tables
3248 bool CheckSwitch (EmitContext ec)
3251 Elements = Sections.Count > 10 ?
3252 (IDictionary)new Hashtable () :
3253 (IDictionary)new ListDictionary ();
3255 foreach (SwitchSection ss in Sections){
3256 foreach (SwitchLabel sl in ss.Labels){
3257 if (sl.Label == null){
3258 if (default_section != null){
3259 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3262 default_section = ss;
3266 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3271 object key = sl.Converted;
3272 if (key == SwitchLabel.NullStringCase)
3273 has_null_case = true;
3276 Elements.Add (key, sl);
3277 } catch (ArgumentException) {
3278 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3286 void EmitObjectInteger (ILGenerator ig, object k)
3289 IntConstant.EmitInt (ig, (int) k);
3290 else if (k is Constant) {
3291 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3294 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3297 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3299 IntConstant.EmitInt (ig, (int) (long) k);
3300 ig.Emit (OpCodes.Conv_I8);
3303 LongConstant.EmitLong (ig, (long) k);
3305 else if (k is ulong)
3307 ulong ul = (ulong) k;
3310 IntConstant.EmitInt (ig, unchecked ((int) ul));
3311 ig.Emit (OpCodes.Conv_U8);
3315 LongConstant.EmitLong (ig, unchecked ((long) ul));
3319 IntConstant.EmitInt (ig, (int) ((char) k));
3320 else if (k is sbyte)
3321 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3323 IntConstant.EmitInt (ig, (int) ((byte) k));
3324 else if (k is short)
3325 IntConstant.EmitInt (ig, (int) ((short) k));
3326 else if (k is ushort)
3327 IntConstant.EmitInt (ig, (int) ((ushort) k));
3329 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3331 throw new Exception ("Unhandled case");
3334 // structure used to hold blocks of keys while calculating table switch
3335 class KeyBlock : IComparable
3337 public KeyBlock (long _first)
3339 first = last = _first;
3343 public ArrayList element_keys = null;
3344 // how many items are in the bucket
3345 public int Size = 1;
3348 get { return (int) (last - first + 1); }
3350 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3352 return kb_last.last - kb_first.first + 1;
3354 public int CompareTo (object obj)
3356 KeyBlock kb = (KeyBlock) obj;
3357 int nLength = Length;
3358 int nLengthOther = kb.Length;
3359 if (nLengthOther == nLength)
3360 return (int) (kb.first - first);
3361 return nLength - nLengthOther;
3366 /// This method emits code for a lookup-based switch statement (non-string)
3367 /// Basically it groups the cases into blocks that are at least half full,
3368 /// and then spits out individual lookup opcodes for each block.
3369 /// It emits the longest blocks first, and short blocks are just
3370 /// handled with direct compares.
3372 /// <param name="ec"></param>
3373 /// <param name="val"></param>
3374 /// <returns></returns>
3375 void TableSwitchEmit (EmitContext ec, Expression val)
3377 int element_count = Elements.Count;
3378 object [] element_keys = new object [element_count];
3379 Elements.Keys.CopyTo (element_keys, 0);
3380 Array.Sort (element_keys);
3382 // initialize the block list with one element per key
3383 ArrayList key_blocks = new ArrayList (element_count);
3384 foreach (object key in element_keys)
3385 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3387 KeyBlock current_kb;
3388 // iteratively merge the blocks while they are at least half full
3389 // there's probably a really cool way to do this with a tree...
3390 while (key_blocks.Count > 1)
3392 ArrayList key_blocks_new = new ArrayList ();
3393 current_kb = (KeyBlock) key_blocks [0];
3394 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3396 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3397 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3400 current_kb.last = kb.last;
3401 current_kb.Size += kb.Size;
3405 // start a new block
3406 key_blocks_new.Add (current_kb);
3410 key_blocks_new.Add (current_kb);
3411 if (key_blocks.Count == key_blocks_new.Count)
3413 key_blocks = key_blocks_new;
3416 // initialize the key lists
3417 foreach (KeyBlock kb in key_blocks)
3418 kb.element_keys = new ArrayList ();
3420 // fill the key lists
3422 if (key_blocks.Count > 0) {
3423 current_kb = (KeyBlock) key_blocks [0];
3424 foreach (object key in element_keys)
3426 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3427 System.Convert.ToInt64 (key) > current_kb.last;
3429 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3430 current_kb.element_keys.Add (key);
3434 // sort the blocks so we can tackle the largest ones first
3437 // okay now we can start...
3438 ILGenerator ig = ec.ig;
3439 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3440 Label lbl_default = default_target;
3442 Type type_keys = null;
3443 if (element_keys.Length > 0)
3444 type_keys = element_keys [0].GetType (); // used for conversions
3448 if (TypeManager.IsEnumType (SwitchType))
3449 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3451 compare_type = SwitchType;
3453 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3455 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3456 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3459 foreach (object key in kb.element_keys) {
3460 SwitchLabel sl = (SwitchLabel) Elements [key];
3461 if (key is int && (int) key == 0) {
3462 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3465 EmitObjectInteger (ig, key);
3466 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3472 // TODO: if all the keys in the block are the same and there are
3473 // no gaps/defaults then just use a range-check.
3474 if (compare_type == TypeManager.int64_type ||
3475 compare_type == TypeManager.uint64_type)
3477 // TODO: optimize constant/I4 cases
3479 // check block range (could be > 2^31)
3481 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3482 ig.Emit (OpCodes.Blt, lbl_default);
3484 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3485 ig.Emit (OpCodes.Bgt, lbl_default);
3491 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3492 ig.Emit (OpCodes.Sub);
3494 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3500 int first = (int) kb.first;
3503 IntConstant.EmitInt (ig, first);
3504 ig.Emit (OpCodes.Sub);
3508 IntConstant.EmitInt (ig, -first);
3509 ig.Emit (OpCodes.Add);
3513 // first, build the list of labels for the switch
3515 int cJumps = kb.Length;
3516 Label [] switch_labels = new Label [cJumps];
3517 for (int iJump = 0; iJump < cJumps; iJump++)
3519 object key = kb.element_keys [iKey];
3520 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3522 SwitchLabel sl = (SwitchLabel) Elements [key];
3523 switch_labels [iJump] = sl.GetILLabel (ec);
3527 switch_labels [iJump] = lbl_default;
3529 // emit the switch opcode
3530 ig.Emit (OpCodes.Switch, switch_labels);
3533 // mark the default for this block
3535 ig.MarkLabel (lbl_default);
3538 // TODO: find the default case and emit it here,
3539 // to prevent having to do the following jump.
3540 // make sure to mark other labels in the default section
3542 // the last default just goes to the end
3543 if (element_keys.Length > 0)
3544 ig.Emit (OpCodes.Br, lbl_default);
3546 // now emit the code for the sections
3547 bool found_default = false;
3549 foreach (SwitchSection ss in Sections) {
3550 foreach (SwitchLabel sl in ss.Labels) {
3551 if (sl.Converted == SwitchLabel.NullStringCase) {
3552 ig.MarkLabel (null_target);
3553 } else if (sl.Label == null) {
3554 ig.MarkLabel (lbl_default);
3555 found_default = true;
3557 ig.MarkLabel (null_target);
3559 ig.MarkLabel (sl.GetILLabel (ec));
3560 ig.MarkLabel (sl.GetILLabelCode (ec));
3565 if (!found_default) {
3566 ig.MarkLabel (lbl_default);
3567 if (!has_null_case) {
3568 ig.MarkLabel (null_target);
3572 ig.MarkLabel (lbl_end);
3575 SwitchSection FindSection (SwitchLabel label)
3577 foreach (SwitchSection ss in Sections){
3578 foreach (SwitchLabel sl in ss.Labels){
3587 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3589 foreach (SwitchSection ss in Sections)
3590 ss.Block.MutateHoistedGenericType (storey);
3593 public static void Reset ()
3596 allowed_types = null;
3599 public override bool Resolve (EmitContext ec)
3601 Expr = Expr.Resolve (ec);
3605 new_expr = SwitchGoverningType (ec, Expr);
3607 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3608 unwrap = Nullable.Unwrap.Create (Expr, false);
3612 new_expr = SwitchGoverningType (ec, unwrap);
3615 if (new_expr == null){
3616 Report.Error (151, loc,
3617 "A switch expression of type `{0}' cannot be converted to an integral type, bool, char, string, enum or nullable type",
3618 TypeManager.CSharpName (Expr.Type));
3623 SwitchType = new_expr.Type;
3625 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3626 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3630 if (!CheckSwitch (ec))
3634 Elements.Remove (SwitchLabel.NullStringCase);
3636 Switch old_switch = ec.Switch;
3638 ec.Switch.SwitchType = SwitchType;
3640 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3641 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3643 is_constant = new_expr is Constant;
3645 object key = ((Constant) new_expr).GetValue ();
3646 SwitchLabel label = (SwitchLabel) Elements [key];
3648 constant_section = FindSection (label);
3649 if (constant_section == null)
3650 constant_section = default_section;
3655 foreach (SwitchSection ss in Sections){
3657 ec.CurrentBranching.CreateSibling (
3658 null, FlowBranching.SiblingType.SwitchSection);
3662 if (is_constant && (ss != constant_section)) {
3663 // If we're a constant switch, we're only emitting
3664 // one single section - mark all the others as
3666 ec.CurrentBranching.CurrentUsageVector.Goto ();
3667 if (!ss.Block.ResolveUnreachable (ec, true)) {
3671 if (!ss.Block.Resolve (ec))
3676 if (default_section == null)
3677 ec.CurrentBranching.CreateSibling (
3678 null, FlowBranching.SiblingType.SwitchSection);
3680 ec.EndFlowBranching ();
3681 ec.Switch = old_switch;
3683 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3688 if (SwitchType == TypeManager.string_type && !is_constant) {
3689 // TODO: Optimize single case, and single+default case
3690 ResolveStringSwitchMap (ec);
3696 void ResolveStringSwitchMap (EmitContext ec)
3698 FullNamedExpression string_dictionary_type;
3699 if (TypeManager.generic_ienumerable_type != null) {
3700 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3701 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3703 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3705 new TypeExpression (TypeManager.string_type, loc),
3706 new TypeExpression (TypeManager.int32_type, loc)), loc);
3708 MemberAccess system_collections_generic = new MemberAccess (
3709 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3711 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3714 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3715 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3716 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3717 if (!field.Define ())
3719 ec.TypeContainer.PartialContainer.AddField (field);
3721 ArrayList init = new ArrayList ();
3724 string value = null;
3725 foreach (SwitchSection section in Sections) {
3726 int last_count = init.Count;
3727 foreach (SwitchLabel sl in section.Labels) {
3728 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase)
3731 value = (string) sl.Converted;
3732 ArrayList init_args = new ArrayList (2);
3733 init_args.Add (new StringLiteral (value, sl.Location));
3734 init_args.Add (new IntConstant (counter, loc));
3735 init.Add (new CollectionElementInitializer (init_args, loc));
3739 // Don't add empty sections
3741 if (last_count == init.Count)
3744 Elements.Add (counter, section.Labels [0]);
3748 Arguments args = new Arguments (1);
3749 args.Add (new Argument (new IntConstant (init.Count, loc)));
3750 Expression initializer = new NewInitialize (string_dictionary_type, args,
3751 new CollectionOrObjectInitializers (init, loc), loc);
3753 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3754 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3757 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3759 ILGenerator ig = ec.ig;
3760 Label l_initialized = ig.DefineLabel ();
3763 // Skip initialization when value is null
3765 value.EmitBranchable (ec, null_target, false);
3768 // Check if string dictionary is initialized and initialize
3770 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3771 string_dictionary.EmitStatement (ec);
3772 ig.MarkLabel (l_initialized);
3774 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3776 if (TypeManager.generic_ienumerable_type != null) {
3777 Arguments get_value_args = new Arguments (2);
3778 get_value_args.Add (new Argument (value));
3779 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3780 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3781 if (get_item == null)
3785 // A value was not found, go to default case
3787 get_item.EmitBranchable (ec, default_target, false);
3789 Arguments get_value_args = new Arguments (1);
3790 get_value_args.Add (new Argument (value));
3792 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3793 if (get_item == null)
3796 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3797 get_item_object.EmitAssign (ec, get_item, true, false);
3798 ec.ig.Emit (OpCodes.Brfalse, default_target);
3800 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3801 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3803 get_item_int.EmitStatement (ec);
3804 get_item_object.Release (ec);
3807 TableSwitchEmit (ec, string_switch_variable);
3808 string_switch_variable.Release (ec);
3811 protected override void DoEmit (EmitContext ec)
3813 ILGenerator ig = ec.ig;
3815 default_target = ig.DefineLabel ();
3816 null_target = ig.DefineLabel ();
3818 // Store variable for comparission purposes
3819 // TODO: Don't duplicate non-captured VariableReference
3820 LocalTemporary value;
3822 value = new LocalTemporary (SwitchType);
3823 unwrap.EmitCheck (ec);
3824 ig.Emit (OpCodes.Brfalse, null_target);
3827 } else if (!is_constant) {
3828 value = new LocalTemporary (SwitchType);
3835 // Setup the codegen context
3837 Label old_end = ec.LoopEnd;
3838 Switch old_switch = ec.Switch;
3840 ec.LoopEnd = ig.DefineLabel ();
3845 if (constant_section != null)
3846 constant_section.Block.Emit (ec);
3847 } else if (string_dictionary != null) {
3848 DoEmitStringSwitch (value, ec);
3850 TableSwitchEmit (ec, value);
3856 // Restore context state.
3857 ig.MarkLabel (ec.LoopEnd);
3860 // Restore the previous context
3862 ec.LoopEnd = old_end;
3863 ec.Switch = old_switch;
3866 protected override void CloneTo (CloneContext clonectx, Statement t)
3868 Switch target = (Switch) t;
3870 target.Expr = Expr.Clone (clonectx);
3871 target.Sections = new ArrayList ();
3872 foreach (SwitchSection ss in Sections){
3873 target.Sections.Add (ss.Clone (clonectx));
3878 // A place where execution can restart in an iterator
3879 public abstract class ResumableStatement : Statement
3882 protected Label resume_point;
3884 public Label PrepareForEmit (EmitContext ec)
3888 resume_point = ec.ig.DefineLabel ();
3890 return resume_point;
3893 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3897 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3902 // Base class for statements that are implemented in terms of try...finally
3903 public abstract class ExceptionStatement : ResumableStatement
3907 protected abstract void EmitPreTryBody (EmitContext ec);
3908 protected abstract void EmitTryBody (EmitContext ec);
3909 protected abstract void EmitFinallyBody (EmitContext ec);
3911 protected sealed override void DoEmit (EmitContext ec)
3913 ILGenerator ig = ec.ig;
3915 EmitPreTryBody (ec);
3917 if (resume_points != null) {
3918 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3919 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3922 ig.BeginExceptionBlock ();
3924 if (resume_points != null) {
3925 ig.MarkLabel (resume_point);
3927 // For normal control flow, we want to fall-through the Switch
3928 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3929 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3930 IntConstant.EmitInt (ig, first_resume_pc);
3931 ig.Emit (OpCodes.Sub);
3933 Label [] labels = new Label [resume_points.Count];
3934 for (int i = 0; i < resume_points.Count; ++i)
3935 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3936 ig.Emit (OpCodes.Switch, labels);
3941 ig.BeginFinallyBlock ();
3943 Label start_finally = ec.ig.DefineLabel ();
3944 if (resume_points != null) {
3945 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3946 ig.Emit (OpCodes.Brfalse_S, start_finally);
3947 ig.Emit (OpCodes.Endfinally);
3950 ig.MarkLabel (start_finally);
3951 EmitFinallyBody (ec);
3953 ig.EndExceptionBlock ();
3956 public void SomeCodeFollows ()
3958 code_follows = true;
3961 protected void ResolveReachability (EmitContext ec)
3963 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3964 // So, ensure there's some IL code after this statement.
3965 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3966 ec.NeedReturnLabel ();
3970 ArrayList resume_points;
3971 int first_resume_pc;
3972 public void AddResumePoint (ResumableStatement stmt, int pc)
3974 if (resume_points == null) {
3975 resume_points = new ArrayList ();
3976 first_resume_pc = pc;
3979 if (pc != first_resume_pc + resume_points.Count)
3980 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3982 resume_points.Add (stmt);
3985 Label dispose_try_block;
3986 bool prepared_for_dispose, emitted_dispose;
3987 public override Label PrepareForDispose (EmitContext ec, Label end)
3989 if (!prepared_for_dispose) {
3990 prepared_for_dispose = true;
3991 dispose_try_block = ec.ig.DefineLabel ();
3993 return dispose_try_block;
3996 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3998 if (emitted_dispose)
4001 emitted_dispose = true;
4003 ILGenerator ig = ec.ig;
4005 Label end_of_try = ig.DefineLabel ();
4007 // Ensure that the only way we can get into this code is through a dispatcher
4008 if (have_dispatcher)
4009 ig.Emit (OpCodes.Br, end);
4011 ig.BeginExceptionBlock ();
4013 ig.MarkLabel (dispose_try_block);
4015 Label [] labels = null;
4016 for (int i = 0; i < resume_points.Count; ++i) {
4017 ResumableStatement s = (ResumableStatement) resume_points [i];
4018 Label ret = s.PrepareForDispose (ec, end_of_try);
4019 if (ret.Equals (end_of_try) && labels == null)
4021 if (labels == null) {
4022 labels = new Label [resume_points.Count];
4023 for (int j = 0; j < i; ++j)
4024 labels [j] = end_of_try;
4029 if (labels != null) {
4031 for (j = 1; j < labels.Length; ++j)
4032 if (!labels [0].Equals (labels [j]))
4034 bool emit_dispatcher = j < labels.Length;
4036 if (emit_dispatcher) {
4037 //SymbolWriter.StartIteratorDispatcher (ec.ig);
4038 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
4039 IntConstant.EmitInt (ig, first_resume_pc);
4040 ig.Emit (OpCodes.Sub);
4041 ig.Emit (OpCodes.Switch, labels);
4042 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4045 foreach (ResumableStatement s in resume_points)
4046 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4049 ig.MarkLabel (end_of_try);
4051 ig.BeginFinallyBlock ();
4053 EmitFinallyBody (ec);
4055 ig.EndExceptionBlock ();
4059 public class Lock : ExceptionStatement {
4061 public Statement Statement;
4062 TemporaryVariable temp;
4064 public Lock (Expression expr, Statement stmt, Location l)
4071 public override bool Resolve (EmitContext ec)
4073 expr = expr.Resolve (ec);
4077 if (!TypeManager.IsReferenceType (expr.Type)){
4078 Report.Error (185, loc,
4079 "`{0}' is not a reference type as required by the lock statement",
4080 TypeManager.CSharpName (expr.Type));
4084 ec.StartFlowBranching (this);
4085 bool ok = Statement.Resolve (ec);
4086 ec.EndFlowBranching ();
4088 ResolveReachability (ec);
4090 // Avoid creating libraries that reference the internal
4093 if (t == TypeManager.null_type)
4094 t = TypeManager.object_type;
4096 temp = new TemporaryVariable (t, loc);
4099 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4100 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4101 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4102 monitor_type, "Enter", loc, TypeManager.object_type);
4103 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4104 monitor_type, "Exit", loc, TypeManager.object_type);
4110 protected override void EmitPreTryBody (EmitContext ec)
4112 ILGenerator ig = ec.ig;
4114 temp.EmitAssign (ec, expr);
4116 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4119 protected override void EmitTryBody (EmitContext ec)
4121 Statement.Emit (ec);
4124 protected override void EmitFinallyBody (EmitContext ec)
4127 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4130 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4132 expr.MutateHoistedGenericType (storey);
4133 temp.MutateHoistedGenericType (storey);
4134 Statement.MutateHoistedGenericType (storey);
4137 protected override void CloneTo (CloneContext clonectx, Statement t)
4139 Lock target = (Lock) t;
4141 target.expr = expr.Clone (clonectx);
4142 target.Statement = Statement.Clone (clonectx);
4146 public class Unchecked : Statement {
4149 public Unchecked (Block b)
4155 public override bool Resolve (EmitContext ec)
4157 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4158 return Block.Resolve (ec);
4161 protected override void DoEmit (EmitContext ec)
4163 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4167 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4169 Block.MutateHoistedGenericType (storey);
4172 protected override void CloneTo (CloneContext clonectx, Statement t)
4174 Unchecked target = (Unchecked) t;
4176 target.Block = clonectx.LookupBlock (Block);
4180 public class Checked : Statement {
4183 public Checked (Block b)
4186 b.Unchecked = false;
4189 public override bool Resolve (EmitContext ec)
4191 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4192 return Block.Resolve (ec);
4195 protected override void DoEmit (EmitContext ec)
4197 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4201 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4203 Block.MutateHoistedGenericType (storey);
4206 protected override void CloneTo (CloneContext clonectx, Statement t)
4208 Checked target = (Checked) t;
4210 target.Block = clonectx.LookupBlock (Block);
4214 public class Unsafe : Statement {
4217 public Unsafe (Block b)
4220 Block.Unsafe = true;
4223 public override bool Resolve (EmitContext ec)
4225 using (ec.With (EmitContext.Flags.InUnsafe, true))
4226 return Block.Resolve (ec);
4229 protected override void DoEmit (EmitContext ec)
4231 using (ec.With (EmitContext.Flags.InUnsafe, true))
4235 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4237 Block.MutateHoistedGenericType (storey);
4240 protected override void CloneTo (CloneContext clonectx, Statement t)
4242 Unsafe target = (Unsafe) t;
4244 target.Block = clonectx.LookupBlock (Block);
4251 public class Fixed : Statement {
4253 ArrayList declarators;
4254 Statement statement;
4259 abstract class Emitter
4261 protected LocalInfo vi;
4262 protected Expression converted;
4264 protected Emitter (Expression expr, LocalInfo li)
4270 public abstract void Emit (EmitContext ec);
4271 public abstract void EmitExit (EmitContext ec);
4274 class ExpressionEmitter : Emitter {
4275 public ExpressionEmitter (Expression converted, LocalInfo li) :
4276 base (converted, li)
4280 public override void Emit (EmitContext ec) {
4282 // Store pointer in pinned location
4284 converted.Emit (ec);
4288 public override void EmitExit (EmitContext ec)
4290 ec.ig.Emit (OpCodes.Ldc_I4_0);
4291 ec.ig.Emit (OpCodes.Conv_U);
4296 class StringEmitter : Emitter
4298 LocalInfo pinned_string;
4300 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4303 pinned_string = new LocalInfo (new TypeExpression (TypeManager.string_type, loc), null, null, loc);
4304 pinned_string.Pinned = true;
4307 public override void Emit (EmitContext ec)
4309 pinned_string.Resolve (ec);
4310 pinned_string.ResolveVariable (ec);
4312 converted.Emit (ec);
4313 pinned_string.EmitAssign (ec);
4315 PropertyInfo p = TypeManager.int_get_offset_to_string_data;
4317 // TODO: Move to resolve
4318 p = TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedProperty (
4319 TypeManager.runtime_helpers_type, "OffsetToStringData", pinned_string.Location, TypeManager.int32_type);
4325 // TODO: Should use Binary::Add
4326 pinned_string.Emit (ec);
4327 ec.ig.Emit (OpCodes.Conv_I);
4329 PropertyExpr pe = new PropertyExpr (pinned_string.VariableType, p, pinned_string.Location);
4330 //pe.InstanceExpression = pinned_string;
4331 pe.Resolve (ec).Emit (ec);
4333 ec.ig.Emit (OpCodes.Add);
4337 public override void EmitExit (EmitContext ec)
4339 ec.ig.Emit (OpCodes.Ldnull);
4340 pinned_string.EmitAssign (ec);
4344 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4347 declarators = decls;
4352 public Statement Statement {
4353 get { return statement; }
4356 public override bool Resolve (EmitContext ec)
4359 Expression.UnsafeError (loc);
4363 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4364 if (texpr == null) {
4365 if (type is VarExpr)
4366 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4371 expr_type = texpr.Type;
4373 data = new Emitter [declarators.Count];
4375 if (!expr_type.IsPointer){
4376 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4381 foreach (Pair p in declarators){
4382 LocalInfo vi = (LocalInfo) p.First;
4383 Expression e = (Expression) p.Second;
4385 vi.VariableInfo.SetAssigned (ec);
4386 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4389 // The rules for the possible declarators are pretty wise,
4390 // but the production on the grammar is more concise.
4392 // So we have to enforce these rules here.
4394 // We do not resolve before doing the case 1 test,
4395 // because the grammar is explicit in that the token &
4396 // is present, so we need to test for this particular case.
4400 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4404 ec.InFixedInitializer = true;
4406 ec.InFixedInitializer = false;
4413 if (e.Type.IsArray){
4414 Type array_type = TypeManager.GetElementType (e.Type);
4417 // Provided that array_type is unmanaged,
4419 if (!TypeManager.VerifyUnManaged (array_type, loc))
4423 // and T* is implicitly convertible to the
4424 // pointer type given in the fixed statement.
4426 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4428 Expression converted = Convert.ImplicitConversionRequired (
4429 ec, array_ptr, vi.VariableType, loc);
4430 if (converted == null)
4434 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4436 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4437 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4438 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4439 new NullPointer (loc),
4442 converted = converted.Resolve (ec);
4444 data [i] = new ExpressionEmitter (converted, vi);
4453 if (e.Type == TypeManager.string_type){
4454 data [i] = new StringEmitter (e, vi, loc);
4459 // Case 4: fixed buffer
4460 if (e is FixedBufferPtr) {
4461 data [i++] = new ExpressionEmitter (e, vi);
4466 // Case 1: & object.
4468 Unary u = e as Unary;
4469 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4470 IVariableReference vr = u.Expr as IVariableReference;
4471 if (vr == null || !vr.IsFixed) {
4472 data [i] = new ExpressionEmitter (e, vi);
4476 if (data [i++] == null)
4477 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4479 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4482 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4483 bool ok = statement.Resolve (ec);
4484 bool flow_unreachable = ec.EndFlowBranching ();
4485 has_ret = flow_unreachable;
4490 protected override void DoEmit (EmitContext ec)
4492 for (int i = 0; i < data.Length; i++) {
4496 statement.Emit (ec);
4502 // Clear the pinned variable
4504 for (int i = 0; i < data.Length; i++) {
4505 data [i].EmitExit (ec);
4509 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4511 // Fixed statement cannot be used inside anonymous methods or lambdas
4512 throw new NotSupportedException ();
4515 protected override void CloneTo (CloneContext clonectx, Statement t)
4517 Fixed target = (Fixed) t;
4519 target.type = type.Clone (clonectx);
4520 target.declarators = new ArrayList (declarators.Count);
4521 foreach (Pair p in declarators) {
4522 LocalInfo vi = (LocalInfo) p.First;
4523 Expression e = (Expression) p.Second;
4525 target.declarators.Add (
4526 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4529 target.statement = statement.Clone (clonectx);
4533 public class Catch : Statement {
4534 public readonly string Name;
4536 public Block VarBlock;
4538 Expression type_expr;
4541 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4546 VarBlock = var_block;
4550 public Type CatchType {
4556 public bool IsGeneral {
4558 return type_expr == null;
4562 protected override void DoEmit (EmitContext ec)
4564 ILGenerator ig = ec.ig;
4566 if (CatchType != null)
4567 ig.BeginCatchBlock (CatchType);
4569 ig.BeginCatchBlock (TypeManager.object_type);
4571 if (VarBlock != null)
4575 // TODO: Move to resolve
4576 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4580 // Only to make verifier happy
4581 if (TypeManager.IsGenericParameter (lvr.Type))
4582 ig.Emit (OpCodes.Unbox_Any, lvr.Type);
4586 if (lvr.IsHoisted) {
4587 LocalTemporary lt = new LocalTemporary (lvr.Type);
4591 // Variable is at the top of the stack
4592 source = EmptyExpression.Null;
4595 lvr.EmitAssign (ec, source, false, false);
4597 ig.Emit (OpCodes.Pop);
4602 public override bool Resolve (EmitContext ec)
4604 using (ec.With (EmitContext.Flags.InCatch, true)) {
4605 if (type_expr != null) {
4606 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4612 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4613 Error (155, "The type caught or thrown must be derived from System.Exception");
4619 if (!Block.Resolve (ec))
4622 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4623 // emit the "unused variable" warnings.
4624 if (VarBlock != null)
4625 return VarBlock.Resolve (ec);
4631 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4634 type = storey.MutateType (type);
4635 if (VarBlock != null)
4636 VarBlock.MutateHoistedGenericType (storey);
4637 Block.MutateHoistedGenericType (storey);
4640 protected override void CloneTo (CloneContext clonectx, Statement t)
4642 Catch target = (Catch) t;
4644 if (type_expr != null)
4645 target.type_expr = type_expr.Clone (clonectx);
4646 if (VarBlock != null)
4647 target.VarBlock = clonectx.LookupBlock (VarBlock);
4648 target.Block = clonectx.LookupBlock (Block);
4652 public class TryFinally : ExceptionStatement {
4656 public TryFinally (Statement stmt, Block fini, Location l)
4663 public override bool Resolve (EmitContext ec)
4667 ec.StartFlowBranching (this);
4669 if (!stmt.Resolve (ec))
4673 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4674 using (ec.With (EmitContext.Flags.InFinally, true)) {
4675 if (!fini.Resolve (ec))
4679 ec.EndFlowBranching ();
4681 ResolveReachability (ec);
4686 protected override void EmitPreTryBody (EmitContext ec)
4690 protected override void EmitTryBody (EmitContext ec)
4695 protected override void EmitFinallyBody (EmitContext ec)
4700 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4702 stmt.MutateHoistedGenericType (storey);
4703 fini.MutateHoistedGenericType (storey);
4706 protected override void CloneTo (CloneContext clonectx, Statement t)
4708 TryFinally target = (TryFinally) t;
4710 target.stmt = (Statement) stmt.Clone (clonectx);
4712 target.fini = clonectx.LookupBlock (fini);
4716 public class TryCatch : Statement {
4718 public ArrayList Specific;
4719 public Catch General;
4720 bool inside_try_finally, code_follows;
4722 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4725 this.Specific = catch_clauses;
4726 this.General = null;
4727 this.inside_try_finally = inside_try_finally;
4729 for (int i = 0; i < catch_clauses.Count; ++i) {
4730 Catch c = (Catch) catch_clauses [i];
4732 if (i != catch_clauses.Count - 1)
4733 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4735 catch_clauses.RemoveAt (i);
4743 public override bool Resolve (EmitContext ec)
4747 ec.StartFlowBranching (this);
4749 if (!Block.Resolve (ec))
4752 Type[] prev_catches = new Type [Specific.Count];
4754 foreach (Catch c in Specific){
4755 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4757 if (c.Name != null) {
4758 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4760 throw new Exception ();
4762 vi.VariableInfo = null;
4765 if (!c.Resolve (ec))
4768 Type resolved_type = c.CatchType;
4769 for (int ii = 0; ii < last_index; ++ii) {
4770 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4771 Report.Error (160, c.loc,
4772 "A previous catch clause already catches all exceptions of this or a super type `{0}'",
4773 TypeManager.CSharpName (prev_catches [ii]));
4778 prev_catches [last_index++] = resolved_type;
4781 if (General != null) {
4782 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4783 foreach (Catch c in Specific){
4784 if (c.CatchType == TypeManager.exception_type && PredefinedAttributes.Get.RuntimeCompatibility.IsDefined) {
4785 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'");
4790 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4792 if (!General.Resolve (ec))
4796 ec.EndFlowBranching ();
4798 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4799 // So, ensure there's some IL code after this statement
4800 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4801 ec.NeedReturnLabel ();
4806 public void SomeCodeFollows ()
4808 code_follows = true;
4811 protected override void DoEmit (EmitContext ec)
4813 ILGenerator ig = ec.ig;
4815 if (!inside_try_finally)
4816 ig.BeginExceptionBlock ();
4820 foreach (Catch c in Specific)
4823 if (General != null)
4826 if (!inside_try_finally)
4827 ig.EndExceptionBlock ();
4830 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4832 Block.MutateHoistedGenericType (storey);
4834 if (General != null)
4835 General.MutateHoistedGenericType (storey);
4836 if (Specific != null) {
4837 foreach (Catch c in Specific)
4838 c.MutateHoistedGenericType (storey);
4842 protected override void CloneTo (CloneContext clonectx, Statement t)
4844 TryCatch target = (TryCatch) t;
4846 target.Block = clonectx.LookupBlock (Block);
4847 if (General != null)
4848 target.General = (Catch) General.Clone (clonectx);
4849 if (Specific != null){
4850 target.Specific = new ArrayList ();
4851 foreach (Catch c in Specific)
4852 target.Specific.Add (c.Clone (clonectx));
4857 // FIXME: Why is it almost exact copy of Using ??
4858 public class UsingTemporary : ExceptionStatement {
4859 TemporaryVariable local_copy;
4860 public Statement Statement;
4864 public UsingTemporary (Expression expr, Statement stmt, Location l)
4871 public override bool Resolve (EmitContext ec)
4873 expr = expr.Resolve (ec);
4877 expr_type = expr.Type;
4879 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4880 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4881 Using.Error_IsNotConvertibleToIDisposable (expr);
4886 local_copy = new TemporaryVariable (expr_type, loc);
4887 local_copy.Resolve (ec);
4889 ec.StartFlowBranching (this);
4891 bool ok = Statement.Resolve (ec);
4893 ec.EndFlowBranching ();
4895 ResolveReachability (ec);
4897 if (TypeManager.void_dispose_void == null) {
4898 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4899 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4905 protected override void EmitPreTryBody (EmitContext ec)
4907 local_copy.EmitAssign (ec, expr);
4910 protected override void EmitTryBody (EmitContext ec)
4912 Statement.Emit (ec);
4915 protected override void EmitFinallyBody (EmitContext ec)
4917 ILGenerator ig = ec.ig;
4918 if (!TypeManager.IsStruct (expr_type)) {
4919 Label skip = ig.DefineLabel ();
4920 local_copy.Emit (ec);
4921 ig.Emit (OpCodes.Brfalse, skip);
4922 local_copy.Emit (ec);
4923 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4924 ig.MarkLabel (skip);
4928 Expression ml = Expression.MemberLookup (
4929 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4930 "Dispose", Location.Null);
4932 if (!(ml is MethodGroupExpr)) {
4933 local_copy.Emit (ec);
4934 ig.Emit (OpCodes.Box, expr_type);
4935 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4939 MethodInfo mi = null;
4941 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4942 if (TypeManager.GetParameterData (mk).Count == 0) {
4949 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4953 local_copy.AddressOf (ec, AddressOp.Load);
4954 ig.Emit (OpCodes.Call, mi);
4957 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4959 expr_type = storey.MutateType (expr_type);
4960 local_copy.MutateHoistedGenericType (storey);
4961 Statement.MutateHoistedGenericType (storey);
4964 protected override void CloneTo (CloneContext clonectx, Statement t)
4966 UsingTemporary target = (UsingTemporary) t;
4968 target.expr = expr.Clone (clonectx);
4969 target.Statement = Statement.Clone (clonectx);
4973 public class Using : ExceptionStatement {
4975 public Statement EmbeddedStatement {
4976 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4982 ExpressionStatement assign;
4984 public Using (Expression var, Expression init, Statement stmt, Location l)
4992 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4994 Report.SymbolRelatedToPreviousError (expr.Type);
4995 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4996 expr.GetSignatureForError ());
4999 protected override void EmitPreTryBody (EmitContext ec)
5001 assign.EmitStatement (ec);
5004 protected override void EmitTryBody (EmitContext ec)
5009 protected override void EmitFinallyBody (EmitContext ec)
5011 ILGenerator ig = ec.ig;
5012 Label skip = ig.DefineLabel ();
5014 bool emit_null_check = !TypeManager.IsValueType (var.Type);
5015 if (emit_null_check) {
5017 ig.Emit (OpCodes.Brfalse, skip);
5020 Invocation.EmitCall (ec, false, var, TypeManager.void_dispose_void, null, loc);
5022 if (emit_null_check)
5023 ig.MarkLabel (skip);
5026 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5028 assign.MutateHoistedGenericType (storey);
5029 var.MutateHoistedGenericType (storey);
5030 stmt.MutateHoistedGenericType (storey);
5033 public override bool Resolve (EmitContext ec)
5035 if (!ResolveVariable (ec))
5038 ec.StartFlowBranching (this);
5040 bool ok = stmt.Resolve (ec);
5042 ec.EndFlowBranching ();
5044 ResolveReachability (ec);
5046 if (TypeManager.void_dispose_void == null) {
5047 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5048 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5054 bool ResolveVariable (EmitContext ec)
5056 assign = new SimpleAssign (var, init, loc);
5057 assign = assign.ResolveStatement (ec);
5061 if (assign.Type == TypeManager.idisposable_type ||
5062 TypeManager.ImplementsInterface (assign.Type, TypeManager.idisposable_type)) {
5066 Expression e = Convert.ImplicitConversionStandard (ec, assign, TypeManager.idisposable_type, var.Location);
5068 Error_IsNotConvertibleToIDisposable (var);
5072 throw new NotImplementedException ("covariance?");
5075 protected override void CloneTo (CloneContext clonectx, Statement t)
5077 Using target = (Using) t;
5079 target.var = var.Clone (clonectx);
5080 target.init = init.Clone (clonectx);
5081 target.stmt = stmt.Clone (clonectx);
5086 /// Implementation of the foreach C# statement
5088 public class Foreach : Statement {
5090 sealed class ArrayForeach : Statement
5092 class ArrayCounter : TemporaryVariable
5094 StatementExpression increment;
5096 public ArrayCounter (Location loc)
5097 : base (TypeManager.int32_type, loc)
5101 public void ResolveIncrement (EmitContext ec)
5103 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5104 increment.Resolve (ec);
5107 public void EmitIncrement (EmitContext ec)
5109 increment.Emit (ec);
5113 readonly Foreach for_each;
5114 readonly Statement statement;
5117 TemporaryVariable[] lengths;
5118 Expression [] length_exprs;
5119 ArrayCounter[] counter;
5121 TemporaryVariable copy;
5124 public ArrayForeach (Foreach @foreach, int rank)
5126 for_each = @foreach;
5127 statement = for_each.statement;
5130 counter = new ArrayCounter [rank];
5131 length_exprs = new Expression [rank];
5134 // Only use temporary length variables when dealing with
5135 // multi-dimensional arrays
5138 lengths = new TemporaryVariable [rank];
5141 protected override void CloneTo (CloneContext clonectx, Statement target)
5143 throw new NotImplementedException ();
5146 public override bool Resolve (EmitContext ec)
5148 copy = new TemporaryVariable (for_each.expr.Type, loc);
5151 int rank = length_exprs.Length;
5152 Arguments list = new Arguments (rank);
5153 for (int i = 0; i < rank; i++) {
5154 counter [i] = new ArrayCounter (loc);
5155 counter [i].ResolveIncrement (ec);
5158 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5160 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5161 lengths [i].Resolve (ec);
5163 Arguments args = new Arguments (1);
5164 args.Add (new Argument (new IntConstant (i, loc)));
5165 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5168 list.Add (new Argument (counter [i]));
5171 access = new ElementAccess (copy, list).Resolve (ec);
5175 Expression var_type = for_each.type;
5176 VarExpr ve = var_type as VarExpr;
5178 // Infer implicitly typed local variable from foreach array type
5179 var_type = new TypeExpression (access.Type, ve.Location);
5182 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5183 if (var_type == null)
5186 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5192 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5193 ec.CurrentBranching.CreateSibling ();
5195 for_each.variable = for_each.variable.ResolveLValue (ec, conv);
5196 if (for_each.variable == null)
5199 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5200 if (!statement.Resolve (ec))
5202 ec.EndFlowBranching ();
5204 // There's no direct control flow from the end of the embedded statement to the end of the loop
5205 ec.CurrentBranching.CurrentUsageVector.Goto ();
5207 ec.EndFlowBranching ();
5212 protected override void DoEmit (EmitContext ec)
5214 ILGenerator ig = ec.ig;
5216 copy.EmitAssign (ec, for_each.expr);
5218 int rank = length_exprs.Length;
5219 Label[] test = new Label [rank];
5220 Label[] loop = new Label [rank];
5222 for (int i = 0; i < rank; i++) {
5223 test [i] = ig.DefineLabel ();
5224 loop [i] = ig.DefineLabel ();
5226 if (lengths != null)
5227 lengths [i].EmitAssign (ec, length_exprs [i]);
5230 IntConstant zero = new IntConstant (0, loc);
5231 for (int i = 0; i < rank; i++) {
5232 counter [i].EmitAssign (ec, zero);
5234 ig.Emit (OpCodes.Br, test [i]);
5235 ig.MarkLabel (loop [i]);
5238 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5240 statement.Emit (ec);
5242 ig.MarkLabel (ec.LoopBegin);
5244 for (int i = rank - 1; i >= 0; i--){
5245 counter [i].EmitIncrement (ec);
5247 ig.MarkLabel (test [i]);
5248 counter [i].Emit (ec);
5250 if (lengths != null)
5251 lengths [i].Emit (ec);
5253 length_exprs [i].Emit (ec);
5255 ig.Emit (OpCodes.Blt, loop [i]);
5258 ig.MarkLabel (ec.LoopEnd);
5261 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5263 for_each.expr.MutateHoistedGenericType (storey);
5265 copy.MutateHoistedGenericType (storey);
5266 conv.MutateHoistedGenericType (storey);
5267 statement.MutateHoistedGenericType (storey);
5269 for (int i = 0; i < counter.Length; i++) {
5270 counter [i].MutateHoistedGenericType (storey);
5271 if (lengths != null)
5272 lengths [i].MutateHoistedGenericType (storey);
5277 sealed class CollectionForeach : Statement
5279 class CollectionForeachStatement : Statement
5282 Expression variable, current, conv;
5283 Statement statement;
5286 public CollectionForeachStatement (Type type, Expression variable,
5287 Expression current, Statement statement,
5291 this.variable = variable;
5292 this.current = current;
5293 this.statement = statement;
5297 protected override void CloneTo (CloneContext clonectx, Statement target)
5299 throw new NotImplementedException ();
5302 public override bool Resolve (EmitContext ec)
5304 current = current.Resolve (ec);
5305 if (current == null)
5308 conv = Convert.ExplicitConversion (ec, current, type, loc);
5312 assign = new SimpleAssign (variable, conv, loc);
5313 if (assign.Resolve (ec) == null)
5316 if (!statement.Resolve (ec))
5322 protected override void DoEmit (EmitContext ec)
5324 assign.EmitStatement (ec);
5325 statement.Emit (ec);
5328 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5330 assign.MutateHoistedGenericType (storey);
5331 statement.MutateHoistedGenericType (storey);
5335 Expression variable, expr;
5336 Statement statement;
5338 TemporaryVariable enumerator;
5343 MethodGroupExpr get_enumerator;
5344 PropertyExpr get_current;
5345 MethodInfo move_next;
5346 Expression var_type;
5347 Type enumerator_type;
5348 bool enumerator_found;
5350 public CollectionForeach (Expression var_type, Expression var,
5351 Expression expr, Statement stmt, Location l)
5353 this.var_type = var_type;
5354 this.variable = var;
5360 protected override void CloneTo (CloneContext clonectx, Statement target)
5362 throw new NotImplementedException ();
5365 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5367 Type return_type = mi.ReturnType;
5370 // Ok, we can access it, now make sure that we can do something
5371 // with this `GetEnumerator'
5374 if (return_type == TypeManager.ienumerator_type ||
5375 TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type)) {
5377 // If it is not an interface, lets try to find the methods ourselves.
5378 // For example, if we have:
5379 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5380 // We can avoid the iface call. This is a runtime perf boost.
5381 // even bigger if we have a ValueType, because we avoid the cost
5384 // We have to make sure that both methods exist for us to take
5385 // this path. If one of the methods does not exist, we will just
5386 // use the interface. Sadly, this complex if statement is the only
5387 // way I could do this without a goto
5390 if (TypeManager.bool_movenext_void == null) {
5391 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5392 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5395 if (TypeManager.ienumerator_getcurrent == null) {
5396 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5397 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5401 // Prefer a generic enumerator over a non-generic one.
5403 if (return_type.IsInterface && TypeManager.IsGenericType (return_type)) {
5404 enumerator_type = return_type;
5405 if (!FetchGetCurrent (ec, return_type))
5406 get_current = new PropertyExpr (
5407 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5408 if (!FetchMoveNext (return_type))
5409 move_next = TypeManager.bool_movenext_void;
5413 if (return_type.IsInterface ||
5414 !FetchMoveNext (return_type) ||
5415 !FetchGetCurrent (ec, return_type)) {
5416 enumerator_type = return_type;
5417 move_next = TypeManager.bool_movenext_void;
5418 get_current = new PropertyExpr (
5419 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5424 // Ok, so they dont return an IEnumerable, we will have to
5425 // find if they support the GetEnumerator pattern.
5428 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5429 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",
5430 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5435 enumerator_type = return_type;
5441 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5443 bool FetchMoveNext (Type t)
5445 MemberInfo[] move_next_list = TypeManager.MemberLookup (null, null, t,
5447 BindingFlags.Public | BindingFlags.Instance,
5450 foreach (MemberInfo m in move_next_list){
5451 MethodInfo mi = (MethodInfo) m;
5453 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5454 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5464 // Retrieves a `public T get_Current ()' method from the Type `t'
5466 bool FetchGetCurrent (EmitContext ec, Type t)
5468 PropertyExpr pe = Expression.MemberLookup (
5469 ec.ContainerType, t, "Current", MemberTypes.Property,
5470 Expression.AllBindingFlags, loc) as PropertyExpr;
5478 void Error_Enumerator ()
5480 if (enumerator_found) {
5484 Report.Error (1579, loc,
5485 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5486 TypeManager.CSharpName (expr.Type));
5489 bool IsOverride (MethodInfo m)
5491 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5493 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5495 if (m is MethodBuilder)
5498 MethodInfo base_method = m.GetBaseDefinition ();
5499 return base_method != m;
5502 bool TryType (EmitContext ec, Type t)
5504 MethodGroupExpr mg = Expression.MemberLookup (
5505 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5506 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5510 MethodInfo result = null;
5511 MethodInfo tmp_move_next = null;
5512 PropertyExpr tmp_get_cur = null;
5513 Type tmp_enumerator_type = enumerator_type;
5514 foreach (MethodInfo mi in mg.Methods) {
5515 if (TypeManager.GetParameterData (mi).Count != 0)
5518 // Check whether GetEnumerator is public
5519 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5522 if (IsOverride (mi))
5525 enumerator_found = true;
5527 if (!GetEnumeratorFilter (ec, mi))
5530 if (result != null) {
5531 if (TypeManager.IsGenericType (result.ReturnType)) {
5532 if (!TypeManager.IsGenericType (mi.ReturnType))
5535 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5536 Report.SymbolRelatedToPreviousError (t);
5537 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5538 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5539 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5543 // Always prefer generics enumerators
5544 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5545 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5546 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5549 Report.SymbolRelatedToPreviousError (result);
5550 Report.SymbolRelatedToPreviousError (mi);
5551 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5552 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5557 tmp_move_next = move_next;
5558 tmp_get_cur = get_current;
5559 tmp_enumerator_type = enumerator_type;
5560 if (mi.DeclaringType == t)
5564 if (result != null) {
5565 move_next = tmp_move_next;
5566 get_current = tmp_get_cur;
5567 enumerator_type = tmp_enumerator_type;
5568 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5569 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5571 if (t != expr.Type) {
5572 expr = Convert.ExplicitConversion (
5575 throw new InternalErrorException ();
5578 get_enumerator.InstanceExpression = expr;
5579 get_enumerator.IsBase = t != expr.Type;
5587 bool ProbeCollectionType (EmitContext ec, Type t)
5589 int errors = Report.Errors;
5590 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5591 if (TryType (ec, tt))
5596 if (Report.Errors > errors)
5600 // Now try to find the method in the interfaces
5602 Type [] ifaces = TypeManager.GetInterfaces (t);
5603 foreach (Type i in ifaces){
5604 if (TryType (ec, i))
5611 public override bool Resolve (EmitContext ec)
5613 enumerator_type = TypeManager.ienumerator_type;
5615 if (!ProbeCollectionType (ec, expr.Type)) {
5616 Error_Enumerator ();
5620 VarExpr ve = var_type as VarExpr;
5622 // Infer implicitly typed local variable from foreach enumerable type
5623 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5626 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5627 if (var_type == null)
5630 enumerator = new TemporaryVariable (enumerator_type, loc);
5631 enumerator.Resolve (ec);
5633 init = new Invocation (get_enumerator, null);
5634 init = init.Resolve (ec);
5638 Expression move_next_expr;
5640 MemberInfo[] mi = new MemberInfo[] { move_next };
5641 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5642 mg.InstanceExpression = enumerator;
5644 move_next_expr = new Invocation (mg, null);
5647 get_current.InstanceExpression = enumerator;
5649 Statement block = new CollectionForeachStatement (
5650 var_type.Type, variable, get_current, statement, loc);
5652 loop = new While (move_next_expr, block, loc);
5655 bool implements_idisposable = TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5656 if (implements_idisposable || !enumerator_type.IsSealed) {
5657 wrapper = new DisposableWrapper (this, implements_idisposable);
5659 wrapper = new NonDisposableWrapper (this);
5662 return wrapper.Resolve (ec);
5665 protected override void DoEmit (EmitContext ec)
5670 class NonDisposableWrapper : Statement {
5671 CollectionForeach parent;
5673 internal NonDisposableWrapper (CollectionForeach parent)
5675 this.parent = parent;
5678 protected override void CloneTo (CloneContext clonectx, Statement target)
5680 throw new NotSupportedException ();
5683 public override bool Resolve (EmitContext ec)
5685 return parent.ResolveLoop (ec);
5688 protected override void DoEmit (EmitContext ec)
5690 parent.EmitLoopInit (ec);
5691 parent.EmitLoopBody (ec);
5694 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5696 throw new NotSupportedException ();
5700 sealed class DisposableWrapper : ExceptionStatement
5702 CollectionForeach parent;
5703 bool implements_idisposable;
5705 internal DisposableWrapper (CollectionForeach parent, bool implements)
5707 this.parent = parent;
5708 this.implements_idisposable = implements;
5711 protected override void CloneTo (CloneContext clonectx, Statement target)
5713 throw new NotSupportedException ();
5716 public override bool Resolve (EmitContext ec)
5720 ec.StartFlowBranching (this);
5722 if (!parent.ResolveLoop (ec))
5725 ec.EndFlowBranching ();
5727 ResolveReachability (ec);
5729 if (TypeManager.void_dispose_void == null) {
5730 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5731 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5736 protected override void EmitPreTryBody (EmitContext ec)
5738 parent.EmitLoopInit (ec);
5741 protected override void EmitTryBody (EmitContext ec)
5743 parent.EmitLoopBody (ec);
5746 protected override void EmitFinallyBody (EmitContext ec)
5748 Expression instance = parent.enumerator;
5749 if (!TypeManager.IsValueType (parent.enumerator_type)) {
5750 ILGenerator ig = ec.ig;
5752 parent.enumerator.Emit (ec);
5754 Label call_dispose = ig.DefineLabel ();
5756 if (!implements_idisposable) {
5757 ec.ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5758 LocalTemporary temp = new LocalTemporary (TypeManager.idisposable_type);
5764 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5766 // using 'endfinally' to empty the evaluation stack
5767 ig.Emit (OpCodes.Endfinally);
5768 ig.MarkLabel (call_dispose);
5771 Invocation.EmitCall (ec, false, instance, TypeManager.void_dispose_void, null, loc);
5774 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5776 throw new NotSupportedException ();
5780 bool ResolveLoop (EmitContext ec)
5782 return loop.Resolve (ec);
5785 void EmitLoopInit (EmitContext ec)
5787 enumerator.EmitAssign (ec, init);
5790 void EmitLoopBody (EmitContext ec)
5795 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5797 enumerator_type = storey.MutateType (enumerator_type);
5798 init.MutateHoistedGenericType (storey);
5799 loop.MutateHoistedGenericType (storey);
5804 Expression variable;
5806 Statement statement;
5808 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5809 Statement stmt, Location l)
5812 this.variable = var;
5818 public Statement Statement {
5819 get { return statement; }
5822 public override bool Resolve (EmitContext ec)
5824 expr = expr.Resolve (ec);
5829 Report.Error (186, loc, "Use of null is not valid in this context");
5833 if (expr.Type == TypeManager.string_type) {
5834 statement = new ArrayForeach (this, 1);
5835 } else if (expr.Type.IsArray) {
5836 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5838 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5839 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5840 expr.ExprClassName);
5844 statement = new CollectionForeach (type, variable, expr, statement, loc);
5847 return statement.Resolve (ec);
5850 protected override void DoEmit (EmitContext ec)
5852 ILGenerator ig = ec.ig;
5854 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5855 ec.LoopBegin = ig.DefineLabel ();
5856 ec.LoopEnd = ig.DefineLabel ();
5858 statement.Emit (ec);
5860 ec.LoopBegin = old_begin;
5861 ec.LoopEnd = old_end;
5864 protected override void CloneTo (CloneContext clonectx, Statement t)
5866 Foreach target = (Foreach) t;
5868 target.type = type.Clone (clonectx);
5869 target.variable = variable.Clone (clonectx);
5870 target.expr = expr.Clone (clonectx);
5871 target.statement = statement.Clone (clonectx);
5874 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5876 statement.MutateHoistedGenericType (storey);