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);
82 /// Return value indicates whether all code paths emitted return.
84 public virtual void Emit (EmitContext ec)
91 // This routine must be overrided in derived classes and make copies
92 // of all the data that might be modified if resolved
94 protected virtual void CloneTo (CloneContext clonectx, Statement target)
96 throw new InternalErrorException ("{0} does not implement Statement.CloneTo", this.GetType ());
99 public Statement Clone (CloneContext clonectx)
101 Statement s = (Statement) this.MemberwiseClone ();
102 CloneTo (clonectx, s);
106 public virtual Expression CreateExpressionTree (EmitContext ec)
108 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
112 public Statement PerformClone ()
114 CloneContext clonectx = new CloneContext ();
116 return Clone (clonectx);
119 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
123 // This class is used during the Statement.Clone operation
124 // to remap objects that have been cloned.
126 // Since blocks are cloned by Block.Clone, we need a way for
127 // expressions that must reference the block to be cloned
128 // pointing to the new cloned block.
130 public class CloneContext {
131 Hashtable block_map = new Hashtable ();
132 Hashtable variable_map;
134 public void AddBlockMap (Block from, Block to)
136 if (block_map.Contains (from))
138 block_map [from] = to;
141 public Block LookupBlock (Block from)
143 Block result = (Block) block_map [from];
146 result = (Block) from.Clone (this);
147 block_map [from] = result;
154 /// Remaps block to cloned copy if one exists.
156 public Block RemapBlockCopy (Block from)
158 Block mapped_to = (Block)block_map[from];
159 if (mapped_to == null)
165 public void AddVariableMap (LocalInfo from, LocalInfo to)
167 if (variable_map == null)
168 variable_map = new Hashtable ();
170 if (variable_map.Contains (from))
172 variable_map [from] = to;
175 public LocalInfo LookupVariable (LocalInfo from)
177 LocalInfo result = (LocalInfo) variable_map [from];
180 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
186 public sealed class EmptyStatement : Statement {
188 private EmptyStatement () {}
190 public static readonly EmptyStatement Value = new EmptyStatement ();
192 public override bool Resolve (EmitContext ec)
197 public override bool ResolveUnreachable (EmitContext ec, bool warn)
202 protected override void DoEmit (EmitContext ec)
206 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
210 protected override void CloneTo (CloneContext clonectx, Statement target)
216 public class If : Statement {
218 public Statement TrueStatement;
219 public Statement FalseStatement;
223 public If (Expression expr, Statement true_statement, Location l)
226 TrueStatement = true_statement;
230 public If (Expression expr,
231 Statement true_statement,
232 Statement false_statement,
236 TrueStatement = true_statement;
237 FalseStatement = false_statement;
241 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
243 expr.MutateHoistedGenericType (storey);
244 TrueStatement.MutateHoistedGenericType (storey);
245 if (FalseStatement != null)
246 FalseStatement.MutateHoistedGenericType (storey);
249 public override bool Resolve (EmitContext ec)
253 Report.Debug (1, "START IF BLOCK", loc);
255 expr = Expression.ResolveBoolean (ec, expr, loc);
261 Assign ass = expr as Assign;
262 if (ass != null && ass.Source is Constant) {
263 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
267 // Dead code elimination
269 if (expr is Constant){
270 bool take = !((Constant) expr).IsDefaultValue;
273 if (!TrueStatement.Resolve (ec))
276 if ((FalseStatement != null) &&
277 !FalseStatement.ResolveUnreachable (ec, true))
279 FalseStatement = null;
281 if (!TrueStatement.ResolveUnreachable (ec, true))
283 TrueStatement = null;
285 if ((FalseStatement != null) &&
286 !FalseStatement.Resolve (ec))
293 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
295 ok &= TrueStatement.Resolve (ec);
297 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
299 ec.CurrentBranching.CreateSibling ();
301 if (FalseStatement != null)
302 ok &= FalseStatement.Resolve (ec);
304 ec.EndFlowBranching ();
306 Report.Debug (1, "END IF BLOCK", loc);
311 protected override void DoEmit (EmitContext ec)
313 ILGenerator ig = ec.ig;
314 Label false_target = ig.DefineLabel ();
318 // If we're a boolean constant, Resolve() already
319 // eliminated dead code for us.
321 Constant c = expr as Constant;
323 c.EmitSideEffect (ec);
325 if (!c.IsDefaultValue)
326 TrueStatement.Emit (ec);
327 else if (FalseStatement != null)
328 FalseStatement.Emit (ec);
333 expr.EmitBranchable (ec, false_target, false);
335 TrueStatement.Emit (ec);
337 if (FalseStatement != null){
338 bool branch_emitted = false;
340 end = ig.DefineLabel ();
342 ig.Emit (OpCodes.Br, end);
343 branch_emitted = true;
346 ig.MarkLabel (false_target);
347 FalseStatement.Emit (ec);
352 ig.MarkLabel (false_target);
356 protected override void CloneTo (CloneContext clonectx, Statement t)
360 target.expr = expr.Clone (clonectx);
361 target.TrueStatement = TrueStatement.Clone (clonectx);
362 if (FalseStatement != null)
363 target.FalseStatement = FalseStatement.Clone (clonectx);
367 public class Do : Statement {
368 public Expression expr;
369 public Statement EmbeddedStatement;
371 public Do (Statement statement, Expression bool_expr, Location l)
374 EmbeddedStatement = statement;
378 public override bool Resolve (EmitContext ec)
382 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
384 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
386 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
387 if (!EmbeddedStatement.Resolve (ec))
389 ec.EndFlowBranching ();
391 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
392 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
394 expr = Expression.ResolveBoolean (ec, expr, loc);
397 else if (expr is Constant){
398 bool infinite = !((Constant) expr).IsDefaultValue;
400 ec.CurrentBranching.CurrentUsageVector.Goto ();
403 ec.EndFlowBranching ();
408 protected override void DoEmit (EmitContext ec)
410 ILGenerator ig = ec.ig;
411 Label loop = ig.DefineLabel ();
412 Label old_begin = ec.LoopBegin;
413 Label old_end = ec.LoopEnd;
415 ec.LoopBegin = ig.DefineLabel ();
416 ec.LoopEnd = ig.DefineLabel ();
419 EmbeddedStatement.Emit (ec);
420 ig.MarkLabel (ec.LoopBegin);
423 // Dead code elimination
425 if (expr is Constant){
426 bool res = !((Constant) expr).IsDefaultValue;
428 expr.EmitSideEffect (ec);
430 ec.ig.Emit (OpCodes.Br, loop);
432 expr.EmitBranchable (ec, loop, true);
434 ig.MarkLabel (ec.LoopEnd);
436 ec.LoopBegin = old_begin;
437 ec.LoopEnd = old_end;
440 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
442 expr.MutateHoistedGenericType (storey);
443 EmbeddedStatement.MutateHoistedGenericType (storey);
446 protected override void CloneTo (CloneContext clonectx, Statement t)
450 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
451 target.expr = expr.Clone (clonectx);
455 public class While : Statement {
456 public Expression expr;
457 public Statement Statement;
458 bool infinite, empty;
460 public While (Expression bool_expr, Statement statement, Location l)
462 this.expr = bool_expr;
463 Statement = statement;
467 public override bool Resolve (EmitContext ec)
471 expr = Expression.ResolveBoolean (ec, expr, loc);
476 // Inform whether we are infinite or not
478 if (expr is Constant){
479 bool value = !((Constant) expr).IsDefaultValue;
482 if (!Statement.ResolveUnreachable (ec, true))
490 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
492 ec.CurrentBranching.CreateSibling ();
494 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
495 if (!Statement.Resolve (ec))
497 ec.EndFlowBranching ();
499 // There's no direct control flow from the end of the embedded statement to the end of the loop
500 ec.CurrentBranching.CurrentUsageVector.Goto ();
502 ec.EndFlowBranching ();
507 protected override void DoEmit (EmitContext ec)
510 expr.EmitSideEffect (ec);
514 ILGenerator ig = ec.ig;
515 Label old_begin = ec.LoopBegin;
516 Label old_end = ec.LoopEnd;
518 ec.LoopBegin = ig.DefineLabel ();
519 ec.LoopEnd = ig.DefineLabel ();
522 // Inform whether we are infinite or not
524 if (expr is Constant){
525 // expr is 'true', since the 'empty' case above handles the 'false' case
526 ig.MarkLabel (ec.LoopBegin);
527 expr.EmitSideEffect (ec);
529 ig.Emit (OpCodes.Br, ec.LoopBegin);
532 // Inform that we are infinite (ie, `we return'), only
533 // if we do not `break' inside the code.
535 ig.MarkLabel (ec.LoopEnd);
537 Label while_loop = ig.DefineLabel ();
539 ig.Emit (OpCodes.Br, ec.LoopBegin);
540 ig.MarkLabel (while_loop);
544 ig.MarkLabel (ec.LoopBegin);
547 expr.EmitBranchable (ec, while_loop, true);
549 ig.MarkLabel (ec.LoopEnd);
552 ec.LoopBegin = old_begin;
553 ec.LoopEnd = old_end;
556 public override void Emit (EmitContext ec)
561 protected override void CloneTo (CloneContext clonectx, Statement t)
563 While target = (While) t;
565 target.expr = expr.Clone (clonectx);
566 target.Statement = Statement.Clone (clonectx);
569 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
571 expr.MutateHoistedGenericType (storey);
572 Statement.MutateHoistedGenericType (storey);
576 public class For : Statement {
578 Statement InitStatement;
580 public Statement Statement;
581 bool infinite, empty;
583 public For (Statement init_statement,
589 InitStatement = init_statement;
591 Increment = increment;
592 Statement = statement;
596 public override bool Resolve (EmitContext ec)
600 if (InitStatement != null){
601 if (!InitStatement.Resolve (ec))
606 Test = Expression.ResolveBoolean (ec, Test, loc);
609 else if (Test is Constant){
610 bool value = !((Constant) Test).IsDefaultValue;
613 if (!Statement.ResolveUnreachable (ec, true))
615 if ((Increment != null) &&
616 !Increment.ResolveUnreachable (ec, false))
626 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
628 ec.CurrentBranching.CreateSibling ();
630 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
632 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
633 if (!Statement.Resolve (ec))
635 ec.EndFlowBranching ();
637 if (Increment != null){
638 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
639 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
642 if (!Increment.Resolve (ec))
647 // There's no direct control flow from the end of the embedded statement to the end of the loop
648 ec.CurrentBranching.CurrentUsageVector.Goto ();
650 ec.EndFlowBranching ();
655 protected override void DoEmit (EmitContext ec)
657 if (InitStatement != null && InitStatement != EmptyStatement.Value)
658 InitStatement.Emit (ec);
661 Test.EmitSideEffect (ec);
665 ILGenerator ig = ec.ig;
666 Label old_begin = ec.LoopBegin;
667 Label old_end = ec.LoopEnd;
668 Label loop = ig.DefineLabel ();
669 Label test = ig.DefineLabel ();
671 ec.LoopBegin = ig.DefineLabel ();
672 ec.LoopEnd = ig.DefineLabel ();
674 ig.Emit (OpCodes.Br, test);
678 ig.MarkLabel (ec.LoopBegin);
679 if (Increment != EmptyStatement.Value)
684 // If test is null, there is no test, and we are just
689 // The Resolve code already catches the case for
690 // Test == Constant (false) so we know that
693 if (Test is Constant) {
694 Test.EmitSideEffect (ec);
695 ig.Emit (OpCodes.Br, loop);
697 Test.EmitBranchable (ec, loop, true);
701 ig.Emit (OpCodes.Br, loop);
702 ig.MarkLabel (ec.LoopEnd);
704 ec.LoopBegin = old_begin;
705 ec.LoopEnd = old_end;
708 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
710 if (InitStatement != null)
711 InitStatement.MutateHoistedGenericType (storey);
713 Test.MutateHoistedGenericType (storey);
714 if (Increment != null)
715 Increment.MutateHoistedGenericType (storey);
717 Statement.MutateHoistedGenericType (storey);
720 protected override void CloneTo (CloneContext clonectx, Statement t)
722 For target = (For) t;
724 if (InitStatement != null)
725 target.InitStatement = InitStatement.Clone (clonectx);
727 target.Test = Test.Clone (clonectx);
728 if (Increment != null)
729 target.Increment = Increment.Clone (clonectx);
730 target.Statement = Statement.Clone (clonectx);
734 public class StatementExpression : Statement {
735 ExpressionStatement expr;
737 public StatementExpression (ExpressionStatement expr)
743 public override bool Resolve (EmitContext ec)
746 expr = expr.ResolveStatement (ec);
750 protected override void DoEmit (EmitContext ec)
752 expr.EmitStatement (ec);
755 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
757 expr.MutateHoistedGenericType (storey);
760 public override string ToString ()
762 return "StatementExpression (" + expr + ")";
765 protected override void CloneTo (CloneContext clonectx, Statement t)
767 StatementExpression target = (StatementExpression) t;
769 target.expr = (ExpressionStatement) expr.Clone (clonectx);
773 // A 'return' or a 'yield break'
774 public abstract class ExitStatement : Statement
776 protected bool unwind_protect;
777 protected abstract bool DoResolve (EmitContext ec);
779 public virtual void Error_FinallyClause ()
781 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
784 public sealed override bool Resolve (EmitContext ec)
789 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
791 ec.NeedReturnLabel ();
792 ec.CurrentBranching.CurrentUsageVector.Goto ();
798 /// Implements the return statement
800 public class Return : ExitStatement {
801 protected Expression Expr;
802 public Return (Expression expr, Location l)
808 protected override bool DoResolve (EmitContext ec)
811 if (ec.ReturnType == TypeManager.void_type)
814 Error (126, "An object of a type convertible to `{0}' is required " +
815 "for the return statement",
816 TypeManager.CSharpName (ec.ReturnType));
820 AnonymousExpression am = ec.CurrentAnonymousMethod;
821 if ((am != null) && am.IsIterator && ec.InIterator) {
822 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
823 "statement to return a value, or yield break to end the iteration");
826 if (am == null && ec.ReturnType == TypeManager.void_type) {
827 MemberCore mc = ec.ResolveContext as MemberCore;
828 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
829 mc.GetSignatureForError ());
832 Expr = Expr.Resolve (ec);
836 if (Expr.Type != ec.ReturnType) {
837 if (ec.InferReturnType) {
839 // void cannot be used in contextual return
841 if (Expr.Type == TypeManager.void_type)
844 ec.ReturnType = Expr.Type;
846 Expr = Convert.ImplicitConversionRequired (
847 ec, Expr, ec.ReturnType, loc);
851 Report.Error (1662, loc,
852 "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",
853 am.ContainerType, am.GetSignatureForError ());
863 protected override void DoEmit (EmitContext ec)
869 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
873 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
875 ec.ig.Emit (OpCodes.Ret);
878 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
881 Expr.MutateHoistedGenericType (storey);
884 protected override void CloneTo (CloneContext clonectx, Statement t)
886 Return target = (Return) t;
887 // It's null for simple return;
889 target.Expr = Expr.Clone (clonectx);
893 public class Goto : Statement {
895 LabeledStatement label;
898 public override bool Resolve (EmitContext ec)
900 int errors = Report.Errors;
901 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
902 ec.CurrentBranching.CurrentUsageVector.Goto ();
903 return errors == Report.Errors;
906 public Goto (string label, Location l)
912 public string Target {
913 get { return target; }
916 public void SetResolvedTarget (LabeledStatement label)
919 label.AddReference ();
922 protected override void DoEmit (EmitContext ec)
925 throw new InternalErrorException ("goto emitted before target resolved");
926 Label l = label.LabelTarget (ec);
927 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
930 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
935 public class LabeledStatement : Statement {
942 FlowBranching.UsageVector vectors;
944 public LabeledStatement (string name, Location l)
950 public Label LabelTarget (EmitContext ec)
955 label = ec.ig.DefineLabel ();
965 public bool IsDefined {
966 get { return defined; }
969 public bool HasBeenReferenced {
970 get { return referenced; }
973 public FlowBranching.UsageVector JumpOrigins {
974 get { return vectors; }
977 public void AddUsageVector (FlowBranching.UsageVector vector)
979 vector = vector.Clone ();
980 vector.Next = vectors;
984 public override bool Resolve (EmitContext ec)
986 // this flow-branching will be terminated when the surrounding block ends
987 ec.StartFlowBranching (this);
991 protected override void DoEmit (EmitContext ec)
993 if (ig != null && ig != ec.ig)
994 throw new InternalErrorException ("cannot happen");
996 ec.ig.MarkLabel (label);
999 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1003 public void AddReference ()
1011 /// `goto default' statement
1013 public class GotoDefault : Statement {
1015 public GotoDefault (Location l)
1020 public override bool Resolve (EmitContext ec)
1022 ec.CurrentBranching.CurrentUsageVector.Goto ();
1026 protected override void DoEmit (EmitContext ec)
1028 if (ec.Switch == null){
1029 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1033 if (!ec.Switch.GotDefault){
1034 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1037 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1040 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1046 /// `goto case' statement
1048 public class GotoCase : Statement {
1052 public GotoCase (Expression e, Location l)
1058 public override bool Resolve (EmitContext ec)
1060 if (ec.Switch == null){
1061 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1065 expr = expr.Resolve (ec);
1069 Constant c = expr as Constant;
1071 Error (150, "A constant value is expected");
1075 Type type = ec.Switch.SwitchType;
1076 if (!Convert.ImplicitStandardConversionExists (c, type))
1077 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1078 "convertible to type `{0}'", TypeManager.CSharpName (type));
1081 object val = c.GetValue ();
1082 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1083 val = TypeManager.ChangeType (val, type, out fail);
1086 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1087 c.GetSignatureForError (), TypeManager.CSharpName (type));
1092 val = SwitchLabel.NullStringCase;
1094 sl = (SwitchLabel) ec.Switch.Elements [val];
1097 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1098 (c.GetValue () == null ? "null" : val.ToString ()));
1102 ec.CurrentBranching.CurrentUsageVector.Goto ();
1106 protected override void DoEmit (EmitContext ec)
1108 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1111 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1113 expr.MutateHoistedGenericType (storey);
1116 protected override void CloneTo (CloneContext clonectx, Statement t)
1118 GotoCase target = (GotoCase) t;
1120 target.expr = expr.Clone (clonectx);
1124 public class Throw : Statement {
1127 public Throw (Expression expr, Location l)
1133 public override bool Resolve (EmitContext ec)
1135 ec.CurrentBranching.CurrentUsageVector.Goto ();
1138 return ec.CurrentBranching.CheckRethrow (loc);
1140 expr = expr.Resolve (ec);
1144 ExprClass eclass = expr.eclass;
1146 if (!(eclass == ExprClass.Variable || eclass == ExprClass.PropertyAccess ||
1147 eclass == ExprClass.Value || eclass == ExprClass.IndexerAccess)) {
1148 expr.Error_UnexpectedKind (ec.DeclContainer, "value, variable, property or indexer access ", loc);
1154 if ((t != TypeManager.exception_type) &&
1155 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1156 !(expr is NullLiteral)) {
1157 Error (155, "The type caught or thrown must be derived from System.Exception");
1163 protected override void DoEmit (EmitContext ec)
1166 ec.ig.Emit (OpCodes.Rethrow);
1170 ec.ig.Emit (OpCodes.Throw);
1174 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1177 expr.MutateHoistedGenericType (storey);
1180 protected override void CloneTo (CloneContext clonectx, Statement t)
1182 Throw target = (Throw) t;
1185 target.expr = expr.Clone (clonectx);
1189 public class Break : Statement {
1191 public Break (Location l)
1196 bool unwind_protect;
1198 public override bool Resolve (EmitContext ec)
1200 int errors = Report.Errors;
1201 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1202 ec.CurrentBranching.CurrentUsageVector.Goto ();
1203 return errors == Report.Errors;
1206 protected override void DoEmit (EmitContext ec)
1208 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1211 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1215 protected override void CloneTo (CloneContext clonectx, Statement t)
1221 public class Continue : Statement {
1223 public Continue (Location l)
1228 bool unwind_protect;
1230 public override bool Resolve (EmitContext ec)
1232 int errors = Report.Errors;
1233 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1234 ec.CurrentBranching.CurrentUsageVector.Goto ();
1235 return errors == Report.Errors;
1238 protected override void DoEmit (EmitContext ec)
1240 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1243 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1247 protected override void CloneTo (CloneContext clonectx, Statement t)
1253 public interface ILocalVariable
1255 void Emit (EmitContext ec);
1256 void EmitAssign (EmitContext ec);
1257 void EmitAddressOf (EmitContext ec);
1260 public interface IKnownVariable {
1261 Block Block { get; }
1262 Location Location { get; }
1266 // The information about a user-perceived local variable
1268 public class LocalInfo : IKnownVariable, ILocalVariable {
1269 public readonly Expression Type;
1271 public Type VariableType;
1272 public readonly string Name;
1273 public readonly Location Location;
1274 public readonly Block Block;
1276 public VariableInfo VariableInfo;
1277 public HoistedVariable HoistedVariableReference;
1286 CompilerGenerated = 64,
1290 public enum ReadOnlyContext: byte {
1297 ReadOnlyContext ro_context;
1298 LocalBuilder builder;
1300 public LocalInfo (Expression type, string name, Block block, Location l)
1308 public LocalInfo (DeclSpace ds, Block block, Location l)
1310 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1315 public void ResolveVariable (EmitContext ec)
1317 if (HoistedVariableReference != null)
1320 if (builder == null) {
1323 // This is needed to compile on both .NET 1.x and .NET 2.x
1324 // the later introduced `DeclareLocal (Type t, bool pinned)'
1326 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1328 builder = ec.ig.DeclareLocal (VariableType);
1332 public void Emit (EmitContext ec)
1334 ec.ig.Emit (OpCodes.Ldloc, builder);
1337 public void EmitAssign (EmitContext ec)
1339 ec.ig.Emit (OpCodes.Stloc, builder);
1342 public void EmitAddressOf (EmitContext ec)
1344 ec.ig.Emit (OpCodes.Ldloca, builder);
1347 public void EmitSymbolInfo (EmitContext ec, string name)
1349 if (builder != null)
1350 ec.DefineLocalVariable (Name, builder);
1353 public bool IsThisAssigned (EmitContext ec)
1355 if (VariableInfo == null)
1356 throw new Exception ();
1358 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1361 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1364 public bool IsAssigned (EmitContext ec)
1366 if (VariableInfo == null)
1367 throw new Exception ();
1369 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1372 public bool Resolve (EmitContext ec)
1374 if (VariableType != null)
1377 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1381 VariableType = texpr.Type;
1383 if (TypeManager.IsGenericParameter (VariableType))
1386 if (VariableType == TypeManager.void_type) {
1387 Expression.Error_VoidInvalidInTheContext (Location);
1391 if (VariableType.IsAbstract && VariableType.IsSealed) {
1392 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1396 if (VariableType.IsPointer && !ec.InUnsafe)
1397 Expression.UnsafeError (Location);
1402 public bool IsConstant {
1403 get { return (flags & Flags.IsConstant) != 0; }
1404 set { flags |= Flags.IsConstant; }
1407 public bool AddressTaken {
1408 get { return (flags & Flags.AddressTaken) != 0; }
1409 set { flags |= Flags.AddressTaken; }
1412 public bool CompilerGenerated {
1413 get { return (flags & Flags.CompilerGenerated) != 0; }
1414 set { flags |= Flags.CompilerGenerated; }
1417 public override string ToString ()
1419 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1420 Name, Type, VariableInfo, Location);
1424 get { return (flags & Flags.Used) != 0; }
1425 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1428 public bool ReadOnly {
1429 get { return (flags & Flags.ReadOnly) != 0; }
1432 public void SetReadOnlyContext (ReadOnlyContext context)
1434 flags |= Flags.ReadOnly;
1435 ro_context = context;
1438 public string GetReadOnlyContext ()
1441 throw new InternalErrorException ("Variable is not readonly");
1443 switch (ro_context) {
1444 case ReadOnlyContext.Fixed:
1445 return "fixed variable";
1446 case ReadOnlyContext.Foreach:
1447 return "foreach iteration variable";
1448 case ReadOnlyContext.Using:
1449 return "using variable";
1451 throw new NotImplementedException ();
1455 // Whether the variable is pinned, if Pinned the variable has been
1456 // allocated in a pinned slot with DeclareLocal.
1458 public bool Pinned {
1459 get { return (flags & Flags.Pinned) != 0; }
1460 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1463 public bool IsThis {
1464 get { return (flags & Flags.IsThis) != 0; }
1465 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1468 Block IKnownVariable.Block {
1469 get { return Block; }
1472 Location IKnownVariable.Location {
1473 get { return Location; }
1476 public LocalInfo Clone (CloneContext clonectx)
1479 // Variables in anonymous block are not resolved yet
1481 if (VariableType == null)
1482 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1485 // Variables in method block are resolved
1487 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1488 li.VariableType = VariableType;
1494 /// Block represents a C# block.
1498 /// This class is used in a number of places: either to represent
1499 /// explicit blocks that the programmer places or implicit blocks.
1501 /// Implicit blocks are used as labels or to introduce variable
1504 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1505 /// they contain extra information that is not necessary on normal blocks.
1507 public class Block : Statement {
1508 public Block Parent;
1509 public readonly Location StartLocation;
1510 public Location EndLocation = Location.Null;
1512 public ExplicitBlock Explicit;
1513 public ToplevelBlock Toplevel; // TODO: Use Explicit
1516 public enum Flags : byte {
1519 VariablesInitialized = 4,
1524 protected Flags flags;
1526 public bool Unchecked {
1527 get { return (flags & Flags.Unchecked) != 0; }
1528 set { flags |= Flags.Unchecked; }
1531 public bool Unsafe {
1532 get { return (flags & Flags.Unsafe) != 0; }
1533 set { flags |= Flags.Unsafe; }
1537 // The statements in this block
1539 protected ArrayList statements;
1542 // An array of Blocks. We keep track of children just
1543 // to generate the local variable declarations.
1545 // Statements and child statements are handled through the
1551 // Labels. (label, block) pairs.
1553 HybridDictionary labels;
1556 // Keeps track of (name, type) pairs
1558 IDictionary variables;
1561 // Keeps track of constants
1562 HybridDictionary constants;
1565 // Temporary variables.
1567 ArrayList temporary_variables;
1570 // If this is a switch section, the enclosing switch block.
1574 ArrayList scope_initializers;
1576 ArrayList anonymous_children;
1578 protected static int id;
1582 int assignable_slots;
1583 bool unreachable_shown;
1586 public Block (Block parent)
1587 : this (parent, (Flags) 0, Location.Null, Location.Null)
1590 public Block (Block parent, Flags flags)
1591 : this (parent, flags, Location.Null, Location.Null)
1594 public Block (Block parent, Location start, Location end)
1595 : this (parent, (Flags) 0, start, end)
1598 public Block (Block parent, Flags flags, Location start, Location end)
1600 if (parent != null) {
1601 parent.AddChild (this);
1603 // the appropriate constructors will fixup these fields
1604 Toplevel = parent.Toplevel;
1605 Explicit = parent.Explicit;
1608 this.Parent = parent;
1610 this.StartLocation = start;
1611 this.EndLocation = end;
1614 statements = new ArrayList (4);
1617 public Block CreateSwitchBlock (Location start)
1619 // FIXME: should this be implicit?
1620 Block new_block = new ExplicitBlock (this, start, start);
1621 new_block.switch_block = this;
1626 get { return this_id; }
1629 public IDictionary Variables {
1631 if (variables == null)
1632 variables = new ListDictionary ();
1637 void AddChild (Block b)
1639 if (children == null)
1640 children = new ArrayList (1);
1645 public void SetEndLocation (Location loc)
1650 protected static void Error_158 (string name, Location loc)
1652 Report.Error (158, loc, "The label `{0}' shadows another label " +
1653 "by the same name in a contained scope", name);
1657 /// Adds a label to the current block.
1661 /// false if the name already exists in this block. true
1665 public bool AddLabel (LabeledStatement target)
1667 if (switch_block != null)
1668 return switch_block.AddLabel (target);
1670 string name = target.Name;
1673 while (cur != null) {
1674 LabeledStatement s = cur.DoLookupLabel (name);
1676 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1677 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1681 if (this == Explicit)
1687 while (cur != null) {
1688 if (cur.DoLookupLabel (name) != null) {
1689 Error_158 (name, target.loc);
1693 if (children != null) {
1694 foreach (Block b in children) {
1695 LabeledStatement s = b.DoLookupLabel (name);
1699 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1700 Error_158 (name, target.loc);
1708 Toplevel.CheckError158 (name, target.loc);
1711 labels = new HybridDictionary();
1713 labels.Add (name, target);
1717 public LabeledStatement LookupLabel (string name)
1719 LabeledStatement s = DoLookupLabel (name);
1723 if (children == null)
1726 foreach (Block child in children) {
1727 if (Explicit != child.Explicit)
1730 s = child.LookupLabel (name);
1738 LabeledStatement DoLookupLabel (string name)
1740 if (switch_block != null)
1741 return switch_block.LookupLabel (name);
1744 if (labels.Contains (name))
1745 return ((LabeledStatement) labels [name]);
1750 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1753 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1754 while (kvi == null) {
1755 b = b.Explicit.Parent;
1758 kvi = b.Explicit.GetKnownVariable (name);
1764 // Is kvi.Block nested inside 'b'
1765 if (b.Explicit != kvi.Block.Explicit) {
1767 // If a variable by the same name it defined in a nested block of this
1768 // block, we violate the invariant meaning in a block.
1771 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1772 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1777 // It's ok if the definition is in a nested subblock of b, but not
1778 // nested inside this block -- a definition in a sibling block
1779 // should not affect us.
1785 // Block 'b' and kvi.Block are the same textual block.
1786 // However, different variables are extant.
1788 // Check if the variable is in scope in both blocks. We use
1789 // an indirect check that depends on AddVariable doing its
1790 // part in maintaining the invariant-meaning-in-block property.
1792 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1796 // Even though we detected the error when the name is used, we
1797 // treat it as if the variable declaration was in error.
1799 Report.SymbolRelatedToPreviousError (loc, name);
1800 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1804 public LocalInfo AddVariable (Expression type, string name, Location l)
1806 LocalInfo vi = GetLocalInfo (name);
1808 Report.SymbolRelatedToPreviousError (vi.Location, name);
1809 if (Explicit == vi.Block.Explicit)
1810 Error_AlreadyDeclared (l, name, null);
1812 Error_AlreadyDeclared (l, name, "parent");
1816 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1818 Report.SymbolRelatedToPreviousError (pi.Location, name);
1819 Error_AlreadyDeclared (loc, name,
1820 pi.Block == Toplevel ? "method argument" : "parent or current");
1824 if (Toplevel.GenericMethod != null) {
1825 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1826 if (tp.Name == name) {
1827 Report.SymbolRelatedToPreviousError (tp);
1828 Error_AlreadyDeclaredTypeParameter (loc, name);
1834 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1836 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1837 Error_AlreadyDeclared (l, name, "child");
1841 vi = new LocalInfo (type, name, this, l);
1844 if ((flags & Flags.VariablesInitialized) != 0)
1845 throw new InternalErrorException ("block has already been resolved");
1850 protected virtual void AddVariable (LocalInfo li)
1852 Variables.Add (li.Name, li);
1853 Explicit.AddKnownVariable (li.Name, li);
1856 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1858 if (reason == null) {
1859 Error_AlreadyDeclared (loc, var);
1863 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1864 "in this scope because it would give a different meaning " +
1865 "to `{0}', which is already used in a `{1}' scope " +
1866 "to denote something else", var, reason);
1869 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1871 Report.Error (128, loc,
1872 "A local variable named `{0}' is already defined in this scope", name);
1875 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1877 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1880 public bool AddConstant (Expression type, string name, Expression value, Location l)
1882 if (AddVariable (type, name, l) == null)
1885 if (constants == null)
1886 constants = new HybridDictionary();
1888 constants.Add (name, value);
1890 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1895 static int next_temp_id = 0;
1897 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1899 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1901 if (temporary_variables == null)
1902 temporary_variables = new ArrayList ();
1904 int id = ++next_temp_id;
1905 string name = "$s_" + id.ToString ();
1907 LocalInfo li = new LocalInfo (te, name, this, loc);
1908 li.CompilerGenerated = true;
1909 temporary_variables.Add (li);
1913 public LocalInfo GetLocalInfo (string name)
1915 for (Block b = this; b != null; b = b.Parent) {
1916 if (b.variables != null) {
1917 LocalInfo ret = b.variables [name] as LocalInfo;
1925 public Expression GetVariableType (string name)
1927 LocalInfo vi = GetLocalInfo (name);
1928 return vi == null ? null : vi.Type;
1931 public Expression GetConstantExpression (string name)
1933 for (Block b = this; b != null; b = b.Parent) {
1934 if (b.constants != null) {
1935 Expression ret = b.constants [name] as Expression;
1944 // It should be used by expressions which require to
1945 // register a statement during resolve process.
1947 public void AddScopeStatement (StatementExpression s)
1949 if (scope_initializers == null)
1950 scope_initializers = new ArrayList ();
1952 scope_initializers.Add (s);
1955 public void AddStatement (Statement s)
1958 flags |= Flags.BlockUsed;
1962 get { return (flags & Flags.BlockUsed) != 0; }
1967 flags |= Flags.BlockUsed;
1970 public bool HasRet {
1971 get { return (flags & Flags.HasRet) != 0; }
1974 public bool IsDestructor {
1975 get { return (flags & Flags.IsDestructor) != 0; }
1978 public void SetDestructor ()
1980 flags |= Flags.IsDestructor;
1983 public int AssignableSlots {
1985 if ((flags & Flags.VariablesInitialized) == 0)
1986 throw new Exception ("Variables have not been initialized yet");
1987 return assignable_slots;
1991 public ArrayList AnonymousChildren {
1992 get { return anonymous_children; }
1995 public void AddAnonymousChild (ToplevelBlock b)
1997 if (anonymous_children == null)
1998 anonymous_children = new ArrayList ();
2000 anonymous_children.Add (b);
2003 void DoResolveConstants (EmitContext ec)
2005 if (constants == null)
2008 if (variables == null)
2009 throw new InternalErrorException ("cannot happen");
2011 foreach (DictionaryEntry de in variables) {
2012 string name = (string) de.Key;
2013 LocalInfo vi = (LocalInfo) de.Value;
2014 Type variable_type = vi.VariableType;
2016 if (variable_type == null) {
2017 if (vi.Type is VarExpr)
2018 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2023 Expression cv = (Expression) constants [name];
2027 // Don't let 'const int Foo = Foo;' succeed.
2028 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2029 // which in turn causes the 'must be constant' error to be triggered.
2030 constants.Remove (name);
2032 if (!Const.IsConstantTypeValid (variable_type)) {
2033 Const.Error_InvalidConstantType (variable_type, loc);
2037 ec.CurrentBlock = this;
2039 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2040 e = cv.Resolve (ec);
2045 Constant ce = e as Constant;
2047 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2051 e = ce.ConvertImplicitly (variable_type);
2053 if (!variable_type.IsValueType && variable_type != TypeManager.string_type && !ce.IsDefaultValue)
2054 Const.Error_ConstantCanBeInitializedWithNullOnly (vi.Location, vi.Name);
2056 ce.Error_ValueCannotBeConverted (null, vi.Location, variable_type, false);
2060 constants.Add (name, e);
2061 vi.IsConstant = true;
2065 protected void ResolveMeta (EmitContext ec, int offset)
2067 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2069 // If some parent block was unsafe, we remain unsafe even if this block
2070 // isn't explicitly marked as such.
2071 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2072 flags |= Flags.VariablesInitialized;
2074 if (variables != null) {
2075 foreach (LocalInfo li in variables.Values) {
2076 if (!li.Resolve (ec))
2078 li.VariableInfo = new VariableInfo (li, offset);
2079 offset += li.VariableInfo.Length;
2082 assignable_slots = offset;
2084 DoResolveConstants (ec);
2086 if (children == null)
2088 foreach (Block b in children)
2089 b.ResolveMeta (ec, offset);
2094 // Emits the local variable declarations for a block
2096 public virtual void EmitMeta (EmitContext ec)
2098 if (variables != null){
2099 foreach (LocalInfo vi in variables.Values)
2100 vi.ResolveVariable (ec);
2103 if (temporary_variables != null) {
2104 for (int i = 0; i < temporary_variables.Count; i++)
2105 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2108 if (children != null) {
2109 for (int i = 0; i < children.Count; i++)
2110 ((Block)children[i]).EmitMeta(ec);
2114 void UsageWarning ()
2116 if (variables == null || Report.WarningLevel < 3)
2119 foreach (DictionaryEntry de in variables) {
2120 LocalInfo vi = (LocalInfo) de.Value;
2123 string name = (string) de.Key;
2125 // vi.VariableInfo can be null for 'catch' variables
2126 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2127 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2129 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2134 private void CheckPossibleMistakenEmptyStatement (Statement s)
2138 // Some statements are wrapped by a Block. Since
2139 // others' internal could be changed, here I treat
2140 // them as possibly wrapped by Block equally.
2141 Block b = s as Block;
2142 if (b != null && b.statements.Count == 1)
2143 s = (Statement) b.statements [0];
2146 body = ((Lock) s).Statement;
2148 body = ((For) s).Statement;
2149 else if (s is Foreach)
2150 body = ((Foreach) s).Statement;
2151 else if (s is While)
2152 body = ((While) s).Statement;
2153 else if (s is Fixed)
2154 body = ((Fixed) s).Statement;
2155 else if (s is Using)
2156 body = ((Using) s).EmbeddedStatement;
2157 else if (s is UsingTemporary)
2158 body = ((UsingTemporary) s).Statement;
2162 if (body == null || body is EmptyStatement)
2163 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2166 public override bool Resolve (EmitContext ec)
2168 Block prev_block = ec.CurrentBlock;
2171 int errors = Report.Errors;
2173 ec.CurrentBlock = this;
2174 ec.StartFlowBranching (this);
2176 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2179 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2180 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2181 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2182 // responsible for handling the situation.
2184 int statement_count = statements.Count;
2185 for (int ix = 0; ix < statement_count; ix++){
2186 Statement s = (Statement) statements [ix];
2187 // Check possible empty statement (CS0642)
2188 if (Report.WarningLevel >= 3 &&
2189 ix + 1 < statement_count &&
2190 statements [ix + 1] is Block)
2191 CheckPossibleMistakenEmptyStatement (s);
2194 // Warn if we detect unreachable code.
2197 if (s is EmptyStatement)
2201 ((Block) s).unreachable = true;
2203 if (!unreachable_shown && !(s is LabeledStatement)) {
2204 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2205 unreachable_shown = true;
2210 // Note that we're not using ResolveUnreachable() for unreachable
2211 // statements here. ResolveUnreachable() creates a temporary
2212 // flow branching and kills it afterwards. This leads to problems
2213 // if you have two unreachable statements where the first one
2214 // assigns a variable and the second one tries to access it.
2217 if (!s.Resolve (ec)) {
2219 if (ec.IsInProbingMode)
2222 statements [ix] = EmptyStatement.Value;
2226 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2227 statements [ix] = EmptyStatement.Value;
2229 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2230 if (unreachable && s is LabeledStatement)
2231 throw new InternalErrorException ("should not happen");
2234 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2235 ec.CurrentBranching, statement_count);
2237 while (ec.CurrentBranching is FlowBranchingLabeled)
2238 ec.EndFlowBranching ();
2240 bool flow_unreachable = ec.EndFlowBranching ();
2242 ec.CurrentBlock = prev_block;
2244 if (flow_unreachable)
2245 flags |= Flags.HasRet;
2247 // If we're a non-static `struct' constructor which doesn't have an
2248 // initializer, then we must initialize all of the struct's fields.
2249 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2252 if ((labels != null) && (Report.WarningLevel >= 2)) {
2253 foreach (LabeledStatement label in labels.Values)
2254 if (!label.HasBeenReferenced)
2255 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2258 if (ok && errors == Report.Errors)
2264 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2266 unreachable_shown = true;
2270 Report.Warning (162, 2, loc, "Unreachable code detected");
2272 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2273 bool ok = Resolve (ec);
2274 ec.KillFlowBranching ();
2279 protected override void DoEmit (EmitContext ec)
2281 for (int ix = 0; ix < statements.Count; ix++){
2282 Statement s = (Statement) statements [ix];
2287 public override void Emit (EmitContext ec)
2289 Block prev_block = ec.CurrentBlock;
2290 ec.CurrentBlock = this;
2292 if (scope_initializers != null) {
2293 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2295 bool omit_debug_info = ec.OmitDebuggingInfo;
2296 ec.OmitDebuggingInfo = true;
2297 foreach (StatementExpression s in scope_initializers)
2299 ec.OmitDebuggingInfo = omit_debug_info;
2301 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2304 ec.Mark (StartLocation, true);
2307 ec.CurrentBlock = prev_block;
2310 protected virtual void EmitSymbolInfo (EmitContext ec)
2312 if (variables != null) {
2313 foreach (DictionaryEntry de in variables) {
2314 string name = (string) de.Key;
2315 LocalInfo vi = (LocalInfo) de.Value;
2317 vi.EmitSymbolInfo (ec, name);
2322 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2324 MutateVariables (storey);
2326 foreach (Statement s in statements)
2327 s.MutateHoistedGenericType (storey);
2330 void MutateVariables (AnonymousMethodStorey storey)
2332 if (variables != null) {
2333 foreach (LocalInfo vi in variables.Values) {
2334 vi.VariableType = storey.MutateType (vi.VariableType);
2338 if (temporary_variables != null) {
2339 foreach (LocalInfo vi in temporary_variables)
2340 vi.VariableType = storey.MutateType (vi.VariableType);
2344 public override string ToString ()
2346 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2349 protected override void CloneTo (CloneContext clonectx, Statement t)
2351 Block target = (Block) t;
2353 clonectx.AddBlockMap (this, target);
2355 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2356 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2358 target.Parent = clonectx.RemapBlockCopy (Parent);
2360 if (variables != null){
2361 target.variables = new Hashtable ();
2363 foreach (DictionaryEntry de in variables){
2364 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2365 target.variables [de.Key] = newlocal;
2366 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2370 target.statements = new ArrayList (statements.Count);
2371 foreach (Statement s in statements)
2372 target.statements.Add (s.Clone (clonectx));
2374 if (target.children != null){
2375 target.children = new ArrayList (children.Count);
2376 foreach (Block b in children){
2377 target.children.Add (clonectx.LookupBlock (b));
2382 // TODO: labels, switch_block, constants (?), anonymous_children
2387 public class ExplicitBlock : Block {
2388 HybridDictionary known_variables;
2389 protected AnonymousMethodStorey am_storey;
2391 public ExplicitBlock (Block parent, Location start, Location end)
2392 : this (parent, (Flags) 0, start, end)
2396 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2397 : base (parent, flags, start, end)
2399 this.Explicit = this;
2403 // Marks a variable with name @name as being used in this or a child block.
2404 // If a variable name has been used in a child block, it's illegal to
2405 // declare a variable with the same name in the current block.
2407 internal void AddKnownVariable (string name, IKnownVariable info)
2409 if (known_variables == null)
2410 known_variables = new HybridDictionary();
2412 known_variables [name] = info;
2415 Parent.Explicit.AddKnownVariable (name, info);
2418 public AnonymousMethodStorey AnonymousMethodStorey {
2419 get { return am_storey; }
2423 // Creates anonymous method storey in current block
2425 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2428 // When referencing a variable in iterator storey from children anonymous method
2430 if (Toplevel.am_storey is IteratorStorey) {
2431 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2432 return Toplevel.am_storey;
2436 // An iterator has only 1 storey block
2438 if (ec.CurrentIterator != null)
2439 return ec.CurrentIterator.Storey;
2441 if (am_storey == null) {
2442 MemberBase mc = ec.ResolveContext as MemberBase;
2443 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2446 // Create anonymous method storey for this block
2448 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2452 // Creates a link between this block and the anonymous method
2454 // An anonymous method can reference variables from any outer block, but they are
2455 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2456 // need to create another link between those variable storeys
2458 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2462 public override void Emit (EmitContext ec)
2464 if (am_storey != null)
2465 am_storey.EmitHoistedVariables (ec);
2467 bool emit_debug_info = SymbolWriter.HasSymbolWriter;
2468 bool is_lexical_block = Parent != null && !(am_storey is IteratorStorey);
2469 if (emit_debug_info && is_lexical_block)
2474 if (emit_debug_info) {
2475 EmitSymbolInfo (ec);
2476 if (is_lexical_block)
2481 public override void EmitMeta (EmitContext ec)
2486 // It has to be done when all storey references are resolved
2488 if (am_storey != null && am_storey.HasHoistedVariables)
2489 am_storey.DefineMembers ();
2492 protected override void EmitSymbolInfo (EmitContext ec)
2494 if (am_storey != null)
2495 SymbolWriter.DefineScopeVariable (am_storey.ID);
2497 base.EmitSymbolInfo (ec);
2500 internal IKnownVariable GetKnownVariable (string name)
2502 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2505 public void PropagateStoreyReference (AnonymousMethodStorey s)
2507 if (Parent != null && am_storey != s) {
2508 if (am_storey != null)
2509 am_storey.AddParentStoreyReference (s);
2511 Parent.Explicit.PropagateStoreyReference (s);
2515 public override bool Resolve (EmitContext ec)
2517 bool ok = base.Resolve (ec);
2520 // Define an anonymous method storey when this block has hoisted variables
2522 if (am_storey != null && am_storey.HasHoistedVariables) {
2523 am_storey.DefineType ();
2524 am_storey.DefineMembers ();
2530 protected override void CloneTo (CloneContext clonectx, Statement t)
2532 ExplicitBlock target = (ExplicitBlock) t;
2533 target.known_variables = null;
2534 base.CloneTo (clonectx, t);
2538 public class ToplevelParameterInfo : IKnownVariable {
2539 public readonly ToplevelBlock Block;
2540 public readonly int Index;
2541 public VariableInfo VariableInfo;
2543 Block IKnownVariable.Block {
2544 get { return Block; }
2546 public Parameter Parameter {
2547 get { return Block.Parameters [Index]; }
2549 public Location Location {
2550 get { return Parameter.Location; }
2553 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2561 // A toplevel block contains extra information, the split is done
2562 // only to separate information that would otherwise bloat the more
2563 // lightweight Block.
2565 // In particular, this was introduced when the support for Anonymous
2566 // Methods was implemented.
2568 public class ToplevelBlock : ExplicitBlock {
2569 GenericMethod generic;
2570 FlowBranchingToplevel top_level_branching;
2571 Parameters parameters;
2572 ToplevelParameterInfo[] parameter_info;
2573 LocalInfo this_variable;
2575 public HoistedVariable HoistedThisVariable;
2578 // The parameters for the block.
2580 public Parameters Parameters {
2581 get { return parameters; }
2584 public GenericMethod GenericMethod {
2585 get { return generic; }
2588 public ToplevelBlock Container {
2589 get { return Parent == null ? null : Parent.Toplevel; }
2592 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2593 this (parent, (Flags) 0, parameters, start)
2597 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2598 this (parent, parameters, start)
2600 this.generic = generic;
2603 public ToplevelBlock (Parameters parameters, Location start) :
2604 this (null, (Flags) 0, parameters, start)
2608 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2609 this (null, flags, parameters, start)
2613 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2614 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2615 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2616 base (null, flags, start, Location.Null)
2618 this.Toplevel = this;
2620 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2621 this.Parent = parent;
2623 parent.AddAnonymousChild (this);
2625 if (this.parameters.Count != 0)
2626 ProcessParameters ();
2629 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2633 protected override void CloneTo (CloneContext clonectx, Statement t)
2635 ToplevelBlock target = (ToplevelBlock) t;
2636 base.CloneTo (clonectx, t);
2638 if (parameters.Count != 0)
2639 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2640 for (int i = 0; i < parameters.Count; ++i)
2641 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2644 public bool CheckError158 (string name, Location loc)
2646 if (AnonymousChildren != null) {
2647 foreach (ToplevelBlock child in AnonymousChildren) {
2648 if (!child.CheckError158 (name, loc))
2653 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2654 if (!c.DoCheckError158 (name, loc))
2661 public virtual Expression GetTransparentIdentifier (string name)
2666 void ProcessParameters ()
2668 int n = parameters.Count;
2669 parameter_info = new ToplevelParameterInfo [n];
2670 for (int i = 0; i < n; ++i) {
2671 parameter_info [i] = new ToplevelParameterInfo (this, i);
2673 Parameter p = parameters [i];
2677 string name = p.Name;
2678 LocalInfo vi = GetLocalInfo (name);
2680 Report.SymbolRelatedToPreviousError (vi.Location, name);
2681 Error_AlreadyDeclared (loc, name, "parent or current");
2685 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2687 Report.SymbolRelatedToPreviousError (pi.Location, name);
2688 Error_AlreadyDeclared (loc, name, "parent or current");
2692 AddKnownVariable (name, parameter_info [i]);
2695 // mark this block as "used" so that we create local declarations in a sub-block
2696 // FIXME: This appears to uncover a lot of bugs
2700 bool DoCheckError158 (string name, Location loc)
2702 LabeledStatement s = LookupLabel (name);
2704 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2705 Error_158 (name, loc);
2712 public override Expression CreateExpressionTree (EmitContext ec)
2714 return ((Statement) statements [0]).CreateExpressionTree (ec);
2718 // Reformats this block to be top-level iterator block
2720 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2722 // Create block with original statements
2723 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2725 // TODO: Change to iter_block.statements = statements;
2726 foreach (Statement stmt in source.statements)
2727 iter_block.AddStatement (stmt);
2729 AddStatement (new IteratorStatement (iterator, iter_block));
2731 source.statements = new ArrayList (1);
2732 source.AddStatement (new Return (iterator, iterator.Location));
2734 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2735 source.am_storey = iterator_storey;
2736 return iterator_storey;
2739 public FlowBranchingToplevel TopLevelBranching {
2740 get { return top_level_branching; }
2744 // Returns a `ParameterReference' for the given name, or null if there
2745 // is no such parameter
2747 public ParameterReference GetParameterReference (string name, Location loc)
2749 ToplevelParameterInfo p = GetParameterInfo (name);
2750 return p == null ? null : new ParameterReference (this, p, loc);
2753 public ToplevelParameterInfo GetParameterInfo (string name)
2756 for (ToplevelBlock t = this; t != null; t = t.Container) {
2757 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2759 return t.parameter_info [idx];
2765 // Returns the "this" instance variable of this block.
2766 // See AddThisVariable() for more information.
2768 public LocalInfo ThisVariable {
2769 get { return this_variable; }
2773 // This is used by non-static `struct' constructors which do not have an
2774 // initializer - in this case, the constructor must initialize all of the
2775 // struct's fields. To do this, we add a "this" variable and use the flow
2776 // analysis code to ensure that it's been fully initialized before control
2777 // leaves the constructor.
2779 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2781 if (this_variable == null) {
2782 this_variable = new LocalInfo (ds, this, l);
2783 this_variable.Used = true;
2784 this_variable.IsThis = true;
2786 Variables.Add ("this", this_variable);
2789 return this_variable;
2792 public bool IsThisAssigned (EmitContext ec)
2794 return this_variable == null || this_variable.IsThisAssigned (ec);
2797 public bool ResolveMeta (EmitContext ec, Parameters ip)
2799 int errors = Report.Errors;
2800 int orig_count = parameters.Count;
2802 if (top_level_branching != null)
2808 // Assert: orig_count != parameter.Count => orig_count == 0
2809 if (orig_count != 0 && orig_count != parameters.Count)
2810 throw new InternalErrorException ("parameter information mismatch");
2812 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2814 for (int i = 0; i < orig_count; ++i) {
2815 Parameter.Modifier mod = parameters.ParameterModifier (i);
2817 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2820 VariableInfo vi = new VariableInfo (ip, i, offset);
2821 parameter_info [i].VariableInfo = vi;
2822 offset += vi.Length;
2825 ResolveMeta (ec, offset);
2827 top_level_branching = ec.StartFlowBranching (this);
2829 return Report.Errors == errors;
2833 // Check whether all `out' parameters have been assigned.
2835 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2837 if (vector.IsUnreachable)
2840 int n = parameter_info == null ? 0 : parameter_info.Length;
2842 for (int i = 0; i < n; i++) {
2843 VariableInfo var = parameter_info [i].VariableInfo;
2848 if (vector.IsAssigned (var, false))
2851 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2856 public override void EmitMeta (EmitContext ec)
2858 parameters.ResolveVariable ();
2860 // Avoid declaring an IL variable for this_variable since it is not accessed
2861 // from the generated IL
2862 if (this_variable != null)
2863 Variables.Remove ("this");
2867 public override void Emit (EmitContext ec)
2870 ec.Mark (EndLocation, true);
2874 public class SwitchLabel {
2881 Label il_label_code;
2882 bool il_label_code_set;
2884 public static readonly object NullStringCase = new object ();
2887 // if expr == null, then it is the default case.
2889 public SwitchLabel (Expression expr, Location l)
2895 public Expression Label {
2901 public object Converted {
2907 public Label GetILLabel (EmitContext ec)
2910 il_label = ec.ig.DefineLabel ();
2911 il_label_set = true;
2916 public Label GetILLabelCode (EmitContext ec)
2918 if (!il_label_code_set){
2919 il_label_code = ec.ig.DefineLabel ();
2920 il_label_code_set = true;
2922 return il_label_code;
2926 // Resolves the expression, reduces it to a literal if possible
2927 // and then converts it to the requested type.
2929 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2931 Expression e = label.Resolve (ec);
2936 Constant c = e as Constant;
2938 Report.Error (150, loc, "A constant value is expected");
2942 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2943 converted = NullStringCase;
2947 if (allow_nullable && c.GetValue () == null) {
2948 converted = NullStringCase;
2952 c = c.ImplicitConversionRequired (required_type, loc);
2956 converted = c.GetValue ();
2960 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
2963 if (converted == null)
2965 else if (converted == NullStringCase)
2967 else if (TypeManager.IsEnumType (switch_type))
2968 label = TypeManager.CSharpEnumValue (switch_type, converted);
2970 label = converted.ToString ();
2972 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
2973 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
2976 public SwitchLabel Clone (CloneContext clonectx)
2978 return new SwitchLabel (label.Clone (clonectx), loc);
2982 public class SwitchSection {
2983 // An array of SwitchLabels.
2984 public readonly ArrayList Labels;
2985 public readonly Block Block;
2987 public SwitchSection (ArrayList labels, Block block)
2993 public SwitchSection Clone (CloneContext clonectx)
2995 ArrayList cloned_labels = new ArrayList ();
2997 foreach (SwitchLabel sl in cloned_labels)
2998 cloned_labels.Add (sl.Clone (clonectx));
3000 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3004 public class Switch : Statement {
3005 public ArrayList Sections;
3006 public Expression Expr;
3009 /// Maps constants whose type type SwitchType to their SwitchLabels.
3011 public IDictionary Elements;
3014 /// The governing switch type
3016 public Type SwitchType;
3021 Label default_target;
3023 Expression new_expr;
3025 SwitchSection constant_section;
3026 SwitchSection default_section;
3030 // Nullable Types support for GMCS.
3032 Nullable.Unwrap unwrap;
3034 protected bool HaveUnwrap {
3035 get { return unwrap != null; }
3038 protected bool HaveUnwrap {
3039 get { return false; }
3044 // The types allowed to be implicitly cast from
3045 // on the governing type
3047 static Type [] allowed_types;
3049 public Switch (Expression e, ArrayList sects, Location l)
3056 public bool GotDefault {
3058 return default_section != null;
3062 public Label DefaultTarget {
3064 return default_target;
3069 // Determines the governing type for a switch. The returned
3070 // expression might be the expression from the switch, or an
3071 // expression that includes any potential conversions to the
3072 // integral types or to string.
3074 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3078 if (t == TypeManager.byte_type ||
3079 t == TypeManager.sbyte_type ||
3080 t == TypeManager.ushort_type ||
3081 t == TypeManager.short_type ||
3082 t == TypeManager.uint32_type ||
3083 t == TypeManager.int32_type ||
3084 t == TypeManager.uint64_type ||
3085 t == TypeManager.int64_type ||
3086 t == TypeManager.char_type ||
3087 t == TypeManager.string_type ||
3088 t == TypeManager.bool_type ||
3089 TypeManager.IsEnumType (t))
3092 if (allowed_types == null){
3093 allowed_types = new Type [] {
3094 TypeManager.sbyte_type,
3095 TypeManager.byte_type,
3096 TypeManager.short_type,
3097 TypeManager.ushort_type,
3098 TypeManager.int32_type,
3099 TypeManager.uint32_type,
3100 TypeManager.int64_type,
3101 TypeManager.uint64_type,
3102 TypeManager.char_type,
3103 TypeManager.string_type,
3104 TypeManager.bool_type
3109 // Try to find a *user* defined implicit conversion.
3111 // If there is no implicit conversion, or if there are multiple
3112 // conversions, we have to report an error
3114 Expression converted = null;
3115 foreach (Type tt in allowed_types){
3118 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3123 // Ignore over-worked ImplicitUserConversions that do
3124 // an implicit conversion in addition to the user conversion.
3126 if (!(e is UserCast))
3129 if (converted != null){
3130 Report.ExtraInformation (
3132 String.Format ("reason: more than one conversion to an integral type exist for type {0}",
3133 TypeManager.CSharpName (expr.Type)));
3143 // Performs the basic sanity checks on the switch statement
3144 // (looks for duplicate keys and non-constant expressions).
3146 // It also returns a hashtable with the keys that we will later
3147 // use to compute the switch tables
3149 bool CheckSwitch (EmitContext ec)
3152 Elements = Sections.Count > 10 ?
3153 (IDictionary)new Hashtable () :
3154 (IDictionary)new ListDictionary ();
3156 foreach (SwitchSection ss in Sections){
3157 foreach (SwitchLabel sl in ss.Labels){
3158 if (sl.Label == null){
3159 if (default_section != null){
3160 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3163 default_section = ss;
3167 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3172 object key = sl.Converted;
3174 Elements.Add (key, sl);
3175 } catch (ArgumentException) {
3176 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3184 void EmitObjectInteger (ILGenerator ig, object k)
3187 IntConstant.EmitInt (ig, (int) k);
3188 else if (k is Constant) {
3189 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3192 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3195 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3197 IntConstant.EmitInt (ig, (int) (long) k);
3198 ig.Emit (OpCodes.Conv_I8);
3201 LongConstant.EmitLong (ig, (long) k);
3203 else if (k is ulong)
3205 ulong ul = (ulong) k;
3208 IntConstant.EmitInt (ig, unchecked ((int) ul));
3209 ig.Emit (OpCodes.Conv_U8);
3213 LongConstant.EmitLong (ig, unchecked ((long) ul));
3217 IntConstant.EmitInt (ig, (int) ((char) k));
3218 else if (k is sbyte)
3219 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3221 IntConstant.EmitInt (ig, (int) ((byte) k));
3222 else if (k is short)
3223 IntConstant.EmitInt (ig, (int) ((short) k));
3224 else if (k is ushort)
3225 IntConstant.EmitInt (ig, (int) ((ushort) k));
3227 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3229 throw new Exception ("Unhandled case");
3232 // structure used to hold blocks of keys while calculating table switch
3233 class KeyBlock : IComparable
3235 public KeyBlock (long _first)
3237 first = last = _first;
3241 public ArrayList element_keys = null;
3242 // how many items are in the bucket
3243 public int Size = 1;
3246 get { return (int) (last - first + 1); }
3248 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3250 return kb_last.last - kb_first.first + 1;
3252 public int CompareTo (object obj)
3254 KeyBlock kb = (KeyBlock) obj;
3255 int nLength = Length;
3256 int nLengthOther = kb.Length;
3257 if (nLengthOther == nLength)
3258 return (int) (kb.first - first);
3259 return nLength - nLengthOther;
3264 /// This method emits code for a lookup-based switch statement (non-string)
3265 /// Basically it groups the cases into blocks that are at least half full,
3266 /// and then spits out individual lookup opcodes for each block.
3267 /// It emits the longest blocks first, and short blocks are just
3268 /// handled with direct compares.
3270 /// <param name="ec"></param>
3271 /// <param name="val"></param>
3272 /// <returns></returns>
3273 void TableSwitchEmit (EmitContext ec, LocalBuilder val)
3275 int element_count = Elements.Count;
3276 object [] element_keys = new object [element_count];
3277 Elements.Keys.CopyTo (element_keys, 0);
3278 Array.Sort (element_keys);
3280 // initialize the block list with one element per key
3281 ArrayList key_blocks = new ArrayList ();
3282 foreach (object key in element_keys)
3283 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3285 KeyBlock current_kb;
3286 // iteratively merge the blocks while they are at least half full
3287 // there's probably a really cool way to do this with a tree...
3288 while (key_blocks.Count > 1)
3290 ArrayList key_blocks_new = new ArrayList ();
3291 current_kb = (KeyBlock) key_blocks [0];
3292 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3294 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3295 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3298 current_kb.last = kb.last;
3299 current_kb.Size += kb.Size;
3303 // start a new block
3304 key_blocks_new.Add (current_kb);
3308 key_blocks_new.Add (current_kb);
3309 if (key_blocks.Count == key_blocks_new.Count)
3311 key_blocks = key_blocks_new;
3314 // initialize the key lists
3315 foreach (KeyBlock kb in key_blocks)
3316 kb.element_keys = new ArrayList ();
3318 // fill the key lists
3320 if (key_blocks.Count > 0) {
3321 current_kb = (KeyBlock) key_blocks [0];
3322 foreach (object key in element_keys)
3324 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3325 System.Convert.ToInt64 (key) > current_kb.last;
3327 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3328 current_kb.element_keys.Add (key);
3332 // sort the blocks so we can tackle the largest ones first
3335 // okay now we can start...
3336 ILGenerator ig = ec.ig;
3337 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3338 Label lbl_default = ig.DefineLabel ();
3340 Type type_keys = null;
3341 if (element_keys.Length > 0)
3342 type_keys = element_keys [0].GetType (); // used for conversions
3346 if (TypeManager.IsEnumType (SwitchType))
3347 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3349 compare_type = SwitchType;
3351 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3353 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3354 lbl_default = (iBlock == 0) ? DefaultTarget : ig.DefineLabel ();
3357 foreach (object key in kb.element_keys)
3359 ig.Emit (OpCodes.Ldloc, val);
3360 EmitObjectInteger (ig, key);
3361 SwitchLabel sl = (SwitchLabel) Elements [key];
3362 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3367 // TODO: if all the keys in the block are the same and there are
3368 // no gaps/defaults then just use a range-check.
3369 if (compare_type == TypeManager.int64_type ||
3370 compare_type == TypeManager.uint64_type)
3372 // TODO: optimize constant/I4 cases
3374 // check block range (could be > 2^31)
3375 ig.Emit (OpCodes.Ldloc, val);
3376 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3377 ig.Emit (OpCodes.Blt, lbl_default);
3378 ig.Emit (OpCodes.Ldloc, val);
3379 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3380 ig.Emit (OpCodes.Bgt, lbl_default);
3383 ig.Emit (OpCodes.Ldloc, val);
3386 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3387 ig.Emit (OpCodes.Sub);
3389 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3394 ig.Emit (OpCodes.Ldloc, val);
3395 int first = (int) kb.first;
3398 IntConstant.EmitInt (ig, first);
3399 ig.Emit (OpCodes.Sub);
3403 IntConstant.EmitInt (ig, -first);
3404 ig.Emit (OpCodes.Add);
3408 // first, build the list of labels for the switch
3410 int cJumps = kb.Length;
3411 Label [] switch_labels = new Label [cJumps];
3412 for (int iJump = 0; iJump < cJumps; iJump++)
3414 object key = kb.element_keys [iKey];
3415 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3417 SwitchLabel sl = (SwitchLabel) Elements [key];
3418 switch_labels [iJump] = sl.GetILLabel (ec);
3422 switch_labels [iJump] = lbl_default;
3424 // emit the switch opcode
3425 ig.Emit (OpCodes.Switch, switch_labels);
3428 // mark the default for this block
3430 ig.MarkLabel (lbl_default);
3433 // TODO: find the default case and emit it here,
3434 // to prevent having to do the following jump.
3435 // make sure to mark other labels in the default section
3437 // the last default just goes to the end
3438 ig.Emit (OpCodes.Br, lbl_default);
3440 // now emit the code for the sections
3441 bool found_default = false;
3442 bool found_null = false;
3443 foreach (SwitchSection ss in Sections)
3445 foreach (SwitchLabel sl in ss.Labels)
3446 if (sl.Converted == SwitchLabel.NullStringCase)
3450 foreach (SwitchSection ss in Sections)
3452 foreach (SwitchLabel sl in ss.Labels)
3454 ig.MarkLabel (sl.GetILLabel (ec));
3455 ig.MarkLabel (sl.GetILLabelCode (ec));
3456 if (sl.Converted == SwitchLabel.NullStringCase)
3457 ig.MarkLabel (null_target);
3458 else if (sl.Label == null) {
3459 ig.MarkLabel (lbl_default);
3460 found_default = true;
3462 ig.MarkLabel (null_target);
3468 if (!found_default) {
3469 ig.MarkLabel (lbl_default);
3470 if (HaveUnwrap && !found_null) {
3471 ig.MarkLabel (null_target);
3475 ig.MarkLabel (lbl_end);
3478 // This simple emit switch works, but does not take advantage of the
3480 // TODO: remove non-string logic from here
3481 // TODO: binary search strings?
3483 void SimpleSwitchEmit (EmitContext ec, LocalBuilder val)
3485 ILGenerator ig = ec.ig;
3486 Label end_of_switch = ig.DefineLabel ();
3487 Label next_test = ig.DefineLabel ();
3488 bool first_test = true;
3489 bool pending_goto_end = false;
3490 bool null_marked = false;
3492 int section_count = Sections.Count;
3494 // TODO: implement switch optimization for string by using Hashtable
3495 //if (SwitchType == TypeManager.string_type && section_count > 7)
3496 // Console.WriteLine ("Switch optimization possible " + loc);
3498 ig.Emit (OpCodes.Ldloc, val);
3500 if (Elements.Contains (SwitchLabel.NullStringCase)){
3501 ig.Emit (OpCodes.Brfalse, null_target);
3503 ig.Emit (OpCodes.Brfalse, default_target);
3505 ig.Emit (OpCodes.Ldloc, val);
3506 ig.Emit (OpCodes.Call, TypeManager.string_isinterned_string);
3507 ig.Emit (OpCodes.Stloc, val);
3509 for (int section = 0; section < section_count; section++){
3510 SwitchSection ss = (SwitchSection) Sections [section];
3512 if (ss == default_section)
3515 Label sec_begin = ig.DefineLabel ();
3517 ig.Emit (OpCodes.Nop);
3519 if (pending_goto_end)
3520 ig.Emit (OpCodes.Br, end_of_switch);
3522 int label_count = ss.Labels.Count;
3524 for (int label = 0; label < label_count; label++){
3525 SwitchLabel sl = (SwitchLabel) ss.Labels [label];
3526 ig.MarkLabel (sl.GetILLabel (ec));
3529 ig.MarkLabel (next_test);
3530 next_test = ig.DefineLabel ();
3533 // If we are the default target
3535 if (sl.Label != null){
3536 object lit = sl.Converted;
3538 if (lit == SwitchLabel.NullStringCase){
3540 if (label + 1 == label_count)
3541 ig.Emit (OpCodes.Br, next_test);
3545 ig.Emit (OpCodes.Ldloc, val);
3546 ig.Emit (OpCodes.Ldstr, (string)lit);
3547 if (label_count == 1)
3548 ig.Emit (OpCodes.Bne_Un, next_test);
3550 if (label+1 == label_count)
3551 ig.Emit (OpCodes.Bne_Un, next_test);
3553 ig.Emit (OpCodes.Beq, sec_begin);
3558 ig.MarkLabel (null_target);
3561 ig.MarkLabel (sec_begin);
3562 foreach (SwitchLabel sl in ss.Labels)
3563 ig.MarkLabel (sl.GetILLabelCode (ec));
3566 pending_goto_end = !ss.Block.HasRet;
3569 ig.MarkLabel (next_test);
3570 ig.MarkLabel (default_target);
3572 ig.MarkLabel (null_target);
3573 if (default_section != null)
3574 default_section.Block.Emit (ec);
3575 ig.MarkLabel (end_of_switch);
3578 SwitchSection FindSection (SwitchLabel label)
3580 foreach (SwitchSection ss in Sections){
3581 foreach (SwitchLabel sl in ss.Labels){
3590 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3592 foreach (SwitchSection ss in Sections)
3593 ss.Block.MutateHoistedGenericType (storey);
3596 public override bool Resolve (EmitContext ec)
3598 Expr = Expr.Resolve (ec);
3602 new_expr = SwitchGoverningType (ec, Expr);
3605 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3606 unwrap = Nullable.Unwrap.Create (Expr, ec);
3610 new_expr = SwitchGoverningType (ec, unwrap);
3614 if (new_expr == null){
3615 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3620 SwitchType = new_expr.Type;
3622 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3623 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3627 if (!CheckSwitch (ec))
3631 Elements.Remove (SwitchLabel.NullStringCase);
3633 Switch old_switch = ec.Switch;
3635 ec.Switch.SwitchType = SwitchType;
3637 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3638 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3640 is_constant = new_expr is Constant;
3642 object key = ((Constant) new_expr).GetValue ();
3643 SwitchLabel label = (SwitchLabel) Elements [key];
3645 constant_section = FindSection (label);
3646 if (constant_section == null)
3647 constant_section = default_section;
3652 foreach (SwitchSection ss in Sections){
3654 ec.CurrentBranching.CreateSibling (
3655 null, FlowBranching.SiblingType.SwitchSection);
3659 if (is_constant && (ss != constant_section)) {
3660 // If we're a constant switch, we're only emitting
3661 // one single section - mark all the others as
3663 ec.CurrentBranching.CurrentUsageVector.Goto ();
3664 if (!ss.Block.ResolveUnreachable (ec, true)) {
3668 if (!ss.Block.Resolve (ec))
3673 if (default_section == null)
3674 ec.CurrentBranching.CreateSibling (
3675 null, FlowBranching.SiblingType.SwitchSection);
3677 ec.EndFlowBranching ();
3678 ec.Switch = old_switch;
3680 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3682 if (TypeManager.string_isinterned_string == null) {
3683 TypeManager.string_isinterned_string = TypeManager.GetPredefinedMethod (TypeManager.string_type,
3684 "IsInterned", loc, TypeManager.string_type);
3690 protected override void DoEmit (EmitContext ec)
3692 ILGenerator ig = ec.ig;
3694 default_target = ig.DefineLabel ();
3695 null_target = ig.DefineLabel ();
3697 // Store variable for comparission purposes
3700 value = ig.DeclareLocal (SwitchType);
3702 unwrap.EmitCheck (ec);
3703 ig.Emit (OpCodes.Brfalse, null_target);
3705 ig.Emit (OpCodes.Stloc, value);
3707 } else if (!is_constant) {
3708 value = ig.DeclareLocal (SwitchType);
3710 ig.Emit (OpCodes.Stloc, value);
3715 // Setup the codegen context
3717 Label old_end = ec.LoopEnd;
3718 Switch old_switch = ec.Switch;
3720 ec.LoopEnd = ig.DefineLabel ();
3725 if (constant_section != null)
3726 constant_section.Block.Emit (ec);
3727 } else if (SwitchType == TypeManager.string_type)
3728 SimpleSwitchEmit (ec, value);
3730 TableSwitchEmit (ec, value);
3732 // Restore context state.
3733 ig.MarkLabel (ec.LoopEnd);
3736 // Restore the previous context
3738 ec.LoopEnd = old_end;
3739 ec.Switch = old_switch;
3742 protected override void CloneTo (CloneContext clonectx, Statement t)
3744 Switch target = (Switch) t;
3746 target.Expr = Expr.Clone (clonectx);
3747 target.Sections = new ArrayList ();
3748 foreach (SwitchSection ss in Sections){
3749 target.Sections.Add (ss.Clone (clonectx));
3754 // A place where execution can restart in an iterator
3755 public abstract class ResumableStatement : Statement
3758 protected Label resume_point;
3760 public Label PrepareForEmit (EmitContext ec)
3764 resume_point = ec.ig.DefineLabel ();
3766 return resume_point;
3769 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3773 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3778 // Base class for statements that are implemented in terms of try...finally
3779 public abstract class ExceptionStatement : ResumableStatement
3783 protected abstract void EmitPreTryBody (EmitContext ec);
3784 protected abstract void EmitTryBody (EmitContext ec);
3785 protected abstract void EmitFinallyBody (EmitContext ec);
3787 protected sealed override void DoEmit (EmitContext ec)
3789 ILGenerator ig = ec.ig;
3791 EmitPreTryBody (ec);
3793 if (resume_points != null) {
3794 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3795 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3798 ig.BeginExceptionBlock ();
3800 if (resume_points != null) {
3801 ig.MarkLabel (resume_point);
3803 // For normal control flow, we want to fall-through the Switch
3804 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3805 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3806 IntConstant.EmitInt (ig, first_resume_pc);
3807 ig.Emit (OpCodes.Sub);
3809 Label [] labels = new Label [resume_points.Count];
3810 for (int i = 0; i < resume_points.Count; ++i)
3811 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3812 ig.Emit (OpCodes.Switch, labels);
3817 ig.BeginFinallyBlock ();
3819 Label start_finally = ec.ig.DefineLabel ();
3820 if (resume_points != null) {
3821 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3822 ig.Emit (OpCodes.Brfalse_S, start_finally);
3823 ig.Emit (OpCodes.Endfinally);
3826 ig.MarkLabel (start_finally);
3827 EmitFinallyBody (ec);
3829 ig.EndExceptionBlock ();
3832 public void SomeCodeFollows ()
3834 code_follows = true;
3837 protected void ResolveReachability (EmitContext ec)
3839 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3840 // So, ensure there's some IL code after this statement.
3841 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3842 ec.NeedReturnLabel ();
3846 ArrayList resume_points;
3847 int first_resume_pc;
3848 public void AddResumePoint (ResumableStatement stmt, Location loc, int pc)
3850 if (resume_points == null) {
3851 resume_points = new ArrayList ();
3852 first_resume_pc = pc;
3855 if (pc != first_resume_pc + resume_points.Count)
3856 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3858 resume_points.Add (stmt);
3861 Label dispose_try_block;
3862 bool prepared_for_dispose, emitted_dispose;
3863 public override Label PrepareForDispose (EmitContext ec, Label end)
3865 if (!prepared_for_dispose) {
3866 prepared_for_dispose = true;
3867 dispose_try_block = ec.ig.DefineLabel ();
3869 return dispose_try_block;
3872 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3874 if (emitted_dispose)
3877 emitted_dispose = true;
3879 ILGenerator ig = ec.ig;
3881 Label end_of_try = ig.DefineLabel ();
3883 // Ensure that the only way we can get into this code is through a dispatcher
3884 if (have_dispatcher)
3885 ig.Emit (OpCodes.Br, end);
3887 ig.BeginExceptionBlock ();
3889 ig.MarkLabel (dispose_try_block);
3891 Label [] labels = null;
3892 for (int i = 0; i < resume_points.Count; ++i) {
3893 ResumableStatement s = (ResumableStatement) resume_points [i];
3894 Label ret = s.PrepareForDispose (ec, end_of_try);
3895 if (ret.Equals (end_of_try) && labels == null)
3897 if (labels == null) {
3898 labels = new Label [resume_points.Count];
3899 for (int j = 0; j < i; ++j)
3900 labels [j] = end_of_try;
3905 if (labels != null) {
3907 for (j = 1; j < labels.Length; ++j)
3908 if (!labels [0].Equals (labels [j]))
3910 bool emit_dispatcher = j < labels.Length;
3912 if (emit_dispatcher) {
3913 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3914 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3915 IntConstant.EmitInt (ig, first_resume_pc);
3916 ig.Emit (OpCodes.Sub);
3917 ig.Emit (OpCodes.Switch, labels);
3918 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3921 foreach (ResumableStatement s in resume_points)
3922 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3925 ig.MarkLabel (end_of_try);
3927 ig.BeginFinallyBlock ();
3929 EmitFinallyBody (ec);
3931 ig.EndExceptionBlock ();
3935 public class Lock : ExceptionStatement {
3937 public Statement Statement;
3938 TemporaryVariable temp;
3940 public Lock (Expression expr, Statement stmt, Location l)
3947 public override bool Resolve (EmitContext ec)
3949 expr = expr.Resolve (ec);
3953 if (expr.Type.IsValueType){
3954 Report.Error (185, loc,
3955 "`{0}' is not a reference type as required by the lock statement",
3956 TypeManager.CSharpName (expr.Type));
3960 ec.StartFlowBranching (this);
3961 bool ok = Statement.Resolve (ec);
3962 ec.EndFlowBranching ();
3964 ResolveReachability (ec);
3966 // Avoid creating libraries that reference the internal
3969 if (t == TypeManager.null_type)
3970 t = TypeManager.object_type;
3972 temp = new TemporaryVariable (t, loc);
3975 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
3976 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
3977 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
3978 monitor_type, "Enter", loc, TypeManager.object_type);
3979 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
3980 monitor_type, "Exit", loc, TypeManager.object_type);
3986 protected override void EmitPreTryBody (EmitContext ec)
3988 ILGenerator ig = ec.ig;
3990 temp.EmitAssign (ec, expr);
3992 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
3995 protected override void EmitTryBody (EmitContext ec)
3997 Statement.Emit (ec);
4000 protected override void EmitFinallyBody (EmitContext ec)
4003 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4006 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4008 expr.MutateHoistedGenericType (storey);
4009 temp.MutateHoistedGenericType (storey);
4010 Statement.MutateHoistedGenericType (storey);
4013 protected override void CloneTo (CloneContext clonectx, Statement t)
4015 Lock target = (Lock) t;
4017 target.expr = expr.Clone (clonectx);
4018 target.Statement = Statement.Clone (clonectx);
4022 public class Unchecked : Statement {
4025 public Unchecked (Block b)
4031 public override bool Resolve (EmitContext ec)
4033 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4034 return Block.Resolve (ec);
4037 protected override void DoEmit (EmitContext ec)
4039 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4043 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4045 Block.MutateHoistedGenericType (storey);
4048 protected override void CloneTo (CloneContext clonectx, Statement t)
4050 Unchecked target = (Unchecked) t;
4052 target.Block = clonectx.LookupBlock (Block);
4056 public class Checked : Statement {
4059 public Checked (Block b)
4062 b.Unchecked = false;
4065 public override bool Resolve (EmitContext ec)
4067 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4068 return Block.Resolve (ec);
4071 protected override void DoEmit (EmitContext ec)
4073 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4077 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4079 Block.MutateHoistedGenericType (storey);
4082 protected override void CloneTo (CloneContext clonectx, Statement t)
4084 Checked target = (Checked) t;
4086 target.Block = clonectx.LookupBlock (Block);
4090 public class Unsafe : Statement {
4093 public Unsafe (Block b)
4096 Block.Unsafe = true;
4099 public override bool Resolve (EmitContext ec)
4101 using (ec.With (EmitContext.Flags.InUnsafe, true))
4102 return Block.Resolve (ec);
4105 protected override void DoEmit (EmitContext ec)
4107 using (ec.With (EmitContext.Flags.InUnsafe, true))
4111 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4113 Block.MutateHoistedGenericType (storey);
4116 protected override void CloneTo (CloneContext clonectx, Statement t)
4118 Unsafe target = (Unsafe) t;
4120 target.Block = clonectx.LookupBlock (Block);
4127 public class Fixed : Statement {
4129 ArrayList declarators;
4130 Statement statement;
4135 abstract class Emitter
4137 protected LocalInfo vi;
4138 protected Expression converted;
4140 protected Emitter (Expression expr, LocalInfo li)
4146 public abstract void Emit (EmitContext ec);
4147 public abstract void EmitExit (EmitContext ec);
4150 class ExpressionEmitter : Emitter {
4151 public ExpressionEmitter (Expression converted, LocalInfo li) :
4152 base (converted, li)
4156 public override void Emit (EmitContext ec) {
4158 // Store pointer in pinned location
4160 converted.Emit (ec);
4164 public override void EmitExit (EmitContext ec)
4166 ec.ig.Emit (OpCodes.Ldc_I4_0);
4167 ec.ig.Emit (OpCodes.Conv_U);
4172 class StringEmitter : Emitter {
4173 class StringPtr : Expression
4177 public StringPtr (LocalBuilder b, Location l)
4180 eclass = ExprClass.Value;
4181 type = TypeManager.char_ptr_type;
4185 public override Expression CreateExpressionTree (EmitContext ec)
4187 throw new NotSupportedException ("ET");
4190 public override Expression DoResolve (EmitContext ec)
4192 // This should never be invoked, we are born in fully
4193 // initialized state.
4198 public override void Emit (EmitContext ec)
4200 if (TypeManager.int_get_offset_to_string_data == null) {
4201 // TODO: Move to resolve !!
4202 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4203 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4206 ILGenerator ig = ec.ig;
4208 ig.Emit (OpCodes.Ldloc, b);
4209 ig.Emit (OpCodes.Conv_I);
4210 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4211 ig.Emit (OpCodes.Add);
4215 LocalBuilder pinned_string;
4218 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4224 public override void Emit (EmitContext ec)
4226 ILGenerator ig = ec.ig;
4227 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4229 converted.Emit (ec);
4230 ig.Emit (OpCodes.Stloc, pinned_string);
4232 Expression sptr = new StringPtr (pinned_string, loc);
4233 converted = Convert.ImplicitConversionRequired (
4234 ec, sptr, vi.VariableType, loc);
4236 if (converted == null)
4239 converted.Emit (ec);
4243 public override void EmitExit (EmitContext ec)
4245 ec.ig.Emit (OpCodes.Ldnull);
4246 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4250 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4253 declarators = decls;
4258 public Statement Statement {
4259 get { return statement; }
4262 public override bool Resolve (EmitContext ec)
4265 Expression.UnsafeError (loc);
4269 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4270 if (texpr == null) {
4271 if (type is VarExpr)
4272 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4277 expr_type = texpr.Type;
4279 data = new Emitter [declarators.Count];
4281 if (!expr_type.IsPointer){
4282 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4287 foreach (Pair p in declarators){
4288 LocalInfo vi = (LocalInfo) p.First;
4289 Expression e = (Expression) p.Second;
4291 vi.VariableInfo.SetAssigned (ec);
4292 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4295 // The rules for the possible declarators are pretty wise,
4296 // but the production on the grammar is more concise.
4298 // So we have to enforce these rules here.
4300 // We do not resolve before doing the case 1 test,
4301 // because the grammar is explicit in that the token &
4302 // is present, so we need to test for this particular case.
4306 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4311 // Case 1: & object.
4313 if (e is Unary && ((Unary) e).Oper == Unary.Operator.AddressOf){
4314 Expression child = ((Unary) e).Expr;
4316 if (child is ParameterReference || child is LocalVariableReference){
4319 "No need to use fixed statement for parameters or " +
4320 "local variable declarations (address is already " +
4325 ec.InFixedInitializer = true;
4327 ec.InFixedInitializer = false;
4331 child = ((Unary) e).Expr;
4333 if (!TypeManager.VerifyUnManaged (child.Type, loc))
4336 if (!Convert.ImplicitConversionExists (ec, e, expr_type)) {
4337 e.Error_ValueCannotBeConverted (ec, e.Location, expr_type, false);
4341 data [i] = new ExpressionEmitter (e, vi);
4347 ec.InFixedInitializer = true;
4349 ec.InFixedInitializer = false;
4356 if (e.Type.IsArray){
4357 Type array_type = TypeManager.GetElementType (e.Type);
4360 // Provided that array_type is unmanaged,
4362 if (!TypeManager.VerifyUnManaged (array_type, loc))
4366 // and T* is implicitly convertible to the
4367 // pointer type given in the fixed statement.
4369 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4371 Expression converted = Convert.ImplicitConversionRequired (
4372 ec, array_ptr, vi.VariableType, loc);
4373 if (converted == null)
4377 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4379 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4380 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4381 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4385 converted = converted.Resolve (ec);
4387 data [i] = new ExpressionEmitter (converted, vi);
4396 if (e.Type == TypeManager.string_type){
4397 data [i] = new StringEmitter (e, vi, loc);
4402 // Case 4: fixed buffer
4403 FixedBufferPtr fixed_buffer_ptr = e as FixedBufferPtr;
4404 if (fixed_buffer_ptr != null) {
4405 data [i++] = new ExpressionEmitter (fixed_buffer_ptr, vi);
4410 // For other cases, flag a `this is already fixed expression'
4412 if (e is LocalVariableReference || e is ParameterReference ||
4413 Convert.ImplicitConversionExists (ec, e, vi.VariableType)){
4415 Report.Error (245, loc, "right hand expression is already fixed, no need to use fixed statement ");
4419 Report.Error (245, loc, "Fixed statement only allowed on strings, arrays or address-of expressions");
4423 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4424 bool ok = statement.Resolve (ec);
4425 bool flow_unreachable = ec.EndFlowBranching ();
4426 has_ret = flow_unreachable;
4431 protected override void DoEmit (EmitContext ec)
4433 for (int i = 0; i < data.Length; i++) {
4437 statement.Emit (ec);
4443 // Clear the pinned variable
4445 for (int i = 0; i < data.Length; i++) {
4446 data [i].EmitExit (ec);
4450 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4452 // Fixed statement cannot be used inside anonymous methods or lambdas
4453 throw new NotSupportedException ();
4456 protected override void CloneTo (CloneContext clonectx, Statement t)
4458 Fixed target = (Fixed) t;
4460 target.type = type.Clone (clonectx);
4461 target.declarators = new ArrayList (declarators.Count);
4462 foreach (Pair p in declarators) {
4463 LocalInfo vi = (LocalInfo) p.First;
4464 Expression e = (Expression) p.Second;
4466 target.declarators.Add (
4467 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4470 target.statement = statement.Clone (clonectx);
4474 public class Catch : Statement {
4475 public readonly string Name;
4477 public Block VarBlock;
4479 Expression type_expr;
4482 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4487 VarBlock = var_block;
4491 public Type CatchType {
4497 public bool IsGeneral {
4499 return type_expr == null;
4503 protected override void DoEmit (EmitContext ec)
4505 ILGenerator ig = ec.ig;
4507 if (CatchType != null)
4508 ig.BeginCatchBlock (CatchType);
4510 ig.BeginCatchBlock (TypeManager.object_type);
4512 if (VarBlock != null)
4516 // TODO: Move to resolve
4517 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4521 if (lvr.IsHoisted) {
4522 LocalTemporary lt = new LocalTemporary (lvr.Type);
4526 // Variable is at the top of the stack
4527 source = EmptyExpression.Null;
4530 lvr.EmitAssign (ec, source, false, false);
4532 ig.Emit (OpCodes.Pop);
4537 public override bool Resolve (EmitContext ec)
4539 using (ec.With (EmitContext.Flags.InCatch, true)) {
4540 if (type_expr != null) {
4541 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4547 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4548 Error (155, "The type caught or thrown must be derived from System.Exception");
4554 if (!Block.Resolve (ec))
4557 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4558 // emit the "unused variable" warnings.
4559 if (VarBlock != null)
4560 return VarBlock.Resolve (ec);
4566 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4569 type = storey.MutateType (type);
4570 if (VarBlock != null)
4571 VarBlock.MutateHoistedGenericType (storey);
4572 Block.MutateHoistedGenericType (storey);
4575 protected override void CloneTo (CloneContext clonectx, Statement t)
4577 Catch target = (Catch) t;
4579 if (type_expr != null)
4580 target.type_expr = type_expr.Clone (clonectx);
4581 if (VarBlock != null)
4582 target.VarBlock = clonectx.LookupBlock (VarBlock);
4583 target.Block = clonectx.LookupBlock (Block);
4587 public class TryFinally : ExceptionStatement {
4591 public TryFinally (Statement stmt, Block fini, Location l)
4598 public override bool Resolve (EmitContext ec)
4602 ec.StartFlowBranching (this);
4604 if (!stmt.Resolve (ec))
4608 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4609 using (ec.With (EmitContext.Flags.InFinally, true)) {
4610 if (!fini.Resolve (ec))
4614 ec.EndFlowBranching ();
4616 ResolveReachability (ec);
4621 protected override void EmitPreTryBody (EmitContext ec)
4625 protected override void EmitTryBody (EmitContext ec)
4630 protected override void EmitFinallyBody (EmitContext ec)
4635 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4637 stmt.MutateHoistedGenericType (storey);
4638 fini.MutateHoistedGenericType (storey);
4641 protected override void CloneTo (CloneContext clonectx, Statement t)
4643 TryFinally target = (TryFinally) t;
4645 target.stmt = (Statement) stmt.Clone (clonectx);
4647 target.fini = clonectx.LookupBlock (fini);
4651 public class TryCatch : Statement {
4653 public ArrayList Specific;
4654 public Catch General;
4655 bool inside_try_finally, code_follows;
4657 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4660 this.Specific = catch_clauses;
4661 this.General = null;
4662 this.inside_try_finally = inside_try_finally;
4664 for (int i = 0; i < catch_clauses.Count; ++i) {
4665 Catch c = (Catch) catch_clauses [i];
4667 if (i != catch_clauses.Count - 1)
4668 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4670 catch_clauses.RemoveAt (i);
4678 public override bool Resolve (EmitContext ec)
4682 ec.StartFlowBranching (this);
4684 if (!Block.Resolve (ec))
4687 Type[] prev_catches = new Type [Specific.Count];
4689 foreach (Catch c in Specific){
4690 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4692 if (c.Name != null) {
4693 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4695 throw new Exception ();
4697 vi.VariableInfo = null;
4700 if (!c.Resolve (ec))
4703 Type resolved_type = c.CatchType;
4704 for (int ii = 0; ii < last_index; ++ii) {
4705 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4706 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4711 prev_catches [last_index++] = resolved_type;
4714 if (General != null) {
4715 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4716 foreach (Catch c in Specific){
4717 if (c.CatchType == TypeManager.exception_type) {
4718 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'");
4723 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4725 if (!General.Resolve (ec))
4729 ec.EndFlowBranching ();
4731 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4732 // So, ensure there's some IL code after this statement
4733 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4734 ec.NeedReturnLabel ();
4739 public void SomeCodeFollows ()
4741 code_follows = true;
4744 protected override void DoEmit (EmitContext ec)
4746 ILGenerator ig = ec.ig;
4748 if (!inside_try_finally)
4749 ig.BeginExceptionBlock ();
4753 foreach (Catch c in Specific)
4756 if (General != null)
4759 if (!inside_try_finally)
4760 ig.EndExceptionBlock ();
4763 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4765 Block.MutateHoistedGenericType (storey);
4767 if (General != null)
4768 General.MutateHoistedGenericType (storey);
4769 if (Specific != null) {
4770 foreach (Catch c in Specific)
4771 c.MutateHoistedGenericType (storey);
4775 protected override void CloneTo (CloneContext clonectx, Statement t)
4777 TryCatch target = (TryCatch) t;
4779 target.Block = clonectx.LookupBlock (Block);
4780 if (General != null)
4781 target.General = (Catch) General.Clone (clonectx);
4782 if (Specific != null){
4783 target.Specific = new ArrayList ();
4784 foreach (Catch c in Specific)
4785 target.Specific.Add (c.Clone (clonectx));
4790 public class UsingTemporary : ExceptionStatement {
4791 TemporaryVariable local_copy;
4792 public Statement Statement;
4796 public UsingTemporary (Expression expr, Statement stmt, Location l)
4803 public override bool Resolve (EmitContext ec)
4805 expr = expr.Resolve (ec);
4809 expr_type = expr.Type;
4811 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4812 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4813 Using.Error_IsNotConvertibleToIDisposable (expr);
4818 local_copy = new TemporaryVariable (expr_type, loc);
4819 local_copy.Resolve (ec);
4821 ec.StartFlowBranching (this);
4823 bool ok = Statement.Resolve (ec);
4825 ec.EndFlowBranching ();
4827 ResolveReachability (ec);
4829 if (TypeManager.void_dispose_void == null) {
4830 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4831 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4837 protected override void EmitPreTryBody (EmitContext ec)
4839 local_copy.EmitAssign (ec, expr);
4842 protected override void EmitTryBody (EmitContext ec)
4844 Statement.Emit (ec);
4847 protected override void EmitFinallyBody (EmitContext ec)
4849 ILGenerator ig = ec.ig;
4850 if (!expr_type.IsValueType) {
4851 Label skip = ig.DefineLabel ();
4852 local_copy.Emit (ec);
4853 ig.Emit (OpCodes.Brfalse, skip);
4854 local_copy.Emit (ec);
4855 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4856 ig.MarkLabel (skip);
4860 Expression ml = Expression.MemberLookup (
4861 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4862 "Dispose", Location.Null);
4864 if (!(ml is MethodGroupExpr)) {
4865 local_copy.Emit (ec);
4866 ig.Emit (OpCodes.Box, expr_type);
4867 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4871 MethodInfo mi = null;
4873 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4874 if (TypeManager.GetParameterData (mk).Count == 0) {
4881 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4885 local_copy.AddressOf (ec, AddressOp.Load);
4886 ig.Emit (OpCodes.Call, mi);
4889 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4891 expr_type = storey.MutateType (expr_type);
4892 local_copy.MutateHoistedGenericType (storey);
4893 Statement.MutateHoistedGenericType (storey);
4896 protected override void CloneTo (CloneContext clonectx, Statement t)
4898 UsingTemporary target = (UsingTemporary) t;
4900 target.expr = expr.Clone (clonectx);
4901 target.Statement = Statement.Clone (clonectx);
4905 public class Using : ExceptionStatement {
4907 public Statement EmbeddedStatement {
4908 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4914 Expression converted_var;
4915 ExpressionStatement assign;
4917 public Using (Expression var, Expression init, Statement stmt, Location l)
4925 bool ResolveVariable (EmitContext ec)
4927 ExpressionStatement a = new SimpleAssign (var, init, loc);
4928 a = a.ResolveStatement (ec);
4934 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4935 converted_var = var;
4939 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4941 Error_IsNotConvertibleToIDisposable (var);
4950 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4952 Report.SymbolRelatedToPreviousError (expr.Type);
4953 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4954 expr.GetSignatureForError ());
4957 protected override void EmitPreTryBody (EmitContext ec)
4959 assign.EmitStatement (ec);
4962 protected override void EmitTryBody (EmitContext ec)
4967 protected override void EmitFinallyBody (EmitContext ec)
4969 ILGenerator ig = ec.ig;
4971 if (!var.Type.IsValueType) {
4972 Label skip = ig.DefineLabel ();
4974 ig.Emit (OpCodes.Brfalse, skip);
4975 converted_var.Emit (ec);
4976 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4977 ig.MarkLabel (skip);
4979 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
4981 if (!(ml is MethodGroupExpr)) {
4983 ig.Emit (OpCodes.Box, var.Type);
4984 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4986 MethodInfo mi = null;
4988 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4989 if (TypeManager.GetParameterData (mk).Count == 0) {
4996 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5000 IMemoryLocation mloc = (IMemoryLocation) var;
5002 mloc.AddressOf (ec, AddressOp.Load);
5003 ig.Emit (OpCodes.Call, mi);
5008 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5010 assign.MutateHoistedGenericType (storey);
5011 var.MutateHoistedGenericType (storey);
5012 stmt.MutateHoistedGenericType (storey);
5015 public override bool Resolve (EmitContext ec)
5017 if (!ResolveVariable (ec))
5020 ec.StartFlowBranching (this);
5022 bool ok = stmt.Resolve (ec);
5024 ec.EndFlowBranching ();
5026 ResolveReachability (ec);
5028 if (TypeManager.void_dispose_void == null) {
5029 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5030 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5036 protected override void CloneTo (CloneContext clonectx, Statement t)
5038 Using target = (Using) t;
5040 target.var = var.Clone (clonectx);
5041 target.init = init.Clone (clonectx);
5042 target.stmt = stmt.Clone (clonectx);
5047 /// Implementation of the foreach C# statement
5049 public class Foreach : Statement {
5051 Expression variable;
5053 Statement statement;
5055 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5056 Statement stmt, Location l)
5059 this.variable = var;
5065 public Statement Statement {
5066 get { return statement; }
5069 public override bool Resolve (EmitContext ec)
5071 expr = expr.Resolve (ec);
5076 Report.Error (186, loc, "Use of null is not valid in this context");
5080 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5081 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5082 expr.ExprClassName);
5086 if (expr.Type.IsArray) {
5087 statement = new ArrayForeach (type, variable, expr, statement, loc);
5089 statement = new CollectionForeach (type, variable, expr, statement, loc);
5092 return statement.Resolve (ec);
5095 protected override void DoEmit (EmitContext ec)
5097 ILGenerator ig = ec.ig;
5099 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5100 ec.LoopBegin = ig.DefineLabel ();
5101 ec.LoopEnd = ig.DefineLabel ();
5103 statement.Emit (ec);
5105 ec.LoopBegin = old_begin;
5106 ec.LoopEnd = old_end;
5109 protected class ArrayCounter : TemporaryVariable
5111 StatementExpression increment;
5113 public ArrayCounter (Location loc)
5114 : base (TypeManager.int32_type, loc)
5118 public void ResolveIncrement (EmitContext ec)
5120 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this, loc));
5121 increment.Resolve (ec);
5124 public void EmitIncrement (EmitContext ec)
5126 increment.Emit (ec);
5130 protected class ArrayForeach : Statement
5132 Expression variable, expr, conv;
5133 Statement statement;
5135 Expression var_type;
5136 TemporaryVariable[] lengths;
5137 ArrayCounter[] counter;
5140 TemporaryVariable copy;
5142 Expression[] length_exprs;
5144 public ArrayForeach (Expression var_type, Expression var,
5145 Expression expr, Statement stmt, Location l)
5147 this.var_type = var_type;
5148 this.variable = var;
5154 public override bool Resolve (EmitContext ec)
5156 array_type = expr.Type;
5157 rank = array_type.GetArrayRank ();
5159 copy = new TemporaryVariable (array_type, loc);
5162 counter = new ArrayCounter [rank];
5163 lengths = new TemporaryVariable [rank];
5164 length_exprs = new Expression [rank];
5166 ArrayList list = new ArrayList (rank);
5167 for (int i = 0; i < rank; i++) {
5168 counter [i] = new ArrayCounter (loc);
5169 counter [i].ResolveIncrement (ec);
5171 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5172 lengths [i].Resolve (ec);
5175 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5177 ArrayList args = new ArrayList (1);
5178 args.Add (new Argument (new IntConstant (i, loc)));
5179 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5182 list.Add (counter [i]);
5185 access = new ElementAccess (copy, list).Resolve (ec);
5189 VarExpr ve = var_type as VarExpr;
5191 // Infer implicitly typed local variable from foreach array type
5192 var_type = new TypeExpression (access.Type, ve.Location);
5195 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5196 if (var_type == null)
5199 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5205 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5206 ec.CurrentBranching.CreateSibling ();
5208 variable = variable.ResolveLValue (ec, conv, loc);
5209 if (variable == null)
5212 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5213 if (!statement.Resolve (ec))
5215 ec.EndFlowBranching ();
5217 // There's no direct control flow from the end of the embedded statement to the end of the loop
5218 ec.CurrentBranching.CurrentUsageVector.Goto ();
5220 ec.EndFlowBranching ();
5225 protected override void DoEmit (EmitContext ec)
5227 ILGenerator ig = ec.ig;
5229 copy.EmitAssign (ec, expr);
5231 Label[] test = new Label [rank];
5232 Label[] loop = new Label [rank];
5234 for (int i = 0; i < rank; i++) {
5235 test [i] = ig.DefineLabel ();
5236 loop [i] = ig.DefineLabel ();
5238 lengths [i].EmitAssign (ec, length_exprs [i]);
5241 IntConstant zero = new IntConstant (0, loc);
5242 for (int i = 0; i < rank; i++) {
5243 counter [i].EmitAssign (ec, zero);
5245 ig.Emit (OpCodes.Br, test [i]);
5246 ig.MarkLabel (loop [i]);
5249 ((IAssignMethod) variable).EmitAssign (ec, conv, false, false);
5251 statement.Emit (ec);
5253 ig.MarkLabel (ec.LoopBegin);
5255 for (int i = rank - 1; i >= 0; i--){
5256 counter [i].EmitIncrement (ec);
5258 ig.MarkLabel (test [i]);
5259 counter [i].Emit (ec);
5260 lengths [i].Emit (ec);
5261 ig.Emit (OpCodes.Blt, loop [i]);
5264 ig.MarkLabel (ec.LoopEnd);
5267 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5269 copy.MutateHoistedGenericType (storey);
5270 conv.MutateHoistedGenericType (storey);
5271 variable.MutateHoistedGenericType (storey);
5272 statement.MutateHoistedGenericType (storey);
5274 for (int i = 0; i < rank; i++) {
5275 counter [i].MutateHoistedGenericType (storey);
5276 lengths [i].MutateHoistedGenericType (storey);
5281 protected class CollectionForeach : Statement
5283 Expression variable, expr;
5284 Statement statement;
5286 TemporaryVariable enumerator;
5291 MethodGroupExpr get_enumerator;
5292 PropertyExpr get_current;
5293 MethodInfo move_next;
5294 Expression var_type;
5295 Type enumerator_type;
5296 bool enumerator_found;
5298 public CollectionForeach (Expression var_type, Expression var,
5299 Expression expr, Statement stmt, Location l)
5301 this.var_type = var_type;
5302 this.variable = var;
5308 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5310 Type return_type = mi.ReturnType;
5312 if ((return_type == TypeManager.ienumerator_type) && (mi.DeclaringType == TypeManager.string_type))
5314 // Apply the same optimization as MS: skip the GetEnumerator
5315 // returning an IEnumerator, and use the one returning a
5316 // CharEnumerator instead. This allows us to avoid the
5317 // try-finally block and the boxing.
5322 // Ok, we can access it, now make sure that we can do something
5323 // with this `GetEnumerator'
5326 if (return_type == TypeManager.ienumerator_type ||
5327 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5328 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5330 // If it is not an interface, lets try to find the methods ourselves.
5331 // For example, if we have:
5332 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5333 // We can avoid the iface call. This is a runtime perf boost.
5334 // even bigger if we have a ValueType, because we avoid the cost
5337 // We have to make sure that both methods exist for us to take
5338 // this path. If one of the methods does not exist, we will just
5339 // use the interface. Sadly, this complex if statement is the only
5340 // way I could do this without a goto
5343 if (TypeManager.bool_movenext_void == null) {
5344 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5345 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5348 if (TypeManager.ienumerator_getcurrent == null) {
5349 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5350 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5355 // Prefer a generic enumerator over a non-generic one.
5357 if (return_type.IsInterface && return_type.IsGenericType) {
5358 enumerator_type = return_type;
5359 if (!FetchGetCurrent (ec, return_type))
5360 get_current = new PropertyExpr (
5361 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5362 if (!FetchMoveNext (return_type))
5363 move_next = TypeManager.bool_movenext_void;
5368 if (return_type.IsInterface ||
5369 !FetchMoveNext (return_type) ||
5370 !FetchGetCurrent (ec, return_type)) {
5371 enumerator_type = return_type;
5372 move_next = TypeManager.bool_movenext_void;
5373 get_current = new PropertyExpr (
5374 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5379 // Ok, so they dont return an IEnumerable, we will have to
5380 // find if they support the GetEnumerator pattern.
5383 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5384 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",
5385 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5390 enumerator_type = return_type;
5396 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5398 bool FetchMoveNext (Type t)
5400 MemberList move_next_list;
5402 move_next_list = TypeContainer.FindMembers (
5403 t, MemberTypes.Method,
5404 BindingFlags.Public | BindingFlags.Instance,
5405 Type.FilterName, "MoveNext");
5406 if (move_next_list.Count == 0)
5409 foreach (MemberInfo m in move_next_list){
5410 MethodInfo mi = (MethodInfo) m;
5412 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5413 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5423 // Retrieves a `public T get_Current ()' method from the Type `t'
5425 bool FetchGetCurrent (EmitContext ec, Type t)
5427 PropertyExpr pe = Expression.MemberLookup (
5428 ec.ContainerType, t, "Current", MemberTypes.Property,
5429 Expression.AllBindingFlags, loc) as PropertyExpr;
5438 // Retrieves a `public void Dispose ()' method from the Type `t'
5440 static MethodInfo FetchMethodDispose (Type t)
5442 MemberList dispose_list;
5444 dispose_list = TypeContainer.FindMembers (
5445 t, MemberTypes.Method,
5446 BindingFlags.Public | BindingFlags.Instance,
5447 Type.FilterName, "Dispose");
5448 if (dispose_list.Count == 0)
5451 foreach (MemberInfo m in dispose_list){
5452 MethodInfo mi = (MethodInfo) m;
5454 if (TypeManager.GetParameterData (mi).Count == 0){
5455 if (mi.ReturnType == TypeManager.void_type)
5462 void Error_Enumerator ()
5464 if (enumerator_found) {
5468 Report.Error (1579, loc,
5469 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5470 TypeManager.CSharpName (expr.Type));
5473 bool IsOverride (MethodInfo m)
5475 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5477 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5479 if (m is MethodBuilder)
5482 MethodInfo base_method = m.GetBaseDefinition ();
5483 return base_method != m;
5486 bool TryType (EmitContext ec, Type t)
5488 MethodGroupExpr mg = Expression.MemberLookup (
5489 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5490 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5494 MethodInfo result = null;
5495 MethodInfo tmp_move_next = null;
5496 PropertyExpr tmp_get_cur = null;
5497 Type tmp_enumerator_type = enumerator_type;
5498 foreach (MethodInfo mi in mg.Methods) {
5499 if (TypeManager.GetParameterData (mi).Count != 0)
5502 // Check whether GetEnumerator is public
5503 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5506 if (IsOverride (mi))
5509 enumerator_found = true;
5511 if (!GetEnumeratorFilter (ec, mi))
5514 if (result != null) {
5515 if (TypeManager.IsGenericType (result.ReturnType)) {
5516 if (!TypeManager.IsGenericType (mi.ReturnType))
5519 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5520 Report.SymbolRelatedToPreviousError (t);
5521 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5522 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5523 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5527 // Always prefer generics enumerators
5528 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5529 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5530 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5533 Report.SymbolRelatedToPreviousError (result);
5534 Report.SymbolRelatedToPreviousError (mi);
5535 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5536 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5541 tmp_move_next = move_next;
5542 tmp_get_cur = get_current;
5543 tmp_enumerator_type = enumerator_type;
5544 if (mi.DeclaringType == t)
5548 if (result != null) {
5549 move_next = tmp_move_next;
5550 get_current = tmp_get_cur;
5551 enumerator_type = tmp_enumerator_type;
5552 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5553 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5555 if (t != expr.Type) {
5556 expr = Convert.ExplicitConversion (
5559 throw new InternalErrorException ();
5562 get_enumerator.InstanceExpression = expr;
5563 get_enumerator.IsBase = t != expr.Type;
5571 bool ProbeCollectionType (EmitContext ec, Type t)
5573 int errors = Report.Errors;
5574 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5575 if (TryType (ec, tt))
5580 if (Report.Errors > errors)
5584 // Now try to find the method in the interfaces
5586 Type [] ifaces = TypeManager.GetInterfaces (t);
5587 foreach (Type i in ifaces){
5588 if (TryType (ec, i))
5595 public override bool Resolve (EmitContext ec)
5597 enumerator_type = TypeManager.ienumerator_type;
5599 if (!ProbeCollectionType (ec, expr.Type)) {
5600 Error_Enumerator ();
5604 bool is_disposable = !enumerator_type.IsSealed ||
5605 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5607 VarExpr ve = var_type as VarExpr;
5609 // Infer implicitly typed local variable from foreach enumerable type
5610 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5613 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5614 if (var_type == null)
5617 enumerator = new TemporaryVariable (enumerator_type, loc);
5618 enumerator.Resolve (ec);
5620 init = new Invocation (get_enumerator, null);
5621 init = init.Resolve (ec);
5625 Expression move_next_expr;
5627 MemberInfo[] mi = new MemberInfo[] { move_next };
5628 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5629 mg.InstanceExpression = enumerator;
5631 move_next_expr = new Invocation (mg, null);
5634 get_current.InstanceExpression = enumerator;
5636 Statement block = new CollectionForeachStatement (
5637 var_type.Type, variable, get_current, statement, loc);
5639 loop = new While (move_next_expr, block, loc);
5641 wrapper = is_disposable ?
5642 (Statement) new DisposableWrapper (this) :
5643 (Statement) new NonDisposableWrapper (this);
5644 return wrapper.Resolve (ec);
5647 protected override void DoEmit (EmitContext ec)
5652 class NonDisposableWrapper : Statement {
5653 CollectionForeach parent;
5655 internal NonDisposableWrapper (CollectionForeach parent)
5657 this.parent = parent;
5660 public override bool Resolve (EmitContext ec)
5662 return parent.ResolveLoop (ec);
5665 protected override void DoEmit (EmitContext ec)
5667 parent.EmitLoopInit (ec);
5668 parent.EmitLoopBody (ec);
5671 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5673 throw new NotSupportedException ();
5677 class DisposableWrapper : ExceptionStatement {
5678 CollectionForeach parent;
5680 internal DisposableWrapper (CollectionForeach parent)
5682 this.parent = parent;
5685 public override bool Resolve (EmitContext ec)
5689 ec.StartFlowBranching (this);
5691 if (!parent.ResolveLoop (ec))
5694 ec.EndFlowBranching ();
5696 ResolveReachability (ec);
5698 if (TypeManager.void_dispose_void == null) {
5699 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5700 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5705 protected override void EmitPreTryBody (EmitContext ec)
5707 parent.EmitLoopInit (ec);
5710 protected override void EmitTryBody (EmitContext ec)
5712 parent.EmitLoopBody (ec);
5715 protected override void EmitFinallyBody (EmitContext ec)
5717 parent.EmitFinallyBody (ec);
5720 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5722 throw new NotSupportedException ();
5726 bool ResolveLoop (EmitContext ec)
5728 return loop.Resolve (ec);
5731 void EmitLoopInit (EmitContext ec)
5733 enumerator.EmitAssign (ec, init);
5736 void EmitLoopBody (EmitContext ec)
5741 void EmitFinallyBody (EmitContext ec)
5743 ILGenerator ig = ec.ig;
5745 if (enumerator_type.IsValueType) {
5746 MethodInfo mi = FetchMethodDispose (enumerator_type);
5748 enumerator.AddressOf (ec, AddressOp.Load);
5749 ig.Emit (OpCodes.Call, mi);
5751 enumerator.Emit (ec);
5752 ig.Emit (OpCodes.Box, enumerator_type);
5753 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5756 Label call_dispose = ig.DefineLabel ();
5758 enumerator.Emit (ec);
5759 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5760 ig.Emit (OpCodes.Dup);
5761 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5763 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5764 // (Partition III, Section 3.35)
5765 ig.Emit (OpCodes.Endfinally);
5767 ig.MarkLabel (call_dispose);
5768 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5772 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5774 enumerator_type = storey.MutateType (enumerator_type);
5775 init.MutateHoistedGenericType (storey);
5776 loop.MutateHoistedGenericType (storey);
5780 protected class CollectionForeachStatement : Statement
5783 Expression variable, current, conv;
5784 Statement statement;
5787 public CollectionForeachStatement (Type type, Expression variable,
5788 Expression current, Statement statement,
5792 this.variable = variable;
5793 this.current = current;
5794 this.statement = statement;
5798 public override bool Resolve (EmitContext ec)
5800 current = current.Resolve (ec);
5801 if (current == null)
5804 conv = Convert.ExplicitConversion (ec, current, type, loc);
5808 assign = new SimpleAssign (variable, conv, loc);
5809 if (assign.Resolve (ec) == null)
5812 if (!statement.Resolve (ec))
5818 protected override void DoEmit (EmitContext ec)
5820 assign.EmitStatement (ec);
5821 statement.Emit (ec);
5824 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5826 assign.MutateHoistedGenericType (storey);
5827 statement.MutateHoistedGenericType (storey);
5831 protected override void CloneTo (CloneContext clonectx, Statement t)
5833 Foreach target = (Foreach) t;
5835 target.type = type.Clone (clonectx);
5836 target.variable = variable.Clone (clonectx);
5837 target.expr = expr.Clone (clonectx);
5838 target.statement = statement.Clone (clonectx);
5841 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5843 statement.MutateHoistedGenericType (storey);