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,
1524 HasCapturedVariable = 64,
1525 HasCapturedThis = 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)
1602 // Useful when TopLevel block is downgraded to normal block
1604 public Block (ToplevelBlock parent, ToplevelBlock source)
1605 : this (parent, source.flags, source.StartLocation, source.EndLocation)
1607 statements = source.statements;
1608 children = source.children;
1609 labels = source.labels;
1610 variables = source.variables;
1611 constants = source.constants;
1612 switch_block = source.switch_block;
1615 public Block (Block parent, Flags flags, Location start, Location end)
1617 if (parent != null) {
1618 parent.AddChild (this);
1620 // the appropriate constructors will fixup these fields
1621 Toplevel = parent.Toplevel;
1622 Explicit = parent.Explicit;
1625 this.Parent = parent;
1627 this.StartLocation = start;
1628 this.EndLocation = end;
1631 statements = new ArrayList (4);
1634 public Block CreateSwitchBlock (Location start)
1636 // FIXME: should this be implicit?
1637 Block new_block = new ExplicitBlock (this, start, start);
1638 new_block.switch_block = this;
1643 get { return this_id; }
1646 public IDictionary Variables {
1648 if (variables == null)
1649 variables = new ListDictionary ();
1654 void AddChild (Block b)
1656 if (children == null)
1657 children = new ArrayList (1);
1662 public void SetEndLocation (Location loc)
1667 protected static void Error_158 (string name, Location loc)
1669 Report.Error (158, loc, "The label `{0}' shadows another label " +
1670 "by the same name in a contained scope", name);
1674 /// Adds a label to the current block.
1678 /// false if the name already exists in this block. true
1682 public bool AddLabel (LabeledStatement target)
1684 if (switch_block != null)
1685 return switch_block.AddLabel (target);
1687 string name = target.Name;
1690 while (cur != null) {
1691 LabeledStatement s = cur.DoLookupLabel (name);
1693 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1694 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1698 if (this == Explicit)
1704 while (cur != null) {
1705 if (cur.DoLookupLabel (name) != null) {
1706 Error_158 (name, target.loc);
1710 if (children != null) {
1711 foreach (Block b in children) {
1712 LabeledStatement s = b.DoLookupLabel (name);
1716 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1717 Error_158 (name, target.loc);
1725 Toplevel.CheckError158 (name, target.loc);
1728 labels = new HybridDictionary();
1730 labels.Add (name, target);
1734 public LabeledStatement LookupLabel (string name)
1736 LabeledStatement s = DoLookupLabel (name);
1740 if (children == null)
1743 foreach (Block child in children) {
1744 if (Explicit != child.Explicit)
1747 s = child.LookupLabel (name);
1755 LabeledStatement DoLookupLabel (string name)
1757 if (switch_block != null)
1758 return switch_block.LookupLabel (name);
1761 if (labels.Contains (name))
1762 return ((LabeledStatement) labels [name]);
1767 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1770 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1771 while (kvi == null) {
1772 b = b.Explicit.Parent;
1775 kvi = b.Explicit.GetKnownVariable (name);
1781 // Is kvi.Block nested inside 'b'
1782 if (b.Explicit != kvi.Block.Explicit) {
1784 // If a variable by the same name it defined in a nested block of this
1785 // block, we violate the invariant meaning in a block.
1788 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1789 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1794 // It's ok if the definition is in a nested subblock of b, but not
1795 // nested inside this block -- a definition in a sibling block
1796 // should not affect us.
1802 // Block 'b' and kvi.Block are the same textual block.
1803 // However, different variables are extant.
1805 // Check if the variable is in scope in both blocks. We use
1806 // an indirect check that depends on AddVariable doing its
1807 // part in maintaining the invariant-meaning-in-block property.
1809 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1812 if (this is ToplevelBlock) {
1813 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1814 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1819 // Even though we detected the error when the name is used, we
1820 // treat it as if the variable declaration was in error.
1822 Report.SymbolRelatedToPreviousError (loc, name);
1823 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1827 protected virtual bool CheckParentConflictName (ToplevelBlock block, string name, Location l)
1829 LocalInfo vi = GetLocalInfo (name);
1831 Report.SymbolRelatedToPreviousError (vi.Location, name);
1832 if (Explicit == vi.Block.Explicit) {
1833 Error_AlreadyDeclared (l, name, null);
1835 Error_AlreadyDeclared (l, name, this is ToplevelBlock ?
1836 "parent or current" : "parent");
1841 if (block != null) {
1842 Expression e = block.GetParameterReference (name, Location.Null);
1844 ParameterReference pr = e as ParameterReference;
1845 if (this is Linq.QueryBlock && (pr != null && pr.Parameter is Linq.QueryBlock.ImplicitQueryParameter || e is MemberAccess))
1846 Error_AlreadyDeclared (loc, name);
1848 Error_AlreadyDeclared (loc, name, "parent or current");
1856 public LocalInfo AddVariable (Expression type, string name, Location l)
1858 if (!CheckParentConflictName (Toplevel, name, l))
1861 if (Toplevel.GenericMethod != null) {
1862 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1863 if (tp.Name == name) {
1864 Report.SymbolRelatedToPreviousError (tp);
1865 Error_AlreadyDeclaredTypeParameter (loc, name, "local variable");
1871 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1873 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1874 Error_AlreadyDeclared (l, name, "child");
1878 LocalInfo vi = new LocalInfo (type, name, this, l);
1881 if ((flags & Flags.VariablesInitialized) != 0)
1882 throw new InternalErrorException ("block has already been resolved");
1887 protected virtual void AddVariable (LocalInfo li)
1889 Variables.Add (li.Name, li);
1890 Explicit.AddKnownVariable (li.Name, li);
1893 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1895 if (reason == null) {
1896 Error_AlreadyDeclared (loc, var);
1900 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1901 "in this scope because it would give a different meaning " +
1902 "to `{0}', which is already used in a `{1}' scope " +
1903 "to denote something else", var, reason);
1906 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1908 Report.Error (128, loc,
1909 "A local variable named `{0}' is already defined in this scope", name);
1912 public virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name, string conflict)
1914 Report.Error (412, loc, "The type parameter name `{0}' is the same as `{1}'",
1918 public bool AddConstant (Expression type, string name, Expression value, Location l)
1920 if (AddVariable (type, name, l) == null)
1923 if (constants == null)
1924 constants = new HybridDictionary();
1926 constants.Add (name, value);
1928 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1933 static int next_temp_id = 0;
1935 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1937 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1939 if (temporary_variables == null)
1940 temporary_variables = new ArrayList ();
1942 int id = ++next_temp_id;
1943 string name = "$s_" + id.ToString ();
1945 LocalInfo li = new LocalInfo (te, name, this, loc);
1946 li.CompilerGenerated = true;
1947 temporary_variables.Add (li);
1951 public LocalInfo GetLocalInfo (string name)
1954 for (Block b = this; b != null; b = b.Parent) {
1955 if (b.variables != null) {
1956 ret = (LocalInfo) b.variables [name];
1965 public Expression GetVariableType (string name)
1967 LocalInfo vi = GetLocalInfo (name);
1968 return vi == null ? null : vi.Type;
1971 public Expression GetConstantExpression (string name)
1973 for (Block b = this; b != null; b = b.Parent) {
1974 if (b.constants != null) {
1975 Expression ret = b.constants [name] as Expression;
1984 // It should be used by expressions which require to
1985 // register a statement during resolve process.
1987 public void AddScopeStatement (Statement s)
1989 if (scope_initializers == null)
1990 scope_initializers = new ArrayList ();
1992 scope_initializers.Add (s);
1995 public void AddStatement (Statement s)
1998 flags |= Flags.BlockUsed;
2002 get { return (flags & Flags.BlockUsed) != 0; }
2007 flags |= Flags.BlockUsed;
2010 public bool HasRet {
2011 get { return (flags & Flags.HasRet) != 0; }
2014 public int AssignableSlots {
2017 // if ((flags & Flags.VariablesInitialized) == 0)
2018 // throw new Exception ("Variables have not been initialized yet");
2019 return assignable_slots;
2023 public ArrayList AnonymousChildren {
2024 get { return anonymous_children; }
2027 public void AddAnonymousChild (ToplevelBlock b)
2029 if (anonymous_children == null)
2030 anonymous_children = new ArrayList ();
2032 anonymous_children.Add (b);
2035 void DoResolveConstants (EmitContext ec)
2037 if (constants == null)
2040 if (variables == null)
2041 throw new InternalErrorException ("cannot happen");
2043 foreach (DictionaryEntry de in variables) {
2044 string name = (string) de.Key;
2045 LocalInfo vi = (LocalInfo) de.Value;
2046 Type variable_type = vi.VariableType;
2048 if (variable_type == null) {
2049 if (vi.Type is VarExpr)
2050 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2055 Expression cv = (Expression) constants [name];
2059 // Don't let 'const int Foo = Foo;' succeed.
2060 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2061 // which in turn causes the 'must be constant' error to be triggered.
2062 constants.Remove (name);
2064 if (!Const.IsConstantTypeValid (variable_type)) {
2065 Const.Error_InvalidConstantType (variable_type, loc);
2069 ec.CurrentBlock = this;
2071 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2072 e = cv.Resolve (ec);
2077 Constant ce = e as Constant;
2079 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2083 e = ce.ConvertImplicitly (variable_type);
2085 if (TypeManager.IsReferenceType (variable_type))
2086 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2088 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2092 constants.Add (name, e);
2093 vi.IsConstant = true;
2097 protected void ResolveMeta (EmitContext ec, int offset)
2099 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2101 // If some parent block was unsafe, we remain unsafe even if this block
2102 // isn't explicitly marked as such.
2103 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2104 flags |= Flags.VariablesInitialized;
2106 if (variables != null) {
2107 foreach (LocalInfo li in variables.Values) {
2108 if (!li.Resolve (ec))
2110 li.VariableInfo = new VariableInfo (li, offset);
2111 offset += li.VariableInfo.Length;
2114 assignable_slots = offset;
2116 DoResolveConstants (ec);
2118 if (children == null)
2120 foreach (Block b in children)
2121 b.ResolveMeta (ec, offset);
2126 // Emits the local variable declarations for a block
2128 public virtual void EmitMeta (EmitContext ec)
2130 if (variables != null){
2131 foreach (LocalInfo vi in variables.Values)
2132 vi.ResolveVariable (ec);
2135 if (temporary_variables != null) {
2136 for (int i = 0; i < temporary_variables.Count; i++)
2137 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2140 if (children != null) {
2141 for (int i = 0; i < children.Count; i++)
2142 ((Block)children[i]).EmitMeta(ec);
2146 void UsageWarning ()
2148 if (variables == null || Report.WarningLevel < 3)
2151 foreach (DictionaryEntry de in variables) {
2152 LocalInfo vi = (LocalInfo) de.Value;
2155 string name = (string) de.Key;
2157 // vi.VariableInfo can be null for 'catch' variables
2158 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2159 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2161 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2166 static void CheckPossibleMistakenEmptyStatement (Statement s)
2170 // Some statements are wrapped by a Block. Since
2171 // others' internal could be changed, here I treat
2172 // them as possibly wrapped by Block equally.
2173 Block b = s as Block;
2174 if (b != null && b.statements.Count == 1)
2175 s = (Statement) b.statements [0];
2178 body = ((Lock) s).Statement;
2180 body = ((For) s).Statement;
2181 else if (s is Foreach)
2182 body = ((Foreach) s).Statement;
2183 else if (s is While)
2184 body = ((While) s).Statement;
2185 else if (s is Fixed)
2186 body = ((Fixed) s).Statement;
2187 else if (s is Using)
2188 body = ((Using) s).EmbeddedStatement;
2189 else if (s is UsingTemporary)
2190 body = ((UsingTemporary) s).Statement;
2194 if (body == null || body is EmptyStatement)
2195 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2198 public override bool Resolve (EmitContext ec)
2200 Block prev_block = ec.CurrentBlock;
2203 int errors = Report.Errors;
2205 ec.CurrentBlock = this;
2206 ec.StartFlowBranching (this);
2208 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2211 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2212 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2213 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2214 // responsible for handling the situation.
2216 int statement_count = statements.Count;
2217 for (int ix = 0; ix < statement_count; ix++){
2218 Statement s = (Statement) statements [ix];
2219 // Check possible empty statement (CS0642)
2220 if (Report.WarningLevel >= 3 &&
2221 ix + 1 < statement_count &&
2222 statements [ix + 1] is ExplicitBlock)
2223 CheckPossibleMistakenEmptyStatement (s);
2226 // Warn if we detect unreachable code.
2229 if (s is EmptyStatement)
2232 if (!unreachable_shown && !(s is LabeledStatement)) {
2233 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2234 unreachable_shown = true;
2237 Block c_block = s as Block;
2238 if (c_block != null)
2239 c_block.unreachable = c_block.unreachable_shown = true;
2243 // Note that we're not using ResolveUnreachable() for unreachable
2244 // statements here. ResolveUnreachable() creates a temporary
2245 // flow branching and kills it afterwards. This leads to problems
2246 // if you have two unreachable statements where the first one
2247 // assigns a variable and the second one tries to access it.
2250 if (!s.Resolve (ec)) {
2252 if (ec.IsInProbingMode)
2255 statements [ix] = EmptyStatement.Value;
2259 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2260 statements [ix] = EmptyStatement.Value;
2262 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2263 if (unreachable && s is LabeledStatement)
2264 throw new InternalErrorException ("should not happen");
2267 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2268 ec.CurrentBranching, statement_count);
2270 while (ec.CurrentBranching is FlowBranchingLabeled)
2271 ec.EndFlowBranching ();
2273 bool flow_unreachable = ec.EndFlowBranching ();
2275 ec.CurrentBlock = prev_block;
2277 if (flow_unreachable)
2278 flags |= Flags.HasRet;
2280 // If we're a non-static `struct' constructor which doesn't have an
2281 // initializer, then we must initialize all of the struct's fields.
2282 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2285 if ((labels != null) && (Report.WarningLevel >= 2)) {
2286 foreach (LabeledStatement label in labels.Values)
2287 if (!label.HasBeenReferenced)
2288 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2291 if (ok && errors == Report.Errors)
2297 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2299 unreachable_shown = true;
2303 Report.Warning (162, 2, loc, "Unreachable code detected");
2305 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2306 bool ok = Resolve (ec);
2307 ec.KillFlowBranching ();
2312 protected override void DoEmit (EmitContext ec)
2314 for (int ix = 0; ix < statements.Count; ix++){
2315 Statement s = (Statement) statements [ix];
2320 public override void Emit (EmitContext ec)
2322 Block prev_block = ec.CurrentBlock;
2323 ec.CurrentBlock = this;
2325 if (scope_initializers != null) {
2326 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2328 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2329 foreach (Statement s in scope_initializers)
2333 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2336 ec.Mark (StartLocation);
2339 if (SymbolWriter.HasSymbolWriter)
2340 EmitSymbolInfo (ec);
2342 ec.CurrentBlock = prev_block;
2345 protected virtual void EmitSymbolInfo (EmitContext ec)
2347 if (variables != null) {
2348 foreach (LocalInfo vi in variables.Values) {
2349 vi.EmitSymbolInfo (ec);
2354 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2356 MutateVariables (storey);
2358 if (scope_initializers != null) {
2359 foreach (Statement s in scope_initializers)
2360 s.MutateHoistedGenericType (storey);
2363 foreach (Statement s in statements)
2364 s.MutateHoistedGenericType (storey);
2367 void MutateVariables (AnonymousMethodStorey storey)
2369 if (variables != null) {
2370 foreach (LocalInfo vi in variables.Values) {
2371 vi.VariableType = storey.MutateType (vi.VariableType);
2375 if (temporary_variables != null) {
2376 foreach (LocalInfo vi in temporary_variables)
2377 vi.VariableType = storey.MutateType (vi.VariableType);
2381 public override string ToString ()
2383 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2386 protected override void CloneTo (CloneContext clonectx, Statement t)
2388 Block target = (Block) t;
2390 clonectx.AddBlockMap (this, target);
2392 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2393 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2395 target.Parent = clonectx.RemapBlockCopy (Parent);
2397 if (variables != null){
2398 target.variables = new Hashtable ();
2400 foreach (DictionaryEntry de in variables){
2401 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2402 target.variables [de.Key] = newlocal;
2403 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2407 target.statements = new ArrayList (statements.Count);
2408 foreach (Statement s in statements)
2409 target.statements.Add (s.Clone (clonectx));
2411 if (target.children != null){
2412 target.children = new ArrayList (children.Count);
2413 foreach (Block b in children){
2414 target.children.Add (clonectx.LookupBlock (b));
2419 // TODO: labels, switch_block, constants (?), anonymous_children
2424 public class ExplicitBlock : Block {
2425 HybridDictionary known_variables;
2426 protected AnonymousMethodStorey am_storey;
2428 public ExplicitBlock (Block parent, Location start, Location end)
2429 : this (parent, (Flags) 0, start, end)
2433 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2434 : base (parent, flags, start, end)
2436 this.Explicit = this;
2440 // Marks a variable with name @name as being used in this or a child block.
2441 // If a variable name has been used in a child block, it's illegal to
2442 // declare a variable with the same name in the current block.
2444 internal void AddKnownVariable (string name, IKnownVariable info)
2446 if (known_variables == null)
2447 known_variables = new HybridDictionary();
2449 known_variables [name] = info;
2452 Parent.Explicit.AddKnownVariable (name, info);
2455 public AnonymousMethodStorey AnonymousMethodStorey {
2456 get { return am_storey; }
2460 // Creates anonymous method storey in current block
2462 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2465 // When referencing a variable in iterator storey from children anonymous method
2467 if (Toplevel.am_storey is IteratorStorey) {
2468 return Toplevel.am_storey;
2472 // An iterator has only 1 storey block
2474 if (ec.CurrentIterator != null)
2475 return ec.CurrentIterator.Storey;
2477 if (am_storey == null) {
2478 MemberBase mc = ec.ResolveContext as MemberBase;
2479 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2482 // Creates anonymous method storey for this block
2484 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2490 public override void Emit (EmitContext ec)
2492 if (am_storey != null)
2493 am_storey.EmitStoreyInstantiation (ec);
2495 bool emit_debug_info = SymbolWriter.HasSymbolWriter && Parent != null && !(am_storey is IteratorStorey);
2496 if (emit_debug_info)
2501 if (emit_debug_info)
2505 public override void EmitMeta (EmitContext ec)
2508 // Creates anonymous method storey
2510 if (am_storey != null) {
2511 if (ec.CurrentAnonymousMethod != null && ec.CurrentAnonymousMethod.Storey != null) {
2512 if (am_storey.OriginalSourceBlock.Explicit.HasCapturedThis) {
2513 ExplicitBlock parent = Toplevel.Parent.Explicit;
2514 while (parent.am_storey == null)
2515 parent = parent.Parent.Explicit;
2517 am_storey.AddParentStoreyReference (parent.am_storey);
2520 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2523 am_storey.DefineType ();
2524 am_storey.ResolveType ();
2525 am_storey.Define ();
2526 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2528 ArrayList ref_blocks = am_storey.ReferencesFromChildrenBlock;
2529 if (ref_blocks != null) {
2530 foreach (ExplicitBlock ref_block in ref_blocks) {
2531 for (ExplicitBlock b = ref_block.Explicit; b != this; b = b.Parent.Explicit) {
2532 if (b.am_storey != null) {
2533 b.am_storey.AddParentStoreyReference (am_storey);
2535 // Stop propagation inside same top block
2536 if (b.Toplevel == Toplevel)
2541 b.HasCapturedVariable = true;
2550 internal IKnownVariable GetKnownVariable (string name)
2552 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2555 public bool HasCapturedThis
2557 set { flags = value ? flags | Flags.HasCapturedThis : flags & ~Flags.HasCapturedThis; }
2558 get { return (flags & Flags.HasCapturedThis) != 0; }
2561 public bool HasCapturedVariable
2563 set { flags = value ? flags | Flags.HasCapturedVariable : flags & ~Flags.HasCapturedVariable; }
2564 get { return (flags & Flags.HasCapturedVariable) != 0; }
2567 protected override void CloneTo (CloneContext clonectx, Statement t)
2569 ExplicitBlock target = (ExplicitBlock) t;
2570 target.known_variables = null;
2571 base.CloneTo (clonectx, t);
2575 public class ToplevelParameterInfo : IKnownVariable {
2576 public readonly ToplevelBlock Block;
2577 public readonly int Index;
2578 public VariableInfo VariableInfo;
2580 Block IKnownVariable.Block {
2581 get { return Block; }
2583 public Parameter Parameter {
2584 get { return Block.Parameters [Index]; }
2587 public Type ParameterType {
2588 get { return Block.Parameters.Types [Index]; }
2591 public Location Location {
2592 get { return Parameter.Location; }
2595 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2603 // A toplevel block contains extra information, the split is done
2604 // only to separate information that would otherwise bloat the more
2605 // lightweight Block.
2607 // In particular, this was introduced when the support for Anonymous
2608 // Methods was implemented.
2610 public class ToplevelBlock : ExplicitBlock {
2611 GenericMethod generic;
2612 FlowBranchingToplevel top_level_branching;
2613 protected Parameters parameters;
2614 ToplevelParameterInfo[] parameter_info;
2615 LocalInfo this_variable;
2617 public HoistedVariable HoistedThisVariable;
2620 // The parameters for the block.
2622 public Parameters Parameters {
2623 get { return parameters; }
2626 public GenericMethod GenericMethod {
2627 get { return generic; }
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)
2753 // Creates block with original statements
2754 AddStatement (new IteratorStatement (iterator, new Block (this, source)));
2756 source.statements = new ArrayList (1);
2757 source.AddStatement (new Return (iterator, iterator.Location));
2758 source.IsIterator = false;
2760 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2761 source.am_storey = iterator_storey;
2762 return iterator_storey;
2765 public FlowBranchingToplevel TopLevelBranching {
2766 get { return top_level_branching; }
2770 // Returns a parameter reference expression for the given name,
2771 // or null if there is no such parameter
2773 public Expression GetParameterReference (string name, Location loc)
2775 for (ToplevelBlock t = this; t != null; t = t.Container) {
2776 Expression expr = t.GetParameterReferenceExpression (name, loc);
2784 protected virtual Expression GetParameterReferenceExpression (string name, Location loc)
2786 int idx = parameters.GetParameterIndexByName (name);
2788 null : new ParameterReference (parameter_info [idx], loc);
2792 // Returns the "this" instance variable of this block.
2793 // See AddThisVariable() for more information.
2795 public LocalInfo ThisVariable {
2796 get { return this_variable; }
2800 // This is used by non-static `struct' constructors which do not have an
2801 // initializer - in this case, the constructor must initialize all of the
2802 // struct's fields. To do this, we add a "this" variable and use the flow
2803 // analysis code to ensure that it's been fully initialized before control
2804 // leaves the constructor.
2806 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2808 if (this_variable == null) {
2809 this_variable = new LocalInfo (ds, this, l);
2810 this_variable.Used = true;
2811 this_variable.IsThis = true;
2813 Variables.Add ("this", this_variable);
2816 return this_variable;
2819 public bool IsIterator {
2820 get { return (flags & Flags.IsIterator) != 0; }
2821 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2824 public bool IsThisAssigned (EmitContext ec)
2826 return this_variable == null || this_variable.IsThisAssigned (ec);
2829 public bool ResolveMeta (EmitContext ec, Parameters ip)
2831 int errors = Report.Errors;
2832 int orig_count = parameters.Count;
2834 if (top_level_branching != null)
2840 // Assert: orig_count != parameter.Count => orig_count == 0
2841 if (orig_count != 0 && orig_count != parameters.Count)
2842 throw new InternalErrorException ("parameter information mismatch");
2844 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2846 for (int i = 0; i < orig_count; ++i) {
2847 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2849 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2852 VariableInfo vi = new VariableInfo (ip, i, offset);
2853 parameter_info [i].VariableInfo = vi;
2854 offset += vi.Length;
2857 ResolveMeta (ec, offset);
2859 top_level_branching = ec.StartFlowBranching (this);
2861 return Report.Errors == errors;
2865 // Check whether all `out' parameters have been assigned.
2867 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2869 if (vector.IsUnreachable)
2872 int n = parameter_info == null ? 0 : parameter_info.Length;
2874 for (int i = 0; i < n; i++) {
2875 VariableInfo var = parameter_info [i].VariableInfo;
2880 if (vector.IsAssigned (var, false))
2883 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2888 public override void EmitMeta (EmitContext ec)
2890 parameters.ResolveVariable ();
2892 // Avoid declaring an IL variable for this_variable since it is not accessed
2893 // from the generated IL
2894 if (this_variable != null)
2895 Variables.Remove ("this");
2899 protected override void EmitSymbolInfo (EmitContext ec)
2901 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2902 if ((ae != null) && (ae.Storey != null))
2903 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2905 base.EmitSymbolInfo (ec);
2908 public override void Emit (EmitContext ec)
2911 ec.Mark (EndLocation);
2915 public class SwitchLabel {
2922 Label il_label_code;
2923 bool il_label_code_set;
2925 public static readonly object NullStringCase = new object ();
2928 // if expr == null, then it is the default case.
2930 public SwitchLabel (Expression expr, Location l)
2936 public Expression Label {
2942 public Location Location {
2946 public object Converted {
2952 public Label GetILLabel (EmitContext ec)
2955 il_label = ec.ig.DefineLabel ();
2956 il_label_set = true;
2961 public Label GetILLabelCode (EmitContext ec)
2963 if (!il_label_code_set){
2964 il_label_code = ec.ig.DefineLabel ();
2965 il_label_code_set = true;
2967 return il_label_code;
2971 // Resolves the expression, reduces it to a literal if possible
2972 // and then converts it to the requested type.
2974 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2976 Expression e = label.Resolve (ec);
2981 Constant c = e as Constant;
2983 Report.Error (150, loc, "A constant value is expected");
2987 if (required_type == TypeManager.string_type && c.GetValue () == null) {
2988 converted = NullStringCase;
2992 if (allow_nullable && c.GetValue () == null) {
2993 converted = NullStringCase;
2997 c = c.ImplicitConversionRequired (ec, required_type, loc);
3001 converted = c.GetValue ();
3005 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3008 if (converted == null)
3010 else if (converted == NullStringCase)
3013 label = converted.ToString ();
3015 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3016 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3019 public SwitchLabel Clone (CloneContext clonectx)
3021 return new SwitchLabel (label.Clone (clonectx), loc);
3025 public class SwitchSection {
3026 // An array of SwitchLabels.
3027 public readonly ArrayList Labels;
3028 public readonly Block Block;
3030 public SwitchSection (ArrayList labels, Block block)
3036 public SwitchSection Clone (CloneContext clonectx)
3038 ArrayList cloned_labels = new ArrayList ();
3040 foreach (SwitchLabel sl in cloned_labels)
3041 cloned_labels.Add (sl.Clone (clonectx));
3043 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3047 public class Switch : Statement {
3048 public ArrayList Sections;
3049 public Expression Expr;
3052 /// Maps constants whose type type SwitchType to their SwitchLabels.
3054 public IDictionary Elements;
3057 /// The governing switch type
3059 public Type SwitchType;
3064 Label default_target;
3066 Expression new_expr;
3069 SwitchSection constant_section;
3070 SwitchSection default_section;
3072 ExpressionStatement string_dictionary;
3073 FieldExpr switch_cache_field;
3074 static int unique_counter;
3078 // Nullable Types support for GMCS.
3080 Nullable.Unwrap unwrap;
3082 protected bool HaveUnwrap {
3083 get { return unwrap != null; }
3086 protected bool HaveUnwrap {
3087 get { return false; }
3092 // The types allowed to be implicitly cast from
3093 // on the governing type
3095 static Type [] allowed_types;
3097 public Switch (Expression e, ArrayList sects, Location l)
3104 public bool GotDefault {
3106 return default_section != null;
3110 public Label DefaultTarget {
3112 return default_target;
3117 // Determines the governing type for a switch. The returned
3118 // expression might be the expression from the switch, or an
3119 // expression that includes any potential conversions to the
3120 // integral types or to string.
3122 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3126 if (t == TypeManager.byte_type ||
3127 t == TypeManager.sbyte_type ||
3128 t == TypeManager.ushort_type ||
3129 t == TypeManager.short_type ||
3130 t == TypeManager.uint32_type ||
3131 t == TypeManager.int32_type ||
3132 t == TypeManager.uint64_type ||
3133 t == TypeManager.int64_type ||
3134 t == TypeManager.char_type ||
3135 t == TypeManager.string_type ||
3136 t == TypeManager.bool_type ||
3137 TypeManager.IsEnumType (t))
3140 if (allowed_types == null){
3141 allowed_types = new Type [] {
3142 TypeManager.sbyte_type,
3143 TypeManager.byte_type,
3144 TypeManager.short_type,
3145 TypeManager.ushort_type,
3146 TypeManager.int32_type,
3147 TypeManager.uint32_type,
3148 TypeManager.int64_type,
3149 TypeManager.uint64_type,
3150 TypeManager.char_type,
3151 TypeManager.string_type
3156 // Try to find a *user* defined implicit conversion.
3158 // If there is no implicit conversion, or if there are multiple
3159 // conversions, we have to report an error
3161 Expression converted = null;
3162 foreach (Type tt in allowed_types){
3165 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3170 // Ignore over-worked ImplicitUserConversions that do
3171 // an implicit conversion in addition to the user conversion.
3173 if (!(e is UserCast))
3176 if (converted != null){
3177 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3187 // Performs the basic sanity checks on the switch statement
3188 // (looks for duplicate keys and non-constant expressions).
3190 // It also returns a hashtable with the keys that we will later
3191 // use to compute the switch tables
3193 bool CheckSwitch (EmitContext ec)
3196 Elements = Sections.Count > 10 ?
3197 (IDictionary)new Hashtable () :
3198 (IDictionary)new ListDictionary ();
3200 foreach (SwitchSection ss in Sections){
3201 foreach (SwitchLabel sl in ss.Labels){
3202 if (sl.Label == null){
3203 if (default_section != null){
3204 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3207 default_section = ss;
3211 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3216 object key = sl.Converted;
3217 if (key == SwitchLabel.NullStringCase)
3218 has_null_case = true;
3221 Elements.Add (key, sl);
3222 } catch (ArgumentException) {
3223 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3231 void EmitObjectInteger (ILGenerator ig, object k)
3234 IntConstant.EmitInt (ig, (int) k);
3235 else if (k is Constant) {
3236 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3239 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3242 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3244 IntConstant.EmitInt (ig, (int) (long) k);
3245 ig.Emit (OpCodes.Conv_I8);
3248 LongConstant.EmitLong (ig, (long) k);
3250 else if (k is ulong)
3252 ulong ul = (ulong) k;
3255 IntConstant.EmitInt (ig, unchecked ((int) ul));
3256 ig.Emit (OpCodes.Conv_U8);
3260 LongConstant.EmitLong (ig, unchecked ((long) ul));
3264 IntConstant.EmitInt (ig, (int) ((char) k));
3265 else if (k is sbyte)
3266 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3268 IntConstant.EmitInt (ig, (int) ((byte) k));
3269 else if (k is short)
3270 IntConstant.EmitInt (ig, (int) ((short) k));
3271 else if (k is ushort)
3272 IntConstant.EmitInt (ig, (int) ((ushort) k));
3274 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3276 throw new Exception ("Unhandled case");
3279 // structure used to hold blocks of keys while calculating table switch
3280 class KeyBlock : IComparable
3282 public KeyBlock (long _first)
3284 first = last = _first;
3288 public ArrayList element_keys = null;
3289 // how many items are in the bucket
3290 public int Size = 1;
3293 get { return (int) (last - first + 1); }
3295 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3297 return kb_last.last - kb_first.first + 1;
3299 public int CompareTo (object obj)
3301 KeyBlock kb = (KeyBlock) obj;
3302 int nLength = Length;
3303 int nLengthOther = kb.Length;
3304 if (nLengthOther == nLength)
3305 return (int) (kb.first - first);
3306 return nLength - nLengthOther;
3311 /// This method emits code for a lookup-based switch statement (non-string)
3312 /// Basically it groups the cases into blocks that are at least half full,
3313 /// and then spits out individual lookup opcodes for each block.
3314 /// It emits the longest blocks first, and short blocks are just
3315 /// handled with direct compares.
3317 /// <param name="ec"></param>
3318 /// <param name="val"></param>
3319 /// <returns></returns>
3320 void TableSwitchEmit (EmitContext ec, Expression val)
3322 int element_count = Elements.Count;
3323 object [] element_keys = new object [element_count];
3324 Elements.Keys.CopyTo (element_keys, 0);
3325 Array.Sort (element_keys);
3327 // initialize the block list with one element per key
3328 ArrayList key_blocks = new ArrayList (element_count);
3329 foreach (object key in element_keys)
3330 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3332 KeyBlock current_kb;
3333 // iteratively merge the blocks while they are at least half full
3334 // there's probably a really cool way to do this with a tree...
3335 while (key_blocks.Count > 1)
3337 ArrayList key_blocks_new = new ArrayList ();
3338 current_kb = (KeyBlock) key_blocks [0];
3339 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3341 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3342 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3345 current_kb.last = kb.last;
3346 current_kb.Size += kb.Size;
3350 // start a new block
3351 key_blocks_new.Add (current_kb);
3355 key_blocks_new.Add (current_kb);
3356 if (key_blocks.Count == key_blocks_new.Count)
3358 key_blocks = key_blocks_new;
3361 // initialize the key lists
3362 foreach (KeyBlock kb in key_blocks)
3363 kb.element_keys = new ArrayList ();
3365 // fill the key lists
3367 if (key_blocks.Count > 0) {
3368 current_kb = (KeyBlock) key_blocks [0];
3369 foreach (object key in element_keys)
3371 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3372 System.Convert.ToInt64 (key) > current_kb.last;
3374 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3375 current_kb.element_keys.Add (key);
3379 // sort the blocks so we can tackle the largest ones first
3382 // okay now we can start...
3383 ILGenerator ig = ec.ig;
3384 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3385 Label lbl_default = default_target;
3387 Type type_keys = null;
3388 if (element_keys.Length > 0)
3389 type_keys = element_keys [0].GetType (); // used for conversions
3393 if (TypeManager.IsEnumType (SwitchType))
3394 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3396 compare_type = SwitchType;
3398 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3400 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3401 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3404 foreach (object key in kb.element_keys) {
3405 SwitchLabel sl = (SwitchLabel) Elements [key];
3406 if (key is int && (int) key == 0) {
3407 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3410 EmitObjectInteger (ig, key);
3411 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3417 // TODO: if all the keys in the block are the same and there are
3418 // no gaps/defaults then just use a range-check.
3419 if (compare_type == TypeManager.int64_type ||
3420 compare_type == TypeManager.uint64_type)
3422 // TODO: optimize constant/I4 cases
3424 // check block range (could be > 2^31)
3426 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3427 ig.Emit (OpCodes.Blt, lbl_default);
3429 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3430 ig.Emit (OpCodes.Bgt, lbl_default);
3436 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3437 ig.Emit (OpCodes.Sub);
3439 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3445 int first = (int) kb.first;
3448 IntConstant.EmitInt (ig, first);
3449 ig.Emit (OpCodes.Sub);
3453 IntConstant.EmitInt (ig, -first);
3454 ig.Emit (OpCodes.Add);
3458 // first, build the list of labels for the switch
3460 int cJumps = kb.Length;
3461 Label [] switch_labels = new Label [cJumps];
3462 for (int iJump = 0; iJump < cJumps; iJump++)
3464 object key = kb.element_keys [iKey];
3465 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3467 SwitchLabel sl = (SwitchLabel) Elements [key];
3468 switch_labels [iJump] = sl.GetILLabel (ec);
3472 switch_labels [iJump] = lbl_default;
3474 // emit the switch opcode
3475 ig.Emit (OpCodes.Switch, switch_labels);
3478 // mark the default for this block
3480 ig.MarkLabel (lbl_default);
3483 // TODO: find the default case and emit it here,
3484 // to prevent having to do the following jump.
3485 // make sure to mark other labels in the default section
3487 // the last default just goes to the end
3488 if (element_keys.Length > 0)
3489 ig.Emit (OpCodes.Br, lbl_default);
3491 // now emit the code for the sections
3492 bool found_default = false;
3494 foreach (SwitchSection ss in Sections) {
3495 foreach (SwitchLabel sl in ss.Labels) {
3496 if (sl.Converted == SwitchLabel.NullStringCase) {
3497 ig.MarkLabel (null_target);
3498 } else if (sl.Label == null) {
3499 ig.MarkLabel (lbl_default);
3500 found_default = true;
3502 ig.MarkLabel (null_target);
3504 ig.MarkLabel (sl.GetILLabel (ec));
3505 ig.MarkLabel (sl.GetILLabelCode (ec));
3510 if (!found_default) {
3511 ig.MarkLabel (lbl_default);
3512 if (!has_null_case) {
3513 ig.MarkLabel (null_target);
3517 ig.MarkLabel (lbl_end);
3520 SwitchSection FindSection (SwitchLabel label)
3522 foreach (SwitchSection ss in Sections){
3523 foreach (SwitchLabel sl in ss.Labels){
3532 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3534 foreach (SwitchSection ss in Sections)
3535 ss.Block.MutateHoistedGenericType (storey);
3538 public static void Reset ()
3543 public override bool Resolve (EmitContext ec)
3545 Expr = Expr.Resolve (ec);
3549 new_expr = SwitchGoverningType (ec, Expr);
3552 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3553 unwrap = Nullable.Unwrap.Create (Expr, ec);
3557 new_expr = SwitchGoverningType (ec, unwrap);
3561 if (new_expr == null){
3562 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3567 SwitchType = new_expr.Type;
3569 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3570 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3574 if (!CheckSwitch (ec))
3578 Elements.Remove (SwitchLabel.NullStringCase);
3580 Switch old_switch = ec.Switch;
3582 ec.Switch.SwitchType = SwitchType;
3584 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3585 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3587 is_constant = new_expr is Constant;
3589 object key = ((Constant) new_expr).GetValue ();
3590 SwitchLabel label = (SwitchLabel) Elements [key];
3592 constant_section = FindSection (label);
3593 if (constant_section == null)
3594 constant_section = default_section;
3599 foreach (SwitchSection ss in Sections){
3601 ec.CurrentBranching.CreateSibling (
3602 null, FlowBranching.SiblingType.SwitchSection);
3606 if (is_constant && (ss != constant_section)) {
3607 // If we're a constant switch, we're only emitting
3608 // one single section - mark all the others as
3610 ec.CurrentBranching.CurrentUsageVector.Goto ();
3611 if (!ss.Block.ResolveUnreachable (ec, true)) {
3615 if (!ss.Block.Resolve (ec))
3620 if (default_section == null)
3621 ec.CurrentBranching.CreateSibling (
3622 null, FlowBranching.SiblingType.SwitchSection);
3624 ec.EndFlowBranching ();
3625 ec.Switch = old_switch;
3627 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3632 if (SwitchType == TypeManager.string_type && !is_constant) {
3633 // TODO: Optimize single case, and single+default case
3634 ResolveStringSwitchMap (ec);
3640 void ResolveStringSwitchMap (EmitContext ec)
3642 FullNamedExpression string_dictionary_type;
3644 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3645 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3647 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3649 new TypeExpression (TypeManager.string_type, loc),
3650 new TypeExpression (TypeManager.int32_type, loc)), loc);
3652 MemberAccess system_collections_generic = new MemberAccess (
3653 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3655 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3657 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3658 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3659 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3660 if (!field.Define ())
3662 ec.TypeContainer.PartialContainer.AddField (field);
3664 ArrayList init = new ArrayList ();
3667 string value = null;
3668 foreach (SwitchSection section in Sections) {
3669 foreach (SwitchLabel sl in section.Labels) {
3670 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3675 value = (string) sl.Converted;
3676 ArrayList init_args = new ArrayList (2);
3677 init_args.Add (new StringLiteral (value, sl.Location));
3678 init_args.Add (new IntConstant (counter, loc));
3679 init.Add (new CollectionElementInitializer (init_args, loc));
3685 Elements.Add (counter, section.Labels [0]);
3689 ArrayList args = new ArrayList (1);
3690 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3691 Expression initializer = new NewInitialize (string_dictionary_type, args,
3692 new CollectionOrObjectInitializers (init, loc), loc);
3694 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3695 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3698 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3700 ILGenerator ig = ec.ig;
3701 Label l_initialized = ig.DefineLabel ();
3704 // Skip initialization when value is null
3706 value.EmitBranchable (ec, null_target, false);
3709 // Check if string dictionary is initialized and initialize
3711 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3712 string_dictionary.EmitStatement (ec);
3713 ig.MarkLabel (l_initialized);
3715 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3718 ArrayList get_value_args = new ArrayList (2);
3719 get_value_args.Add (new Argument (value));
3720 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3721 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3722 if (get_item == null)
3726 // A value was not found, go to default case
3728 get_item.EmitBranchable (ec, default_target, false);
3730 ArrayList get_value_args = new ArrayList (1);
3731 get_value_args.Add (value);
3733 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3734 if (get_item == null)
3737 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3738 get_item_object.EmitAssign (ec, get_item, true, false);
3739 ec.ig.Emit (OpCodes.Brfalse, default_target);
3741 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3742 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3744 get_item_int.EmitStatement (ec);
3745 get_item_object.Release (ec);
3747 TableSwitchEmit (ec, string_switch_variable);
3748 string_switch_variable.Release (ec);
3751 protected override void DoEmit (EmitContext ec)
3753 ILGenerator ig = ec.ig;
3755 default_target = ig.DefineLabel ();
3756 null_target = ig.DefineLabel ();
3758 // Store variable for comparission purposes
3759 // TODO: Don't duplicate non-captured VariableReference
3760 LocalTemporary value;
3762 value = new LocalTemporary (SwitchType);
3764 unwrap.EmitCheck (ec);
3765 ig.Emit (OpCodes.Brfalse, null_target);
3769 } else if (!is_constant) {
3770 value = new LocalTemporary (SwitchType);
3777 // Setup the codegen context
3779 Label old_end = ec.LoopEnd;
3780 Switch old_switch = ec.Switch;
3782 ec.LoopEnd = ig.DefineLabel ();
3787 if (constant_section != null)
3788 constant_section.Block.Emit (ec);
3789 } else if (string_dictionary != null) {
3790 DoEmitStringSwitch (value, ec);
3792 TableSwitchEmit (ec, value);
3798 // Restore context state.
3799 ig.MarkLabel (ec.LoopEnd);
3802 // Restore the previous context
3804 ec.LoopEnd = old_end;
3805 ec.Switch = old_switch;
3808 protected override void CloneTo (CloneContext clonectx, Statement t)
3810 Switch target = (Switch) t;
3812 target.Expr = Expr.Clone (clonectx);
3813 target.Sections = new ArrayList ();
3814 foreach (SwitchSection ss in Sections){
3815 target.Sections.Add (ss.Clone (clonectx));
3820 // A place where execution can restart in an iterator
3821 public abstract class ResumableStatement : Statement
3824 protected Label resume_point;
3826 public Label PrepareForEmit (EmitContext ec)
3830 resume_point = ec.ig.DefineLabel ();
3832 return resume_point;
3835 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3839 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3844 // Base class for statements that are implemented in terms of try...finally
3845 public abstract class ExceptionStatement : ResumableStatement
3849 protected abstract void EmitPreTryBody (EmitContext ec);
3850 protected abstract void EmitTryBody (EmitContext ec);
3851 protected abstract void EmitFinallyBody (EmitContext ec);
3853 protected sealed override void DoEmit (EmitContext ec)
3855 ILGenerator ig = ec.ig;
3857 EmitPreTryBody (ec);
3859 if (resume_points != null) {
3860 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3861 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3864 ig.BeginExceptionBlock ();
3866 if (resume_points != null) {
3867 ig.MarkLabel (resume_point);
3869 // For normal control flow, we want to fall-through the Switch
3870 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3871 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3872 IntConstant.EmitInt (ig, first_resume_pc);
3873 ig.Emit (OpCodes.Sub);
3875 Label [] labels = new Label [resume_points.Count];
3876 for (int i = 0; i < resume_points.Count; ++i)
3877 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3878 ig.Emit (OpCodes.Switch, labels);
3883 ig.BeginFinallyBlock ();
3885 Label start_finally = ec.ig.DefineLabel ();
3886 if (resume_points != null) {
3887 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3888 ig.Emit (OpCodes.Brfalse_S, start_finally);
3889 ig.Emit (OpCodes.Endfinally);
3892 ig.MarkLabel (start_finally);
3893 EmitFinallyBody (ec);
3895 ig.EndExceptionBlock ();
3898 public void SomeCodeFollows ()
3900 code_follows = true;
3903 protected void ResolveReachability (EmitContext ec)
3905 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3906 // So, ensure there's some IL code after this statement.
3907 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3908 ec.NeedReturnLabel ();
3912 ArrayList resume_points;
3913 int first_resume_pc;
3914 public void AddResumePoint (ResumableStatement stmt, int pc)
3916 if (resume_points == null) {
3917 resume_points = new ArrayList ();
3918 first_resume_pc = pc;
3921 if (pc != first_resume_pc + resume_points.Count)
3922 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3924 resume_points.Add (stmt);
3927 Label dispose_try_block;
3928 bool prepared_for_dispose, emitted_dispose;
3929 public override Label PrepareForDispose (EmitContext ec, Label end)
3931 if (!prepared_for_dispose) {
3932 prepared_for_dispose = true;
3933 dispose_try_block = ec.ig.DefineLabel ();
3935 return dispose_try_block;
3938 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3940 if (emitted_dispose)
3943 emitted_dispose = true;
3945 ILGenerator ig = ec.ig;
3947 Label end_of_try = ig.DefineLabel ();
3949 // Ensure that the only way we can get into this code is through a dispatcher
3950 if (have_dispatcher)
3951 ig.Emit (OpCodes.Br, end);
3953 ig.BeginExceptionBlock ();
3955 ig.MarkLabel (dispose_try_block);
3957 Label [] labels = null;
3958 for (int i = 0; i < resume_points.Count; ++i) {
3959 ResumableStatement s = (ResumableStatement) resume_points [i];
3960 Label ret = s.PrepareForDispose (ec, end_of_try);
3961 if (ret.Equals (end_of_try) && labels == null)
3963 if (labels == null) {
3964 labels = new Label [resume_points.Count];
3965 for (int j = 0; j < i; ++j)
3966 labels [j] = end_of_try;
3971 if (labels != null) {
3973 for (j = 1; j < labels.Length; ++j)
3974 if (!labels [0].Equals (labels [j]))
3976 bool emit_dispatcher = j < labels.Length;
3978 if (emit_dispatcher) {
3979 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3980 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3981 IntConstant.EmitInt (ig, first_resume_pc);
3982 ig.Emit (OpCodes.Sub);
3983 ig.Emit (OpCodes.Switch, labels);
3984 //SymbolWriter.EndIteratorDispatcher (ec.ig);
3987 foreach (ResumableStatement s in resume_points)
3988 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
3991 ig.MarkLabel (end_of_try);
3993 ig.BeginFinallyBlock ();
3995 EmitFinallyBody (ec);
3997 ig.EndExceptionBlock ();
4001 public class Lock : ExceptionStatement {
4003 public Statement Statement;
4004 TemporaryVariable temp;
4006 public Lock (Expression expr, Statement stmt, Location l)
4013 public override bool Resolve (EmitContext ec)
4015 expr = expr.Resolve (ec);
4019 if (expr.Type.IsValueType){
4020 Report.Error (185, loc,
4021 "`{0}' is not a reference type as required by the lock statement",
4022 TypeManager.CSharpName (expr.Type));
4026 ec.StartFlowBranching (this);
4027 bool ok = Statement.Resolve (ec);
4028 ec.EndFlowBranching ();
4030 ResolveReachability (ec);
4032 // Avoid creating libraries that reference the internal
4035 if (t == TypeManager.null_type)
4036 t = TypeManager.object_type;
4038 temp = new TemporaryVariable (t, loc);
4041 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4042 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4043 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4044 monitor_type, "Enter", loc, TypeManager.object_type);
4045 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4046 monitor_type, "Exit", loc, TypeManager.object_type);
4052 protected override void EmitPreTryBody (EmitContext ec)
4054 ILGenerator ig = ec.ig;
4056 temp.EmitAssign (ec, expr);
4058 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4061 protected override void EmitTryBody (EmitContext ec)
4063 Statement.Emit (ec);
4066 protected override void EmitFinallyBody (EmitContext ec)
4069 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4072 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4074 expr.MutateHoistedGenericType (storey);
4075 temp.MutateHoistedGenericType (storey);
4076 Statement.MutateHoistedGenericType (storey);
4079 protected override void CloneTo (CloneContext clonectx, Statement t)
4081 Lock target = (Lock) t;
4083 target.expr = expr.Clone (clonectx);
4084 target.Statement = Statement.Clone (clonectx);
4088 public class Unchecked : Statement {
4091 public Unchecked (Block b)
4097 public override bool Resolve (EmitContext ec)
4099 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4100 return Block.Resolve (ec);
4103 protected override void DoEmit (EmitContext ec)
4105 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4109 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4111 Block.MutateHoistedGenericType (storey);
4114 protected override void CloneTo (CloneContext clonectx, Statement t)
4116 Unchecked target = (Unchecked) t;
4118 target.Block = clonectx.LookupBlock (Block);
4122 public class Checked : Statement {
4125 public Checked (Block b)
4128 b.Unchecked = false;
4131 public override bool Resolve (EmitContext ec)
4133 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4134 return Block.Resolve (ec);
4137 protected override void DoEmit (EmitContext ec)
4139 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4143 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4145 Block.MutateHoistedGenericType (storey);
4148 protected override void CloneTo (CloneContext clonectx, Statement t)
4150 Checked target = (Checked) t;
4152 target.Block = clonectx.LookupBlock (Block);
4156 public class Unsafe : Statement {
4159 public Unsafe (Block b)
4162 Block.Unsafe = true;
4165 public override bool Resolve (EmitContext ec)
4167 using (ec.With (EmitContext.Flags.InUnsafe, true))
4168 return Block.Resolve (ec);
4171 protected override void DoEmit (EmitContext ec)
4173 using (ec.With (EmitContext.Flags.InUnsafe, true))
4177 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4179 Block.MutateHoistedGenericType (storey);
4182 protected override void CloneTo (CloneContext clonectx, Statement t)
4184 Unsafe target = (Unsafe) t;
4186 target.Block = clonectx.LookupBlock (Block);
4193 public class Fixed : Statement {
4195 ArrayList declarators;
4196 Statement statement;
4201 abstract class Emitter
4203 protected LocalInfo vi;
4204 protected Expression converted;
4206 protected Emitter (Expression expr, LocalInfo li)
4212 public abstract void Emit (EmitContext ec);
4213 public abstract void EmitExit (EmitContext ec);
4216 class ExpressionEmitter : Emitter {
4217 public ExpressionEmitter (Expression converted, LocalInfo li) :
4218 base (converted, li)
4222 public override void Emit (EmitContext ec) {
4224 // Store pointer in pinned location
4226 converted.Emit (ec);
4230 public override void EmitExit (EmitContext ec)
4232 ec.ig.Emit (OpCodes.Ldc_I4_0);
4233 ec.ig.Emit (OpCodes.Conv_U);
4238 class StringEmitter : Emitter {
4239 class StringPtr : Expression
4243 public StringPtr (LocalBuilder b, Location l)
4246 eclass = ExprClass.Value;
4247 type = TypeManager.char_ptr_type;
4251 public override Expression CreateExpressionTree (EmitContext ec)
4253 throw new NotSupportedException ("ET");
4256 public override Expression DoResolve (EmitContext ec)
4258 // This should never be invoked, we are born in fully
4259 // initialized state.
4264 public override void Emit (EmitContext ec)
4266 if (TypeManager.int_get_offset_to_string_data == null) {
4267 // TODO: Move to resolve !!
4268 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4269 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4272 ILGenerator ig = ec.ig;
4274 ig.Emit (OpCodes.Ldloc, b);
4275 ig.Emit (OpCodes.Conv_I);
4276 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4277 ig.Emit (OpCodes.Add);
4281 LocalBuilder pinned_string;
4284 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4290 public override void Emit (EmitContext ec)
4292 ILGenerator ig = ec.ig;
4293 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4295 converted.Emit (ec);
4296 ig.Emit (OpCodes.Stloc, pinned_string);
4298 Expression sptr = new StringPtr (pinned_string, loc);
4299 converted = Convert.ImplicitConversionRequired (
4300 ec, sptr, vi.VariableType, loc);
4302 if (converted == null)
4305 converted.Emit (ec);
4309 public override void EmitExit (EmitContext ec)
4311 ec.ig.Emit (OpCodes.Ldnull);
4312 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4316 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4319 declarators = decls;
4324 public Statement Statement {
4325 get { return statement; }
4328 public override bool Resolve (EmitContext ec)
4331 Expression.UnsafeError (loc);
4335 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4336 if (texpr == null) {
4337 if (type is VarExpr)
4338 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4343 expr_type = texpr.Type;
4345 data = new Emitter [declarators.Count];
4347 if (!expr_type.IsPointer){
4348 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4353 foreach (Pair p in declarators){
4354 LocalInfo vi = (LocalInfo) p.First;
4355 Expression e = (Expression) p.Second;
4357 vi.VariableInfo.SetAssigned (ec);
4358 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4361 // The rules for the possible declarators are pretty wise,
4362 // but the production on the grammar is more concise.
4364 // So we have to enforce these rules here.
4366 // We do not resolve before doing the case 1 test,
4367 // because the grammar is explicit in that the token &
4368 // is present, so we need to test for this particular case.
4372 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4376 ec.InFixedInitializer = true;
4378 ec.InFixedInitializer = false;
4385 if (e.Type.IsArray){
4386 Type array_type = TypeManager.GetElementType (e.Type);
4389 // Provided that array_type is unmanaged,
4391 if (!TypeManager.VerifyUnManaged (array_type, loc))
4395 // and T* is implicitly convertible to the
4396 // pointer type given in the fixed statement.
4398 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4400 Expression converted = Convert.ImplicitConversionRequired (
4401 ec, array_ptr, vi.VariableType, loc);
4402 if (converted == null)
4406 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4408 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4409 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4410 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4411 new NullPointer (loc),
4414 converted = converted.Resolve (ec);
4416 data [i] = new ExpressionEmitter (converted, vi);
4425 if (e.Type == TypeManager.string_type){
4426 data [i] = new StringEmitter (e, vi, loc);
4431 // Case 4: fixed buffer
4432 if (e is FixedBufferPtr) {
4433 data [i++] = new ExpressionEmitter (e, vi);
4438 // Case 1: & object.
4440 Unary u = e as Unary;
4441 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4442 IVariableReference vr = u.Expr as IVariableReference;
4443 if (vr == null || !vr.IsFixed) {
4444 data [i] = new ExpressionEmitter (e, vi);
4448 if (data [i++] == null)
4449 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4451 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4454 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4455 bool ok = statement.Resolve (ec);
4456 bool flow_unreachable = ec.EndFlowBranching ();
4457 has_ret = flow_unreachable;
4462 protected override void DoEmit (EmitContext ec)
4464 for (int i = 0; i < data.Length; i++) {
4468 statement.Emit (ec);
4474 // Clear the pinned variable
4476 for (int i = 0; i < data.Length; i++) {
4477 data [i].EmitExit (ec);
4481 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4483 // Fixed statement cannot be used inside anonymous methods or lambdas
4484 throw new NotSupportedException ();
4487 protected override void CloneTo (CloneContext clonectx, Statement t)
4489 Fixed target = (Fixed) t;
4491 target.type = type.Clone (clonectx);
4492 target.declarators = new ArrayList (declarators.Count);
4493 foreach (Pair p in declarators) {
4494 LocalInfo vi = (LocalInfo) p.First;
4495 Expression e = (Expression) p.Second;
4497 target.declarators.Add (
4498 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4501 target.statement = statement.Clone (clonectx);
4505 public class Catch : Statement {
4506 public readonly string Name;
4508 public Block VarBlock;
4510 Expression type_expr;
4513 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4518 VarBlock = var_block;
4522 public Type CatchType {
4528 public bool IsGeneral {
4530 return type_expr == null;
4534 protected override void DoEmit (EmitContext ec)
4536 ILGenerator ig = ec.ig;
4538 if (CatchType != null)
4539 ig.BeginCatchBlock (CatchType);
4541 ig.BeginCatchBlock (TypeManager.object_type);
4543 if (VarBlock != null)
4547 // TODO: Move to resolve
4548 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4552 if (lvr.IsHoisted) {
4553 LocalTemporary lt = new LocalTemporary (lvr.Type);
4557 // Variable is at the top of the stack
4558 source = EmptyExpression.Null;
4561 lvr.EmitAssign (ec, source, false, false);
4563 ig.Emit (OpCodes.Pop);
4568 public override bool Resolve (EmitContext ec)
4570 using (ec.With (EmitContext.Flags.InCatch, true)) {
4571 if (type_expr != null) {
4572 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4578 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4579 Error (155, "The type caught or thrown must be derived from System.Exception");
4585 if (!Block.Resolve (ec))
4588 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4589 // emit the "unused variable" warnings.
4590 if (VarBlock != null)
4591 return VarBlock.Resolve (ec);
4597 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4600 type = storey.MutateType (type);
4601 if (VarBlock != null)
4602 VarBlock.MutateHoistedGenericType (storey);
4603 Block.MutateHoistedGenericType (storey);
4606 protected override void CloneTo (CloneContext clonectx, Statement t)
4608 Catch target = (Catch) t;
4610 if (type_expr != null)
4611 target.type_expr = type_expr.Clone (clonectx);
4612 if (VarBlock != null)
4613 target.VarBlock = clonectx.LookupBlock (VarBlock);
4614 target.Block = clonectx.LookupBlock (Block);
4618 public class TryFinally : ExceptionStatement {
4622 public TryFinally (Statement stmt, Block fini, Location l)
4629 public override bool Resolve (EmitContext ec)
4633 ec.StartFlowBranching (this);
4635 if (!stmt.Resolve (ec))
4639 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4640 using (ec.With (EmitContext.Flags.InFinally, true)) {
4641 if (!fini.Resolve (ec))
4645 ec.EndFlowBranching ();
4647 ResolveReachability (ec);
4652 protected override void EmitPreTryBody (EmitContext ec)
4656 protected override void EmitTryBody (EmitContext ec)
4661 protected override void EmitFinallyBody (EmitContext ec)
4666 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4668 stmt.MutateHoistedGenericType (storey);
4669 fini.MutateHoistedGenericType (storey);
4672 protected override void CloneTo (CloneContext clonectx, Statement t)
4674 TryFinally target = (TryFinally) t;
4676 target.stmt = (Statement) stmt.Clone (clonectx);
4678 target.fini = clonectx.LookupBlock (fini);
4682 public class TryCatch : Statement {
4684 public ArrayList Specific;
4685 public Catch General;
4686 bool inside_try_finally, code_follows;
4688 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4691 this.Specific = catch_clauses;
4692 this.General = null;
4693 this.inside_try_finally = inside_try_finally;
4695 for (int i = 0; i < catch_clauses.Count; ++i) {
4696 Catch c = (Catch) catch_clauses [i];
4698 if (i != catch_clauses.Count - 1)
4699 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4701 catch_clauses.RemoveAt (i);
4709 public override bool Resolve (EmitContext ec)
4713 ec.StartFlowBranching (this);
4715 if (!Block.Resolve (ec))
4718 Type[] prev_catches = new Type [Specific.Count];
4720 foreach (Catch c in Specific){
4721 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4723 if (c.Name != null) {
4724 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4726 throw new Exception ();
4728 vi.VariableInfo = null;
4731 if (!c.Resolve (ec))
4734 Type resolved_type = c.CatchType;
4735 for (int ii = 0; ii < last_index; ++ii) {
4736 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4737 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4742 prev_catches [last_index++] = resolved_type;
4745 if (General != null) {
4746 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4747 foreach (Catch c in Specific){
4748 if (c.CatchType == TypeManager.exception_type) {
4749 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'");
4754 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4756 if (!General.Resolve (ec))
4760 ec.EndFlowBranching ();
4762 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4763 // So, ensure there's some IL code after this statement
4764 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4765 ec.NeedReturnLabel ();
4770 public void SomeCodeFollows ()
4772 code_follows = true;
4775 protected override void DoEmit (EmitContext ec)
4777 ILGenerator ig = ec.ig;
4779 if (!inside_try_finally)
4780 ig.BeginExceptionBlock ();
4784 foreach (Catch c in Specific)
4787 if (General != null)
4790 if (!inside_try_finally)
4791 ig.EndExceptionBlock ();
4794 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4796 Block.MutateHoistedGenericType (storey);
4798 if (General != null)
4799 General.MutateHoistedGenericType (storey);
4800 if (Specific != null) {
4801 foreach (Catch c in Specific)
4802 c.MutateHoistedGenericType (storey);
4806 protected override void CloneTo (CloneContext clonectx, Statement t)
4808 TryCatch target = (TryCatch) t;
4810 target.Block = clonectx.LookupBlock (Block);
4811 if (General != null)
4812 target.General = (Catch) General.Clone (clonectx);
4813 if (Specific != null){
4814 target.Specific = new ArrayList ();
4815 foreach (Catch c in Specific)
4816 target.Specific.Add (c.Clone (clonectx));
4821 public class UsingTemporary : ExceptionStatement {
4822 TemporaryVariable local_copy;
4823 public Statement Statement;
4827 public UsingTemporary (Expression expr, Statement stmt, Location l)
4834 public override bool Resolve (EmitContext ec)
4836 expr = expr.Resolve (ec);
4840 expr_type = expr.Type;
4842 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4843 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4844 Using.Error_IsNotConvertibleToIDisposable (expr);
4849 local_copy = new TemporaryVariable (expr_type, loc);
4850 local_copy.Resolve (ec);
4852 ec.StartFlowBranching (this);
4854 bool ok = Statement.Resolve (ec);
4856 ec.EndFlowBranching ();
4858 ResolveReachability (ec);
4860 if (TypeManager.void_dispose_void == null) {
4861 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4862 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4868 protected override void EmitPreTryBody (EmitContext ec)
4870 local_copy.EmitAssign (ec, expr);
4873 protected override void EmitTryBody (EmitContext ec)
4875 Statement.Emit (ec);
4878 protected override void EmitFinallyBody (EmitContext ec)
4880 ILGenerator ig = ec.ig;
4881 if (!expr_type.IsValueType) {
4882 Label skip = ig.DefineLabel ();
4883 local_copy.Emit (ec);
4884 ig.Emit (OpCodes.Brfalse, skip);
4885 local_copy.Emit (ec);
4886 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4887 ig.MarkLabel (skip);
4891 Expression ml = Expression.MemberLookup (
4892 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4893 "Dispose", Location.Null);
4895 if (!(ml is MethodGroupExpr)) {
4896 local_copy.Emit (ec);
4897 ig.Emit (OpCodes.Box, expr_type);
4898 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4902 MethodInfo mi = null;
4904 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4905 if (TypeManager.GetParameterData (mk).Count == 0) {
4912 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4916 local_copy.AddressOf (ec, AddressOp.Load);
4917 ig.Emit (OpCodes.Call, mi);
4920 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4922 expr_type = storey.MutateType (expr_type);
4923 local_copy.MutateHoistedGenericType (storey);
4924 Statement.MutateHoistedGenericType (storey);
4927 protected override void CloneTo (CloneContext clonectx, Statement t)
4929 UsingTemporary target = (UsingTemporary) t;
4931 target.expr = expr.Clone (clonectx);
4932 target.Statement = Statement.Clone (clonectx);
4936 public class Using : ExceptionStatement {
4938 public Statement EmbeddedStatement {
4939 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4945 Expression converted_var;
4946 ExpressionStatement assign;
4948 public Using (Expression var, Expression init, Statement stmt, Location l)
4956 bool ResolveVariable (EmitContext ec)
4958 ExpressionStatement a = new SimpleAssign (var, init, loc);
4959 a = a.ResolveStatement (ec);
4965 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4966 converted_var = var;
4970 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4972 Error_IsNotConvertibleToIDisposable (var);
4981 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4983 Report.SymbolRelatedToPreviousError (expr.Type);
4984 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4985 expr.GetSignatureForError ());
4988 protected override void EmitPreTryBody (EmitContext ec)
4990 assign.EmitStatement (ec);
4993 protected override void EmitTryBody (EmitContext ec)
4998 protected override void EmitFinallyBody (EmitContext ec)
5000 ILGenerator ig = ec.ig;
5002 if (!var.Type.IsValueType) {
5003 Label skip = ig.DefineLabel ();
5005 ig.Emit (OpCodes.Brfalse, skip);
5006 converted_var.Emit (ec);
5007 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5008 ig.MarkLabel (skip);
5010 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5012 if (!(ml is MethodGroupExpr)) {
5014 ig.Emit (OpCodes.Box, var.Type);
5015 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5017 MethodInfo mi = null;
5019 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5020 if (TypeManager.GetParameterData (mk).Count == 0) {
5027 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5031 IMemoryLocation mloc = (IMemoryLocation) var;
5033 mloc.AddressOf (ec, AddressOp.Load);
5034 ig.Emit (OpCodes.Call, mi);
5039 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5041 assign.MutateHoistedGenericType (storey);
5042 var.MutateHoistedGenericType (storey);
5043 stmt.MutateHoistedGenericType (storey);
5046 public override bool Resolve (EmitContext ec)
5048 if (!ResolveVariable (ec))
5051 ec.StartFlowBranching (this);
5053 bool ok = stmt.Resolve (ec);
5055 ec.EndFlowBranching ();
5057 ResolveReachability (ec);
5059 if (TypeManager.void_dispose_void == null) {
5060 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5061 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5067 protected override void CloneTo (CloneContext clonectx, Statement t)
5069 Using target = (Using) t;
5071 target.var = var.Clone (clonectx);
5072 target.init = init.Clone (clonectx);
5073 target.stmt = stmt.Clone (clonectx);
5078 /// Implementation of the foreach C# statement
5080 public class Foreach : Statement {
5082 sealed class ArrayForeach : Statement
5084 class ArrayCounter : TemporaryVariable
5086 StatementExpression increment;
5088 public ArrayCounter (Location loc)
5089 : base (TypeManager.int32_type, loc)
5093 public void ResolveIncrement (EmitContext ec)
5095 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5096 increment.Resolve (ec);
5099 public void EmitIncrement (EmitContext ec)
5101 increment.Emit (ec);
5105 readonly Foreach for_each;
5106 readonly Statement statement;
5109 TemporaryVariable[] lengths;
5110 Expression [] length_exprs;
5111 ArrayCounter[] counter;
5113 TemporaryVariable copy;
5116 public ArrayForeach (Foreach @foreach, int rank)
5118 for_each = @foreach;
5119 statement = for_each.statement;
5122 counter = new ArrayCounter [rank];
5123 length_exprs = new Expression [rank];
5126 // Only use temporary length variables when dealing with
5127 // multi-dimensional arrays
5130 lengths = new TemporaryVariable [rank];
5133 protected override void CloneTo (CloneContext clonectx, Statement target)
5135 throw new NotImplementedException ();
5138 public override bool Resolve (EmitContext ec)
5140 copy = new TemporaryVariable (for_each.expr.Type, loc);
5143 int rank = length_exprs.Length;
5144 ArrayList list = new ArrayList (rank);
5145 for (int i = 0; i < rank; i++) {
5146 counter [i] = new ArrayCounter (loc);
5147 counter [i].ResolveIncrement (ec);
5150 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5152 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5153 lengths [i].Resolve (ec);
5155 ArrayList args = new ArrayList (1);
5156 args.Add (new Argument (new IntConstant (i, loc)));
5157 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5160 list.Add (counter [i]);
5163 access = new ElementAccess (copy, list).Resolve (ec);
5167 Expression var_type = for_each.type;
5168 VarExpr ve = var_type as VarExpr;
5170 // Infer implicitly typed local variable from foreach array type
5171 var_type = new TypeExpression (access.Type, ve.Location);
5174 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5175 if (var_type == null)
5178 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5184 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5185 ec.CurrentBranching.CreateSibling ();
5187 for_each.variable = for_each.variable.ResolveLValue (ec, conv, loc);
5188 if (for_each.variable == null)
5191 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5192 if (!statement.Resolve (ec))
5194 ec.EndFlowBranching ();
5196 // There's no direct control flow from the end of the embedded statement to the end of the loop
5197 ec.CurrentBranching.CurrentUsageVector.Goto ();
5199 ec.EndFlowBranching ();
5204 protected override void DoEmit (EmitContext ec)
5206 ILGenerator ig = ec.ig;
5208 copy.EmitAssign (ec, for_each.expr);
5210 int rank = length_exprs.Length;
5211 Label[] test = new Label [rank];
5212 Label[] loop = new Label [rank];
5214 for (int i = 0; i < rank; i++) {
5215 test [i] = ig.DefineLabel ();
5216 loop [i] = ig.DefineLabel ();
5218 if (lengths != null)
5219 lengths [i].EmitAssign (ec, length_exprs [i]);
5222 IntConstant zero = new IntConstant (0, loc);
5223 for (int i = 0; i < rank; i++) {
5224 counter [i].EmitAssign (ec, zero);
5226 ig.Emit (OpCodes.Br, test [i]);
5227 ig.MarkLabel (loop [i]);
5230 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5232 statement.Emit (ec);
5234 ig.MarkLabel (ec.LoopBegin);
5236 for (int i = rank - 1; i >= 0; i--){
5237 counter [i].EmitIncrement (ec);
5239 ig.MarkLabel (test [i]);
5240 counter [i].Emit (ec);
5242 if (lengths != null)
5243 lengths [i].Emit (ec);
5245 length_exprs [i].Emit (ec);
5247 ig.Emit (OpCodes.Blt, loop [i]);
5250 ig.MarkLabel (ec.LoopEnd);
5253 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5255 for_each.expr.MutateHoistedGenericType (storey);
5257 copy.MutateHoistedGenericType (storey);
5258 conv.MutateHoistedGenericType (storey);
5259 statement.MutateHoistedGenericType (storey);
5261 for (int i = 0; i < counter.Length; i++) {
5262 counter [i].MutateHoistedGenericType (storey);
5263 if (lengths != null)
5264 lengths [i].MutateHoistedGenericType (storey);
5269 sealed class CollectionForeach : Statement
5271 class CollectionForeachStatement : Statement
5274 Expression variable, current, conv;
5275 Statement statement;
5278 public CollectionForeachStatement (Type type, Expression variable,
5279 Expression current, Statement statement,
5283 this.variable = variable;
5284 this.current = current;
5285 this.statement = statement;
5289 protected override void CloneTo (CloneContext clonectx, Statement target)
5291 throw new NotImplementedException ();
5294 public override bool Resolve (EmitContext ec)
5296 current = current.Resolve (ec);
5297 if (current == null)
5300 conv = Convert.ExplicitConversion (ec, current, type, loc);
5304 assign = new SimpleAssign (variable, conv, loc);
5305 if (assign.Resolve (ec) == null)
5308 if (!statement.Resolve (ec))
5314 protected override void DoEmit (EmitContext ec)
5316 assign.EmitStatement (ec);
5317 statement.Emit (ec);
5320 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5322 assign.MutateHoistedGenericType (storey);
5323 statement.MutateHoistedGenericType (storey);
5327 Expression variable, expr;
5328 Statement statement;
5330 TemporaryVariable enumerator;
5335 MethodGroupExpr get_enumerator;
5336 PropertyExpr get_current;
5337 MethodInfo move_next;
5338 Expression var_type;
5339 Type enumerator_type;
5340 bool enumerator_found;
5342 public CollectionForeach (Expression var_type, Expression var,
5343 Expression expr, Statement stmt, Location l)
5345 this.var_type = var_type;
5346 this.variable = var;
5352 protected override void CloneTo (CloneContext clonectx, Statement target)
5354 throw new NotImplementedException ();
5357 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5359 Type return_type = mi.ReturnType;
5362 // Ok, we can access it, now make sure that we can do something
5363 // with this `GetEnumerator'
5366 if (return_type == TypeManager.ienumerator_type ||
5367 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5368 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5370 // If it is not an interface, lets try to find the methods ourselves.
5371 // For example, if we have:
5372 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5373 // We can avoid the iface call. This is a runtime perf boost.
5374 // even bigger if we have a ValueType, because we avoid the cost
5377 // We have to make sure that both methods exist for us to take
5378 // this path. If one of the methods does not exist, we will just
5379 // use the interface. Sadly, this complex if statement is the only
5380 // way I could do this without a goto
5383 if (TypeManager.bool_movenext_void == null) {
5384 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5385 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5388 if (TypeManager.ienumerator_getcurrent == null) {
5389 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5390 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5395 // Prefer a generic enumerator over a non-generic one.
5397 if (return_type.IsInterface && return_type.IsGenericType) {
5398 enumerator_type = return_type;
5399 if (!FetchGetCurrent (ec, return_type))
5400 get_current = new PropertyExpr (
5401 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5402 if (!FetchMoveNext (return_type))
5403 move_next = TypeManager.bool_movenext_void;
5408 if (return_type.IsInterface ||
5409 !FetchMoveNext (return_type) ||
5410 !FetchGetCurrent (ec, return_type)) {
5411 enumerator_type = return_type;
5412 move_next = TypeManager.bool_movenext_void;
5413 get_current = new PropertyExpr (
5414 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5419 // Ok, so they dont return an IEnumerable, we will have to
5420 // find if they support the GetEnumerator pattern.
5423 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5424 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",
5425 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5430 enumerator_type = return_type;
5436 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5438 bool FetchMoveNext (Type t)
5440 MemberList move_next_list;
5442 move_next_list = TypeContainer.FindMembers (
5443 t, MemberTypes.Method,
5444 BindingFlags.Public | BindingFlags.Instance,
5445 Type.FilterName, "MoveNext");
5446 if (move_next_list.Count == 0)
5449 foreach (MemberInfo m in move_next_list){
5450 MethodInfo mi = (MethodInfo) m;
5452 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5453 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5463 // Retrieves a `public T get_Current ()' method from the Type `t'
5465 bool FetchGetCurrent (EmitContext ec, Type t)
5467 PropertyExpr pe = Expression.MemberLookup (
5468 ec.ContainerType, t, "Current", MemberTypes.Property,
5469 Expression.AllBindingFlags, loc) as PropertyExpr;
5478 // Retrieves a `public void Dispose ()' method from the Type `t'
5480 static MethodInfo FetchMethodDispose (Type t)
5482 MemberList dispose_list;
5484 dispose_list = TypeContainer.FindMembers (
5485 t, MemberTypes.Method,
5486 BindingFlags.Public | BindingFlags.Instance,
5487 Type.FilterName, "Dispose");
5488 if (dispose_list.Count == 0)
5491 foreach (MemberInfo m in dispose_list){
5492 MethodInfo mi = (MethodInfo) m;
5494 if (TypeManager.GetParameterData (mi).Count == 0){
5495 if (mi.ReturnType == TypeManager.void_type)
5502 void Error_Enumerator ()
5504 if (enumerator_found) {
5508 Report.Error (1579, loc,
5509 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5510 TypeManager.CSharpName (expr.Type));
5513 bool IsOverride (MethodInfo m)
5515 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5517 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5519 if (m is MethodBuilder)
5522 MethodInfo base_method = m.GetBaseDefinition ();
5523 return base_method != m;
5526 bool TryType (EmitContext ec, Type t)
5528 MethodGroupExpr mg = Expression.MemberLookup (
5529 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5530 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5534 MethodInfo result = null;
5535 MethodInfo tmp_move_next = null;
5536 PropertyExpr tmp_get_cur = null;
5537 Type tmp_enumerator_type = enumerator_type;
5538 foreach (MethodInfo mi in mg.Methods) {
5539 if (TypeManager.GetParameterData (mi).Count != 0)
5542 // Check whether GetEnumerator is public
5543 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5546 if (IsOverride (mi))
5549 enumerator_found = true;
5551 if (!GetEnumeratorFilter (ec, mi))
5554 if (result != null) {
5555 if (TypeManager.IsGenericType (result.ReturnType)) {
5556 if (!TypeManager.IsGenericType (mi.ReturnType))
5559 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5560 Report.SymbolRelatedToPreviousError (t);
5561 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5562 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5563 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5567 // Always prefer generics enumerators
5568 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5569 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5570 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5573 Report.SymbolRelatedToPreviousError (result);
5574 Report.SymbolRelatedToPreviousError (mi);
5575 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5576 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5581 tmp_move_next = move_next;
5582 tmp_get_cur = get_current;
5583 tmp_enumerator_type = enumerator_type;
5584 if (mi.DeclaringType == t)
5588 if (result != null) {
5589 move_next = tmp_move_next;
5590 get_current = tmp_get_cur;
5591 enumerator_type = tmp_enumerator_type;
5592 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5593 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5595 if (t != expr.Type) {
5596 expr = Convert.ExplicitConversion (
5599 throw new InternalErrorException ();
5602 get_enumerator.InstanceExpression = expr;
5603 get_enumerator.IsBase = t != expr.Type;
5611 bool ProbeCollectionType (EmitContext ec, Type t)
5613 int errors = Report.Errors;
5614 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5615 if (TryType (ec, tt))
5620 if (Report.Errors > errors)
5624 // Now try to find the method in the interfaces
5626 Type [] ifaces = TypeManager.GetInterfaces (t);
5627 foreach (Type i in ifaces){
5628 if (TryType (ec, i))
5635 public override bool Resolve (EmitContext ec)
5637 enumerator_type = TypeManager.ienumerator_type;
5639 if (!ProbeCollectionType (ec, expr.Type)) {
5640 Error_Enumerator ();
5644 bool is_disposable = !enumerator_type.IsSealed ||
5645 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5647 VarExpr ve = var_type as VarExpr;
5649 // Infer implicitly typed local variable from foreach enumerable type
5650 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5653 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5654 if (var_type == null)
5657 enumerator = new TemporaryVariable (enumerator_type, loc);
5658 enumerator.Resolve (ec);
5660 init = new Invocation (get_enumerator, null);
5661 init = init.Resolve (ec);
5665 Expression move_next_expr;
5667 MemberInfo[] mi = new MemberInfo[] { move_next };
5668 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5669 mg.InstanceExpression = enumerator;
5671 move_next_expr = new Invocation (mg, null);
5674 get_current.InstanceExpression = enumerator;
5676 Statement block = new CollectionForeachStatement (
5677 var_type.Type, variable, get_current, statement, loc);
5679 loop = new While (move_next_expr, block, loc);
5681 wrapper = is_disposable ?
5682 (Statement) new DisposableWrapper (this) :
5683 (Statement) new NonDisposableWrapper (this);
5684 return wrapper.Resolve (ec);
5687 protected override void DoEmit (EmitContext ec)
5692 class NonDisposableWrapper : Statement {
5693 CollectionForeach parent;
5695 internal NonDisposableWrapper (CollectionForeach parent)
5697 this.parent = parent;
5700 protected override void CloneTo (CloneContext clonectx, Statement target)
5702 throw new NotSupportedException ();
5705 public override bool Resolve (EmitContext ec)
5707 return parent.ResolveLoop (ec);
5710 protected override void DoEmit (EmitContext ec)
5712 parent.EmitLoopInit (ec);
5713 parent.EmitLoopBody (ec);
5716 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5718 throw new NotSupportedException ();
5722 class DisposableWrapper : ExceptionStatement {
5723 CollectionForeach parent;
5725 internal DisposableWrapper (CollectionForeach parent)
5727 this.parent = parent;
5730 protected override void CloneTo (CloneContext clonectx, Statement target)
5732 throw new NotSupportedException ();
5735 public override bool Resolve (EmitContext ec)
5739 ec.StartFlowBranching (this);
5741 if (!parent.ResolveLoop (ec))
5744 ec.EndFlowBranching ();
5746 ResolveReachability (ec);
5748 if (TypeManager.void_dispose_void == null) {
5749 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5750 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5755 protected override void EmitPreTryBody (EmitContext ec)
5757 parent.EmitLoopInit (ec);
5760 protected override void EmitTryBody (EmitContext ec)
5762 parent.EmitLoopBody (ec);
5765 protected override void EmitFinallyBody (EmitContext ec)
5767 parent.EmitFinallyBody (ec);
5770 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5772 throw new NotSupportedException ();
5776 bool ResolveLoop (EmitContext ec)
5778 return loop.Resolve (ec);
5781 void EmitLoopInit (EmitContext ec)
5783 enumerator.EmitAssign (ec, init);
5786 void EmitLoopBody (EmitContext ec)
5791 void EmitFinallyBody (EmitContext ec)
5793 ILGenerator ig = ec.ig;
5795 if (enumerator_type.IsValueType) {
5796 MethodInfo mi = FetchMethodDispose (enumerator_type);
5798 enumerator.AddressOf (ec, AddressOp.Load);
5799 ig.Emit (OpCodes.Call, mi);
5801 enumerator.Emit (ec);
5802 ig.Emit (OpCodes.Box, enumerator_type);
5803 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5806 Label call_dispose = ig.DefineLabel ();
5808 enumerator.Emit (ec);
5809 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5810 ig.Emit (OpCodes.Dup);
5811 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5813 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5814 // (Partition III, Section 3.35)
5815 ig.Emit (OpCodes.Endfinally);
5817 ig.MarkLabel (call_dispose);
5818 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5822 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5824 enumerator_type = storey.MutateType (enumerator_type);
5825 init.MutateHoistedGenericType (storey);
5826 loop.MutateHoistedGenericType (storey);
5831 Expression variable;
5833 Statement statement;
5835 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5836 Statement stmt, Location l)
5839 this.variable = var;
5845 public Statement Statement {
5846 get { return statement; }
5849 public override bool Resolve (EmitContext ec)
5851 expr = expr.Resolve (ec);
5856 Report.Error (186, loc, "Use of null is not valid in this context");
5860 if (expr.Type == TypeManager.string_type) {
5861 statement = new ArrayForeach (this, 1);
5862 } else if (expr.Type.IsArray) {
5863 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5865 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5866 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5867 expr.ExprClassName);
5871 statement = new CollectionForeach (type, variable, expr, statement, loc);
5874 return statement.Resolve (ec);
5877 protected override void DoEmit (EmitContext ec)
5879 ILGenerator ig = ec.ig;
5881 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5882 ec.LoopBegin = ig.DefineLabel ();
5883 ec.LoopEnd = ig.DefineLabel ();
5885 statement.Emit (ec);
5887 ec.LoopBegin = old_begin;
5888 ec.LoopEnd = old_end;
5891 protected override void CloneTo (CloneContext clonectx, Statement t)
5893 Foreach target = (Foreach) t;
5895 target.type = type.Clone (clonectx);
5896 target.variable = variable.Clone (clonectx);
5897 target.expr = expr.Clone (clonectx);
5898 target.statement = statement.Clone (clonectx);
5901 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5903 statement.MutateHoistedGenericType (storey);