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 abstract void CloneTo (CloneContext clonectx, Statement target);
96 public Statement Clone (CloneContext clonectx)
98 Statement s = (Statement) this.MemberwiseClone ();
99 CloneTo (clonectx, s);
103 public virtual Expression CreateExpressionTree (EmitContext ec)
105 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
109 public Statement PerformClone ()
111 CloneContext clonectx = new CloneContext ();
113 return Clone (clonectx);
116 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
120 // This class is used during the Statement.Clone operation
121 // to remap objects that have been cloned.
123 // Since blocks are cloned by Block.Clone, we need a way for
124 // expressions that must reference the block to be cloned
125 // pointing to the new cloned block.
127 public class CloneContext {
128 Hashtable block_map = new Hashtable ();
129 Hashtable variable_map;
131 public void AddBlockMap (Block from, Block to)
133 if (block_map.Contains (from))
135 block_map [from] = to;
138 public Block LookupBlock (Block from)
140 Block result = (Block) block_map [from];
143 result = (Block) from.Clone (this);
144 block_map [from] = result;
151 /// Remaps block to cloned copy if one exists.
153 public Block RemapBlockCopy (Block from)
155 Block mapped_to = (Block)block_map[from];
156 if (mapped_to == null)
162 public void AddVariableMap (LocalInfo from, LocalInfo to)
164 if (variable_map == null)
165 variable_map = new Hashtable ();
167 if (variable_map.Contains (from))
169 variable_map [from] = to;
172 public LocalInfo LookupVariable (LocalInfo from)
174 LocalInfo result = (LocalInfo) variable_map [from];
177 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
183 public sealed class EmptyStatement : Statement {
185 private EmptyStatement () {}
187 public static readonly EmptyStatement Value = new EmptyStatement ();
189 public override bool Resolve (EmitContext ec)
194 public override bool ResolveUnreachable (EmitContext ec, bool warn)
199 protected override void DoEmit (EmitContext ec)
203 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
207 protected override void CloneTo (CloneContext clonectx, Statement target)
213 public class If : Statement {
215 public Statement TrueStatement;
216 public Statement FalseStatement;
220 public If (Expression expr, Statement true_statement, Location l)
223 TrueStatement = true_statement;
227 public If (Expression expr,
228 Statement true_statement,
229 Statement false_statement,
233 TrueStatement = true_statement;
234 FalseStatement = false_statement;
238 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
240 expr.MutateHoistedGenericType (storey);
241 TrueStatement.MutateHoistedGenericType (storey);
242 if (FalseStatement != null)
243 FalseStatement.MutateHoistedGenericType (storey);
246 public override bool Resolve (EmitContext ec)
250 Report.Debug (1, "START IF BLOCK", loc);
252 expr = Expression.ResolveBoolean (ec, expr, loc);
258 Assign ass = expr as Assign;
259 if (ass != null && ass.Source is Constant) {
260 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
264 // Dead code elimination
266 if (expr is Constant){
267 bool take = !((Constant) expr).IsDefaultValue;
270 if (!TrueStatement.Resolve (ec))
273 if ((FalseStatement != null) &&
274 !FalseStatement.ResolveUnreachable (ec, true))
276 FalseStatement = null;
278 if (!TrueStatement.ResolveUnreachable (ec, true))
280 TrueStatement = null;
282 if ((FalseStatement != null) &&
283 !FalseStatement.Resolve (ec))
290 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
292 ok &= TrueStatement.Resolve (ec);
294 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
296 ec.CurrentBranching.CreateSibling ();
298 if (FalseStatement != null)
299 ok &= FalseStatement.Resolve (ec);
301 ec.EndFlowBranching ();
303 Report.Debug (1, "END IF BLOCK", loc);
308 protected override void DoEmit (EmitContext ec)
310 ILGenerator ig = ec.ig;
311 Label false_target = ig.DefineLabel ();
315 // If we're a boolean constant, Resolve() already
316 // eliminated dead code for us.
318 Constant c = expr as Constant;
320 c.EmitSideEffect (ec);
322 if (!c.IsDefaultValue)
323 TrueStatement.Emit (ec);
324 else if (FalseStatement != null)
325 FalseStatement.Emit (ec);
330 expr.EmitBranchable (ec, false_target, false);
332 TrueStatement.Emit (ec);
334 if (FalseStatement != null){
335 bool branch_emitted = false;
337 end = ig.DefineLabel ();
339 ig.Emit (OpCodes.Br, end);
340 branch_emitted = true;
343 ig.MarkLabel (false_target);
344 FalseStatement.Emit (ec);
349 ig.MarkLabel (false_target);
353 protected override void CloneTo (CloneContext clonectx, Statement t)
357 target.expr = expr.Clone (clonectx);
358 target.TrueStatement = TrueStatement.Clone (clonectx);
359 if (FalseStatement != null)
360 target.FalseStatement = FalseStatement.Clone (clonectx);
364 public class Do : Statement {
365 public Expression expr;
366 public Statement EmbeddedStatement;
368 public Do (Statement statement, Expression bool_expr, Location l)
371 EmbeddedStatement = statement;
375 public override bool Resolve (EmitContext ec)
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!EmbeddedStatement.Resolve (ec))
386 ec.EndFlowBranching ();
388 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
389 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
391 expr = Expression.ResolveBoolean (ec, expr, loc);
394 else if (expr is Constant){
395 bool infinite = !((Constant) expr).IsDefaultValue;
397 ec.CurrentBranching.CurrentUsageVector.Goto ();
400 ec.EndFlowBranching ();
405 protected override void DoEmit (EmitContext ec)
407 ILGenerator ig = ec.ig;
408 Label loop = ig.DefineLabel ();
409 Label old_begin = ec.LoopBegin;
410 Label old_end = ec.LoopEnd;
412 ec.LoopBegin = ig.DefineLabel ();
413 ec.LoopEnd = ig.DefineLabel ();
416 EmbeddedStatement.Emit (ec);
417 ig.MarkLabel (ec.LoopBegin);
420 // Dead code elimination
422 if (expr is Constant){
423 bool res = !((Constant) expr).IsDefaultValue;
425 expr.EmitSideEffect (ec);
427 ec.ig.Emit (OpCodes.Br, loop);
429 expr.EmitBranchable (ec, loop, true);
431 ig.MarkLabel (ec.LoopEnd);
433 ec.LoopBegin = old_begin;
434 ec.LoopEnd = old_end;
437 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
439 expr.MutateHoistedGenericType (storey);
440 EmbeddedStatement.MutateHoistedGenericType (storey);
443 protected override void CloneTo (CloneContext clonectx, Statement t)
447 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
448 target.expr = expr.Clone (clonectx);
452 public class While : Statement {
453 public Expression expr;
454 public Statement Statement;
455 bool infinite, empty;
457 public While (Expression bool_expr, Statement statement, Location l)
459 this.expr = bool_expr;
460 Statement = statement;
464 public override bool Resolve (EmitContext ec)
468 expr = Expression.ResolveBoolean (ec, expr, loc);
473 // Inform whether we are infinite or not
475 if (expr is Constant){
476 bool value = !((Constant) expr).IsDefaultValue;
479 if (!Statement.ResolveUnreachable (ec, true))
487 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
489 ec.CurrentBranching.CreateSibling ();
491 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
492 if (!Statement.Resolve (ec))
494 ec.EndFlowBranching ();
496 // There's no direct control flow from the end of the embedded statement to the end of the loop
497 ec.CurrentBranching.CurrentUsageVector.Goto ();
499 ec.EndFlowBranching ();
504 protected override void DoEmit (EmitContext ec)
507 expr.EmitSideEffect (ec);
511 ILGenerator ig = ec.ig;
512 Label old_begin = ec.LoopBegin;
513 Label old_end = ec.LoopEnd;
515 ec.LoopBegin = ig.DefineLabel ();
516 ec.LoopEnd = ig.DefineLabel ();
519 // Inform whether we are infinite or not
521 if (expr is Constant){
522 // expr is 'true', since the 'empty' case above handles the 'false' case
523 ig.MarkLabel (ec.LoopBegin);
524 expr.EmitSideEffect (ec);
526 ig.Emit (OpCodes.Br, ec.LoopBegin);
529 // Inform that we are infinite (ie, `we return'), only
530 // if we do not `break' inside the code.
532 ig.MarkLabel (ec.LoopEnd);
534 Label while_loop = ig.DefineLabel ();
536 ig.Emit (OpCodes.Br, ec.LoopBegin);
537 ig.MarkLabel (while_loop);
541 ig.MarkLabel (ec.LoopBegin);
544 expr.EmitBranchable (ec, while_loop, true);
546 ig.MarkLabel (ec.LoopEnd);
549 ec.LoopBegin = old_begin;
550 ec.LoopEnd = old_end;
553 public override void Emit (EmitContext ec)
558 protected override void CloneTo (CloneContext clonectx, Statement t)
560 While target = (While) t;
562 target.expr = expr.Clone (clonectx);
563 target.Statement = Statement.Clone (clonectx);
566 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
568 expr.MutateHoistedGenericType (storey);
569 Statement.MutateHoistedGenericType (storey);
573 public class For : Statement {
575 Statement InitStatement;
577 public Statement Statement;
578 bool infinite, empty;
580 public For (Statement init_statement,
586 InitStatement = init_statement;
588 Increment = increment;
589 Statement = statement;
593 public override bool Resolve (EmitContext ec)
597 if (InitStatement != null){
598 if (!InitStatement.Resolve (ec))
603 Test = Expression.ResolveBoolean (ec, Test, loc);
606 else if (Test is Constant){
607 bool value = !((Constant) Test).IsDefaultValue;
610 if (!Statement.ResolveUnreachable (ec, true))
612 if ((Increment != null) &&
613 !Increment.ResolveUnreachable (ec, false))
623 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
625 ec.CurrentBranching.CreateSibling ();
627 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
629 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
630 if (!Statement.Resolve (ec))
632 ec.EndFlowBranching ();
634 if (Increment != null){
635 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
636 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
639 if (!Increment.Resolve (ec))
644 // There's no direct control flow from the end of the embedded statement to the end of the loop
645 ec.CurrentBranching.CurrentUsageVector.Goto ();
647 ec.EndFlowBranching ();
652 protected override void DoEmit (EmitContext ec)
654 if (InitStatement != null && InitStatement != EmptyStatement.Value)
655 InitStatement.Emit (ec);
658 Test.EmitSideEffect (ec);
662 ILGenerator ig = ec.ig;
663 Label old_begin = ec.LoopBegin;
664 Label old_end = ec.LoopEnd;
665 Label loop = ig.DefineLabel ();
666 Label test = ig.DefineLabel ();
668 ec.LoopBegin = ig.DefineLabel ();
669 ec.LoopEnd = ig.DefineLabel ();
671 ig.Emit (OpCodes.Br, test);
675 ig.MarkLabel (ec.LoopBegin);
676 if (Increment != EmptyStatement.Value)
681 // If test is null, there is no test, and we are just
686 // The Resolve code already catches the case for
687 // Test == Constant (false) so we know that
690 if (Test is Constant) {
691 Test.EmitSideEffect (ec);
692 ig.Emit (OpCodes.Br, loop);
694 Test.EmitBranchable (ec, loop, true);
698 ig.Emit (OpCodes.Br, loop);
699 ig.MarkLabel (ec.LoopEnd);
701 ec.LoopBegin = old_begin;
702 ec.LoopEnd = old_end;
705 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
707 if (InitStatement != null)
708 InitStatement.MutateHoistedGenericType (storey);
710 Test.MutateHoistedGenericType (storey);
711 if (Increment != null)
712 Increment.MutateHoistedGenericType (storey);
714 Statement.MutateHoistedGenericType (storey);
717 protected override void CloneTo (CloneContext clonectx, Statement t)
719 For target = (For) t;
721 if (InitStatement != null)
722 target.InitStatement = InitStatement.Clone (clonectx);
724 target.Test = Test.Clone (clonectx);
725 if (Increment != null)
726 target.Increment = Increment.Clone (clonectx);
727 target.Statement = Statement.Clone (clonectx);
731 public class StatementExpression : Statement {
732 ExpressionStatement expr;
734 public StatementExpression (ExpressionStatement expr)
740 public override bool Resolve (EmitContext ec)
743 expr = expr.ResolveStatement (ec);
747 protected override void DoEmit (EmitContext ec)
749 expr.EmitStatement (ec);
752 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
754 expr.MutateHoistedGenericType (storey);
757 public override string ToString ()
759 return "StatementExpression (" + expr + ")";
762 protected override void CloneTo (CloneContext clonectx, Statement t)
764 StatementExpression target = (StatementExpression) t;
766 target.expr = (ExpressionStatement) expr.Clone (clonectx);
770 // A 'return' or a 'yield break'
771 public abstract class ExitStatement : Statement
773 protected bool unwind_protect;
774 protected abstract bool DoResolve (EmitContext ec);
776 public virtual void Error_FinallyClause ()
778 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
781 public sealed override bool Resolve (EmitContext ec)
786 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
788 ec.NeedReturnLabel ();
789 ec.CurrentBranching.CurrentUsageVector.Goto ();
795 /// Implements the return statement
797 public class Return : ExitStatement {
798 protected Expression Expr;
799 public Return (Expression expr, Location l)
805 protected override bool DoResolve (EmitContext ec)
808 if (ec.ReturnType == TypeManager.void_type)
811 Error (126, "An object of a type convertible to `{0}' is required " +
812 "for the return statement",
813 TypeManager.CSharpName (ec.ReturnType));
817 if (ec.CurrentBlock.Toplevel.IsIterator) {
818 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
819 "statement to return a value, or yield break to end the iteration");
822 AnonymousExpression am = ec.CurrentAnonymousMethod;
823 if (am == null && ec.ReturnType == TypeManager.void_type) {
824 MemberCore mc = ec.ResolveContext as MemberCore;
825 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
826 mc.GetSignatureForError ());
829 Expr = Expr.Resolve (ec);
833 if (Expr.Type != ec.ReturnType) {
834 if (ec.InferReturnType) {
836 // void cannot be used in contextual return
838 if (Expr.Type == TypeManager.void_type)
841 ec.ReturnType = Expr.Type;
843 Expr = Convert.ImplicitConversionRequired (
844 ec, Expr, ec.ReturnType, loc);
848 Report.Error (1662, loc,
849 "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",
850 am.ContainerType, am.GetSignatureForError ());
860 protected override void DoEmit (EmitContext ec)
866 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
870 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
872 ec.ig.Emit (OpCodes.Ret);
875 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
878 Expr.MutateHoistedGenericType (storey);
881 protected override void CloneTo (CloneContext clonectx, Statement t)
883 Return target = (Return) t;
884 // It's null for simple return;
886 target.Expr = Expr.Clone (clonectx);
890 public class Goto : Statement {
892 LabeledStatement label;
895 public override bool Resolve (EmitContext ec)
897 int errors = Report.Errors;
898 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
899 ec.CurrentBranching.CurrentUsageVector.Goto ();
900 return errors == Report.Errors;
903 public Goto (string label, Location l)
909 public string Target {
910 get { return target; }
913 public void SetResolvedTarget (LabeledStatement label)
916 label.AddReference ();
919 protected override void CloneTo (CloneContext clonectx, Statement target)
924 protected override void DoEmit (EmitContext ec)
927 throw new InternalErrorException ("goto emitted before target resolved");
928 Label l = label.LabelTarget (ec);
929 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
932 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
937 public class LabeledStatement : Statement {
944 FlowBranching.UsageVector vectors;
946 public LabeledStatement (string name, Location l)
952 public Label LabelTarget (EmitContext ec)
957 label = ec.ig.DefineLabel ();
967 public bool IsDefined {
968 get { return defined; }
971 public bool HasBeenReferenced {
972 get { return referenced; }
975 public FlowBranching.UsageVector JumpOrigins {
976 get { return vectors; }
979 public void AddUsageVector (FlowBranching.UsageVector vector)
981 vector = vector.Clone ();
982 vector.Next = vectors;
986 protected override void CloneTo (CloneContext clonectx, Statement target)
991 public override bool Resolve (EmitContext ec)
993 // this flow-branching will be terminated when the surrounding block ends
994 ec.StartFlowBranching (this);
998 protected override void DoEmit (EmitContext ec)
1000 if (ig != null && ig != ec.ig)
1001 throw new InternalErrorException ("cannot happen");
1003 ec.ig.MarkLabel (label);
1006 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1010 public void AddReference ()
1018 /// `goto default' statement
1020 public class GotoDefault : Statement {
1022 public GotoDefault (Location l)
1027 protected override void CloneTo (CloneContext clonectx, Statement target)
1032 public override bool Resolve (EmitContext ec)
1034 ec.CurrentBranching.CurrentUsageVector.Goto ();
1038 protected override void DoEmit (EmitContext ec)
1040 if (ec.Switch == null){
1041 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1045 if (!ec.Switch.GotDefault){
1046 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1049 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1052 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1058 /// `goto case' statement
1060 public class GotoCase : Statement {
1064 public GotoCase (Expression e, Location l)
1070 public override bool Resolve (EmitContext ec)
1072 if (ec.Switch == null){
1073 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1077 expr = expr.Resolve (ec);
1081 Constant c = expr as Constant;
1083 Error (150, "A constant value is expected");
1087 Type type = ec.Switch.SwitchType;
1088 if (!Convert.ImplicitStandardConversionExists (c, type))
1089 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1090 "convertible to type `{0}'", TypeManager.CSharpName (type));
1093 object val = c.GetValue ();
1094 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1095 val = TypeManager.ChangeType (val, type, out fail);
1098 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1099 c.GetSignatureForError (), TypeManager.CSharpName (type));
1104 val = SwitchLabel.NullStringCase;
1106 sl = (SwitchLabel) ec.Switch.Elements [val];
1109 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1110 (c.GetValue () == null ? "null" : val.ToString ()));
1114 ec.CurrentBranching.CurrentUsageVector.Goto ();
1118 protected override void DoEmit (EmitContext ec)
1120 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1123 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1125 expr.MutateHoistedGenericType (storey);
1128 protected override void CloneTo (CloneContext clonectx, Statement t)
1130 GotoCase target = (GotoCase) t;
1132 target.expr = expr.Clone (clonectx);
1136 public class Throw : Statement {
1139 public Throw (Expression expr, Location l)
1145 public override bool Resolve (EmitContext ec)
1148 ec.CurrentBranching.CurrentUsageVector.Goto ();
1149 return ec.CurrentBranching.CheckRethrow (loc);
1152 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1153 ec.CurrentBranching.CurrentUsageVector.Goto ();
1160 if ((t != TypeManager.exception_type) &&
1161 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1162 !(expr is NullLiteral)) {
1163 Error (155, "The type caught or thrown must be derived from System.Exception");
1169 protected override void DoEmit (EmitContext ec)
1172 ec.ig.Emit (OpCodes.Rethrow);
1176 ec.ig.Emit (OpCodes.Throw);
1180 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1183 expr.MutateHoistedGenericType (storey);
1186 protected override void CloneTo (CloneContext clonectx, Statement t)
1188 Throw target = (Throw) t;
1191 target.expr = expr.Clone (clonectx);
1195 public class Break : Statement {
1197 public Break (Location l)
1202 bool unwind_protect;
1204 public override bool Resolve (EmitContext ec)
1206 int errors = Report.Errors;
1207 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1208 ec.CurrentBranching.CurrentUsageVector.Goto ();
1209 return errors == Report.Errors;
1212 protected override void DoEmit (EmitContext ec)
1214 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1217 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1221 protected override void CloneTo (CloneContext clonectx, Statement t)
1227 public class Continue : Statement {
1229 public Continue (Location l)
1234 bool unwind_protect;
1236 public override bool Resolve (EmitContext ec)
1238 int errors = Report.Errors;
1239 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1240 ec.CurrentBranching.CurrentUsageVector.Goto ();
1241 return errors == Report.Errors;
1244 protected override void DoEmit (EmitContext ec)
1246 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1249 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1253 protected override void CloneTo (CloneContext clonectx, Statement t)
1259 public interface ILocalVariable
1261 void Emit (EmitContext ec);
1262 void EmitAssign (EmitContext ec);
1263 void EmitAddressOf (EmitContext ec);
1266 public interface IKnownVariable {
1267 Block Block { get; }
1268 Location Location { get; }
1272 // The information about a user-perceived local variable
1274 public class LocalInfo : IKnownVariable, ILocalVariable {
1275 public readonly Expression Type;
1277 public Type VariableType;
1278 public readonly string Name;
1279 public readonly Location Location;
1280 public readonly Block Block;
1282 public VariableInfo VariableInfo;
1283 public HoistedVariable HoistedVariableReference;
1292 CompilerGenerated = 64,
1296 public enum ReadOnlyContext: byte {
1303 ReadOnlyContext ro_context;
1304 LocalBuilder builder;
1306 public LocalInfo (Expression type, string name, Block block, Location l)
1314 public LocalInfo (DeclSpace ds, Block block, Location l)
1316 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1321 public void ResolveVariable (EmitContext ec)
1323 if (HoistedVariableReference != null)
1326 if (builder == null) {
1329 // This is needed to compile on both .NET 1.x and .NET 2.x
1330 // the later introduced `DeclareLocal (Type t, bool pinned)'
1332 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1334 builder = ec.ig.DeclareLocal (VariableType);
1338 public void Emit (EmitContext ec)
1340 ec.ig.Emit (OpCodes.Ldloc, builder);
1343 public void EmitAssign (EmitContext ec)
1345 ec.ig.Emit (OpCodes.Stloc, builder);
1348 public void EmitAddressOf (EmitContext ec)
1350 ec.ig.Emit (OpCodes.Ldloca, builder);
1353 public void EmitSymbolInfo (EmitContext ec)
1355 if (builder != null)
1356 ec.DefineLocalVariable (Name, builder);
1359 public bool IsThisAssigned (EmitContext ec)
1361 if (VariableInfo == null)
1362 throw new Exception ();
1364 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1367 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1370 public bool IsAssigned (EmitContext ec)
1372 if (VariableInfo == null)
1373 throw new Exception ();
1375 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1378 public bool Resolve (EmitContext ec)
1380 if (VariableType != null)
1383 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1387 VariableType = texpr.Type;
1389 if (TypeManager.IsGenericParameter (VariableType))
1392 if (VariableType.IsAbstract && VariableType.IsSealed) {
1393 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1397 if (VariableType.IsPointer && !ec.InUnsafe)
1398 Expression.UnsafeError (Location);
1403 public bool IsConstant {
1404 get { return (flags & Flags.IsConstant) != 0; }
1405 set { flags |= Flags.IsConstant; }
1408 public bool AddressTaken {
1409 get { return (flags & Flags.AddressTaken) != 0; }
1410 set { flags |= Flags.AddressTaken; }
1413 public bool CompilerGenerated {
1414 get { return (flags & Flags.CompilerGenerated) != 0; }
1415 set { flags |= Flags.CompilerGenerated; }
1418 public override string ToString ()
1420 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1421 Name, Type, VariableInfo, Location);
1425 get { return (flags & Flags.Used) != 0; }
1426 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1429 public bool ReadOnly {
1430 get { return (flags & Flags.ReadOnly) != 0; }
1433 public void SetReadOnlyContext (ReadOnlyContext context)
1435 flags |= Flags.ReadOnly;
1436 ro_context = context;
1439 public string GetReadOnlyContext ()
1442 throw new InternalErrorException ("Variable is not readonly");
1444 switch (ro_context) {
1445 case ReadOnlyContext.Fixed:
1446 return "fixed variable";
1447 case ReadOnlyContext.Foreach:
1448 return "foreach iteration variable";
1449 case ReadOnlyContext.Using:
1450 return "using variable";
1452 throw new NotImplementedException ();
1456 // Whether the variable is pinned, if Pinned the variable has been
1457 // allocated in a pinned slot with DeclareLocal.
1459 public bool Pinned {
1460 get { return (flags & Flags.Pinned) != 0; }
1461 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1464 public bool IsThis {
1465 get { return (flags & Flags.IsThis) != 0; }
1466 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1469 Block IKnownVariable.Block {
1470 get { return Block; }
1473 Location IKnownVariable.Location {
1474 get { return Location; }
1477 public LocalInfo Clone (CloneContext clonectx)
1480 // Variables in anonymous block are not resolved yet
1482 if (VariableType == null)
1483 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1486 // Variables in method block are resolved
1488 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1489 li.VariableType = VariableType;
1495 /// Block represents a C# block.
1499 /// This class is used in a number of places: either to represent
1500 /// explicit blocks that the programmer places or implicit blocks.
1502 /// Implicit blocks are used as labels or to introduce variable
1505 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1506 /// they contain extra information that is not necessary on normal blocks.
1508 public class Block : Statement {
1509 public Block Parent;
1510 public Location StartLocation;
1511 public Location EndLocation = Location.Null;
1513 public ExplicitBlock Explicit;
1514 public ToplevelBlock Toplevel; // TODO: Use Explicit
1517 public enum Flags : byte {
1520 VariablesInitialized = 4,
1525 HasStoreyAccess = 128
1527 protected Flags flags;
1529 public bool Unchecked {
1530 get { return (flags & Flags.Unchecked) != 0; }
1531 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1534 public bool Unsafe {
1535 get { return (flags & Flags.Unsafe) != 0; }
1536 set { flags |= Flags.Unsafe; }
1540 // The statements in this block
1542 protected ArrayList statements;
1545 // An array of Blocks. We keep track of children just
1546 // to generate the local variable declarations.
1548 // Statements and child statements are handled through the
1554 // Labels. (label, block) pairs.
1556 protected HybridDictionary labels;
1559 // Keeps track of (name, type) pairs
1561 IDictionary variables;
1564 // Keeps track of constants
1565 HybridDictionary constants;
1568 // Temporary variables.
1570 ArrayList temporary_variables;
1573 // If this is a switch section, the enclosing switch block.
1577 ArrayList scope_initializers;
1579 ArrayList anonymous_children;
1581 protected static int id;
1585 int assignable_slots;
1586 bool unreachable_shown;
1589 public Block (Block parent)
1590 : this (parent, (Flags) 0, Location.Null, Location.Null)
1593 public Block (Block parent, Flags flags)
1594 : this (parent, flags, Location.Null, Location.Null)
1597 public Block (Block parent, Location start, Location end)
1598 : this (parent, (Flags) 0, start, end)
1601 public Block (Block parent, Flags flags, Location start, Location end)
1603 if (parent != null) {
1604 parent.AddChild (this);
1606 // the appropriate constructors will fixup these fields
1607 Toplevel = parent.Toplevel;
1608 Explicit = parent.Explicit;
1611 this.Parent = parent;
1613 this.StartLocation = start;
1614 this.EndLocation = end;
1617 statements = new ArrayList (4);
1620 public Block CreateSwitchBlock (Location start)
1622 // FIXME: should this be implicit?
1623 Block new_block = new ExplicitBlock (this, start, start);
1624 new_block.switch_block = this;
1629 get { return this_id; }
1632 public IDictionary Variables {
1634 if (variables == null)
1635 variables = new ListDictionary ();
1640 void AddChild (Block b)
1642 if (children == null)
1643 children = new ArrayList (1);
1648 public void SetEndLocation (Location loc)
1653 protected static void Error_158 (string name, Location loc)
1655 Report.Error (158, loc, "The label `{0}' shadows another label " +
1656 "by the same name in a contained scope", name);
1660 /// Adds a label to the current block.
1664 /// false if the name already exists in this block. true
1668 public bool AddLabel (LabeledStatement target)
1670 if (switch_block != null)
1671 return switch_block.AddLabel (target);
1673 string name = target.Name;
1676 while (cur != null) {
1677 LabeledStatement s = cur.DoLookupLabel (name);
1679 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1680 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1684 if (this == Explicit)
1690 while (cur != null) {
1691 if (cur.DoLookupLabel (name) != null) {
1692 Error_158 (name, target.loc);
1696 if (children != null) {
1697 foreach (Block b in children) {
1698 LabeledStatement s = b.DoLookupLabel (name);
1702 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1703 Error_158 (name, target.loc);
1711 Toplevel.CheckError158 (name, target.loc);
1714 labels = new HybridDictionary();
1716 labels.Add (name, target);
1720 public LabeledStatement LookupLabel (string name)
1722 LabeledStatement s = DoLookupLabel (name);
1726 if (children == null)
1729 foreach (Block child in children) {
1730 if (Explicit != child.Explicit)
1733 s = child.LookupLabel (name);
1741 LabeledStatement DoLookupLabel (string name)
1743 if (switch_block != null)
1744 return switch_block.LookupLabel (name);
1747 if (labels.Contains (name))
1748 return ((LabeledStatement) labels [name]);
1753 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1756 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1757 while (kvi == null) {
1758 b = b.Explicit.Parent;
1761 kvi = b.Explicit.GetKnownVariable (name);
1767 // Is kvi.Block nested inside 'b'
1768 if (b.Explicit != kvi.Block.Explicit) {
1770 // If a variable by the same name it defined in a nested block of this
1771 // block, we violate the invariant meaning in a block.
1774 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1775 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1780 // It's ok if the definition is in a nested subblock of b, but not
1781 // nested inside this block -- a definition in a sibling block
1782 // should not affect us.
1788 // Block 'b' and kvi.Block are the same textual block.
1789 // However, different variables are extant.
1791 // Check if the variable is in scope in both blocks. We use
1792 // an indirect check that depends on AddVariable doing its
1793 // part in maintaining the invariant-meaning-in-block property.
1795 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1798 if (this is ToplevelBlock) {
1799 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1800 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1805 // Even though we detected the error when the name is used, we
1806 // treat it as if the variable declaration was in error.
1808 Report.SymbolRelatedToPreviousError (loc, name);
1809 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1813 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1815 LocalInfo vi = GetLocalInfo (name);
1817 Report.SymbolRelatedToPreviousError (vi.Location, name);
1818 if (Explicit == vi.Block.Explicit) {
1819 Error_AlreadyDeclared (l, name, null);
1821 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1822 "parent or current" : "parent");
1827 if (block != null) {
1828 Expression e = block.GetParameterReference (name, Location.Null);
1830 ParameterReference pr = e as ParameterReference;
1831 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1832 Error_AlreadyDeclared (loc, name);
1834 Error_AlreadyDeclared (loc, name, "parent or current");
1842 public LocalInfo AddVariable (Expression type, string name, Location l)
1844 if (!CheckParentConflictName (Toplevel, name, l))
1847 if (Toplevel.GenericMethod != null) {
1848 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1849 if (tp.Name == name) {
1850 Report.SymbolRelatedToPreviousError (tp);
1851 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1857 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1859 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1860 Error_AlreadyDeclared (l, name, "child");
1864 LocalInfo vi = new LocalInfo (type, name, this, l);
1867 if ((flags & Flags.VariablesInitialized) != 0)
1868 throw new InternalErrorException ("block has already been resolved");
1873 protected virtual void AddVariable (LocalInfo li)
1875 Variables.Add (li.Name, li);
1876 Explicit.AddKnownVariable (li.Name, li);
1879 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1881 if (reason == null) {
1882 Error_AlreadyDeclared (loc, var);
1886 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1887 "in this scope because it would give a different meaning " +
1888 "to `{0}', which is already used in a `{1}' scope " +
1889 "to denote something else", var, reason);
1892 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1894 Report.Error (128, loc,
1895 "A local variable named `{0}' is already defined in this scope", name);
1898 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1900 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1904 public bool AddConstant (Expression type, string name, Expression value, Location l)
1906 if (AddVariable (type, name, l) == null)
1909 if (constants == null)
1910 constants = new HybridDictionary();
1912 constants.Add (name, value);
1914 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1919 static int next_temp_id = 0;
1921 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1923 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1925 if (temporary_variables == null)
1926 temporary_variables = new ArrayList ();
1928 int id = ++next_temp_id;
1929 string name = "$s_" + id.ToString ();
1931 LocalInfo li = new LocalInfo (te, name, this, loc);
1932 li.CompilerGenerated = true;
1933 temporary_variables.Add (li);
1937 public LocalInfo GetLocalInfo (string name)
1940 for (Block b = this; b != null; b = b.Parent) {
1941 if (b.variables != null) {
1942 ret = (LocalInfo) b.variables [name];
1951 public Expression GetVariableType (string name)
1953 LocalInfo vi = GetLocalInfo (name);
1954 return vi == null ? null : vi.Type;
1957 public Expression GetConstantExpression (string name)
1959 for (Block b = this; b != null; b = b.Parent) {
1960 if (b.constants != null) {
1961 Expression ret = b.constants [name] as Expression;
1970 // It should be used by expressions which require to
1971 // register a statement during resolve process.
1973 public void AddScopeStatement (Statement s)
1975 if (scope_initializers == null)
1976 scope_initializers = new ArrayList ();
1978 scope_initializers.Add (s);
1981 public void AddStatement (Statement s)
1984 flags |= Flags.BlockUsed;
1988 get { return (flags & Flags.BlockUsed) != 0; }
1993 flags |= Flags.BlockUsed;
1996 public bool HasRet {
1997 get { return (flags & Flags.HasRet) != 0; }
2000 public bool IsDestructor {
2001 get { return (flags & Flags.IsDestructor) != 0; }
2004 public void SetDestructor ()
2006 flags |= Flags.IsDestructor;
2009 public int AssignableSlots {
2012 // if ((flags & Flags.VariablesInitialized) == 0)
2013 // throw new Exception ("Variables have not been initialized yet");
2014 return assignable_slots;
2018 public ArrayList AnonymousChildren {
2019 get { return anonymous_children; }
2022 public void AddAnonymousChild (ToplevelBlock b)
2024 if (anonymous_children == null)
2025 anonymous_children = new ArrayList ();
2027 anonymous_children.Add (b);
2030 void DoResolveConstants (EmitContext ec)
2032 if (constants == null)
2035 if (variables == null)
2036 throw new InternalErrorException ("cannot happen");
2038 foreach (DictionaryEntry de in variables) {
2039 string name = (string) de.Key;
2040 LocalInfo vi = (LocalInfo) de.Value;
2041 Type variable_type = vi.VariableType;
2043 if (variable_type == null) {
2044 if (vi.Type is VarExpr)
2045 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2050 Expression cv = (Expression) constants [name];
2054 // Don't let 'const int Foo = Foo;' succeed.
2055 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2056 // which in turn causes the 'must be constant' error to be triggered.
2057 constants.Remove (name);
2059 if (!Const.IsConstantTypeValid (variable_type)) {
2060 Const.Error_InvalidConstantType (variable_type, loc);
2064 ec.CurrentBlock = this;
2066 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2067 e = cv.Resolve (ec);
2072 Constant ce = e as Constant;
2074 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2078 e = ce.ConvertImplicitly (variable_type);
2080 if (TypeManager.IsReferenceType (variable_type))
2081 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2083 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2087 constants.Add (name, e);
2088 vi.IsConstant = true;
2092 protected void ResolveMeta (EmitContext ec, int offset)
2094 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2096 // If some parent block was unsafe, we remain unsafe even if this block
2097 // isn't explicitly marked as such.
2098 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2099 flags |= Flags.VariablesInitialized;
2101 if (variables != null) {
2102 foreach (LocalInfo li in variables.Values) {
2103 if (!li.Resolve (ec))
2105 li.VariableInfo = new VariableInfo (li, offset);
2106 offset += li.VariableInfo.Length;
2109 assignable_slots = offset;
2111 DoResolveConstants (ec);
2113 if (children == null)
2115 foreach (Block b in children)
2116 b.ResolveMeta (ec, offset);
2121 // Emits the local variable declarations for a block
2123 public virtual void EmitMeta (EmitContext ec)
2125 if (variables != null){
2126 foreach (LocalInfo vi in variables.Values)
2127 vi.ResolveVariable (ec);
2130 if (temporary_variables != null) {
2131 for (int i = 0; i < temporary_variables.Count; i++)
2132 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2135 if (children != null) {
2136 for (int i = 0; i < children.Count; i++)
2137 ((Block)children[i]).EmitMeta(ec);
2141 void UsageWarning ()
2143 if (variables == null || Report.WarningLevel < 3)
2146 foreach (DictionaryEntry de in variables) {
2147 LocalInfo vi = (LocalInfo) de.Value;
2150 string name = (string) de.Key;
2152 // vi.VariableInfo can be null for 'catch' variables
2153 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2154 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2156 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2161 static void CheckPossibleMistakenEmptyStatement (Statement s)
2165 // Some statements are wrapped by a Block. Since
2166 // others' internal could be changed, here I treat
2167 // them as possibly wrapped by Block equally.
2168 Block b = s as Block;
2169 if (b != null && b.statements.Count == 1)
2170 s = (Statement) b.statements [0];
2173 body = ((Lock) s).Statement;
2175 body = ((For) s).Statement;
2176 else if (s is Foreach)
2177 body = ((Foreach) s).Statement;
2178 else if (s is While)
2179 body = ((While) s).Statement;
2180 else if (s is Fixed)
2181 body = ((Fixed) s).Statement;
2182 else if (s is Using)
2183 body = ((Using) s).EmbeddedStatement;
2184 else if (s is UsingTemporary)
2185 body = ((UsingTemporary) s).Statement;
2189 if (body == null || body is EmptyStatement)
2190 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2193 public override bool Resolve (EmitContext ec)
2195 Block prev_block = ec.CurrentBlock;
2198 int errors = Report.Errors;
2200 ec.CurrentBlock = this;
2201 ec.StartFlowBranching (this);
2203 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2206 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2207 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2208 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2209 // responsible for handling the situation.
2211 int statement_count = statements.Count;
2212 for (int ix = 0; ix < statement_count; ix++){
2213 Statement s = (Statement) statements [ix];
2214 // Check possible empty statement (CS0642)
2215 if (Report.WarningLevel >= 3 &&
2216 ix + 1 < statement_count &&
2217 statements [ix + 1] is ExplicitBlock)
2218 CheckPossibleMistakenEmptyStatement (s);
2221 // Warn if we detect unreachable code.
2224 if (s is EmptyStatement)
2227 if (!unreachable_shown && !(s is LabeledStatement)) {
2228 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2229 unreachable_shown = true;
2232 Block c_block = s as Block;
2233 if (c_block != null)
2234 c_block.unreachable = c_block.unreachable_shown = true;
2238 // Note that we're not using ResolveUnreachable() for unreachable
2239 // statements here. ResolveUnreachable() creates a temporary
2240 // flow branching and kills it afterwards. This leads to problems
2241 // if you have two unreachable statements where the first one
2242 // assigns a variable and the second one tries to access it.
2245 if (!s.Resolve (ec)) {
2247 if (ec.IsInProbingMode)
2250 statements [ix] = EmptyStatement.Value;
2254 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2255 statements [ix] = EmptyStatement.Value;
2257 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2258 if (unreachable && s is LabeledStatement)
2259 throw new InternalErrorException ("should not happen");
2262 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2263 ec.CurrentBranching, statement_count);
2265 while (ec.CurrentBranching is FlowBranchingLabeled)
2266 ec.EndFlowBranching ();
2268 bool flow_unreachable = ec.EndFlowBranching ();
2270 ec.CurrentBlock = prev_block;
2272 if (flow_unreachable)
2273 flags |= Flags.HasRet;
2275 // If we're a non-static `struct' constructor which doesn't have an
2276 // initializer, then we must initialize all of the struct's fields.
2277 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2280 if ((labels != null) && (Report.WarningLevel >= 2)) {
2281 foreach (LabeledStatement label in labels.Values)
2282 if (!label.HasBeenReferenced)
2283 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2286 if (ok && errors == Report.Errors)
2292 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2294 unreachable_shown = true;
2298 Report.Warning (162, 2, loc, "Unreachable code detected");
2300 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2301 bool ok = Resolve (ec);
2302 ec.KillFlowBranching ();
2307 protected override void DoEmit (EmitContext ec)
2309 for (int ix = 0; ix < statements.Count; ix++){
2310 Statement s = (Statement) statements [ix];
2315 public override void Emit (EmitContext ec)
2317 Block prev_block = ec.CurrentBlock;
2318 ec.CurrentBlock = this;
2320 if (scope_initializers != null) {
2321 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2323 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2324 foreach (Statement s in scope_initializers)
2328 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2331 ec.Mark (StartLocation);
2334 if (SymbolWriter.HasSymbolWriter)
2335 EmitSymbolInfo (ec);
2337 ec.CurrentBlock = prev_block;
2340 protected virtual void EmitSymbolInfo (EmitContext ec)
2342 if (variables != null) {
2343 foreach (LocalInfo vi in variables.Values) {
2344 vi.EmitSymbolInfo (ec);
2349 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2351 MutateVariables (storey);
2353 if (scope_initializers != null) {
2354 foreach (Statement s in scope_initializers)
2355 s.MutateHoistedGenericType (storey);
2358 foreach (Statement s in statements)
2359 s.MutateHoistedGenericType (storey);
2362 void MutateVariables (AnonymousMethodStorey storey)
2364 if (variables != null) {
2365 foreach (LocalInfo vi in variables.Values) {
2366 vi.VariableType = storey.MutateType (vi.VariableType);
2370 if (temporary_variables != null) {
2371 foreach (LocalInfo vi in temporary_variables)
2372 vi.VariableType = storey.MutateType (vi.VariableType);
2376 public override string ToString ()
2378 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2381 protected override void CloneTo (CloneContext clonectx, Statement t)
2383 Block target = (Block) t;
2385 clonectx.AddBlockMap (this, target);
2387 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2388 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2390 target.Parent = clonectx.RemapBlockCopy (Parent);
2392 if (variables != null){
2393 target.variables = new Hashtable ();
2395 foreach (DictionaryEntry de in variables){
2396 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2397 target.variables [de.Key] = newlocal;
2398 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2402 target.statements = new ArrayList (statements.Count);
2403 foreach (Statement s in statements)
2404 target.statements.Add (s.Clone (clonectx));
2406 if (target.children != null){
2407 target.children = new ArrayList (children.Count);
2408 foreach (Block b in children){
2409 target.children.Add (clonectx.LookupBlock (b));
2414 // TODO: labels, switch_block, constants (?), anonymous_children
2419 public class ExplicitBlock : Block {
2420 HybridDictionary known_variables;
2421 protected AnonymousMethodStorey am_storey;
2423 public ExplicitBlock (Block parent, Location start, Location end)
2424 : this (parent, (Flags) 0, start, end)
2428 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2429 : base (parent, flags, start, end)
2431 this.Explicit = this;
2435 // Marks a variable with name @name as being used in this or a child block.
2436 // If a variable name has been used in a child block, it's illegal to
2437 // declare a variable with the same name in the current block.
2439 internal void AddKnownVariable (string name, IKnownVariable info)
2441 if (known_variables == null)
2442 known_variables = new HybridDictionary();
2444 known_variables [name] = info;
2447 Parent.Explicit.AddKnownVariable (name, info);
2450 public AnonymousMethodStorey AnonymousMethodStorey {
2451 get { return am_storey; }
2455 // Creates anonymous method storey in current block
2457 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2460 // When referencing a variable in iterator storey from children anonymous method
2462 if (Toplevel.am_storey is IteratorStorey) {
2463 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2464 return Toplevel.am_storey;
2468 // An iterator has only 1 storey block
2470 if (ec.CurrentIterator != null)
2471 return ec.CurrentIterator.Storey;
2473 if (am_storey == null) {
2474 MemberBase mc = ec.ResolveContext as MemberBase;
2475 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2478 // Create anonymous method storey for this block
2480 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2484 // Creates a link between this block and the anonymous method
2486 // An anonymous method can reference variables from any outer block, but they are
2487 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2488 // need to create another link between those variable storeys
2490 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2494 public override void Emit (EmitContext ec)
2496 if (am_storey != null)
2497 am_storey.EmitStoreyInstantiation (ec);
2499 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2500 if (emit_debug_info)
2505 if (emit_debug_info)
2509 public override void EmitMeta (EmitContext ec)
2512 // Creates anonymous method storey
2514 if (am_storey != null) {
2515 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2516 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2519 am_storey.DefineType ();
2520 am_storey.ResolveType ();
2521 am_storey.Define ();
2522 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2528 internal IKnownVariable GetKnownVariable (string name)
2530 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2533 public void PropagateStoreyReference (AnonymousMethodStorey s)
2535 if (Parent != null && am_storey != s) {
2536 if (am_storey != null)
2537 am_storey.AddParentStoreyReference (s);
2539 Parent.Explicit.PropagateStoreyReference (s);
2543 public override bool Resolve (EmitContext ec)
2545 bool ok = base.Resolve (ec);
2548 // Discard an anonymous method storey when this block has no hoisted variables
2550 if (am_storey != null) {
2551 if (am_storey.HasHoistedVariables) {
2552 AddScopeStatement (new AnonymousMethodStorey.ThisInitializer (am_storey));
2562 protected override void CloneTo (CloneContext clonectx, Statement t)
2564 ExplicitBlock target = (ExplicitBlock) t;
2565 target.known_variables = null;
2566 base.CloneTo (clonectx, t);
2570 public class ToplevelParameterInfo : IKnownVariable {
2571 public readonly ToplevelBlock Block;
2572 public readonly int Index;
2573 public VariableInfo VariableInfo;
2575 Block IKnownVariable.Block {
2576 get { return Block; }
2578 public Parameter Parameter {
2579 get { return Block.Parameters [Index]; }
2582 public Type ParameterType {
2583 get { return Block.Parameters.Types [Index]; }
2586 public Location Location {
2587 get { return Parameter.Location; }
2590 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2598 // A toplevel block contains extra information, the split is done
2599 // only to separate information that would otherwise bloat the more
2600 // lightweight Block.
2602 // In particular, this was introduced when the support for Anonymous
2603 // Methods was implemented.
2605 public class ToplevelBlock : ExplicitBlock {
2606 GenericMethod generic;
2607 FlowBranchingToplevel top_level_branching;
2608 protected Parameters parameters;
2609 ToplevelParameterInfo[] parameter_info;
2610 LocalInfo this_variable;
2612 public HoistedVariable HoistedThisVariable;
2615 // The parameters for the block.
2617 public Parameters Parameters {
2618 get { return parameters; }
2621 public GenericMethod GenericMethod {
2622 get { return generic; }
2625 public bool HasStoreyAccess {
2626 set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
2627 get { return (flags & Flags.HasStoreyAccess) != 0; }
2630 public ToplevelBlock Container {
2631 get { return Parent == null ? null : Parent.Toplevel; }
2634 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2635 this (parent, (Flags) 0, parameters, start)
2639 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2640 this (parent, parameters, start)
2642 this.generic = generic;
2645 public ToplevelBlock (Parameters parameters, Location start) :
2646 this (null, (Flags) 0, parameters, start)
2650 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2651 this (null, flags, parameters, start)
2655 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2656 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2657 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2658 base (null, flags, start, Location.Null)
2660 this.Toplevel = this;
2662 this.parameters = parameters;
2663 this.Parent = parent;
2665 parent.AddAnonymousChild (this);
2667 if (!this.parameters.IsEmpty)
2668 ProcessParameters ();
2671 public ToplevelBlock (Location loc)
2672 : this (null, (Flags) 0, Parameters.EmptyReadOnlyParameters, loc)
2676 protected override void CloneTo (CloneContext clonectx, Statement t)
2678 ToplevelBlock target = (ToplevelBlock) t;
2679 base.CloneTo (clonectx, t);
2681 if (parameters.Count != 0)
2682 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2683 for (int i = 0; i < parameters.Count; ++i)
2684 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2687 public bool CheckError158 (string name, Location loc)
2689 if (AnonymousChildren != null) {
2690 foreach (ToplevelBlock child in AnonymousChildren) {
2691 if (!child.CheckError158 (name, loc))
2696 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2697 if (!c.DoCheckError158 (name, loc))
2704 void ProcessParameters ()
2706 int n = parameters.Count;
2707 parameter_info = new ToplevelParameterInfo [n];
2708 ToplevelBlock top_parent = Parent == null ? null : Parent.Toplevel;
2709 for (int i = 0; i < n; ++i) {
2710 parameter_info [i] = new ToplevelParameterInfo (this, i);
2712 Parameter p = parameters [i];
2716 string name = p.Name;
2717 if (CheckParentConflictName (top_parent, name, loc))
2718 AddKnownVariable (name, parameter_info [i]);
2721 // mark this block as "used" so that we create local declarations in a sub-block
2722 // FIXME: This appears to uncover a lot of bugs
2726 bool DoCheckError158 (string name, Location loc)
2728 LabeledStatement s = LookupLabel (name);
2730 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2731 Error_158 (name, loc);
2738 public override Expression CreateExpressionTree (EmitContext ec)
2740 if (statements.Count == 1)
2741 return ((Statement) statements [0]).CreateExpressionTree (ec);
2743 return base.CreateExpressionTree (ec);
2747 // Reformats this block to be top-level iterator block
2749 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2751 // Create block with original statements
2752 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2755 // TODO: Change to iter_block.statements = statements;
2756 foreach (Statement stmt in source.statements)
2757 iter_block.AddStatement (stmt);
2758 labels = source.labels;
2760 AddStatement (new IteratorStatement (iterator, iter_block));
2762 source.statements = new ArrayList (1);
2763 source.AddStatement (new Return (iterator, iterator.Location));
2764 source.IsIterator = false;
2766 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2767 source.am_storey = iterator_storey;
2768 return iterator_storey;
2771 public FlowBranchingToplevel TopLevelBranching {
2772 get { return top_level_branching; }
2776 // Returns a parameter reference expression for the given name,
2777 // or null if there is no such parameter
2779 public Expression GetParameterReference (string name, Location loc)
2781 for (ToplevelBlock t = this; t != null; t = t.Container) {
2782 Expression expr = t.GetParameterReferenceExpression (name, loc);
2790 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2792 int idx = parameters.GetParameterIndexByName (name);
2794 null : new ParameterReference (parameter_info [idx], loc);
2798 // Returns the "this" instance variable of this block.
2799 // See AddThisVariable() for more information.
2801 public LocalInfo ThisVariable {
2802 get { return this_variable; }
2806 // This is used by non-static `struct' constructors which do not have an
2807 // initializer - in this case, the constructor must initialize all of the
2808 // struct's fields. To do this, we add a "this" variable and use the flow
2809 // analysis code to ensure that it's been fully initialized before control
2810 // leaves the constructor.
2812 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2814 if (this_variable == null) {
2815 this_variable = new LocalInfo (ds, this, l);
2816 this_variable.Used = true;
2817 this_variable.IsThis = true;
2819 Variables.Add ("this", this_variable);
2822 return this_variable;
2825 public bool IsIterator {
2826 get { return (flags & Flags.IsIterator) != 0; }
2827 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2830 public bool IsThisAssigned (EmitContext ec)
2832 return this_variable == null || this_variable.IsThisAssigned (ec);
2835 public bool ResolveMeta (EmitContext ec, Parameters ip)
2837 int errors = Report.Errors;
2838 int orig_count = parameters.Count;
2840 if (top_level_branching != null)
2846 // Assert: orig_count != parameter.Count => orig_count == 0
2847 if (orig_count != 0 && orig_count != parameters.Count)
2848 throw new InternalErrorException ("parameter information mismatch");
2850 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2852 for (int i = 0; i < orig_count; ++i) {
2853 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2855 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2858 VariableInfo vi = new VariableInfo (ip, i, offset);
2859 parameter_info [i].VariableInfo = vi;
2860 offset += vi.Length;
2863 ResolveMeta (ec, offset);
2865 top_level_branching = ec.StartFlowBranching (this);
2867 return Report.Errors == errors;
2871 // Check whether all `out' parameters have been assigned.
2873 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2875 if (vector.IsUnreachable)
2878 int n = parameter_info == null ? 0 : parameter_info.Length;
2880 for (int i = 0; i < n; i++) {
2881 VariableInfo var = parameter_info [i].VariableInfo;
2886 if (vector.IsAssigned (var, false))
2889 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2894 public override void EmitMeta (EmitContext ec)
2896 parameters.ResolveVariable ();
2898 // Avoid declaring an IL variable for this_variable since it is not accessed
2899 // from the generated IL
2900 if (this_variable != null)
2901 Variables.Remove ("this");
2905 protected override void EmitSymbolInfo (EmitContext ec)
2907 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2908 if ((ae != null) && (ae.Storey != null))
2909 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2911 base.EmitSymbolInfo (ec);
2914 public override void Emit (EmitContext ec)
2917 ec.Mark (EndLocation);
2921 public class SwitchLabel {
2928 Label il_label_code;
2929 bool il_label_code_set;
2931 public static readonly object NullStringCase = new object ();
2934 // if expr == null, then it is the default case.
2936 public SwitchLabel (Expression expr, Location l)
2942 public Expression Label {
2948 public Location Location {
2952 public object Converted {
2958 public Label GetILLabel (EmitContext ec)
2961 il_label = ec.ig.DefineLabel ();
2962 il_label_set = true;
2967 public Label GetILLabelCode (EmitContext ec)
2969 if (!il_label_code_set){
2970 il_label_code = ec.ig.DefineLabel ();
2971 il_label_code_set = true;
2973 return il_label_code;
2977 // Resolves the expression, reduces it to a literal if possible
2978 // and then converts it to the requested type.
2980 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2982 Expression e = label.Resolve (ec);
2987 Constant c = e as Constant;
2989 Report.Error (150, loc, "A constant value is expected");
2993 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2994 converted = NullStringCase;
2998 if (allow_nullable && c.GetValue () == null) {
2999 converted = NullStringCase;
3003 c = c.ImplicitConversionRequired (ec, required_type, loc);
3007 converted = c.GetValue ();
3011 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3014 if (converted == null)
3016 else if (converted == NullStringCase)
3019 label = converted.ToString ();
3021 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3022 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3025 public SwitchLabel Clone (CloneContext clonectx)
3027 return new SwitchLabel (label.Clone (clonectx), loc);
3031 public class SwitchSection {
3032 // An array of SwitchLabels.
3033 public readonly ArrayList Labels;
3034 public readonly Block Block;
3036 public SwitchSection (ArrayList labels, Block block)
3042 public SwitchSection Clone (CloneContext clonectx)
3044 ArrayList cloned_labels = new ArrayList ();
3046 foreach (SwitchLabel sl in cloned_labels)
3047 cloned_labels.Add (sl.Clone (clonectx));
3049 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3053 public class Switch : Statement {
3054 public ArrayList Sections;
3055 public Expression Expr;
3058 /// Maps constants whose type type SwitchType to their SwitchLabels.
3060 public IDictionary Elements;
3063 /// The governing switch type
3065 public Type SwitchType;
3070 Label default_target;
3072 Expression new_expr;
3075 SwitchSection constant_section;
3076 SwitchSection default_section;
3078 ExpressionStatement string_dictionary;
3079 FieldExpr switch_cache_field;
3080 static int unique_counter;
3084 // Nullable Types support for GMCS.
3086 Nullable.Unwrap unwrap;
3088 protected bool HaveUnwrap {
3089 get { return unwrap != null; }
3092 protected bool HaveUnwrap {
3093 get { return false; }
3098 // The types allowed to be implicitly cast from
3099 // on the governing type
3101 static Type [] allowed_types;
3103 public Switch (Expression e, ArrayList sects, Location l)
3110 public bool GotDefault {
3112 return default_section != null;
3116 public Label DefaultTarget {
3118 return default_target;
3123 // Determines the governing type for a switch. The returned
3124 // expression might be the expression from the switch, or an
3125 // expression that includes any potential conversions to the
3126 // integral types or to string.
3128 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3132 if (t == TypeManager.byte_type ||
3133 t == TypeManager.sbyte_type ||
3134 t == TypeManager.ushort_type ||
3135 t == TypeManager.short_type ||
3136 t == TypeManager.uint32_type ||
3137 t == TypeManager.int32_type ||
3138 t == TypeManager.uint64_type ||
3139 t == TypeManager.int64_type ||
3140 t == TypeManager.char_type ||
3141 t == TypeManager.string_type ||
3142 t == TypeManager.bool_type ||
3143 TypeManager.IsEnumType (t))
3146 if (allowed_types == null){
3147 allowed_types = new Type [] {
3148 TypeManager.sbyte_type,
3149 TypeManager.byte_type,
3150 TypeManager.short_type,
3151 TypeManager.ushort_type,
3152 TypeManager.int32_type,
3153 TypeManager.uint32_type,
3154 TypeManager.int64_type,
3155 TypeManager.uint64_type,
3156 TypeManager.char_type,
3157 TypeManager.string_type
3162 // Try to find a *user* defined implicit conversion.
3164 // If there is no implicit conversion, or if there are multiple
3165 // conversions, we have to report an error
3167 Expression converted = null;
3168 foreach (Type tt in allowed_types){
3171 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3176 // Ignore over-worked ImplicitUserConversions that do
3177 // an implicit conversion in addition to the user conversion.
3179 if (!(e is UserCast))
3182 if (converted != null){
3183 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3193 // Performs the basic sanity checks on the switch statement
3194 // (looks for duplicate keys and non-constant expressions).
3196 // It also returns a hashtable with the keys that we will later
3197 // use to compute the switch tables
3199 bool CheckSwitch (EmitContext ec)
3202 Elements = Sections.Count > 10 ?
3203 (IDictionary)new Hashtable () :
3204 (IDictionary)new ListDictionary ();
3206 foreach (SwitchSection ss in Sections){
3207 foreach (SwitchLabel sl in ss.Labels){
3208 if (sl.Label == null){
3209 if (default_section != null){
3210 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3213 default_section = ss;
3217 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3222 object key = sl.Converted;
3223 if (key == SwitchLabel.NullStringCase)
3224 has_null_case = true;
3227 Elements.Add (key, sl);
3228 } catch (ArgumentException) {
3229 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3237 void EmitObjectInteger (ILGenerator ig, object k)
3240 IntConstant.EmitInt (ig, (int) k);
3241 else if (k is Constant) {
3242 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3245 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3248 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3250 IntConstant.EmitInt (ig, (int) (long) k);
3251 ig.Emit (OpCodes.Conv_I8);
3254 LongConstant.EmitLong (ig, (long) k);
3256 else if (k is ulong)
3258 ulong ul = (ulong) k;
3261 IntConstant.EmitInt (ig, unchecked ((int) ul));
3262 ig.Emit (OpCodes.Conv_U8);
3266 LongConstant.EmitLong (ig, unchecked ((long) ul));
3270 IntConstant.EmitInt (ig, (int) ((char) k));
3271 else if (k is sbyte)
3272 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3274 IntConstant.EmitInt (ig, (int) ((byte) k));
3275 else if (k is short)
3276 IntConstant.EmitInt (ig, (int) ((short) k));
3277 else if (k is ushort)
3278 IntConstant.EmitInt (ig, (int) ((ushort) k));
3280 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3282 throw new Exception ("Unhandled case");
3285 // structure used to hold blocks of keys while calculating table switch
3286 class KeyBlock : IComparable
3288 public KeyBlock (long _first)
3290 first = last = _first;
3294 public ArrayList element_keys = null;
3295 // how many items are in the bucket
3296 public int Size = 1;
3299 get { return (int) (last - first + 1); }
3301 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3303 return kb_last.last - kb_first.first + 1;
3305 public int CompareTo (object obj)
3307 KeyBlock kb = (KeyBlock) obj;
3308 int nLength = Length;
3309 int nLengthOther = kb.Length;
3310 if (nLengthOther == nLength)
3311 return (int) (kb.first - first);
3312 return nLength - nLengthOther;
3317 /// This method emits code for a lookup-based switch statement (non-string)
3318 /// Basically it groups the cases into blocks that are at least half full,
3319 /// and then spits out individual lookup opcodes for each block.
3320 /// It emits the longest blocks first, and short blocks are just
3321 /// handled with direct compares.
3323 /// <param name="ec"></param>
3324 /// <param name="val"></param>
3325 /// <returns></returns>
3326 void TableSwitchEmit (EmitContext ec, Expression val)
3328 int element_count = Elements.Count;
3329 object [] element_keys = new object [element_count];
3330 Elements.Keys.CopyTo (element_keys, 0);
3331 Array.Sort (element_keys);
3333 // initialize the block list with one element per key
3334 ArrayList key_blocks = new ArrayList (element_count);
3335 foreach (object key in element_keys)
3336 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3338 KeyBlock current_kb;
3339 // iteratively merge the blocks while they are at least half full
3340 // there's probably a really cool way to do this with a tree...
3341 while (key_blocks.Count > 1)
3343 ArrayList key_blocks_new = new ArrayList ();
3344 current_kb = (KeyBlock) key_blocks [0];
3345 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3347 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3348 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3351 current_kb.last = kb.last;
3352 current_kb.Size += kb.Size;
3356 // start a new block
3357 key_blocks_new.Add (current_kb);
3361 key_blocks_new.Add (current_kb);
3362 if (key_blocks.Count == key_blocks_new.Count)
3364 key_blocks = key_blocks_new;
3367 // initialize the key lists
3368 foreach (KeyBlock kb in key_blocks)
3369 kb.element_keys = new ArrayList ();
3371 // fill the key lists
3373 if (key_blocks.Count > 0) {
3374 current_kb = (KeyBlock) key_blocks [0];
3375 foreach (object key in element_keys)
3377 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3378 System.Convert.ToInt64 (key) > current_kb.last;
3380 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3381 current_kb.element_keys.Add (key);
3385 // sort the blocks so we can tackle the largest ones first
3388 // okay now we can start...
3389 ILGenerator ig = ec.ig;
3390 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3391 Label lbl_default = default_target;
3393 Type type_keys = null;
3394 if (element_keys.Length > 0)
3395 type_keys = element_keys [0].GetType (); // used for conversions
3399 if (TypeManager.IsEnumType (SwitchType))
3400 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3402 compare_type = SwitchType;
3404 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3406 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3407 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3410 foreach (object key in kb.element_keys) {
3411 SwitchLabel sl = (SwitchLabel) Elements [key];
3412 if (key is int && (int) key == 0) {
3413 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3416 EmitObjectInteger (ig, key);
3417 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3423 // TODO: if all the keys in the block are the same and there are
3424 // no gaps/defaults then just use a range-check.
3425 if (compare_type == TypeManager.int64_type ||
3426 compare_type == TypeManager.uint64_type)
3428 // TODO: optimize constant/I4 cases
3430 // check block range (could be > 2^31)
3432 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3433 ig.Emit (OpCodes.Blt, lbl_default);
3435 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3436 ig.Emit (OpCodes.Bgt, lbl_default);
3442 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3443 ig.Emit (OpCodes.Sub);
3445 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3451 int first = (int) kb.first;
3454 IntConstant.EmitInt (ig, first);
3455 ig.Emit (OpCodes.Sub);
3459 IntConstant.EmitInt (ig, -first);
3460 ig.Emit (OpCodes.Add);
3464 // first, build the list of labels for the switch
3466 int cJumps = kb.Length;
3467 Label [] switch_labels = new Label [cJumps];
3468 for (int iJump = 0; iJump < cJumps; iJump++)
3470 object key = kb.element_keys [iKey];
3471 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3473 SwitchLabel sl = (SwitchLabel) Elements [key];
3474 switch_labels [iJump] = sl.GetILLabel (ec);
3478 switch_labels [iJump] = lbl_default;
3480 // emit the switch opcode
3481 ig.Emit (OpCodes.Switch, switch_labels);
3484 // mark the default for this block
3486 ig.MarkLabel (lbl_default);
3489 // TODO: find the default case and emit it here,
3490 // to prevent having to do the following jump.
3491 // make sure to mark other labels in the default section
3493 // the last default just goes to the end
3494 if (element_keys.Length > 0)
3495 ig.Emit (OpCodes.Br, lbl_default);
3497 // now emit the code for the sections
3498 bool found_default = false;
3500 foreach (SwitchSection ss in Sections) {
3501 foreach (SwitchLabel sl in ss.Labels) {
3502 if (sl.Converted == SwitchLabel.NullStringCase) {
3503 ig.MarkLabel (null_target);
3504 } else if (sl.Label == null) {
3505 ig.MarkLabel (lbl_default);
3506 found_default = true;
3508 ig.MarkLabel (null_target);
3510 ig.MarkLabel (sl.GetILLabel (ec));
3511 ig.MarkLabel (sl.GetILLabelCode (ec));
3516 if (!found_default) {
3517 ig.MarkLabel (lbl_default);
3518 if (!has_null_case) {
3519 ig.MarkLabel (null_target);
3523 ig.MarkLabel (lbl_end);
3526 SwitchSection FindSection (SwitchLabel label)
3528 foreach (SwitchSection ss in Sections){
3529 foreach (SwitchLabel sl in ss.Labels){
3538 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3540 foreach (SwitchSection ss in Sections)
3541 ss.Block.MutateHoistedGenericType (storey);
3544 public static void Reset ()
3549 public override bool Resolve (EmitContext ec)
3551 Expr = Expr.Resolve (ec);
3555 new_expr = SwitchGoverningType (ec, Expr);
3558 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3559 unwrap = Nullable.Unwrap.Create (Expr, ec);
3563 new_expr = SwitchGoverningType (ec, unwrap);
3567 if (new_expr == null){
3568 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3573 SwitchType = new_expr.Type;
3575 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3576 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3580 if (!CheckSwitch (ec))
3584 Elements.Remove (SwitchLabel.NullStringCase);
3586 Switch old_switch = ec.Switch;
3588 ec.Switch.SwitchType = SwitchType;
3590 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3591 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3593 is_constant = new_expr is Constant;
3595 object key = ((Constant) new_expr).GetValue ();
3596 SwitchLabel label = (SwitchLabel) Elements [key];
3598 constant_section = FindSection (label);
3599 if (constant_section == null)
3600 constant_section = default_section;
3605 foreach (SwitchSection ss in Sections){
3607 ec.CurrentBranching.CreateSibling (
3608 null, FlowBranching.SiblingType.SwitchSection);
3612 if (is_constant && (ss != constant_section)) {
3613 // If we're a constant switch, we're only emitting
3614 // one single section - mark all the others as
3616 ec.CurrentBranching.CurrentUsageVector.Goto ();
3617 if (!ss.Block.ResolveUnreachable (ec, true)) {
3621 if (!ss.Block.Resolve (ec))
3626 if (default_section == null)
3627 ec.CurrentBranching.CreateSibling (
3628 null, FlowBranching.SiblingType.SwitchSection);
3630 ec.EndFlowBranching ();
3631 ec.Switch = old_switch;
3633 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3638 if (SwitchType == TypeManager.string_type && !is_constant) {
3639 // TODO: Optimize single case, and single+default case
3640 ResolveStringSwitchMap (ec);
3646 void ResolveStringSwitchMap (EmitContext ec)
3648 FullNamedExpression string_dictionary_type;
3650 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3651 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3653 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3655 new TypeExpression (TypeManager.string_type, loc),
3656 new TypeExpression (TypeManager.int32_type, loc)), loc);
3658 MemberAccess system_collections_generic = new MemberAccess (
3659 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3661 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3663 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3664 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3665 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3666 if (!field.Define ())
3668 ec.TypeContainer.PartialContainer.AddField (field);
3670 ArrayList init = new ArrayList ();
3673 string value = null;
3674 foreach (SwitchSection section in Sections) {
3675 foreach (SwitchLabel sl in section.Labels) {
3676 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3681 value = (string) sl.Converted;
3682 ArrayList init_args = new ArrayList (2);
3683 init_args.Add (new StringLiteral (value, sl.Location));
3684 init_args.Add (new IntConstant (counter, loc));
3685 init.Add (new CollectionElementInitializer (init_args, loc));
3691 Elements.Add (counter, section.Labels [0]);
3695 ArrayList args = new ArrayList (1);
3696 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3697 Expression initializer = new NewInitialize (string_dictionary_type, args,
3698 new CollectionOrObjectInitializers (init, loc), loc);
3700 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3701 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3704 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3706 ILGenerator ig = ec.ig;
3707 Label l_initialized = ig.DefineLabel ();
3710 // Skip initialization when value is null
3712 value.EmitBranchable (ec, null_target, false);
3715 // Check if string dictionary is initialized and initialize
3717 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3718 string_dictionary.EmitStatement (ec);
3719 ig.MarkLabel (l_initialized);
3721 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3724 ArrayList get_value_args = new ArrayList (2);
3725 get_value_args.Add (new Argument (value));
3726 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3727 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3728 if (get_item == null)
3732 // A value was not found, go to default case
3734 get_item.EmitBranchable (ec, default_target, false);
3736 ArrayList get_value_args = new ArrayList (1);
3737 get_value_args.Add (value);
3739 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3740 if (get_item == null)
3743 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3744 get_item_object.EmitAssign (ec, get_item, true, false);
3745 ec.ig.Emit (OpCodes.Brfalse, default_target);
3747 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3748 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3750 get_item_int.EmitStatement (ec);
3751 get_item_object.Release (ec);
3753 TableSwitchEmit (ec, string_switch_variable);
3754 string_switch_variable.Release (ec);
3757 protected override void DoEmit (EmitContext ec)
3759 ILGenerator ig = ec.ig;
3761 default_target = ig.DefineLabel ();
3762 null_target = ig.DefineLabel ();
3764 // Store variable for comparission purposes
3765 // TODO: Don't duplicate non-captured VariableReference
3766 LocalTemporary value;
3768 value = new LocalTemporary (SwitchType);
3770 unwrap.EmitCheck (ec);
3771 ig.Emit (OpCodes.Brfalse, null_target);
3775 } else if (!is_constant) {
3776 value = new LocalTemporary (SwitchType);
3783 // Setup the codegen context
3785 Label old_end = ec.LoopEnd;
3786 Switch old_switch = ec.Switch;
3788 ec.LoopEnd = ig.DefineLabel ();
3793 if (constant_section != null)
3794 constant_section.Block.Emit (ec);
3795 } else if (string_dictionary != null) {
3796 DoEmitStringSwitch (value, ec);
3798 TableSwitchEmit (ec, value);
3804 // Restore context state.
3805 ig.MarkLabel (ec.LoopEnd);
3808 // Restore the previous context
3810 ec.LoopEnd = old_end;
3811 ec.Switch = old_switch;
3814 protected override void CloneTo (CloneContext clonectx, Statement t)
3816 Switch target = (Switch) t;
3818 target.Expr = Expr.Clone (clonectx);
3819 target.Sections = new ArrayList ();
3820 foreach (SwitchSection ss in Sections){
3821 target.Sections.Add (ss.Clone (clonectx));
3826 // A place where execution can restart in an iterator
3827 public abstract class ResumableStatement : Statement
3830 protected Label resume_point;
3832 public Label PrepareForEmit (EmitContext ec)
3836 resume_point = ec.ig.DefineLabel ();
3838 return resume_point;
3841 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3845 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3850 // Base class for statements that are implemented in terms of try...finally
3851 public abstract class ExceptionStatement : ResumableStatement
3855 protected abstract void EmitPreTryBody (EmitContext ec);
3856 protected abstract void EmitTryBody (EmitContext ec);
3857 protected abstract void EmitFinallyBody (EmitContext ec);
3859 protected sealed override void DoEmit (EmitContext ec)
3861 ILGenerator ig = ec.ig;
3863 EmitPreTryBody (ec);
3865 if (resume_points != null) {
3866 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3867 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3870 ig.BeginExceptionBlock ();
3872 if (resume_points != null) {
3873 ig.MarkLabel (resume_point);
3875 // For normal control flow, we want to fall-through the Switch
3876 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3877 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3878 IntConstant.EmitInt (ig, first_resume_pc);
3879 ig.Emit (OpCodes.Sub);
3881 Label [] labels = new Label [resume_points.Count];
3882 for (int i = 0; i < resume_points.Count; ++i)
3883 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3884 ig.Emit (OpCodes.Switch, labels);
3889 ig.BeginFinallyBlock ();
3891 Label start_finally = ec.ig.DefineLabel ();
3892 if (resume_points != null) {
3893 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3894 ig.Emit (OpCodes.Brfalse_S, start_finally);
3895 ig.Emit (OpCodes.Endfinally);
3898 ig.MarkLabel (start_finally);
3899 EmitFinallyBody (ec);
3901 ig.EndExceptionBlock ();
3904 public void SomeCodeFollows ()
3906 code_follows = true;
3909 protected void ResolveReachability (EmitContext ec)
3911 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3912 // So, ensure there's some IL code after this statement.
3913 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3914 ec.NeedReturnLabel ();
3918 ArrayList resume_points;
3919 int first_resume_pc;
3920 public void AddResumePoint (ResumableStatement stmt, int pc)
3922 if (resume_points == null) {
3923 resume_points = new ArrayList ();
3924 first_resume_pc = pc;
3927 if (pc != first_resume_pc + resume_points.Count)
3928 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3930 resume_points.Add (stmt);
3933 Label dispose_try_block;
3934 bool prepared_for_dispose, emitted_dispose;
3935 public override Label PrepareForDispose (EmitContext ec, Label end)
3937 if (!prepared_for_dispose) {
3938 prepared_for_dispose = true;
3939 dispose_try_block = ec.ig.DefineLabel ();
3941 return dispose_try_block;
3944 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3946 if (emitted_dispose)
3949 emitted_dispose = true;
3951 ILGenerator ig = ec.ig;
3953 Label end_of_try = ig.DefineLabel ();
3955 // Ensure that the only way we can get into this code is through a dispatcher
3956 if (have_dispatcher)
3957 ig.Emit (OpCodes.Br, end);
3959 ig.BeginExceptionBlock ();
3961 ig.MarkLabel (dispose_try_block);
3963 Label [] labels = null;
3964 for (int i = 0; i < resume_points.Count; ++i) {
3965 ResumableStatement s = (ResumableStatement) resume_points [i];
3966 Label ret = s.PrepareForDispose (ec, end_of_try);
3967 if (ret.Equals (end_of_try) && labels == null)
3969 if (labels == null) {
3970 labels = new Label [resume_points.Count];
3971 for (int j = 0; j < i; ++j)
3972 labels [j] = end_of_try;
3977 if (labels != null) {
3979 for (j = 1; j < labels.Length; ++j)
3980 if (!labels [0].Equals (labels [j]))
3982 bool emit_dispatcher = j < labels.Length;
3984 if (emit_dispatcher) {
3985 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3986 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3987 IntConstant.EmitInt (ig, first_resume_pc);
3988 ig.Emit (OpCodes.Sub);
3989 ig.Emit (OpCodes.Switch, labels);
3990 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3993 foreach (ResumableStatement s in resume_points)
3994 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3997 ig.MarkLabel (end_of_try);
3999 ig.BeginFinallyBlock ();
4001 EmitFinallyBody (ec);
4003 ig.EndExceptionBlock ();
4007 public class Lock : ExceptionStatement {
4009 public Statement Statement;
4010 TemporaryVariable temp;
4012 public Lock (Expression expr, Statement stmt, Location l)
4019 public override bool Resolve (EmitContext ec)
4021 expr = expr.Resolve (ec);
4025 if (expr.Type.IsValueType){
4026 Report.Error (185, loc,
4027 "`{0}' is not a reference type as required by the lock statement",
4028 TypeManager.CSharpName (expr.Type));
4032 ec.StartFlowBranching (this);
4033 bool ok = Statement.Resolve (ec);
4034 ec.EndFlowBranching ();
4036 ResolveReachability (ec);
4038 // Avoid creating libraries that reference the internal
4041 if (t == TypeManager.null_type)
4042 t = TypeManager.object_type;
4044 temp = new TemporaryVariable (t, loc);
4047 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4048 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4049 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4050 monitor_type, "Enter", loc, TypeManager.object_type);
4051 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4052 monitor_type, "Exit", loc, TypeManager.object_type);
4058 protected override void EmitPreTryBody (EmitContext ec)
4060 ILGenerator ig = ec.ig;
4062 temp.EmitAssign (ec, expr);
4064 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4067 protected override void EmitTryBody (EmitContext ec)
4069 Statement.Emit (ec);
4072 protected override void EmitFinallyBody (EmitContext ec)
4075 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4078 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4080 expr.MutateHoistedGenericType (storey);
4081 temp.MutateHoistedGenericType (storey);
4082 Statement.MutateHoistedGenericType (storey);
4085 protected override void CloneTo (CloneContext clonectx, Statement t)
4087 Lock target = (Lock) t;
4089 target.expr = expr.Clone (clonectx);
4090 target.Statement = Statement.Clone (clonectx);
4094 public class Unchecked : Statement {
4097 public Unchecked (Block b)
4103 public override bool Resolve (EmitContext ec)
4105 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4106 return Block.Resolve (ec);
4109 protected override void DoEmit (EmitContext ec)
4111 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4115 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4117 Block.MutateHoistedGenericType (storey);
4120 protected override void CloneTo (CloneContext clonectx, Statement t)
4122 Unchecked target = (Unchecked) t;
4124 target.Block = clonectx.LookupBlock (Block);
4128 public class Checked : Statement {
4131 public Checked (Block b)
4134 b.Unchecked = false;
4137 public override bool Resolve (EmitContext ec)
4139 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4140 return Block.Resolve (ec);
4143 protected override void DoEmit (EmitContext ec)
4145 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4149 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4151 Block.MutateHoistedGenericType (storey);
4154 protected override void CloneTo (CloneContext clonectx, Statement t)
4156 Checked target = (Checked) t;
4158 target.Block = clonectx.LookupBlock (Block);
4162 public class Unsafe : Statement {
4165 public Unsafe (Block b)
4168 Block.Unsafe = true;
4171 public override bool Resolve (EmitContext ec)
4173 using (ec.With (EmitContext.Flags.InUnsafe, true))
4174 return Block.Resolve (ec);
4177 protected override void DoEmit (EmitContext ec)
4179 using (ec.With (EmitContext.Flags.InUnsafe, true))
4183 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4185 Block.MutateHoistedGenericType (storey);
4188 protected override void CloneTo (CloneContext clonectx, Statement t)
4190 Unsafe target = (Unsafe) t;
4192 target.Block = clonectx.LookupBlock (Block);
4199 public class Fixed : Statement {
4201 ArrayList declarators;
4202 Statement statement;
4207 abstract class Emitter
4209 protected LocalInfo vi;
4210 protected Expression converted;
4212 protected Emitter (Expression expr, LocalInfo li)
4218 public abstract void Emit (EmitContext ec);
4219 public abstract void EmitExit (EmitContext ec);
4222 class ExpressionEmitter : Emitter {
4223 public ExpressionEmitter (Expression converted, LocalInfo li) :
4224 base (converted, li)
4228 public override void Emit (EmitContext ec) {
4230 // Store pointer in pinned location
4232 converted.Emit (ec);
4236 public override void EmitExit (EmitContext ec)
4238 ec.ig.Emit (OpCodes.Ldc_I4_0);
4239 ec.ig.Emit (OpCodes.Conv_U);
4244 class StringEmitter : Emitter {
4245 class StringPtr : Expression
4249 public StringPtr (LocalBuilder b, Location l)
4252 eclass = ExprClass.Value;
4253 type = TypeManager.char_ptr_type;
4257 public override Expression CreateExpressionTree (EmitContext ec)
4259 throw new NotSupportedException ("ET");
4262 public override Expression DoResolve (EmitContext ec)
4264 // This should never be invoked, we are born in fully
4265 // initialized state.
4270 public override void Emit (EmitContext ec)
4272 if (TypeManager.int_get_offset_to_string_data == null) {
4273 // TODO: Move to resolve !!
4274 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4275 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4278 ILGenerator ig = ec.ig;
4280 ig.Emit (OpCodes.Ldloc, b);
4281 ig.Emit (OpCodes.Conv_I);
4282 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4283 ig.Emit (OpCodes.Add);
4287 LocalBuilder pinned_string;
4290 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4296 public override void Emit (EmitContext ec)
4298 ILGenerator ig = ec.ig;
4299 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4301 converted.Emit (ec);
4302 ig.Emit (OpCodes.Stloc, pinned_string);
4304 Expression sptr = new StringPtr (pinned_string, loc);
4305 converted = Convert.ImplicitConversionRequired (
4306 ec, sptr, vi.VariableType, loc);
4308 if (converted == null)
4311 converted.Emit (ec);
4315 public override void EmitExit (EmitContext ec)
4317 ec.ig.Emit (OpCodes.Ldnull);
4318 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4322 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4325 declarators = decls;
4330 public Statement Statement {
4331 get { return statement; }
4334 public override bool Resolve (EmitContext ec)
4337 Expression.UnsafeError (loc);
4341 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4342 if (texpr == null) {
4343 if (type is VarExpr)
4344 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4349 expr_type = texpr.Type;
4351 data = new Emitter [declarators.Count];
4353 if (!expr_type.IsPointer){
4354 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4359 foreach (Pair p in declarators){
4360 LocalInfo vi = (LocalInfo) p.First;
4361 Expression e = (Expression) p.Second;
4363 vi.VariableInfo.SetAssigned (ec);
4364 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4367 // The rules for the possible declarators are pretty wise,
4368 // but the production on the grammar is more concise.
4370 // So we have to enforce these rules here.
4372 // We do not resolve before doing the case 1 test,
4373 // because the grammar is explicit in that the token &
4374 // is present, so we need to test for this particular case.
4378 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4382 ec.InFixedInitializer = true;
4384 ec.InFixedInitializer = false;
4391 if (e.Type.IsArray){
4392 Type array_type = TypeManager.GetElementType (e.Type);
4395 // Provided that array_type is unmanaged,
4397 if (!TypeManager.VerifyUnManaged (array_type, loc))
4401 // and T* is implicitly convertible to the
4402 // pointer type given in the fixed statement.
4404 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4406 Expression converted = Convert.ImplicitConversionRequired (
4407 ec, array_ptr, vi.VariableType, loc);
4408 if (converted == null)
4412 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4414 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4415 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4416 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4417 new NullPointer (loc),
4420 converted = converted.Resolve (ec);
4422 data [i] = new ExpressionEmitter (converted, vi);
4431 if (e.Type == TypeManager.string_type){
4432 data [i] = new StringEmitter (e, vi, loc);
4437 // Case 4: fixed buffer
4438 if (e is FixedBufferPtr) {
4439 data [i++] = new ExpressionEmitter (e, vi);
4444 // Case 1: & object.
4446 Unary u = e as Unary;
4447 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4448 IVariableReference vr = u.Expr as IVariableReference;
4449 if (vr == null || !vr.IsFixed) {
4450 data [i] = new ExpressionEmitter (e, vi);
4454 if (data [i++] == null)
4455 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4457 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4460 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4461 bool ok = statement.Resolve (ec);
4462 bool flow_unreachable = ec.EndFlowBranching ();
4463 has_ret = flow_unreachable;
4468 protected override void DoEmit (EmitContext ec)
4470 for (int i = 0; i < data.Length; i++) {
4474 statement.Emit (ec);
4480 // Clear the pinned variable
4482 for (int i = 0; i < data.Length; i++) {
4483 data [i].EmitExit (ec);
4487 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4489 // Fixed statement cannot be used inside anonymous methods or lambdas
4490 throw new NotSupportedException ();
4493 protected override void CloneTo (CloneContext clonectx, Statement t)
4495 Fixed target = (Fixed) t;
4497 target.type = type.Clone (clonectx);
4498 target.declarators = new ArrayList (declarators.Count);
4499 foreach (Pair p in declarators) {
4500 LocalInfo vi = (LocalInfo) p.First;
4501 Expression e = (Expression) p.Second;
4503 target.declarators.Add (
4504 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4507 target.statement = statement.Clone (clonectx);
4511 public class Catch : Statement {
4512 public readonly string Name;
4514 public Block VarBlock;
4516 Expression type_expr;
4519 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4524 VarBlock = var_block;
4528 public Type CatchType {
4534 public bool IsGeneral {
4536 return type_expr == null;
4540 protected override void DoEmit (EmitContext ec)
4542 ILGenerator ig = ec.ig;
4544 if (CatchType != null)
4545 ig.BeginCatchBlock (CatchType);
4547 ig.BeginCatchBlock (TypeManager.object_type);
4549 if (VarBlock != null)
4553 // TODO: Move to resolve
4554 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4558 if (lvr.IsHoisted) {
4559 LocalTemporary lt = new LocalTemporary (lvr.Type);
4563 // Variable is at the top of the stack
4564 source = EmptyExpression.Null;
4567 lvr.EmitAssign (ec, source, false, false);
4569 ig.Emit (OpCodes.Pop);
4574 public override bool Resolve (EmitContext ec)
4576 using (ec.With (EmitContext.Flags.InCatch, true)) {
4577 if (type_expr != null) {
4578 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4584 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4585 Error (155, "The type caught or thrown must be derived from System.Exception");
4591 if (!Block.Resolve (ec))
4594 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4595 // emit the "unused variable" warnings.
4596 if (VarBlock != null)
4597 return VarBlock.Resolve (ec);
4603 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4606 type = storey.MutateType (type);
4607 if (VarBlock != null)
4608 VarBlock.MutateHoistedGenericType (storey);
4609 Block.MutateHoistedGenericType (storey);
4612 protected override void CloneTo (CloneContext clonectx, Statement t)
4614 Catch target = (Catch) t;
4616 if (type_expr != null)
4617 target.type_expr = type_expr.Clone (clonectx);
4618 if (VarBlock != null)
4619 target.VarBlock = clonectx.LookupBlock (VarBlock);
4620 target.Block = clonectx.LookupBlock (Block);
4624 public class TryFinally : ExceptionStatement {
4628 public TryFinally (Statement stmt, Block fini, Location l)
4635 public override bool Resolve (EmitContext ec)
4639 ec.StartFlowBranching (this);
4641 if (!stmt.Resolve (ec))
4645 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4646 using (ec.With (EmitContext.Flags.InFinally, true)) {
4647 if (!fini.Resolve (ec))
4651 ec.EndFlowBranching ();
4653 ResolveReachability (ec);
4658 protected override void EmitPreTryBody (EmitContext ec)
4662 protected override void EmitTryBody (EmitContext ec)
4667 protected override void EmitFinallyBody (EmitContext ec)
4672 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4674 stmt.MutateHoistedGenericType (storey);
4675 fini.MutateHoistedGenericType (storey);
4678 protected override void CloneTo (CloneContext clonectx, Statement t)
4680 TryFinally target = (TryFinally) t;
4682 target.stmt = (Statement) stmt.Clone (clonectx);
4684 target.fini = clonectx.LookupBlock (fini);
4688 public class TryCatch : Statement {
4690 public ArrayList Specific;
4691 public Catch General;
4692 bool inside_try_finally, code_follows;
4694 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4697 this.Specific = catch_clauses;
4698 this.General = null;
4699 this.inside_try_finally = inside_try_finally;
4701 for (int i = 0; i < catch_clauses.Count; ++i) {
4702 Catch c = (Catch) catch_clauses [i];
4704 if (i != catch_clauses.Count - 1)
4705 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4707 catch_clauses.RemoveAt (i);
4715 public override bool Resolve (EmitContext ec)
4719 ec.StartFlowBranching (this);
4721 if (!Block.Resolve (ec))
4724 Type[] prev_catches = new Type [Specific.Count];
4726 foreach (Catch c in Specific){
4727 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4729 if (c.Name != null) {
4730 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4732 throw new Exception ();
4734 vi.VariableInfo = null;
4737 if (!c.Resolve (ec))
4740 Type resolved_type = c.CatchType;
4741 for (int ii = 0; ii < last_index; ++ii) {
4742 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4743 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4748 prev_catches [last_index++] = resolved_type;
4751 if (General != null) {
4752 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4753 foreach (Catch c in Specific){
4754 if (c.CatchType == TypeManager.exception_type) {
4755 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'");
4760 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4762 if (!General.Resolve (ec))
4766 ec.EndFlowBranching ();
4768 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4769 // So, ensure there's some IL code after this statement
4770 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4771 ec.NeedReturnLabel ();
4776 public void SomeCodeFollows ()
4778 code_follows = true;
4781 protected override void DoEmit (EmitContext ec)
4783 ILGenerator ig = ec.ig;
4785 if (!inside_try_finally)
4786 ig.BeginExceptionBlock ();
4790 foreach (Catch c in Specific)
4793 if (General != null)
4796 if (!inside_try_finally)
4797 ig.EndExceptionBlock ();
4800 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4802 Block.MutateHoistedGenericType (storey);
4804 if (General != null)
4805 General.MutateHoistedGenericType (storey);
4806 if (Specific != null) {
4807 foreach (Catch c in Specific)
4808 c.MutateHoistedGenericType (storey);
4812 protected override void CloneTo (CloneContext clonectx, Statement t)
4814 TryCatch target = (TryCatch) t;
4816 target.Block = clonectx.LookupBlock (Block);
4817 if (General != null)
4818 target.General = (Catch) General.Clone (clonectx);
4819 if (Specific != null){
4820 target.Specific = new ArrayList ();
4821 foreach (Catch c in Specific)
4822 target.Specific.Add (c.Clone (clonectx));
4827 public class UsingTemporary : ExceptionStatement {
4828 TemporaryVariable local_copy;
4829 public Statement Statement;
4833 public UsingTemporary (Expression expr, Statement stmt, Location l)
4840 public override bool Resolve (EmitContext ec)
4842 expr = expr.Resolve (ec);
4846 expr_type = expr.Type;
4848 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4849 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4850 Using.Error_IsNotConvertibleToIDisposable (expr);
4855 local_copy = new TemporaryVariable (expr_type, loc);
4856 local_copy.Resolve (ec);
4858 ec.StartFlowBranching (this);
4860 bool ok = Statement.Resolve (ec);
4862 ec.EndFlowBranching ();
4864 ResolveReachability (ec);
4866 if (TypeManager.void_dispose_void == null) {
4867 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4868 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4874 protected override void EmitPreTryBody (EmitContext ec)
4876 local_copy.EmitAssign (ec, expr);
4879 protected override void EmitTryBody (EmitContext ec)
4881 Statement.Emit (ec);
4884 protected override void EmitFinallyBody (EmitContext ec)
4886 ILGenerator ig = ec.ig;
4887 if (!expr_type.IsValueType) {
4888 Label skip = ig.DefineLabel ();
4889 local_copy.Emit (ec);
4890 ig.Emit (OpCodes.Brfalse, skip);
4891 local_copy.Emit (ec);
4892 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4893 ig.MarkLabel (skip);
4897 Expression ml = Expression.MemberLookup (
4898 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4899 "Dispose", Location.Null);
4901 if (!(ml is MethodGroupExpr)) {
4902 local_copy.Emit (ec);
4903 ig.Emit (OpCodes.Box, expr_type);
4904 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4908 MethodInfo mi = null;
4910 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4911 if (TypeManager.GetParameterData (mk).Count == 0) {
4918 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4922 local_copy.AddressOf (ec, AddressOp.Load);
4923 ig.Emit (OpCodes.Call, mi);
4926 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4928 expr_type = storey.MutateType (expr_type);
4929 local_copy.MutateHoistedGenericType (storey);
4930 Statement.MutateHoistedGenericType (storey);
4933 protected override void CloneTo (CloneContext clonectx, Statement t)
4935 UsingTemporary target = (UsingTemporary) t;
4937 target.expr = expr.Clone (clonectx);
4938 target.Statement = Statement.Clone (clonectx);
4942 public class Using : ExceptionStatement {
4944 public Statement EmbeddedStatement {
4945 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4951 Expression converted_var;
4952 ExpressionStatement assign;
4954 public Using (Expression var, Expression init, Statement stmt, Location l)
4962 bool ResolveVariable (EmitContext ec)
4964 ExpressionStatement a = new SimpleAssign (var, init, loc);
4965 a = a.ResolveStatement (ec);
4971 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4972 converted_var = var;
4976 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4978 Error_IsNotConvertibleToIDisposable (var);
4987 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4989 Report.SymbolRelatedToPreviousError (expr.Type);
4990 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4991 expr.GetSignatureForError ());
4994 protected override void EmitPreTryBody (EmitContext ec)
4996 assign.EmitStatement (ec);
4999 protected override void EmitTryBody (EmitContext ec)
5004 protected override void EmitFinallyBody (EmitContext ec)
5006 ILGenerator ig = ec.ig;
5008 if (!var.Type.IsValueType) {
5009 Label skip = ig.DefineLabel ();
5011 ig.Emit (OpCodes.Brfalse, skip);
5012 converted_var.Emit (ec);
5013 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5014 ig.MarkLabel (skip);
5016 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5018 if (!(ml is MethodGroupExpr)) {
5020 ig.Emit (OpCodes.Box, var.Type);
5021 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5023 MethodInfo mi = null;
5025 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5026 if (TypeManager.GetParameterData (mk).Count == 0) {
5033 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5037 IMemoryLocation mloc = (IMemoryLocation) var;
5039 mloc.AddressOf (ec, AddressOp.Load);
5040 ig.Emit (OpCodes.Call, mi);
5045 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5047 assign.MutateHoistedGenericType (storey);
5048 var.MutateHoistedGenericType (storey);
5049 stmt.MutateHoistedGenericType (storey);
5052 public override bool Resolve (EmitContext ec)
5054 if (!ResolveVariable (ec))
5057 ec.StartFlowBranching (this);
5059 bool ok = stmt.Resolve (ec);
5061 ec.EndFlowBranching ();
5063 ResolveReachability (ec);
5065 if (TypeManager.void_dispose_void == null) {
5066 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5067 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5073 protected override void CloneTo (CloneContext clonectx, Statement t)
5075 Using target = (Using) t;
5077 target.var = var.Clone (clonectx);
5078 target.init = init.Clone (clonectx);
5079 target.stmt = stmt.Clone (clonectx);
5084 /// Implementation of the foreach C# statement
5086 public class Foreach : Statement {
5088 sealed class ArrayForeach : Statement
5090 class ArrayCounter : TemporaryVariable
5092 StatementExpression increment;
5094 public ArrayCounter (Location loc)
5095 : base (TypeManager.int32_type, loc)
5099 public void ResolveIncrement (EmitContext ec)
5101 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5102 increment.Resolve (ec);
5105 public void EmitIncrement (EmitContext ec)
5107 increment.Emit (ec);
5111 readonly Foreach for_each;
5112 readonly Statement statement;
5115 TemporaryVariable[] lengths;
5116 Expression [] length_exprs;
5117 ArrayCounter[] counter;
5119 TemporaryVariable copy;
5122 public ArrayForeach (Foreach @foreach, int rank)
5124 for_each = @foreach;
5125 statement = for_each.statement;
5128 counter = new ArrayCounter [rank];
5129 length_exprs = new Expression [rank];
5132 // Only use temporary length variables when dealing with
5133 // multi-dimensional arrays
5136 lengths = new TemporaryVariable [rank];
5139 protected override void CloneTo (CloneContext clonectx, Statement target)
5141 throw new NotImplementedException ();
5144 public override bool Resolve (EmitContext ec)
5146 copy = new TemporaryVariable (for_each.expr.Type, loc);
5149 int rank = length_exprs.Length;
5150 ArrayList list = new ArrayList (rank);
5151 for (int i = 0; i < rank; i++) {
5152 counter [i] = new ArrayCounter (loc);
5153 counter [i].ResolveIncrement (ec);
5156 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5158 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5159 lengths [i].Resolve (ec);
5161 ArrayList args = new ArrayList (1);
5162 args.Add (new Argument (new IntConstant (i, loc)));
5163 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5166 list.Add (counter [i]);
5169 access = new ElementAccess (copy, list).Resolve (ec);
5173 Expression var_type = for_each.type;
5174 VarExpr ve = var_type as VarExpr;
5176 // Infer implicitly typed local variable from foreach array type
5177 var_type = new TypeExpression (access.Type, ve.Location);
5180 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5181 if (var_type == null)
5184 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5190 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5191 ec.CurrentBranching.CreateSibling ();
5193 for_each.variable = for_each.variable.ResolveLValue (ec, conv, loc);
5194 if (for_each.variable == null)
5197 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5198 if (!statement.Resolve (ec))
5200 ec.EndFlowBranching ();
5202 // There's no direct control flow from the end of the embedded statement to the end of the loop
5203 ec.CurrentBranching.CurrentUsageVector.Goto ();
5205 ec.EndFlowBranching ();
5210 protected override void DoEmit (EmitContext ec)
5212 ILGenerator ig = ec.ig;
5214 copy.EmitAssign (ec, for_each.expr);
5216 int rank = length_exprs.Length;
5217 Label[] test = new Label [rank];
5218 Label[] loop = new Label [rank];
5220 for (int i = 0; i < rank; i++) {
5221 test [i] = ig.DefineLabel ();
5222 loop [i] = ig.DefineLabel ();
5224 if (lengths != null)
5225 lengths [i].EmitAssign (ec, length_exprs [i]);
5228 IntConstant zero = new IntConstant (0, loc);
5229 for (int i = 0; i < rank; i++) {
5230 counter [i].EmitAssign (ec, zero);
5232 ig.Emit (OpCodes.Br, test [i]);
5233 ig.MarkLabel (loop [i]);
5236 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5238 statement.Emit (ec);
5240 ig.MarkLabel (ec.LoopBegin);
5242 for (int i = rank - 1; i >= 0; i--){
5243 counter [i].EmitIncrement (ec);
5245 ig.MarkLabel (test [i]);
5246 counter [i].Emit (ec);
5248 if (lengths != null)
5249 lengths [i].Emit (ec);
5251 length_exprs [i].Emit (ec);
5253 ig.Emit (OpCodes.Blt, loop [i]);
5256 ig.MarkLabel (ec.LoopEnd);
5259 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5261 for_each.expr.MutateHoistedGenericType (storey);
5263 copy.MutateHoistedGenericType (storey);
5264 conv.MutateHoistedGenericType (storey);
5265 statement.MutateHoistedGenericType (storey);
5267 for (int i = 0; i < counter.Length; i++) {
5268 counter [i].MutateHoistedGenericType (storey);
5269 if (lengths != null)
5270 lengths [i].MutateHoistedGenericType (storey);
5275 sealed class CollectionForeach : Statement
5277 class CollectionForeachStatement : Statement
5280 Expression variable, current, conv;
5281 Statement statement;
5284 public CollectionForeachStatement (Type type, Expression variable,
5285 Expression current, Statement statement,
5289 this.variable = variable;
5290 this.current = current;
5291 this.statement = statement;
5295 protected override void CloneTo (CloneContext clonectx, Statement target)
5297 throw new NotImplementedException ();
5300 public override bool Resolve (EmitContext ec)
5302 current = current.Resolve (ec);
5303 if (current == null)
5306 conv = Convert.ExplicitConversion (ec, current, type, loc);
5310 assign = new SimpleAssign (variable, conv, loc);
5311 if (assign.Resolve (ec) == null)
5314 if (!statement.Resolve (ec))
5320 protected override void DoEmit (EmitContext ec)
5322 assign.EmitStatement (ec);
5323 statement.Emit (ec);
5326 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5328 assign.MutateHoistedGenericType (storey);
5329 statement.MutateHoistedGenericType (storey);
5333 Expression variable, expr;
5334 Statement statement;
5336 TemporaryVariable enumerator;
5341 MethodGroupExpr get_enumerator;
5342 PropertyExpr get_current;
5343 MethodInfo move_next;
5344 Expression var_type;
5345 Type enumerator_type;
5346 bool enumerator_found;
5348 public CollectionForeach (Expression var_type, Expression var,
5349 Expression expr, Statement stmt, Location l)
5351 this.var_type = var_type;
5352 this.variable = var;
5358 protected override void CloneTo (CloneContext clonectx, Statement target)
5360 throw new NotImplementedException ();
5363 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5365 Type return_type = mi.ReturnType;
5368 // Ok, we can access it, now make sure that we can do something
5369 // with this `GetEnumerator'
5372 if (return_type == TypeManager.ienumerator_type ||
5373 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5374 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5376 // If it is not an interface, lets try to find the methods ourselves.
5377 // For example, if we have:
5378 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5379 // We can avoid the iface call. This is a runtime perf boost.
5380 // even bigger if we have a ValueType, because we avoid the cost
5383 // We have to make sure that both methods exist for us to take
5384 // this path. If one of the methods does not exist, we will just
5385 // use the interface. Sadly, this complex if statement is the only
5386 // way I could do this without a goto
5389 if (TypeManager.bool_movenext_void == null) {
5390 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5391 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5394 if (TypeManager.ienumerator_getcurrent == null) {
5395 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5396 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5401 // Prefer a generic enumerator over a non-generic one.
5403 if (return_type.IsInterface && return_type.IsGenericType) {
5404 enumerator_type = return_type;
5405 if (!FetchGetCurrent (ec, return_type))
5406 get_current = new PropertyExpr (
5407 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5408 if (!FetchMoveNext (return_type))
5409 move_next = TypeManager.bool_movenext_void;
5414 if (return_type.IsInterface ||
5415 !FetchMoveNext (return_type) ||
5416 !FetchGetCurrent (ec, return_type)) {
5417 enumerator_type = return_type;
5418 move_next = TypeManager.bool_movenext_void;
5419 get_current = new PropertyExpr (
5420 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5425 // Ok, so they dont return an IEnumerable, we will have to
5426 // find if they support the GetEnumerator pattern.
5429 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5430 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",
5431 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5436 enumerator_type = return_type;
5442 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5444 bool FetchMoveNext (Type t)
5446 MemberList move_next_list;
5448 move_next_list = TypeContainer.FindMembers (
5449 t, MemberTypes.Method,
5450 BindingFlags.Public | BindingFlags.Instance,
5451 Type.FilterName, "MoveNext");
5452 if (move_next_list.Count == 0)
5455 foreach (MemberInfo m in move_next_list){
5456 MethodInfo mi = (MethodInfo) m;
5458 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5459 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5469 // Retrieves a `public T get_Current ()' method from the Type `t'
5471 bool FetchGetCurrent (EmitContext ec, Type t)
5473 PropertyExpr pe = Expression.MemberLookup (
5474 ec.ContainerType, t, "Current", MemberTypes.Property,
5475 Expression.AllBindingFlags, loc) as PropertyExpr;
5484 // Retrieves a `public void Dispose ()' method from the Type `t'
5486 static MethodInfo FetchMethodDispose (Type t)
5488 MemberList dispose_list;
5490 dispose_list = TypeContainer.FindMembers (
5491 t, MemberTypes.Method,
5492 BindingFlags.Public | BindingFlags.Instance,
5493 Type.FilterName, "Dispose");
5494 if (dispose_list.Count == 0)
5497 foreach (MemberInfo m in dispose_list){
5498 MethodInfo mi = (MethodInfo) m;
5500 if (TypeManager.GetParameterData (mi).Count == 0){
5501 if (mi.ReturnType == TypeManager.void_type)
5508 void Error_Enumerator ()
5510 if (enumerator_found) {
5514 Report.Error (1579, loc,
5515 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5516 TypeManager.CSharpName (expr.Type));
5519 bool IsOverride (MethodInfo m)
5521 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5523 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5525 if (m is MethodBuilder)
5528 MethodInfo base_method = m.GetBaseDefinition ();
5529 return base_method != m;
5532 bool TryType (EmitContext ec, Type t)
5534 MethodGroupExpr mg = Expression.MemberLookup (
5535 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5536 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5540 MethodInfo result = null;
5541 MethodInfo tmp_move_next = null;
5542 PropertyExpr tmp_get_cur = null;
5543 Type tmp_enumerator_type = enumerator_type;
5544 foreach (MethodInfo mi in mg.Methods) {
5545 if (TypeManager.GetParameterData (mi).Count != 0)
5548 // Check whether GetEnumerator is public
5549 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5552 if (IsOverride (mi))
5555 enumerator_found = true;
5557 if (!GetEnumeratorFilter (ec, mi))
5560 if (result != null) {
5561 if (TypeManager.IsGenericType (result.ReturnType)) {
5562 if (!TypeManager.IsGenericType (mi.ReturnType))
5565 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5566 Report.SymbolRelatedToPreviousError (t);
5567 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5568 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5569 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5573 // Always prefer generics enumerators
5574 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5575 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5576 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5579 Report.SymbolRelatedToPreviousError (result);
5580 Report.SymbolRelatedToPreviousError (mi);
5581 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5582 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5587 tmp_move_next = move_next;
5588 tmp_get_cur = get_current;
5589 tmp_enumerator_type = enumerator_type;
5590 if (mi.DeclaringType == t)
5594 if (result != null) {
5595 move_next = tmp_move_next;
5596 get_current = tmp_get_cur;
5597 enumerator_type = tmp_enumerator_type;
5598 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5599 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5601 if (t != expr.Type) {
5602 expr = Convert.ExplicitConversion (
5605 throw new InternalErrorException ();
5608 get_enumerator.InstanceExpression = expr;
5609 get_enumerator.IsBase = t != expr.Type;
5617 bool ProbeCollectionType (EmitContext ec, Type t)
5619 int errors = Report.Errors;
5620 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5621 if (TryType (ec, tt))
5626 if (Report.Errors > errors)
5630 // Now try to find the method in the interfaces
5632 Type [] ifaces = TypeManager.GetInterfaces (t);
5633 foreach (Type i in ifaces){
5634 if (TryType (ec, i))
5641 public override bool Resolve (EmitContext ec)
5643 enumerator_type = TypeManager.ienumerator_type;
5645 if (!ProbeCollectionType (ec, expr.Type)) {
5646 Error_Enumerator ();
5650 bool is_disposable = !enumerator_type.IsSealed ||
5651 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5653 VarExpr ve = var_type as VarExpr;
5655 // Infer implicitly typed local variable from foreach enumerable type
5656 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5659 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5660 if (var_type == null)
5663 enumerator = new TemporaryVariable (enumerator_type, loc);
5664 enumerator.Resolve (ec);
5666 init = new Invocation (get_enumerator, null);
5667 init = init.Resolve (ec);
5671 Expression move_next_expr;
5673 MemberInfo[] mi = new MemberInfo[] { move_next };
5674 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5675 mg.InstanceExpression = enumerator;
5677 move_next_expr = new Invocation (mg, null);
5680 get_current.InstanceExpression = enumerator;
5682 Statement block = new CollectionForeachStatement (
5683 var_type.Type, variable, get_current, statement, loc);
5685 loop = new While (move_next_expr, block, loc);
5687 wrapper = is_disposable ?
5688 (Statement) new DisposableWrapper (this) :
5689 (Statement) new NonDisposableWrapper (this);
5690 return wrapper.Resolve (ec);
5693 protected override void DoEmit (EmitContext ec)
5698 class NonDisposableWrapper : Statement {
5699 CollectionForeach parent;
5701 internal NonDisposableWrapper (CollectionForeach parent)
5703 this.parent = parent;
5706 protected override void CloneTo (CloneContext clonectx, Statement target)
5708 throw new NotSupportedException ();
5711 public override bool Resolve (EmitContext ec)
5713 return parent.ResolveLoop (ec);
5716 protected override void DoEmit (EmitContext ec)
5718 parent.EmitLoopInit (ec);
5719 parent.EmitLoopBody (ec);
5722 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5724 throw new NotSupportedException ();
5728 class DisposableWrapper : ExceptionStatement {
5729 CollectionForeach parent;
5731 internal DisposableWrapper (CollectionForeach parent)
5733 this.parent = parent;
5736 protected override void CloneTo (CloneContext clonectx, Statement target)
5738 throw new NotSupportedException ();
5741 public override bool Resolve (EmitContext ec)
5745 ec.StartFlowBranching (this);
5747 if (!parent.ResolveLoop (ec))
5750 ec.EndFlowBranching ();
5752 ResolveReachability (ec);
5754 if (TypeManager.void_dispose_void == null) {
5755 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5756 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5761 protected override void EmitPreTryBody (EmitContext ec)
5763 parent.EmitLoopInit (ec);
5766 protected override void EmitTryBody (EmitContext ec)
5768 parent.EmitLoopBody (ec);
5771 protected override void EmitFinallyBody (EmitContext ec)
5773 parent.EmitFinallyBody (ec);
5776 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5778 throw new NotSupportedException ();
5782 bool ResolveLoop (EmitContext ec)
5784 return loop.Resolve (ec);
5787 void EmitLoopInit (EmitContext ec)
5789 enumerator.EmitAssign (ec, init);
5792 void EmitLoopBody (EmitContext ec)
5797 void EmitFinallyBody (EmitContext ec)
5799 ILGenerator ig = ec.ig;
5801 if (enumerator_type.IsValueType) {
5802 MethodInfo mi = FetchMethodDispose (enumerator_type);
5804 enumerator.AddressOf (ec, AddressOp.Load);
5805 ig.Emit (OpCodes.Call, mi);
5807 enumerator.Emit (ec);
5808 ig.Emit (OpCodes.Box, enumerator_type);
5809 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5812 Label call_dispose = ig.DefineLabel ();
5814 enumerator.Emit (ec);
5815 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5816 ig.Emit (OpCodes.Dup);
5817 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5819 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5820 // (Partition III, Section 3.35)
5821 ig.Emit (OpCodes.Endfinally);
5823 ig.MarkLabel (call_dispose);
5824 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5828 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5830 enumerator_type = storey.MutateType (enumerator_type);
5831 init.MutateHoistedGenericType (storey);
5832 loop.MutateHoistedGenericType (storey);
5837 Expression variable;
5839 Statement statement;
5841 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5842 Statement stmt, Location l)
5845 this.variable = var;
5851 public Statement Statement {
5852 get { return statement; }
5855 public override bool Resolve (EmitContext ec)
5857 expr = expr.Resolve (ec);
5862 Report.Error (186, loc, "Use of null is not valid in this context");
5866 if (expr.Type == TypeManager.string_type) {
5867 statement = new ArrayForeach (this, 1);
5868 } else if (expr.Type.IsArray) {
5869 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5871 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5872 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5873 expr.ExprClassName);
5877 statement = new CollectionForeach (type, variable, expr, statement, loc);
5880 return statement.Resolve (ec);
5883 protected override void DoEmit (EmitContext ec)
5885 ILGenerator ig = ec.ig;
5887 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5888 ec.LoopBegin = ig.DefineLabel ();
5889 ec.LoopEnd = ig.DefineLabel ();
5891 statement.Emit (ec);
5893 ec.LoopBegin = old_begin;
5894 ec.LoopEnd = old_end;
5897 protected override void CloneTo (CloneContext clonectx, Statement t)
5899 Foreach target = (Foreach) t;
5901 target.type = type.Clone (clonectx);
5902 target.variable = variable.Clone (clonectx);
5903 target.expr = expr.Clone (clonectx);
5904 target.statement = statement.Clone (clonectx);
5907 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5909 statement.MutateHoistedGenericType (storey);