2 // statement.cs: Statement representation for the IL tree.
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@seznam.cz)
9 // Copyright 2001, 2002, 2003 Ximian, Inc.
10 // Copyright 2003, 2004 Novell, Inc.
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Diagnostics;
18 using System.Collections;
19 using System.Collections.Specialized;
21 namespace Mono.CSharp {
23 public abstract class Statement {
27 /// Resolves the statement, true means that all sub-statements
30 public virtual bool Resolve (EmitContext ec)
36 /// We already know that the statement is unreachable, but we still
37 /// need to resolve it to catch errors.
39 public virtual bool ResolveUnreachable (EmitContext ec, bool warn)
42 // This conflicts with csc's way of doing this, but IMHO it's
43 // the right thing to do.
45 // If something is unreachable, we still check whether it's
46 // correct. This means that you cannot use unassigned variables
47 // in unreachable code, for instance.
51 Report.Warning (162, 2, loc, "Unreachable code detected");
53 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
54 bool ok = Resolve (ec);
55 ec.KillFlowBranching ();
61 /// Return value indicates whether all code paths emitted return.
63 protected abstract void DoEmit (EmitContext ec);
66 /// Utility wrapper routine for Error, just to beautify the code
68 public void Error (int error, string format, params object[] args)
70 Error (error, String.Format (format, args));
73 public void Error (int error, string s)
76 Report.Error (error, loc, s);
78 Report.Error (error, s);
82 /// Return value indicates whether all code paths emitted return.
84 public virtual void Emit (EmitContext ec)
91 // This routine must be overrided in derived classes and make copies
92 // of all the data that might be modified if resolved
94 protected abstract void CloneTo (CloneContext clonectx, Statement target);
96 public Statement Clone (CloneContext clonectx)
98 Statement s = (Statement) this.MemberwiseClone ();
99 CloneTo (clonectx, s);
103 public virtual Expression CreateExpressionTree (EmitContext ec)
105 Report.Error (834, loc, "A lambda expression with statement body cannot be converted to an expresion tree");
109 public Statement PerformClone ()
111 CloneContext clonectx = new CloneContext ();
113 return Clone (clonectx);
116 public abstract void MutateHoistedGenericType (AnonymousMethodStorey storey);
120 // This class is used during the Statement.Clone operation
121 // to remap objects that have been cloned.
123 // Since blocks are cloned by Block.Clone, we need a way for
124 // expressions that must reference the block to be cloned
125 // pointing to the new cloned block.
127 public class CloneContext {
128 Hashtable block_map = new Hashtable ();
129 Hashtable variable_map;
131 public void AddBlockMap (Block from, Block to)
133 if (block_map.Contains (from))
135 block_map [from] = to;
138 public Block LookupBlock (Block from)
140 Block result = (Block) block_map [from];
143 result = (Block) from.Clone (this);
144 block_map [from] = result;
151 /// Remaps block to cloned copy if one exists.
153 public Block RemapBlockCopy (Block from)
155 Block mapped_to = (Block)block_map[from];
156 if (mapped_to == null)
162 public void AddVariableMap (LocalInfo from, LocalInfo to)
164 if (variable_map == null)
165 variable_map = new Hashtable ();
167 if (variable_map.Contains (from))
169 variable_map [from] = to;
172 public LocalInfo LookupVariable (LocalInfo from)
174 LocalInfo result = (LocalInfo) variable_map [from];
177 throw new Exception ("LookupVariable: looking up a variable that has not been registered yet");
183 public sealed class EmptyStatement : Statement {
185 private EmptyStatement () {}
187 public static readonly EmptyStatement Value = new EmptyStatement ();
189 public override bool Resolve (EmitContext ec)
194 public override bool ResolveUnreachable (EmitContext ec, bool warn)
199 protected override void DoEmit (EmitContext ec)
203 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
207 protected override void CloneTo (CloneContext clonectx, Statement target)
213 public class If : Statement {
215 public Statement TrueStatement;
216 public Statement FalseStatement;
220 public If (Expression expr, Statement true_statement, Location l)
223 TrueStatement = true_statement;
227 public If (Expression expr,
228 Statement true_statement,
229 Statement false_statement,
233 TrueStatement = true_statement;
234 FalseStatement = false_statement;
238 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
240 expr.MutateHoistedGenericType (storey);
241 TrueStatement.MutateHoistedGenericType (storey);
242 if (FalseStatement != null)
243 FalseStatement.MutateHoistedGenericType (storey);
246 public override bool Resolve (EmitContext ec)
250 Report.Debug (1, "START IF BLOCK", loc);
252 expr = Expression.ResolveBoolean (ec, expr, loc);
258 Assign ass = expr as Assign;
259 if (ass != null && ass.Source is Constant) {
260 Report.Warning (665, 3, loc, "Assignment in conditional expression is always constant; did you mean to use == instead of = ?");
264 // Dead code elimination
266 if (expr is Constant){
267 bool take = !((Constant) expr).IsDefaultValue;
270 if (!TrueStatement.Resolve (ec))
273 if ((FalseStatement != null) &&
274 !FalseStatement.ResolveUnreachable (ec, true))
276 FalseStatement = null;
278 if (!TrueStatement.ResolveUnreachable (ec, true))
280 TrueStatement = null;
282 if ((FalseStatement != null) &&
283 !FalseStatement.Resolve (ec))
290 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
292 ok &= TrueStatement.Resolve (ec);
294 is_true_ret = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
296 ec.CurrentBranching.CreateSibling ();
298 if (FalseStatement != null)
299 ok &= FalseStatement.Resolve (ec);
301 ec.EndFlowBranching ();
303 Report.Debug (1, "END IF BLOCK", loc);
308 protected override void DoEmit (EmitContext ec)
310 ILGenerator ig = ec.ig;
311 Label false_target = ig.DefineLabel ();
315 // If we're a boolean constant, Resolve() already
316 // eliminated dead code for us.
318 Constant c = expr as Constant;
320 c.EmitSideEffect (ec);
322 if (!c.IsDefaultValue)
323 TrueStatement.Emit (ec);
324 else if (FalseStatement != null)
325 FalseStatement.Emit (ec);
330 expr.EmitBranchable (ec, false_target, false);
332 TrueStatement.Emit (ec);
334 if (FalseStatement != null){
335 bool branch_emitted = false;
337 end = ig.DefineLabel ();
339 ig.Emit (OpCodes.Br, end);
340 branch_emitted = true;
343 ig.MarkLabel (false_target);
344 FalseStatement.Emit (ec);
349 ig.MarkLabel (false_target);
353 protected override void CloneTo (CloneContext clonectx, Statement t)
357 target.expr = expr.Clone (clonectx);
358 target.TrueStatement = TrueStatement.Clone (clonectx);
359 if (FalseStatement != null)
360 target.FalseStatement = FalseStatement.Clone (clonectx);
364 public class Do : Statement {
365 public Expression expr;
366 public Statement EmbeddedStatement;
368 public Do (Statement statement, Expression bool_expr, Location l)
371 EmbeddedStatement = statement;
375 public override bool Resolve (EmitContext ec)
379 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
381 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
383 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
384 if (!EmbeddedStatement.Resolve (ec))
386 ec.EndFlowBranching ();
388 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable && !was_unreachable)
389 Report.Warning (162, 2, expr.Location, "Unreachable code detected");
391 expr = Expression.ResolveBoolean (ec, expr, loc);
394 else if (expr is Constant){
395 bool infinite = !((Constant) expr).IsDefaultValue;
397 ec.CurrentBranching.CurrentUsageVector.Goto ();
400 ec.EndFlowBranching ();
405 protected override void DoEmit (EmitContext ec)
407 ILGenerator ig = ec.ig;
408 Label loop = ig.DefineLabel ();
409 Label old_begin = ec.LoopBegin;
410 Label old_end = ec.LoopEnd;
412 ec.LoopBegin = ig.DefineLabel ();
413 ec.LoopEnd = ig.DefineLabel ();
416 EmbeddedStatement.Emit (ec);
417 ig.MarkLabel (ec.LoopBegin);
420 // Dead code elimination
422 if (expr is Constant){
423 bool res = !((Constant) expr).IsDefaultValue;
425 expr.EmitSideEffect (ec);
427 ec.ig.Emit (OpCodes.Br, loop);
429 expr.EmitBranchable (ec, loop, true);
431 ig.MarkLabel (ec.LoopEnd);
433 ec.LoopBegin = old_begin;
434 ec.LoopEnd = old_end;
437 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
439 expr.MutateHoistedGenericType (storey);
440 EmbeddedStatement.MutateHoistedGenericType (storey);
443 protected override void CloneTo (CloneContext clonectx, Statement t)
447 target.EmbeddedStatement = EmbeddedStatement.Clone (clonectx);
448 target.expr = expr.Clone (clonectx);
452 public class While : Statement {
453 public Expression expr;
454 public Statement Statement;
455 bool infinite, empty;
457 public While (Expression bool_expr, Statement statement, Location l)
459 this.expr = bool_expr;
460 Statement = statement;
464 public override bool Resolve (EmitContext ec)
468 expr = Expression.ResolveBoolean (ec, expr, loc);
473 // Inform whether we are infinite or not
475 if (expr is Constant){
476 bool value = !((Constant) expr).IsDefaultValue;
479 if (!Statement.ResolveUnreachable (ec, true))
487 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
489 ec.CurrentBranching.CreateSibling ();
491 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
492 if (!Statement.Resolve (ec))
494 ec.EndFlowBranching ();
496 // There's no direct control flow from the end of the embedded statement to the end of the loop
497 ec.CurrentBranching.CurrentUsageVector.Goto ();
499 ec.EndFlowBranching ();
504 protected override void DoEmit (EmitContext ec)
507 expr.EmitSideEffect (ec);
511 ILGenerator ig = ec.ig;
512 Label old_begin = ec.LoopBegin;
513 Label old_end = ec.LoopEnd;
515 ec.LoopBegin = ig.DefineLabel ();
516 ec.LoopEnd = ig.DefineLabel ();
519 // Inform whether we are infinite or not
521 if (expr is Constant){
522 // expr is 'true', since the 'empty' case above handles the 'false' case
523 ig.MarkLabel (ec.LoopBegin);
524 expr.EmitSideEffect (ec);
526 ig.Emit (OpCodes.Br, ec.LoopBegin);
529 // Inform that we are infinite (ie, `we return'), only
530 // if we do not `break' inside the code.
532 ig.MarkLabel (ec.LoopEnd);
534 Label while_loop = ig.DefineLabel ();
536 ig.Emit (OpCodes.Br, ec.LoopBegin);
537 ig.MarkLabel (while_loop);
541 ig.MarkLabel (ec.LoopBegin);
544 expr.EmitBranchable (ec, while_loop, true);
546 ig.MarkLabel (ec.LoopEnd);
549 ec.LoopBegin = old_begin;
550 ec.LoopEnd = old_end;
553 public override void Emit (EmitContext ec)
558 protected override void CloneTo (CloneContext clonectx, Statement t)
560 While target = (While) t;
562 target.expr = expr.Clone (clonectx);
563 target.Statement = Statement.Clone (clonectx);
566 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
568 expr.MutateHoistedGenericType (storey);
569 Statement.MutateHoistedGenericType (storey);
573 public class For : Statement {
575 Statement InitStatement;
577 public Statement Statement;
578 bool infinite, empty;
580 public For (Statement init_statement,
586 InitStatement = init_statement;
588 Increment = increment;
589 Statement = statement;
593 public override bool Resolve (EmitContext ec)
597 if (InitStatement != null){
598 if (!InitStatement.Resolve (ec))
603 Test = Expression.ResolveBoolean (ec, Test, loc);
606 else if (Test is Constant){
607 bool value = !((Constant) Test).IsDefaultValue;
610 if (!Statement.ResolveUnreachable (ec, true))
612 if ((Increment != null) &&
613 !Increment.ResolveUnreachable (ec, false))
623 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
625 ec.CurrentBranching.CreateSibling ();
627 bool was_unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
629 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
630 if (!Statement.Resolve (ec))
632 ec.EndFlowBranching ();
634 if (Increment != null){
635 if (ec.CurrentBranching.CurrentUsageVector.IsUnreachable) {
636 if (!Increment.ResolveUnreachable (ec, !was_unreachable))
639 if (!Increment.Resolve (ec))
644 // There's no direct control flow from the end of the embedded statement to the end of the loop
645 ec.CurrentBranching.CurrentUsageVector.Goto ();
647 ec.EndFlowBranching ();
652 protected override void DoEmit (EmitContext ec)
654 if (InitStatement != null && InitStatement != EmptyStatement.Value)
655 InitStatement.Emit (ec);
658 Test.EmitSideEffect (ec);
662 ILGenerator ig = ec.ig;
663 Label old_begin = ec.LoopBegin;
664 Label old_end = ec.LoopEnd;
665 Label loop = ig.DefineLabel ();
666 Label test = ig.DefineLabel ();
668 ec.LoopBegin = ig.DefineLabel ();
669 ec.LoopEnd = ig.DefineLabel ();
671 ig.Emit (OpCodes.Br, test);
675 ig.MarkLabel (ec.LoopBegin);
676 if (Increment != EmptyStatement.Value)
681 // If test is null, there is no test, and we are just
686 // The Resolve code already catches the case for
687 // Test == Constant (false) so we know that
690 if (Test is Constant) {
691 Test.EmitSideEffect (ec);
692 ig.Emit (OpCodes.Br, loop);
694 Test.EmitBranchable (ec, loop, true);
698 ig.Emit (OpCodes.Br, loop);
699 ig.MarkLabel (ec.LoopEnd);
701 ec.LoopBegin = old_begin;
702 ec.LoopEnd = old_end;
705 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
707 if (InitStatement != null)
708 InitStatement.MutateHoistedGenericType (storey);
710 Test.MutateHoistedGenericType (storey);
711 if (Increment != null)
712 Increment.MutateHoistedGenericType (storey);
714 Statement.MutateHoistedGenericType (storey);
717 protected override void CloneTo (CloneContext clonectx, Statement t)
719 For target = (For) t;
721 if (InitStatement != null)
722 target.InitStatement = InitStatement.Clone (clonectx);
724 target.Test = Test.Clone (clonectx);
725 if (Increment != null)
726 target.Increment = Increment.Clone (clonectx);
727 target.Statement = Statement.Clone (clonectx);
731 public class StatementExpression : Statement {
732 ExpressionStatement expr;
734 public StatementExpression (ExpressionStatement expr)
740 public override bool Resolve (EmitContext ec)
743 expr = expr.ResolveStatement (ec);
747 protected override void DoEmit (EmitContext ec)
749 expr.EmitStatement (ec);
752 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
754 expr.MutateHoistedGenericType (storey);
757 public override string ToString ()
759 return "StatementExpression (" + expr + ")";
762 protected override void CloneTo (CloneContext clonectx, Statement t)
764 StatementExpression target = (StatementExpression) t;
766 target.expr = (ExpressionStatement) expr.Clone (clonectx);
770 // A 'return' or a 'yield break'
771 public abstract class ExitStatement : Statement
773 protected bool unwind_protect;
774 protected abstract bool DoResolve (EmitContext ec);
776 public virtual void Error_FinallyClause ()
778 Report.Error (157, loc, "Control cannot leave the body of a finally clause");
781 public sealed override bool Resolve (EmitContext ec)
786 unwind_protect = ec.CurrentBranching.AddReturnOrigin (ec.CurrentBranching.CurrentUsageVector, this);
788 ec.NeedReturnLabel ();
789 ec.CurrentBranching.CurrentUsageVector.Goto ();
795 /// Implements the return statement
797 public class Return : ExitStatement {
798 protected Expression Expr;
799 public Return (Expression expr, Location l)
805 protected override bool DoResolve (EmitContext ec)
808 if (ec.ReturnType == TypeManager.void_type)
811 Error (126, "An object of a type convertible to `{0}' is required " +
812 "for the return statement",
813 TypeManager.CSharpName (ec.ReturnType));
817 if (ec.CurrentBlock.Toplevel.IsIterator) {
818 Report.Error (1622, loc, "Cannot return a value from iterators. Use the yield return " +
819 "statement to return a value, or yield break to end the iteration");
822 AnonymousExpression am = ec.CurrentAnonymousMethod;
823 if (am == null && ec.ReturnType == TypeManager.void_type) {
824 MemberCore mc = ec.ResolveContext as MemberCore;
825 Report.Error (127, loc, "`{0}': A return keyword must not be followed by any expression when method returns void",
826 mc.GetSignatureForError ());
829 Expr = Expr.Resolve (ec);
833 if (Expr.Type != ec.ReturnType) {
834 if (ec.InferReturnType) {
836 // void cannot be used in contextual return
838 if (Expr.Type == TypeManager.void_type)
841 ec.ReturnType = Expr.Type;
843 Expr = Convert.ImplicitConversionRequired (
844 ec, Expr, ec.ReturnType, loc);
848 Report.Error (1662, loc,
849 "Cannot convert `{0}' to delegate type `{1}' because some of the return types in the block are not implicitly convertible to the delegate return type",
850 am.ContainerType, am.GetSignatureForError ());
860 protected override void DoEmit (EmitContext ec)
866 ec.ig.Emit (OpCodes.Stloc, ec.TemporaryReturn ());
870 ec.ig.Emit (OpCodes.Leave, ec.ReturnLabel);
872 ec.ig.Emit (OpCodes.Ret);
875 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
878 Expr.MutateHoistedGenericType (storey);
881 protected override void CloneTo (CloneContext clonectx, Statement t)
883 Return target = (Return) t;
884 // It's null for simple return;
886 target.Expr = Expr.Clone (clonectx);
890 public class Goto : Statement {
892 LabeledStatement label;
895 public override bool Resolve (EmitContext ec)
897 int errors = Report.Errors;
898 unwind_protect = ec.CurrentBranching.AddGotoOrigin (ec.CurrentBranching.CurrentUsageVector, this);
899 ec.CurrentBranching.CurrentUsageVector.Goto ();
900 return errors == Report.Errors;
903 public Goto (string label, Location l)
909 public string Target {
910 get { return target; }
913 public void SetResolvedTarget (LabeledStatement label)
916 label.AddReference ();
919 protected override void CloneTo (CloneContext clonectx, Statement target)
924 protected override void DoEmit (EmitContext ec)
927 throw new InternalErrorException ("goto emitted before target resolved");
928 Label l = label.LabelTarget (ec);
929 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, l);
932 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
937 public class LabeledStatement : Statement {
944 FlowBranching.UsageVector vectors;
946 public LabeledStatement (string name, Location l)
952 public Label LabelTarget (EmitContext ec)
957 label = ec.ig.DefineLabel ();
967 public bool IsDefined {
968 get { return defined; }
971 public bool HasBeenReferenced {
972 get { return referenced; }
975 public FlowBranching.UsageVector JumpOrigins {
976 get { return vectors; }
979 public void AddUsageVector (FlowBranching.UsageVector vector)
981 vector = vector.Clone ();
982 vector.Next = vectors;
986 protected override void CloneTo (CloneContext clonectx, Statement target)
991 public override bool Resolve (EmitContext ec)
993 // this flow-branching will be terminated when the surrounding block ends
994 ec.StartFlowBranching (this);
998 protected override void DoEmit (EmitContext ec)
1000 if (ig != null && ig != ec.ig)
1001 throw new InternalErrorException ("cannot happen");
1003 ec.ig.MarkLabel (label);
1006 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1010 public void AddReference ()
1018 /// `goto default' statement
1020 public class GotoDefault : Statement {
1022 public GotoDefault (Location l)
1027 protected override void CloneTo (CloneContext clonectx, Statement target)
1032 public override bool Resolve (EmitContext ec)
1034 ec.CurrentBranching.CurrentUsageVector.Goto ();
1038 protected override void DoEmit (EmitContext ec)
1040 if (ec.Switch == null){
1041 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1045 if (!ec.Switch.GotDefault){
1046 FlowBranchingBlock.Error_UnknownLabel (loc, "default");
1049 ec.ig.Emit (OpCodes.Br, ec.Switch.DefaultTarget);
1052 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1058 /// `goto case' statement
1060 public class GotoCase : Statement {
1064 public GotoCase (Expression e, Location l)
1070 public override bool Resolve (EmitContext ec)
1072 if (ec.Switch == null){
1073 Report.Error (153, loc, "A goto case is only valid inside a switch statement");
1077 expr = expr.Resolve (ec);
1081 Constant c = expr as Constant;
1083 Error (150, "A constant value is expected");
1087 Type type = ec.Switch.SwitchType;
1088 if (!Convert.ImplicitStandardConversionExists (c, type))
1089 Report.Warning (469, 2, loc, "The `goto case' value is not implicitly " +
1090 "convertible to type `{0}'", TypeManager.CSharpName (type));
1093 object val = c.GetValue ();
1094 if ((val != null) && (c.Type != type) && (c.Type != TypeManager.object_type))
1095 val = TypeManager.ChangeType (val, type, out fail);
1098 Report.Error (30, loc, "Cannot convert type `{0}' to `{1}'",
1099 c.GetSignatureForError (), TypeManager.CSharpName (type));
1104 val = SwitchLabel.NullStringCase;
1106 sl = (SwitchLabel) ec.Switch.Elements [val];
1109 FlowBranchingBlock.Error_UnknownLabel (loc, "case " +
1110 (c.GetValue () == null ? "null" : val.ToString ()));
1114 ec.CurrentBranching.CurrentUsageVector.Goto ();
1118 protected override void DoEmit (EmitContext ec)
1120 ec.ig.Emit (OpCodes.Br, sl.GetILLabelCode (ec));
1123 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1125 expr.MutateHoistedGenericType (storey);
1128 protected override void CloneTo (CloneContext clonectx, Statement t)
1130 GotoCase target = (GotoCase) t;
1132 target.expr = expr.Clone (clonectx);
1136 public class Throw : Statement {
1139 public Throw (Expression expr, Location l)
1145 public override bool Resolve (EmitContext ec)
1148 ec.CurrentBranching.CurrentUsageVector.Goto ();
1149 return ec.CurrentBranching.CheckRethrow (loc);
1152 expr = expr.Resolve (ec, ResolveFlags.Type | ResolveFlags.VariableOrValue);
1153 ec.CurrentBranching.CurrentUsageVector.Goto ();
1160 if ((t != TypeManager.exception_type) &&
1161 !TypeManager.IsSubclassOf (t, TypeManager.exception_type) &&
1162 !(expr is NullLiteral)) {
1163 Error (155, "The type caught or thrown must be derived from System.Exception");
1169 protected override void DoEmit (EmitContext ec)
1172 ec.ig.Emit (OpCodes.Rethrow);
1176 ec.ig.Emit (OpCodes.Throw);
1180 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1183 expr.MutateHoistedGenericType (storey);
1186 protected override void CloneTo (CloneContext clonectx, Statement t)
1188 Throw target = (Throw) t;
1191 target.expr = expr.Clone (clonectx);
1195 public class Break : Statement {
1197 public Break (Location l)
1202 bool unwind_protect;
1204 public override bool Resolve (EmitContext ec)
1206 int errors = Report.Errors;
1207 unwind_protect = ec.CurrentBranching.AddBreakOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1208 ec.CurrentBranching.CurrentUsageVector.Goto ();
1209 return errors == Report.Errors;
1212 protected override void DoEmit (EmitContext ec)
1214 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopEnd);
1217 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1221 protected override void CloneTo (CloneContext clonectx, Statement t)
1227 public class Continue : Statement {
1229 public Continue (Location l)
1234 bool unwind_protect;
1236 public override bool Resolve (EmitContext ec)
1238 int errors = Report.Errors;
1239 unwind_protect = ec.CurrentBranching.AddContinueOrigin (ec.CurrentBranching.CurrentUsageVector, loc);
1240 ec.CurrentBranching.CurrentUsageVector.Goto ();
1241 return errors == Report.Errors;
1244 protected override void DoEmit (EmitContext ec)
1246 ec.ig.Emit (unwind_protect ? OpCodes.Leave : OpCodes.Br, ec.LoopBegin);
1249 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
1253 protected override void CloneTo (CloneContext clonectx, Statement t)
1259 public interface ILocalVariable
1261 void Emit (EmitContext ec);
1262 void EmitAssign (EmitContext ec);
1263 void EmitAddressOf (EmitContext ec);
1266 public interface IKnownVariable {
1267 Block Block { get; }
1268 Location Location { get; }
1272 // The information about a user-perceived local variable
1274 public class LocalInfo : IKnownVariable, ILocalVariable {
1275 public readonly Expression Type;
1277 public Type VariableType;
1278 public readonly string Name;
1279 public readonly Location Location;
1280 public readonly Block Block;
1282 public VariableInfo VariableInfo;
1283 public HoistedVariable HoistedVariableReference;
1292 CompilerGenerated = 64,
1296 public enum ReadOnlyContext: byte {
1303 ReadOnlyContext ro_context;
1304 LocalBuilder builder;
1306 public LocalInfo (Expression type, string name, Block block, Location l)
1314 public LocalInfo (DeclSpace ds, Block block, Location l)
1316 VariableType = ds.IsGeneric ? ds.CurrentType : ds.TypeBuilder;
1321 public void ResolveVariable (EmitContext ec)
1323 if (HoistedVariableReference != null)
1326 if (builder == null) {
1329 // This is needed to compile on both .NET 1.x and .NET 2.x
1330 // the later introduced `DeclareLocal (Type t, bool pinned)'
1332 builder = TypeManager.DeclareLocalPinned (ec.ig, VariableType);
1334 builder = ec.ig.DeclareLocal (VariableType);
1338 public void Emit (EmitContext ec)
1340 ec.ig.Emit (OpCodes.Ldloc, builder);
1343 public void EmitAssign (EmitContext ec)
1345 ec.ig.Emit (OpCodes.Stloc, builder);
1348 public void EmitAddressOf (EmitContext ec)
1350 ec.ig.Emit (OpCodes.Ldloca, builder);
1353 public void EmitSymbolInfo (EmitContext ec)
1355 if (builder != null)
1356 ec.DefineLocalVariable (Name, builder);
1359 public bool IsThisAssigned (EmitContext ec)
1361 if (VariableInfo == null)
1362 throw new Exception ();
1364 if (!ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo))
1367 return VariableInfo.TypeInfo.IsFullyInitialized (ec.CurrentBranching, VariableInfo, ec.loc);
1370 public bool IsAssigned (EmitContext ec)
1372 if (VariableInfo == null)
1373 throw new Exception ();
1375 return !ec.DoFlowAnalysis || ec.CurrentBranching.IsAssigned (VariableInfo);
1378 public bool Resolve (EmitContext ec)
1380 if (VariableType != null)
1383 TypeExpr texpr = Type.ResolveAsContextualType (ec, false);
1387 VariableType = texpr.Type;
1389 if (TypeManager.IsGenericParameter (VariableType))
1392 if (VariableType.IsAbstract && VariableType.IsSealed) {
1393 FieldBase.Error_VariableOfStaticClass (Location, Name, VariableType);
1397 if (VariableType.IsPointer && !ec.InUnsafe)
1398 Expression.UnsafeError (Location);
1403 public bool IsConstant {
1404 get { return (flags & Flags.IsConstant) != 0; }
1405 set { flags |= Flags.IsConstant; }
1408 public bool AddressTaken {
1409 get { return (flags & Flags.AddressTaken) != 0; }
1410 set { flags |= Flags.AddressTaken; }
1413 public bool CompilerGenerated {
1414 get { return (flags & Flags.CompilerGenerated) != 0; }
1415 set { flags |= Flags.CompilerGenerated; }
1418 public override string ToString ()
1420 return String.Format ("LocalInfo ({0},{1},{2},{3})",
1421 Name, Type, VariableInfo, Location);
1425 get { return (flags & Flags.Used) != 0; }
1426 set { flags = value ? (flags | Flags.Used) : (unchecked (flags & ~Flags.Used)); }
1429 public bool ReadOnly {
1430 get { return (flags & Flags.ReadOnly) != 0; }
1433 public void SetReadOnlyContext (ReadOnlyContext context)
1435 flags |= Flags.ReadOnly;
1436 ro_context = context;
1439 public string GetReadOnlyContext ()
1442 throw new InternalErrorException ("Variable is not readonly");
1444 switch (ro_context) {
1445 case ReadOnlyContext.Fixed:
1446 return "fixed variable";
1447 case ReadOnlyContext.Foreach:
1448 return "foreach iteration variable";
1449 case ReadOnlyContext.Using:
1450 return "using variable";
1452 throw new NotImplementedException ();
1456 // Whether the variable is pinned, if Pinned the variable has been
1457 // allocated in a pinned slot with DeclareLocal.
1459 public bool Pinned {
1460 get { return (flags & Flags.Pinned) != 0; }
1461 set { flags = value ? (flags | Flags.Pinned) : (flags & ~Flags.Pinned); }
1464 public bool IsThis {
1465 get { return (flags & Flags.IsThis) != 0; }
1466 set { flags = value ? (flags | Flags.IsThis) : (flags & ~Flags.IsThis); }
1469 Block IKnownVariable.Block {
1470 get { return Block; }
1473 Location IKnownVariable.Location {
1474 get { return Location; }
1477 public LocalInfo Clone (CloneContext clonectx)
1480 // Variables in anonymous block are not resolved yet
1482 if (VariableType == null)
1483 return new LocalInfo (Type.Clone (clonectx), Name, clonectx.LookupBlock (Block), Location);
1486 // Variables in method block are resolved
1488 LocalInfo li = new LocalInfo (null, Name, clonectx.LookupBlock (Block), Location);
1489 li.VariableType = VariableType;
1495 /// Block represents a C# block.
1499 /// This class is used in a number of places: either to represent
1500 /// explicit blocks that the programmer places or implicit blocks.
1502 /// Implicit blocks are used as labels or to introduce variable
1505 /// Top-level blocks derive from Block, and they are called ToplevelBlock
1506 /// they contain extra information that is not necessary on normal blocks.
1508 public class Block : Statement {
1509 public Block Parent;
1510 public Location StartLocation;
1511 public Location EndLocation = Location.Null;
1513 public ExplicitBlock Explicit;
1514 public ToplevelBlock Toplevel; // TODO: Use Explicit
1517 public enum Flags : byte {
1520 VariablesInitialized = 4,
1525 HasStoreyAccess = 128
1527 protected Flags flags;
1529 public bool Unchecked {
1530 get { return (flags & Flags.Unchecked) != 0; }
1531 set { flags = value ? flags | Flags.Unchecked : flags & ~Flags.Unchecked; }
1534 public bool Unsafe {
1535 get { return (flags & Flags.Unsafe) != 0; }
1536 set { flags |= Flags.Unsafe; }
1540 // The statements in this block
1542 protected ArrayList statements;
1545 // An array of Blocks. We keep track of children just
1546 // to generate the local variable declarations.
1548 // Statements and child statements are handled through the
1554 // Labels. (label, block) pairs.
1556 protected HybridDictionary labels;
1559 // Keeps track of (name, type) pairs
1561 IDictionary variables;
1562 protected IDictionary range_variables;
1565 // Keeps track of constants
1566 HybridDictionary constants;
1569 // Temporary variables.
1571 ArrayList temporary_variables;
1574 // If this is a switch section, the enclosing switch block.
1578 ArrayList scope_initializers;
1580 ArrayList anonymous_children;
1582 protected static int id;
1586 int assignable_slots;
1587 bool unreachable_shown;
1590 public Block (Block parent)
1591 : this (parent, (Flags) 0, Location.Null, Location.Null)
1594 public Block (Block parent, Flags flags)
1595 : this (parent, flags, Location.Null, Location.Null)
1598 public Block (Block parent, Location start, Location end)
1599 : this (parent, (Flags) 0, start, end)
1602 public Block (Block parent, Flags flags, Location start, Location end)
1604 if (parent != null) {
1605 parent.AddChild (this);
1607 // the appropriate constructors will fixup these fields
1608 Toplevel = parent.Toplevel;
1609 Explicit = parent.Explicit;
1612 this.Parent = parent;
1614 this.StartLocation = start;
1615 this.EndLocation = end;
1618 statements = new ArrayList (4);
1621 public Block CreateSwitchBlock (Location start)
1623 // FIXME: should this be implicit?
1624 Block new_block = new ExplicitBlock (this, start, start);
1625 new_block.switch_block = this;
1630 get { return this_id; }
1633 public IDictionary Variables {
1635 if (variables == null)
1636 variables = new ListDictionary ();
1641 void AddChild (Block b)
1643 if (children == null)
1644 children = new ArrayList (1);
1649 public void SetEndLocation (Location loc)
1654 protected static void Error_158 (string name, Location loc)
1656 Report.Error (158, loc, "The label `{0}' shadows another label " +
1657 "by the same name in a contained scope", name);
1661 /// Adds a label to the current block.
1665 /// false if the name already exists in this block. true
1669 public bool AddLabel (LabeledStatement target)
1671 if (switch_block != null)
1672 return switch_block.AddLabel (target);
1674 string name = target.Name;
1677 while (cur != null) {
1678 LabeledStatement s = cur.DoLookupLabel (name);
1680 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1681 Report.Error (140, target.loc, "The label `{0}' is a duplicate", name);
1685 if (this == Explicit)
1691 while (cur != null) {
1692 if (cur.DoLookupLabel (name) != null) {
1693 Error_158 (name, target.loc);
1697 if (children != null) {
1698 foreach (Block b in children) {
1699 LabeledStatement s = b.DoLookupLabel (name);
1703 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
1704 Error_158 (name, target.loc);
1712 Toplevel.CheckError158 (name, target.loc);
1715 labels = new HybridDictionary();
1717 labels.Add (name, target);
1721 public LabeledStatement LookupLabel (string name)
1723 LabeledStatement s = DoLookupLabel (name);
1727 if (children == null)
1730 foreach (Block child in children) {
1731 if (Explicit != child.Explicit)
1734 s = child.LookupLabel (name);
1742 LabeledStatement DoLookupLabel (string name)
1744 if (switch_block != null)
1745 return switch_block.LookupLabel (name);
1748 if (labels.Contains (name))
1749 return ((LabeledStatement) labels [name]);
1754 public bool CheckInvariantMeaningInBlock (string name, Expression e, Location loc)
1757 IKnownVariable kvi = b.Explicit.GetKnownVariable (name);
1758 while (kvi == null) {
1759 b = b.Explicit.Parent;
1762 kvi = b.Explicit.GetKnownVariable (name);
1768 // Is kvi.Block nested inside 'b'
1769 if (b.Explicit != kvi.Block.Explicit) {
1771 // If a variable by the same name it defined in a nested block of this
1772 // block, we violate the invariant meaning in a block.
1775 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1776 Report.Error (135, loc, "`{0}' conflicts with a declaration in a child block", name);
1781 // It's ok if the definition is in a nested subblock of b, but not
1782 // nested inside this block -- a definition in a sibling block
1783 // should not affect us.
1789 // Block 'b' and kvi.Block are the same textual block.
1790 // However, different variables are extant.
1792 // Check if the variable is in scope in both blocks. We use
1793 // an indirect check that depends on AddVariable doing its
1794 // part in maintaining the invariant-meaning-in-block property.
1796 if (e is VariableReference || (e is Constant && b.GetLocalInfo (name) != null))
1799 if (this is ToplevelBlock) {
1800 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1801 e.Error_VariableIsUsedBeforeItIsDeclared (name);
1806 // Even though we detected the error when the name is used, we
1807 // treat it as if the variable declaration was in error.
1809 Report.SymbolRelatedToPreviousError (loc, name);
1810 Error_AlreadyDeclared (kvi.Location, name, "parent or current");
1814 public LocalInfo AddVariable (Expression type, string name, Location l)
1816 LocalInfo vi = GetLocalInfo (name);
1818 Report.SymbolRelatedToPreviousError (vi.Location, name);
1819 if (Explicit == vi.Block.Explicit) {
1820 if (type == Linq.ImplicitQueryParameter.ImplicitType.Instance && type == vi.Type)
1821 Error_AlreadyDeclared (l, name);
1823 Error_AlreadyDeclared (l, name, null);
1825 Error_AlreadyDeclared (l, name, "parent");
1830 ToplevelParameterInfo pi = Toplevel.GetParameterInfo (name);
1832 Report.SymbolRelatedToPreviousError (pi.Location, name);
1833 Error_AlreadyDeclared (loc, name,
1834 pi.Block == Toplevel ? "method argument" : "parent or current");
1838 if (Toplevel.GenericMethod != null) {
1839 foreach (TypeParameter tp in Toplevel.GenericMethod.CurrentTypeParameters) {
1840 if (tp.Name == name) {
1841 Report.SymbolRelatedToPreviousError (tp);
1842 Error_AlreadyDeclaredTypeParameter (loc, name);
1848 IKnownVariable kvi = Explicit.GetKnownVariable (name);
1850 Report.SymbolRelatedToPreviousError (kvi.Location, name);
1851 Error_AlreadyDeclared (l, name, "child");
1855 vi = new LocalInfo (type, name, this, l);
1858 if ((flags & Flags.VariablesInitialized) != 0)
1859 throw new InternalErrorException ("block has already been resolved");
1864 protected virtual void AddVariable (LocalInfo li)
1866 Variables.Add (li.Name, li);
1867 Explicit.AddKnownVariable (li.Name, li);
1870 protected virtual void Error_AlreadyDeclared (Location loc, string var, string reason)
1872 if (reason == null) {
1873 Error_AlreadyDeclared (loc, var);
1877 Report.Error (136, loc, "A local variable named `{0}' cannot be declared " +
1878 "in this scope because it would give a different meaning " +
1879 "to `{0}', which is already used in a `{1}' scope " +
1880 "to denote something else", var, reason);
1883 protected virtual void Error_AlreadyDeclared (Location loc, string name)
1885 Report.Error (128, loc,
1886 "A local variable named `{0}' is already defined in this scope", name);
1889 protected virtual void Error_AlreadyDeclaredTypeParameter (Location loc, string name)
1891 GenericMethod.Error_ParameterNameCollision (loc, name, "local variable");
1894 public bool AddConstant (Expression type, string name, Expression value, Location l)
1896 if (AddVariable (type, name, l) == null)
1899 if (constants == null)
1900 constants = new HybridDictionary();
1902 constants.Add (name, value);
1904 // A block is considered used if we perform an initialization in a local declaration, even if it is constant.
1909 static int next_temp_id = 0;
1911 public LocalInfo AddTemporaryVariable (TypeExpr te, Location loc)
1913 Report.Debug (64, "ADD TEMPORARY", this, Toplevel, loc);
1915 if (temporary_variables == null)
1916 temporary_variables = new ArrayList ();
1918 int id = ++next_temp_id;
1919 string name = "$s_" + id.ToString ();
1921 LocalInfo li = new LocalInfo (te, name, this, loc);
1922 li.CompilerGenerated = true;
1923 temporary_variables.Add (li);
1927 public LocalInfo GetLocalInfo (string name)
1930 for (Block b = this; b != null; b = b.Parent) {
1931 if (b.variables != null) {
1932 ret = (LocalInfo) b.variables [name];
1937 if (b.range_variables != null) {
1938 ret = (LocalInfo) b.range_variables [name];
1947 public Expression GetVariableType (string name)
1949 LocalInfo vi = GetLocalInfo (name);
1950 return vi == null ? null : vi.Type;
1953 public Expression GetConstantExpression (string name)
1955 for (Block b = this; b != null; b = b.Parent) {
1956 if (b.constants != null) {
1957 Expression ret = b.constants [name] as Expression;
1966 // It should be used by expressions which require to
1967 // register a statement during resolve process.
1969 public void AddScopeStatement (StatementExpression s)
1971 if (scope_initializers == null)
1972 scope_initializers = new ArrayList ();
1974 scope_initializers.Add (s);
1977 public void AddStatement (Statement s)
1980 flags |= Flags.BlockUsed;
1984 get { return (flags & Flags.BlockUsed) != 0; }
1989 flags |= Flags.BlockUsed;
1992 public bool HasRet {
1993 get { return (flags & Flags.HasRet) != 0; }
1996 public bool IsDestructor {
1997 get { return (flags & Flags.IsDestructor) != 0; }
2000 public void SetDestructor ()
2002 flags |= Flags.IsDestructor;
2005 public int AssignableSlots {
2008 // if ((flags & Flags.VariablesInitialized) == 0)
2009 // throw new Exception ("Variables have not been initialized yet");
2010 return assignable_slots;
2014 public ArrayList AnonymousChildren {
2015 get { return anonymous_children; }
2018 public void AddAnonymousChild (ToplevelBlock b)
2020 if (anonymous_children == null)
2021 anonymous_children = new ArrayList ();
2023 anonymous_children.Add (b);
2026 void DoResolveConstants (EmitContext ec)
2028 if (constants == null)
2031 if (variables == null)
2032 throw new InternalErrorException ("cannot happen");
2034 foreach (DictionaryEntry de in variables) {
2035 string name = (string) de.Key;
2036 LocalInfo vi = (LocalInfo) de.Value;
2037 Type variable_type = vi.VariableType;
2039 if (variable_type == null) {
2040 if (vi.Type is VarExpr)
2041 Report.Error (822, vi.Type.Location, "An implicitly typed local variable cannot be a constant");
2046 Expression cv = (Expression) constants [name];
2050 // Don't let 'const int Foo = Foo;' succeed.
2051 // Removing the name from 'constants' ensures that we get a LocalVariableReference below,
2052 // which in turn causes the 'must be constant' error to be triggered.
2053 constants.Remove (name);
2055 if (!Const.IsConstantTypeValid (variable_type)) {
2056 Const.Error_InvalidConstantType (variable_type, loc);
2060 ec.CurrentBlock = this;
2062 using (ec.With (EmitContext.Flags.ConstantCheckState, (flags & Flags.Unchecked) == 0)) {
2063 e = cv.Resolve (ec);
2068 Constant ce = e as Constant;
2070 Const.Error_ExpressionMustBeConstant (vi.Location, name);
2074 e = ce.ConvertImplicitly (variable_type);
2076 if (TypeManager.IsReferenceType (variable_type))
2077 Const.Error_ConstantCanBeInitializedWithNullOnly (variable_type, vi.Location, vi.Name);
2079 ce.Error_ValueCannotBeConverted (ec, vi.Location, variable_type, false);
2083 constants.Add (name, e);
2084 vi.IsConstant = true;
2088 protected void ResolveMeta (EmitContext ec, int offset)
2090 Report.Debug (64, "BLOCK RESOLVE META", this, Parent);
2092 // If some parent block was unsafe, we remain unsafe even if this block
2093 // isn't explicitly marked as such.
2094 using (ec.With (EmitContext.Flags.InUnsafe, ec.InUnsafe | Unsafe)) {
2095 flags |= Flags.VariablesInitialized;
2097 if (variables != null) {
2098 foreach (LocalInfo li in variables.Values) {
2099 if (!li.Resolve (ec))
2101 li.VariableInfo = new VariableInfo (li, offset);
2102 offset += li.VariableInfo.Length;
2105 assignable_slots = offset;
2107 DoResolveConstants (ec);
2109 if (children == null)
2111 foreach (Block b in children)
2112 b.ResolveMeta (ec, offset);
2117 // Emits the local variable declarations for a block
2119 public virtual void EmitMeta (EmitContext ec)
2121 if (variables != null){
2122 foreach (LocalInfo vi in variables.Values)
2123 vi.ResolveVariable (ec);
2126 if (temporary_variables != null) {
2127 for (int i = 0; i < temporary_variables.Count; i++)
2128 ((LocalInfo)temporary_variables[i]).ResolveVariable(ec);
2131 if (children != null) {
2132 for (int i = 0; i < children.Count; i++)
2133 ((Block)children[i]).EmitMeta(ec);
2137 void UsageWarning ()
2139 if (variables == null || Report.WarningLevel < 3)
2142 foreach (DictionaryEntry de in variables) {
2143 LocalInfo vi = (LocalInfo) de.Value;
2146 string name = (string) de.Key;
2148 // vi.VariableInfo can be null for 'catch' variables
2149 if (vi.VariableInfo != null && vi.VariableInfo.IsEverAssigned)
2150 Report.Warning (219, 3, vi.Location, "The variable `{0}' is assigned but its value is never used", name);
2152 Report.Warning (168, 3, vi.Location, "The variable `{0}' is declared but never used", name);
2157 static void CheckPossibleMistakenEmptyStatement (Statement s)
2161 // Some statements are wrapped by a Block. Since
2162 // others' internal could be changed, here I treat
2163 // them as possibly wrapped by Block equally.
2164 Block b = s as Block;
2165 if (b != null && b.statements.Count == 1)
2166 s = (Statement) b.statements [0];
2169 body = ((Lock) s).Statement;
2171 body = ((For) s).Statement;
2172 else if (s is Foreach)
2173 body = ((Foreach) s).Statement;
2174 else if (s is While)
2175 body = ((While) s).Statement;
2176 else if (s is Fixed)
2177 body = ((Fixed) s).Statement;
2178 else if (s is Using)
2179 body = ((Using) s).EmbeddedStatement;
2180 else if (s is UsingTemporary)
2181 body = ((UsingTemporary) s).Statement;
2185 if (body == null || body is EmptyStatement)
2186 Report.Warning (642, 3, s.loc, "Possible mistaken empty statement");
2189 public override bool Resolve (EmitContext ec)
2191 Block prev_block = ec.CurrentBlock;
2194 int errors = Report.Errors;
2196 ec.CurrentBlock = this;
2197 ec.StartFlowBranching (this);
2199 Report.Debug (4, "RESOLVE BLOCK", StartLocation, ec.CurrentBranching);
2202 // This flag is used to notate nested statements as unreachable from the beginning of this block.
2203 // For the purposes of this resolution, it doesn't matter that the whole block is unreachable
2204 // from the beginning of the function. The outer Resolve() that detected the unreachability is
2205 // responsible for handling the situation.
2207 int statement_count = statements.Count;
2208 for (int ix = 0; ix < statement_count; ix++){
2209 Statement s = (Statement) statements [ix];
2210 // Check possible empty statement (CS0642)
2211 if (Report.WarningLevel >= 3 &&
2212 ix + 1 < statement_count &&
2213 statements [ix + 1] is ExplicitBlock)
2214 CheckPossibleMistakenEmptyStatement (s);
2217 // Warn if we detect unreachable code.
2220 if (s is EmptyStatement)
2223 if (!unreachable_shown && !(s is LabeledStatement)) {
2224 Report.Warning (162, 2, s.loc, "Unreachable code detected");
2225 unreachable_shown = true;
2228 Block c_block = s as Block;
2229 if (c_block != null)
2230 c_block.unreachable = c_block.unreachable_shown = true;
2234 // Note that we're not using ResolveUnreachable() for unreachable
2235 // statements here. ResolveUnreachable() creates a temporary
2236 // flow branching and kills it afterwards. This leads to problems
2237 // if you have two unreachable statements where the first one
2238 // assigns a variable and the second one tries to access it.
2241 if (!s.Resolve (ec)) {
2243 if (ec.IsInProbingMode)
2246 statements [ix] = EmptyStatement.Value;
2250 if (unreachable && !(s is LabeledStatement) && !(s is Block))
2251 statements [ix] = EmptyStatement.Value;
2253 unreachable = ec.CurrentBranching.CurrentUsageVector.IsUnreachable;
2254 if (unreachable && s is LabeledStatement)
2255 throw new InternalErrorException ("should not happen");
2258 Report.Debug (4, "RESOLVE BLOCK DONE", StartLocation,
2259 ec.CurrentBranching, statement_count);
2261 while (ec.CurrentBranching is FlowBranchingLabeled)
2262 ec.EndFlowBranching ();
2264 bool flow_unreachable = ec.EndFlowBranching ();
2266 ec.CurrentBlock = prev_block;
2268 if (flow_unreachable)
2269 flags |= Flags.HasRet;
2271 // If we're a non-static `struct' constructor which doesn't have an
2272 // initializer, then we must initialize all of the struct's fields.
2273 if (this == Toplevel && !Toplevel.IsThisAssigned (ec) && !flow_unreachable)
2276 if ((labels != null) && (Report.WarningLevel >= 2)) {
2277 foreach (LabeledStatement label in labels.Values)
2278 if (!label.HasBeenReferenced)
2279 Report.Warning (164, 2, label.loc, "This label has not been referenced");
2282 if (ok && errors == Report.Errors)
2288 public override bool ResolveUnreachable (EmitContext ec, bool warn)
2290 unreachable_shown = true;
2294 Report.Warning (162, 2, loc, "Unreachable code detected");
2296 ec.StartFlowBranching (FlowBranching.BranchingType.Block, loc);
2297 bool ok = Resolve (ec);
2298 ec.KillFlowBranching ();
2303 protected override void DoEmit (EmitContext ec)
2305 for (int ix = 0; ix < statements.Count; ix++){
2306 Statement s = (Statement) statements [ix];
2311 public override void Emit (EmitContext ec)
2313 Block prev_block = ec.CurrentBlock;
2314 ec.CurrentBlock = this;
2316 if (scope_initializers != null) {
2317 SymbolWriter.OpenCompilerGeneratedBlock (ec.ig);
2319 using (ec.Set (EmitContext.Flags.OmitDebuggingInfo)) {
2320 foreach (StatementExpression s in scope_initializers)
2324 SymbolWriter.CloseCompilerGeneratedBlock (ec.ig);
2327 ec.Mark (StartLocation);
2330 if (SymbolWriter.HasSymbolWriter)
2331 EmitSymbolInfo (ec);
2333 ec.CurrentBlock = prev_block;
2336 protected virtual void EmitSymbolInfo (EmitContext ec)
2338 if (variables != null) {
2339 foreach (LocalInfo vi in variables.Values) {
2340 vi.EmitSymbolInfo (ec);
2345 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
2347 MutateVariables (storey);
2349 if (scope_initializers != null) {
2350 foreach (Statement s in scope_initializers)
2351 s.MutateHoistedGenericType (storey);
2354 foreach (Statement s in statements)
2355 s.MutateHoistedGenericType (storey);
2358 void MutateVariables (AnonymousMethodStorey storey)
2360 if (variables != null) {
2361 foreach (LocalInfo vi in variables.Values) {
2362 vi.VariableType = storey.MutateType (vi.VariableType);
2366 if (temporary_variables != null) {
2367 foreach (LocalInfo vi in temporary_variables)
2368 vi.VariableType = storey.MutateType (vi.VariableType);
2372 public override string ToString ()
2374 return String.Format ("{0} ({1}:{2})", GetType (),ID, StartLocation);
2377 protected override void CloneTo (CloneContext clonectx, Statement t)
2379 Block target = (Block) t;
2381 clonectx.AddBlockMap (this, target);
2383 //target.Toplevel = (ToplevelBlock) clonectx.LookupBlock (Toplevel);
2384 target.Explicit = (ExplicitBlock) clonectx.LookupBlock (Explicit);
2386 target.Parent = clonectx.RemapBlockCopy (Parent);
2388 if (variables != null){
2389 target.variables = new Hashtable ();
2391 foreach (DictionaryEntry de in variables){
2392 LocalInfo newlocal = ((LocalInfo) de.Value).Clone (clonectx);
2393 target.variables [de.Key] = newlocal;
2394 clonectx.AddVariableMap ((LocalInfo) de.Value, newlocal);
2398 target.statements = new ArrayList (statements.Count);
2399 foreach (Statement s in statements)
2400 target.statements.Add (s.Clone (clonectx));
2402 if (target.children != null){
2403 target.children = new ArrayList (children.Count);
2404 foreach (Block b in children){
2405 target.children.Add (clonectx.LookupBlock (b));
2410 // TODO: labels, switch_block, constants (?), anonymous_children
2415 public class ExplicitBlock : Block {
2416 HybridDictionary known_variables;
2417 protected AnonymousMethodStorey am_storey;
2419 public ExplicitBlock (Block parent, Location start, Location end)
2420 : this (parent, (Flags) 0, start, end)
2424 public ExplicitBlock (Block parent, Flags flags, Location start, Location end)
2425 : base (parent, flags, start, end)
2427 this.Explicit = this;
2431 // Marks a variable with name @name as being used in this or a child block.
2432 // If a variable name has been used in a child block, it's illegal to
2433 // declare a variable with the same name in the current block.
2435 internal void AddKnownVariable (string name, IKnownVariable info)
2437 if (known_variables == null)
2438 known_variables = new HybridDictionary();
2440 known_variables [name] = info;
2443 Parent.Explicit.AddKnownVariable (name, info);
2446 public AnonymousMethodStorey AnonymousMethodStorey {
2447 get { return am_storey; }
2451 // Creates anonymous method storey in current block
2453 public AnonymousMethodStorey CreateAnonymousMethodStorey (EmitContext ec)
2456 // When referencing a variable in iterator storey from children anonymous method
2458 if (Toplevel.am_storey is IteratorStorey) {
2459 ec.CurrentAnonymousMethod.AddStoreyReference (Toplevel.am_storey);
2460 return Toplevel.am_storey;
2464 // An iterator has only 1 storey block
2466 if (ec.CurrentIterator != null)
2467 return ec.CurrentIterator.Storey;
2469 if (am_storey == null) {
2470 MemberBase mc = ec.ResolveContext as MemberBase;
2471 GenericMethod gm = mc == null ? null : mc.GenericMethod;
2474 // Create anonymous method storey for this block
2476 am_storey = new AnonymousMethodStorey (this, ec.TypeContainer, mc, gm, "AnonStorey");
2480 // Creates a link between this block and the anonymous method
2482 // An anonymous method can reference variables from any outer block, but they are
2483 // hoisted in their own ExplicitBlock. When more than one block is referenced we
2484 // need to create another link between those variable storeys
2486 ec.CurrentAnonymousMethod.AddStoreyReference (am_storey);
2490 public override void Emit (EmitContext ec)
2492 if (am_storey != null)
2493 am_storey.EmitHoistedVariables (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 am_storey.ChangeParentStorey (ec.CurrentAnonymousMethod.Storey);
2515 am_storey.DefineType ();
2516 am_storey.ResolveType ();
2517 am_storey.Define ();
2518 am_storey.Parent.PartialContainer.AddCompilerGeneratedClass (am_storey);
2524 internal IKnownVariable GetKnownVariable (string name)
2526 return known_variables == null ? null : (IKnownVariable) known_variables [name];
2529 public void PropagateStoreyReference (AnonymousMethodStorey s)
2531 if (Parent != null && am_storey != s) {
2532 if (am_storey != null)
2533 am_storey.AddParentStoreyReference (s);
2535 Parent.Explicit.PropagateStoreyReference (s);
2539 public override bool Resolve (EmitContext ec)
2541 bool ok = base.Resolve (ec);
2544 // Discard an anonymous method storey when this block has no hoisted variables
2546 if (am_storey != null && !am_storey.HasHoistedVariables) {
2554 protected override void CloneTo (CloneContext clonectx, Statement t)
2556 ExplicitBlock target = (ExplicitBlock) t;
2557 target.known_variables = null;
2558 base.CloneTo (clonectx, t);
2562 public class ToplevelParameterInfo : IKnownVariable {
2563 public readonly ToplevelBlock Block;
2564 public readonly int Index;
2565 public VariableInfo VariableInfo;
2567 Block IKnownVariable.Block {
2568 get { return Block; }
2570 public Parameter Parameter {
2571 get { return Block.Parameters [Index]; }
2574 public Type ParameterType {
2575 get { return Block.Parameters.Types [Index]; }
2578 public Location Location {
2579 get { return Parameter.Location; }
2582 public ToplevelParameterInfo (ToplevelBlock block, int idx)
2590 // A toplevel block contains extra information, the split is done
2591 // only to separate information that would otherwise bloat the more
2592 // lightweight Block.
2594 // In particular, this was introduced when the support for Anonymous
2595 // Methods was implemented.
2597 public class ToplevelBlock : ExplicitBlock {
2598 GenericMethod generic;
2599 FlowBranchingToplevel top_level_branching;
2600 Parameters parameters;
2601 ToplevelParameterInfo[] parameter_info;
2602 LocalInfo this_variable;
2604 public HoistedVariable HoistedThisVariable;
2607 // The parameters for the block.
2609 public Parameters Parameters {
2610 get { return parameters; }
2613 public GenericMethod GenericMethod {
2614 get { return generic; }
2617 public bool HasStoreyAccess {
2618 set { flags = value ? flags | Flags.HasStoreyAccess : flags & ~Flags.HasStoreyAccess; }
2619 get { return (flags & Flags.HasStoreyAccess) != 0; }
2622 public ToplevelBlock Container {
2623 get { return Parent == null ? null : Parent.Toplevel; }
2626 public ToplevelBlock (Block parent, Parameters parameters, Location start) :
2627 this (parent, (Flags) 0, parameters, start)
2631 public ToplevelBlock (Block parent, Parameters parameters, GenericMethod generic, Location start) :
2632 this (parent, parameters, start)
2634 this.generic = generic;
2637 public ToplevelBlock (Parameters parameters, Location start) :
2638 this (null, (Flags) 0, parameters, start)
2642 ToplevelBlock (Flags flags, Parameters parameters, Location start) :
2643 this (null, flags, parameters, start)
2647 // We use 'Parent' to hook up to the containing block, but don't want to register the current block as a child.
2648 // So, we use a two-stage setup -- first pass a null parent to the base constructor, and then override 'Parent'.
2649 public ToplevelBlock (Block parent, Flags flags, Parameters parameters, Location start) :
2650 base (null, flags, start, Location.Null)
2652 this.Toplevel = this;
2654 this.parameters = parameters == null ? Parameters.EmptyReadOnlyParameters : parameters;
2655 this.Parent = parent;
2657 parent.AddAnonymousChild (this);
2659 if (!this.parameters.IsEmpty)
2660 ProcessParameters ();
2663 public ToplevelBlock (Location loc) : this (null, (Flags) 0, null, loc)
2667 protected override void CloneTo (CloneContext clonectx, Statement t)
2669 ToplevelBlock target = (ToplevelBlock) t;
2670 base.CloneTo (clonectx, t);
2672 if (parameters.Count != 0)
2673 target.parameter_info = new ToplevelParameterInfo [parameters.Count];
2674 for (int i = 0; i < parameters.Count; ++i)
2675 target.parameter_info [i] = new ToplevelParameterInfo (target, i);
2678 public bool CheckError158 (string name, Location loc)
2680 if (AnonymousChildren != null) {
2681 foreach (ToplevelBlock child in AnonymousChildren) {
2682 if (!child.CheckError158 (name, loc))
2687 for (ToplevelBlock c = Container; c != null; c = c.Container) {
2688 if (!c.DoCheckError158 (name, loc))
2695 public virtual Expression GetTransparentIdentifier (string name)
2700 void ProcessParameters ()
2702 int n = parameters.Count;
2703 parameter_info = new ToplevelParameterInfo [n];
2704 for (int i = 0; i < n; ++i) {
2705 parameter_info [i] = new ToplevelParameterInfo (this, i);
2707 Parameter p = parameters [i];
2711 string name = p.Name;
2712 LocalInfo vi = GetLocalInfo (name);
2714 Report.SymbolRelatedToPreviousError (vi.Location, name);
2715 Error_AlreadyDeclared (loc, name, "parent or current");
2719 ToplevelParameterInfo pi = Parent == null ? null : Parent.Toplevel.GetParameterInfo (name);
2721 Report.SymbolRelatedToPreviousError (pi.Location, name);
2722 Error_AlreadyDeclared (loc, name, "parent or current");
2726 AddKnownVariable (name, parameter_info [i]);
2729 // mark this block as "used" so that we create local declarations in a sub-block
2730 // FIXME: This appears to uncover a lot of bugs
2734 bool DoCheckError158 (string name, Location loc)
2736 LabeledStatement s = LookupLabel (name);
2738 Report.SymbolRelatedToPreviousError (s.loc, s.Name);
2739 Error_158 (name, loc);
2746 public override Expression CreateExpressionTree (EmitContext ec)
2748 if (statements.Count == 1)
2749 return ((Statement) statements [0]).CreateExpressionTree (ec);
2751 return base.CreateExpressionTree (ec);
2755 // Reformats this block to be top-level iterator block
2757 public IteratorStorey ChangeToIterator (Iterator iterator, ToplevelBlock source)
2759 // Create block with original statements
2760 ExplicitBlock iter_block = new ExplicitBlock (this, flags, StartLocation, EndLocation);
2763 // TODO: Change to iter_block.statements = statements;
2764 foreach (Statement stmt in source.statements)
2765 iter_block.AddStatement (stmt);
2766 labels = source.labels;
2768 AddStatement (new IteratorStatement (iterator, iter_block));
2770 source.statements = new ArrayList (1);
2771 source.AddStatement (new Return (iterator, iterator.Location));
2772 source.IsIterator = false;
2774 IteratorStorey iterator_storey = new IteratorStorey (iterator);
2775 source.am_storey = iterator_storey;
2776 return iterator_storey;
2779 public FlowBranchingToplevel TopLevelBranching {
2780 get { return top_level_branching; }
2784 // Returns a `ParameterReference' for the given name, or null if there
2785 // is no such parameter
2787 public ParameterReference GetParameterReference (string name, Location loc)
2789 ToplevelParameterInfo p = GetParameterInfo (name);
2790 return p == null ? null : new ParameterReference (this, p, loc);
2793 public ToplevelParameterInfo GetParameterInfo (string name)
2796 for (ToplevelBlock t = this; t != null; t = t.Container) {
2797 Parameter par = t.Parameters.GetParameterByName (name, out idx);
2799 return t.parameter_info [idx];
2805 // Returns the "this" instance variable of this block.
2806 // See AddThisVariable() for more information.
2808 public LocalInfo ThisVariable {
2809 get { return this_variable; }
2813 // This is used by non-static `struct' constructors which do not have an
2814 // initializer - in this case, the constructor must initialize all of the
2815 // struct's fields. To do this, we add a "this" variable and use the flow
2816 // analysis code to ensure that it's been fully initialized before control
2817 // leaves the constructor.
2819 public LocalInfo AddThisVariable (DeclSpace ds, Location l)
2821 if (this_variable == null) {
2822 this_variable = new LocalInfo (ds, this, l);
2823 this_variable.Used = true;
2824 this_variable.IsThis = true;
2826 Variables.Add ("this", this_variable);
2829 return this_variable;
2832 public bool IsIterator {
2833 get { return (flags & Flags.IsIterator) != 0; }
2834 set { flags = value ? flags | Flags.IsIterator : flags & ~Flags.IsIterator; }
2837 public bool IsThisAssigned (EmitContext ec)
2839 return this_variable == null || this_variable.IsThisAssigned (ec);
2842 public bool ResolveMeta (EmitContext ec, Parameters ip)
2844 int errors = Report.Errors;
2845 int orig_count = parameters.Count;
2847 if (top_level_branching != null)
2853 // Assert: orig_count != parameter.Count => orig_count == 0
2854 if (orig_count != 0 && orig_count != parameters.Count)
2855 throw new InternalErrorException ("parameter information mismatch");
2857 int offset = Parent == null ? 0 : Parent.AssignableSlots;
2859 for (int i = 0; i < orig_count; ++i) {
2860 Parameter.Modifier mod = parameters.FixedParameters [i].ModFlags;
2862 if ((mod & Parameter.Modifier.OUT) != Parameter.Modifier.OUT)
2865 VariableInfo vi = new VariableInfo (ip, i, offset);
2866 parameter_info [i].VariableInfo = vi;
2867 offset += vi.Length;
2870 ResolveMeta (ec, offset);
2872 top_level_branching = ec.StartFlowBranching (this);
2874 return Report.Errors == errors;
2878 // Check whether all `out' parameters have been assigned.
2880 public void CheckOutParameters (FlowBranching.UsageVector vector, Location loc)
2882 if (vector.IsUnreachable)
2885 int n = parameter_info == null ? 0 : parameter_info.Length;
2887 for (int i = 0; i < n; i++) {
2888 VariableInfo var = parameter_info [i].VariableInfo;
2893 if (vector.IsAssigned (var, false))
2896 Report.Error (177, loc, "The out parameter `{0}' must be assigned to before control leaves the current method",
2901 public override void EmitMeta (EmitContext ec)
2903 parameters.ResolveVariable ();
2905 // Avoid declaring an IL variable for this_variable since it is not accessed
2906 // from the generated IL
2907 if (this_variable != null)
2908 Variables.Remove ("this");
2912 protected override void EmitSymbolInfo (EmitContext ec)
2914 AnonymousExpression ae = ec.CurrentAnonymousMethod;
2915 if ((ae != null) && (ae.Storey != null))
2916 SymbolWriter.DefineScopeVariable (ae.Storey.ID);
2918 base.EmitSymbolInfo (ec);
2921 public override void Emit (EmitContext ec)
2924 ec.Mark (EndLocation);
2928 public class SwitchLabel {
2935 Label il_label_code;
2936 bool il_label_code_set;
2938 public static readonly object NullStringCase = new object ();
2941 // if expr == null, then it is the default case.
2943 public SwitchLabel (Expression expr, Location l)
2949 public Expression Label {
2955 public Location Location {
2959 public object Converted {
2965 public Label GetILLabel (EmitContext ec)
2968 il_label = ec.ig.DefineLabel ();
2969 il_label_set = true;
2974 public Label GetILLabelCode (EmitContext ec)
2976 if (!il_label_code_set){
2977 il_label_code = ec.ig.DefineLabel ();
2978 il_label_code_set = true;
2980 return il_label_code;
2984 // Resolves the expression, reduces it to a literal if possible
2985 // and then converts it to the requested type.
2987 public bool ResolveAndReduce (EmitContext ec, Type required_type, bool allow_nullable)
2989 Expression e = label.Resolve (ec);
2994 Constant c = e as Constant;
2996 Report.Error (150, loc, "A constant value is expected");
3000 if (required_type == TypeManager.string_type && c.GetValue () == null) {
3001 converted = NullStringCase;
3005 if (allow_nullable && c.GetValue () == null) {
3006 converted = NullStringCase;
3010 c = c.ImplicitConversionRequired (ec, required_type, loc);
3014 converted = c.GetValue ();
3018 public void Error_AlreadyOccurs (Type switch_type, SwitchLabel collision_with)
3021 if (converted == null)
3023 else if (converted == NullStringCase)
3026 label = converted.ToString ();
3028 Report.SymbolRelatedToPreviousError (collision_with.loc, null);
3029 Report.Error (152, loc, "The label `case {0}:' already occurs in this switch statement", label);
3032 public SwitchLabel Clone (CloneContext clonectx)
3034 return new SwitchLabel (label.Clone (clonectx), loc);
3038 public class SwitchSection {
3039 // An array of SwitchLabels.
3040 public readonly ArrayList Labels;
3041 public readonly Block Block;
3043 public SwitchSection (ArrayList labels, Block block)
3049 public SwitchSection Clone (CloneContext clonectx)
3051 ArrayList cloned_labels = new ArrayList ();
3053 foreach (SwitchLabel sl in cloned_labels)
3054 cloned_labels.Add (sl.Clone (clonectx));
3056 return new SwitchSection (cloned_labels, clonectx.LookupBlock (Block));
3060 public class Switch : Statement {
3061 public ArrayList Sections;
3062 public Expression Expr;
3065 /// Maps constants whose type type SwitchType to their SwitchLabels.
3067 public IDictionary Elements;
3070 /// The governing switch type
3072 public Type SwitchType;
3077 Label default_target;
3079 Expression new_expr;
3082 SwitchSection constant_section;
3083 SwitchSection default_section;
3085 ExpressionStatement string_dictionary;
3086 FieldExpr switch_cache_field;
3087 static int unique_counter;
3091 // Nullable Types support for GMCS.
3093 Nullable.Unwrap unwrap;
3095 protected bool HaveUnwrap {
3096 get { return unwrap != null; }
3099 protected bool HaveUnwrap {
3100 get { return false; }
3105 // The types allowed to be implicitly cast from
3106 // on the governing type
3108 static Type [] allowed_types;
3110 public Switch (Expression e, ArrayList sects, Location l)
3117 public bool GotDefault {
3119 return default_section != null;
3123 public Label DefaultTarget {
3125 return default_target;
3130 // Determines the governing type for a switch. The returned
3131 // expression might be the expression from the switch, or an
3132 // expression that includes any potential conversions to the
3133 // integral types or to string.
3135 Expression SwitchGoverningType (EmitContext ec, Expression expr)
3139 if (t == TypeManager.byte_type ||
3140 t == TypeManager.sbyte_type ||
3141 t == TypeManager.ushort_type ||
3142 t == TypeManager.short_type ||
3143 t == TypeManager.uint32_type ||
3144 t == TypeManager.int32_type ||
3145 t == TypeManager.uint64_type ||
3146 t == TypeManager.int64_type ||
3147 t == TypeManager.char_type ||
3148 t == TypeManager.string_type ||
3149 t == TypeManager.bool_type ||
3150 TypeManager.IsEnumType (t))
3153 if (allowed_types == null){
3154 allowed_types = new Type [] {
3155 TypeManager.sbyte_type,
3156 TypeManager.byte_type,
3157 TypeManager.short_type,
3158 TypeManager.ushort_type,
3159 TypeManager.int32_type,
3160 TypeManager.uint32_type,
3161 TypeManager.int64_type,
3162 TypeManager.uint64_type,
3163 TypeManager.char_type,
3164 TypeManager.string_type
3169 // Try to find a *user* defined implicit conversion.
3171 // If there is no implicit conversion, or if there are multiple
3172 // conversions, we have to report an error
3174 Expression converted = null;
3175 foreach (Type tt in allowed_types){
3178 e = Convert.ImplicitUserConversion (ec, expr, tt, loc);
3183 // Ignore over-worked ImplicitUserConversions that do
3184 // an implicit conversion in addition to the user conversion.
3186 if (!(e is UserCast))
3189 if (converted != null){
3190 Report.ExtraInformation (loc, "(Ambiguous implicit user defined conversion in previous ");
3200 // Performs the basic sanity checks on the switch statement
3201 // (looks for duplicate keys and non-constant expressions).
3203 // It also returns a hashtable with the keys that we will later
3204 // use to compute the switch tables
3206 bool CheckSwitch (EmitContext ec)
3209 Elements = Sections.Count > 10 ?
3210 (IDictionary)new Hashtable () :
3211 (IDictionary)new ListDictionary ();
3213 foreach (SwitchSection ss in Sections){
3214 foreach (SwitchLabel sl in ss.Labels){
3215 if (sl.Label == null){
3216 if (default_section != null){
3217 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)default_section.Labels [0]);
3220 default_section = ss;
3224 if (!sl.ResolveAndReduce (ec, SwitchType, HaveUnwrap)) {
3229 object key = sl.Converted;
3230 if (key == SwitchLabel.NullStringCase)
3231 has_null_case = true;
3234 Elements.Add (key, sl);
3235 } catch (ArgumentException) {
3236 sl.Error_AlreadyOccurs (SwitchType, (SwitchLabel)Elements [key]);
3244 void EmitObjectInteger (ILGenerator ig, object k)
3247 IntConstant.EmitInt (ig, (int) k);
3248 else if (k is Constant) {
3249 EmitObjectInteger (ig, ((Constant) k).GetValue ());
3252 IntConstant.EmitInt (ig, unchecked ((int) (uint) k));
3255 if ((long) k >= int.MinValue && (long) k <= int.MaxValue)
3257 IntConstant.EmitInt (ig, (int) (long) k);
3258 ig.Emit (OpCodes.Conv_I8);
3261 LongConstant.EmitLong (ig, (long) k);
3263 else if (k is ulong)
3265 ulong ul = (ulong) k;
3268 IntConstant.EmitInt (ig, unchecked ((int) ul));
3269 ig.Emit (OpCodes.Conv_U8);
3273 LongConstant.EmitLong (ig, unchecked ((long) ul));
3277 IntConstant.EmitInt (ig, (int) ((char) k));
3278 else if (k is sbyte)
3279 IntConstant.EmitInt (ig, (int) ((sbyte) k));
3281 IntConstant.EmitInt (ig, (int) ((byte) k));
3282 else if (k is short)
3283 IntConstant.EmitInt (ig, (int) ((short) k));
3284 else if (k is ushort)
3285 IntConstant.EmitInt (ig, (int) ((ushort) k));
3287 IntConstant.EmitInt (ig, ((bool) k) ? 1 : 0);
3289 throw new Exception ("Unhandled case");
3292 // structure used to hold blocks of keys while calculating table switch
3293 class KeyBlock : IComparable
3295 public KeyBlock (long _first)
3297 first = last = _first;
3301 public ArrayList element_keys = null;
3302 // how many items are in the bucket
3303 public int Size = 1;
3306 get { return (int) (last - first + 1); }
3308 public static long TotalLength (KeyBlock kb_first, KeyBlock kb_last)
3310 return kb_last.last - kb_first.first + 1;
3312 public int CompareTo (object obj)
3314 KeyBlock kb = (KeyBlock) obj;
3315 int nLength = Length;
3316 int nLengthOther = kb.Length;
3317 if (nLengthOther == nLength)
3318 return (int) (kb.first - first);
3319 return nLength - nLengthOther;
3324 /// This method emits code for a lookup-based switch statement (non-string)
3325 /// Basically it groups the cases into blocks that are at least half full,
3326 /// and then spits out individual lookup opcodes for each block.
3327 /// It emits the longest blocks first, and short blocks are just
3328 /// handled with direct compares.
3330 /// <param name="ec"></param>
3331 /// <param name="val"></param>
3332 /// <returns></returns>
3333 void TableSwitchEmit (EmitContext ec, Expression val)
3335 int element_count = Elements.Count;
3336 object [] element_keys = new object [element_count];
3337 Elements.Keys.CopyTo (element_keys, 0);
3338 Array.Sort (element_keys);
3340 // initialize the block list with one element per key
3341 ArrayList key_blocks = new ArrayList (element_count);
3342 foreach (object key in element_keys)
3343 key_blocks.Add (new KeyBlock (System.Convert.ToInt64 (key)));
3345 KeyBlock current_kb;
3346 // iteratively merge the blocks while they are at least half full
3347 // there's probably a really cool way to do this with a tree...
3348 while (key_blocks.Count > 1)
3350 ArrayList key_blocks_new = new ArrayList ();
3351 current_kb = (KeyBlock) key_blocks [0];
3352 for (int ikb = 1; ikb < key_blocks.Count; ikb++)
3354 KeyBlock kb = (KeyBlock) key_blocks [ikb];
3355 if ((current_kb.Size + kb.Size) * 2 >= KeyBlock.TotalLength (current_kb, kb))
3358 current_kb.last = kb.last;
3359 current_kb.Size += kb.Size;
3363 // start a new block
3364 key_blocks_new.Add (current_kb);
3368 key_blocks_new.Add (current_kb);
3369 if (key_blocks.Count == key_blocks_new.Count)
3371 key_blocks = key_blocks_new;
3374 // initialize the key lists
3375 foreach (KeyBlock kb in key_blocks)
3376 kb.element_keys = new ArrayList ();
3378 // fill the key lists
3380 if (key_blocks.Count > 0) {
3381 current_kb = (KeyBlock) key_blocks [0];
3382 foreach (object key in element_keys)
3384 bool next_block = (key is UInt64) ? (ulong) key > (ulong) current_kb.last :
3385 System.Convert.ToInt64 (key) > current_kb.last;
3387 current_kb = (KeyBlock) key_blocks [++iBlockCurr];
3388 current_kb.element_keys.Add (key);
3392 // sort the blocks so we can tackle the largest ones first
3395 // okay now we can start...
3396 ILGenerator ig = ec.ig;
3397 Label lbl_end = ig.DefineLabel (); // at the end ;-)
3398 Label lbl_default = default_target;
3400 Type type_keys = null;
3401 if (element_keys.Length > 0)
3402 type_keys = element_keys [0].GetType (); // used for conversions
3406 if (TypeManager.IsEnumType (SwitchType))
3407 compare_type = TypeManager.GetEnumUnderlyingType (SwitchType);
3409 compare_type = SwitchType;
3411 for (int iBlock = key_blocks.Count - 1; iBlock >= 0; --iBlock)
3413 KeyBlock kb = ((KeyBlock) key_blocks [iBlock]);
3414 lbl_default = (iBlock == 0) ? default_target : ig.DefineLabel ();
3417 foreach (object key in kb.element_keys) {
3418 SwitchLabel sl = (SwitchLabel) Elements [key];
3419 if (key is int && (int) key == 0) {
3420 val.EmitBranchable (ec, sl.GetILLabel (ec), false);
3423 EmitObjectInteger (ig, key);
3424 ig.Emit (OpCodes.Beq, sl.GetILLabel (ec));
3430 // TODO: if all the keys in the block are the same and there are
3431 // no gaps/defaults then just use a range-check.
3432 if (compare_type == TypeManager.int64_type ||
3433 compare_type == TypeManager.uint64_type)
3435 // TODO: optimize constant/I4 cases
3437 // check block range (could be > 2^31)
3439 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3440 ig.Emit (OpCodes.Blt, lbl_default);
3442 EmitObjectInteger (ig, System.Convert.ChangeType (kb.last, type_keys));
3443 ig.Emit (OpCodes.Bgt, lbl_default);
3449 EmitObjectInteger (ig, System.Convert.ChangeType (kb.first, type_keys));
3450 ig.Emit (OpCodes.Sub);
3452 ig.Emit (OpCodes.Conv_I4); // assumes < 2^31 labels!
3458 int first = (int) kb.first;
3461 IntConstant.EmitInt (ig, first);
3462 ig.Emit (OpCodes.Sub);
3466 IntConstant.EmitInt (ig, -first);
3467 ig.Emit (OpCodes.Add);
3471 // first, build the list of labels for the switch
3473 int cJumps = kb.Length;
3474 Label [] switch_labels = new Label [cJumps];
3475 for (int iJump = 0; iJump < cJumps; iJump++)
3477 object key = kb.element_keys [iKey];
3478 if (System.Convert.ToInt64 (key) == kb.first + iJump)
3480 SwitchLabel sl = (SwitchLabel) Elements [key];
3481 switch_labels [iJump] = sl.GetILLabel (ec);
3485 switch_labels [iJump] = lbl_default;
3487 // emit the switch opcode
3488 ig.Emit (OpCodes.Switch, switch_labels);
3491 // mark the default for this block
3493 ig.MarkLabel (lbl_default);
3496 // TODO: find the default case and emit it here,
3497 // to prevent having to do the following jump.
3498 // make sure to mark other labels in the default section
3500 // the last default just goes to the end
3501 if (element_keys.Length > 0)
3502 ig.Emit (OpCodes.Br, lbl_default);
3504 // now emit the code for the sections
3505 bool found_default = false;
3507 foreach (SwitchSection ss in Sections) {
3508 foreach (SwitchLabel sl in ss.Labels) {
3509 if (sl.Converted == SwitchLabel.NullStringCase) {
3510 ig.MarkLabel (null_target);
3511 } else if (sl.Label == null) {
3512 ig.MarkLabel (lbl_default);
3513 found_default = true;
3515 ig.MarkLabel (null_target);
3517 ig.MarkLabel (sl.GetILLabel (ec));
3518 ig.MarkLabel (sl.GetILLabelCode (ec));
3523 if (!found_default) {
3524 ig.MarkLabel (lbl_default);
3525 if (!has_null_case) {
3526 ig.MarkLabel (null_target);
3530 ig.MarkLabel (lbl_end);
3533 SwitchSection FindSection (SwitchLabel label)
3535 foreach (SwitchSection ss in Sections){
3536 foreach (SwitchLabel sl in ss.Labels){
3545 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
3547 foreach (SwitchSection ss in Sections)
3548 ss.Block.MutateHoistedGenericType (storey);
3551 public static void Reset ()
3556 public override bool Resolve (EmitContext ec)
3558 Expr = Expr.Resolve (ec);
3562 new_expr = SwitchGoverningType (ec, Expr);
3565 if ((new_expr == null) && TypeManager.IsNullableType (Expr.Type)) {
3566 unwrap = Nullable.Unwrap.Create (Expr, ec);
3570 new_expr = SwitchGoverningType (ec, unwrap);
3574 if (new_expr == null){
3575 Report.Error (151, loc, "A value of an integral type or string expected for switch");
3580 SwitchType = new_expr.Type;
3582 if (RootContext.Version == LanguageVersion.ISO_1 && SwitchType == TypeManager.bool_type) {
3583 Report.FeatureIsNotAvailable (loc, "switch expression of boolean type");
3587 if (!CheckSwitch (ec))
3591 Elements.Remove (SwitchLabel.NullStringCase);
3593 Switch old_switch = ec.Switch;
3595 ec.Switch.SwitchType = SwitchType;
3597 Report.Debug (1, "START OF SWITCH BLOCK", loc, ec.CurrentBranching);
3598 ec.StartFlowBranching (FlowBranching.BranchingType.Switch, loc);
3600 is_constant = new_expr is Constant;
3602 object key = ((Constant) new_expr).GetValue ();
3603 SwitchLabel label = (SwitchLabel) Elements [key];
3605 constant_section = FindSection (label);
3606 if (constant_section == null)
3607 constant_section = default_section;
3612 foreach (SwitchSection ss in Sections){
3614 ec.CurrentBranching.CreateSibling (
3615 null, FlowBranching.SiblingType.SwitchSection);
3619 if (is_constant && (ss != constant_section)) {
3620 // If we're a constant switch, we're only emitting
3621 // one single section - mark all the others as
3623 ec.CurrentBranching.CurrentUsageVector.Goto ();
3624 if (!ss.Block.ResolveUnreachable (ec, true)) {
3628 if (!ss.Block.Resolve (ec))
3633 if (default_section == null)
3634 ec.CurrentBranching.CreateSibling (
3635 null, FlowBranching.SiblingType.SwitchSection);
3637 ec.EndFlowBranching ();
3638 ec.Switch = old_switch;
3640 Report.Debug (1, "END OF SWITCH BLOCK", loc, ec.CurrentBranching);
3645 if (SwitchType == TypeManager.string_type && !is_constant) {
3646 // TODO: Optimize single case, and single+default case
3647 ResolveStringSwitchMap (ec);
3653 void ResolveStringSwitchMap (EmitContext ec)
3655 FullNamedExpression string_dictionary_type;
3657 MemberAccess system_collections_generic = new MemberAccess (new MemberAccess (
3658 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc), "Generic", loc);
3660 string_dictionary_type = new MemberAccess (system_collections_generic, "Dictionary",
3662 new TypeExpression (TypeManager.string_type, loc),
3663 new TypeExpression (TypeManager.int32_type, loc)), loc);
3665 MemberAccess system_collections_generic = new MemberAccess (
3666 new QualifiedAliasMember (QualifiedAliasMember.GlobalAlias, "System", loc), "Collections", loc);
3668 string_dictionary_type = new MemberAccess (system_collections_generic, "Hashtable", loc);
3670 Field field = new Field (ec.TypeContainer, string_dictionary_type,
3671 Modifiers.STATIC | Modifiers.PRIVATE | Modifiers.COMPILER_GENERATED,
3672 new MemberName (CompilerGeneratedClass.MakeName (null, "f", "switch$map", unique_counter++), loc), null);
3673 if (!field.Define ())
3675 ec.TypeContainer.PartialContainer.AddField (field);
3677 ArrayList init = new ArrayList ();
3680 string value = null;
3681 foreach (SwitchSection section in Sections) {
3682 foreach (SwitchLabel sl in section.Labels) {
3683 if (sl.Label == null || sl.Converted == SwitchLabel.NullStringCase) {
3688 value = (string) sl.Converted;
3689 ArrayList init_args = new ArrayList (2);
3690 init_args.Add (new StringLiteral (value, sl.Location));
3691 init_args.Add (new IntConstant (counter, loc));
3692 init.Add (new CollectionElementInitializer (init_args, loc));
3698 Elements.Add (counter, section.Labels [0]);
3702 ArrayList args = new ArrayList (1);
3703 args.Add (new Argument (new IntConstant (Sections.Count, loc)));
3704 Expression initializer = new NewInitialize (string_dictionary_type, args,
3705 new CollectionOrObjectInitializers (init, loc), loc);
3707 switch_cache_field = new FieldExpr (field.FieldBuilder, loc);
3708 string_dictionary = new SimpleAssign (switch_cache_field, initializer.Resolve (ec));
3711 void DoEmitStringSwitch (LocalTemporary value, EmitContext ec)
3713 ILGenerator ig = ec.ig;
3714 Label l_initialized = ig.DefineLabel ();
3717 // Skip initialization when value is null
3719 value.EmitBranchable (ec, null_target, false);
3722 // Check if string dictionary is initialized and initialize
3724 switch_cache_field.EmitBranchable (ec, l_initialized, true);
3725 string_dictionary.EmitStatement (ec);
3726 ig.MarkLabel (l_initialized);
3728 LocalTemporary string_switch_variable = new LocalTemporary (TypeManager.int32_type);
3731 ArrayList get_value_args = new ArrayList (2);
3732 get_value_args.Add (new Argument (value));
3733 get_value_args.Add (new Argument (string_switch_variable, Argument.AType.Out));
3734 Expression get_item = new Invocation (new MemberAccess (switch_cache_field, "TryGetValue", loc), get_value_args).Resolve (ec);
3735 if (get_item == null)
3739 // A value was not found, go to default case
3741 get_item.EmitBranchable (ec, default_target, false);
3743 ArrayList get_value_args = new ArrayList (1);
3744 get_value_args.Add (value);
3746 Expression get_item = new IndexerAccess (new ElementAccess (switch_cache_field, get_value_args), loc).Resolve (ec);
3747 if (get_item == null)
3750 LocalTemporary get_item_object = new LocalTemporary (TypeManager.object_type);
3751 get_item_object.EmitAssign (ec, get_item, true, false);
3752 ec.ig.Emit (OpCodes.Brfalse, default_target);
3754 ExpressionStatement get_item_int = (ExpressionStatement) new SimpleAssign (string_switch_variable,
3755 new Cast (new TypeExpression (TypeManager.int32_type, loc), get_item_object, loc)).Resolve (ec);
3757 get_item_int.EmitStatement (ec);
3758 get_item_object.Release (ec);
3760 TableSwitchEmit (ec, string_switch_variable);
3761 string_switch_variable.Release (ec);
3764 protected override void DoEmit (EmitContext ec)
3766 ILGenerator ig = ec.ig;
3768 default_target = ig.DefineLabel ();
3769 null_target = ig.DefineLabel ();
3771 // Store variable for comparission purposes
3772 // TODO: Don't duplicate non-captured VariableReference
3773 LocalTemporary value;
3775 value = new LocalTemporary (SwitchType);
3777 unwrap.EmitCheck (ec);
3778 ig.Emit (OpCodes.Brfalse, null_target);
3782 } else if (!is_constant) {
3783 value = new LocalTemporary (SwitchType);
3790 // Setup the codegen context
3792 Label old_end = ec.LoopEnd;
3793 Switch old_switch = ec.Switch;
3795 ec.LoopEnd = ig.DefineLabel ();
3800 if (constant_section != null)
3801 constant_section.Block.Emit (ec);
3802 } else if (string_dictionary != null) {
3803 DoEmitStringSwitch (value, ec);
3805 TableSwitchEmit (ec, value);
3811 // Restore context state.
3812 ig.MarkLabel (ec.LoopEnd);
3815 // Restore the previous context
3817 ec.LoopEnd = old_end;
3818 ec.Switch = old_switch;
3821 protected override void CloneTo (CloneContext clonectx, Statement t)
3823 Switch target = (Switch) t;
3825 target.Expr = Expr.Clone (clonectx);
3826 target.Sections = new ArrayList ();
3827 foreach (SwitchSection ss in Sections){
3828 target.Sections.Add (ss.Clone (clonectx));
3833 // A place where execution can restart in an iterator
3834 public abstract class ResumableStatement : Statement
3837 protected Label resume_point;
3839 public Label PrepareForEmit (EmitContext ec)
3843 resume_point = ec.ig.DefineLabel ();
3845 return resume_point;
3848 public virtual Label PrepareForDispose (EmitContext ec, Label end)
3852 public virtual void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3857 // Base class for statements that are implemented in terms of try...finally
3858 public abstract class ExceptionStatement : ResumableStatement
3862 protected abstract void EmitPreTryBody (EmitContext ec);
3863 protected abstract void EmitTryBody (EmitContext ec);
3864 protected abstract void EmitFinallyBody (EmitContext ec);
3866 protected sealed override void DoEmit (EmitContext ec)
3868 ILGenerator ig = ec.ig;
3870 EmitPreTryBody (ec);
3872 if (resume_points != null) {
3873 IntConstant.EmitInt (ig, (int) Iterator.State.Running);
3874 ig.Emit (OpCodes.Stloc, ec.CurrentIterator.CurrentPC);
3877 ig.BeginExceptionBlock ();
3879 if (resume_points != null) {
3880 ig.MarkLabel (resume_point);
3882 // For normal control flow, we want to fall-through the Switch
3883 // So, we use CurrentPC rather than the $PC field, and initialize it to an outside value above
3884 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.CurrentPC);
3885 IntConstant.EmitInt (ig, first_resume_pc);
3886 ig.Emit (OpCodes.Sub);
3888 Label [] labels = new Label [resume_points.Count];
3889 for (int i = 0; i < resume_points.Count; ++i)
3890 labels [i] = ((ResumableStatement) resume_points [i]).PrepareForEmit (ec);
3891 ig.Emit (OpCodes.Switch, labels);
3896 ig.BeginFinallyBlock ();
3898 Label start_finally = ec.ig.DefineLabel ();
3899 if (resume_points != null) {
3900 ig.Emit (OpCodes.Ldloc, ec.CurrentIterator.SkipFinally);
3901 ig.Emit (OpCodes.Brfalse_S, start_finally);
3902 ig.Emit (OpCodes.Endfinally);
3905 ig.MarkLabel (start_finally);
3906 EmitFinallyBody (ec);
3908 ig.EndExceptionBlock ();
3911 public void SomeCodeFollows ()
3913 code_follows = true;
3916 protected void ResolveReachability (EmitContext ec)
3918 // System.Reflection.Emit automatically emits a 'leave' at the end of a try clause
3919 // So, ensure there's some IL code after this statement.
3920 if (!code_follows && resume_points == null && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
3921 ec.NeedReturnLabel ();
3925 ArrayList resume_points;
3926 int first_resume_pc;
3927 public void AddResumePoint (ResumableStatement stmt, int pc)
3929 if (resume_points == null) {
3930 resume_points = new ArrayList ();
3931 first_resume_pc = pc;
3934 if (pc != first_resume_pc + resume_points.Count)
3935 throw new InternalErrorException ("missed an intervening AddResumePoint?");
3937 resume_points.Add (stmt);
3940 Label dispose_try_block;
3941 bool prepared_for_dispose, emitted_dispose;
3942 public override Label PrepareForDispose (EmitContext ec, Label end)
3944 if (!prepared_for_dispose) {
3945 prepared_for_dispose = true;
3946 dispose_try_block = ec.ig.DefineLabel ();
3948 return dispose_try_block;
3951 public override void EmitForDispose (EmitContext ec, Iterator iterator, Label end, bool have_dispatcher)
3953 if (emitted_dispose)
3956 emitted_dispose = true;
3958 ILGenerator ig = ec.ig;
3960 Label end_of_try = ig.DefineLabel ();
3962 // Ensure that the only way we can get into this code is through a dispatcher
3963 if (have_dispatcher)
3964 ig.Emit (OpCodes.Br, end);
3966 ig.BeginExceptionBlock ();
3968 ig.MarkLabel (dispose_try_block);
3970 Label [] labels = null;
3971 for (int i = 0; i < resume_points.Count; ++i) {
3972 ResumableStatement s = (ResumableStatement) resume_points [i];
3973 Label ret = s.PrepareForDispose (ec, end_of_try);
3974 if (ret.Equals (end_of_try) && labels == null)
3976 if (labels == null) {
3977 labels = new Label [resume_points.Count];
3978 for (int j = 0; j < i; ++j)
3979 labels [j] = end_of_try;
3984 if (labels != null) {
3986 for (j = 1; j < labels.Length; ++j)
3987 if (!labels [0].Equals (labels [j]))
3989 bool emit_dispatcher = j < labels.Length;
3991 if (emit_dispatcher) {
3992 //SymbolWriter.StartIteratorDispatcher (ec.ig);
3993 ig.Emit (OpCodes.Ldloc, iterator.CurrentPC);
3994 IntConstant.EmitInt (ig, first_resume_pc);
3995 ig.Emit (OpCodes.Sub);
3996 ig.Emit (OpCodes.Switch, labels);
3997 //SymbolWriter.EndIteratorDispatcher (ec.ig);
4000 foreach (ResumableStatement s in resume_points)
4001 s.EmitForDispose (ec, iterator, end_of_try, emit_dispatcher);
4004 ig.MarkLabel (end_of_try);
4006 ig.BeginFinallyBlock ();
4008 EmitFinallyBody (ec);
4010 ig.EndExceptionBlock ();
4014 public class Lock : ExceptionStatement {
4016 public Statement Statement;
4017 TemporaryVariable temp;
4019 public Lock (Expression expr, Statement stmt, Location l)
4026 public override bool Resolve (EmitContext ec)
4028 expr = expr.Resolve (ec);
4032 if (expr.Type.IsValueType){
4033 Report.Error (185, loc,
4034 "`{0}' is not a reference type as required by the lock statement",
4035 TypeManager.CSharpName (expr.Type));
4039 ec.StartFlowBranching (this);
4040 bool ok = Statement.Resolve (ec);
4041 ec.EndFlowBranching ();
4043 ResolveReachability (ec);
4045 // Avoid creating libraries that reference the internal
4048 if (t == TypeManager.null_type)
4049 t = TypeManager.object_type;
4051 temp = new TemporaryVariable (t, loc);
4054 if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
4055 Type monitor_type = TypeManager.CoreLookupType ("System.Threading", "Monitor", Kind.Class, true);
4056 TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
4057 monitor_type, "Enter", loc, TypeManager.object_type);
4058 TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
4059 monitor_type, "Exit", loc, TypeManager.object_type);
4065 protected override void EmitPreTryBody (EmitContext ec)
4067 ILGenerator ig = ec.ig;
4069 temp.EmitAssign (ec, expr);
4071 ig.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
4074 protected override void EmitTryBody (EmitContext ec)
4076 Statement.Emit (ec);
4079 protected override void EmitFinallyBody (EmitContext ec)
4082 ec.ig.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
4085 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4087 expr.MutateHoistedGenericType (storey);
4088 temp.MutateHoistedGenericType (storey);
4089 Statement.MutateHoistedGenericType (storey);
4092 protected override void CloneTo (CloneContext clonectx, Statement t)
4094 Lock target = (Lock) t;
4096 target.expr = expr.Clone (clonectx);
4097 target.Statement = Statement.Clone (clonectx);
4101 public class Unchecked : Statement {
4104 public Unchecked (Block b)
4110 public override bool Resolve (EmitContext ec)
4112 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4113 return Block.Resolve (ec);
4116 protected override void DoEmit (EmitContext ec)
4118 using (ec.With (EmitContext.Flags.AllCheckStateFlags, false))
4122 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4124 Block.MutateHoistedGenericType (storey);
4127 protected override void CloneTo (CloneContext clonectx, Statement t)
4129 Unchecked target = (Unchecked) t;
4131 target.Block = clonectx.LookupBlock (Block);
4135 public class Checked : Statement {
4138 public Checked (Block b)
4141 b.Unchecked = false;
4144 public override bool Resolve (EmitContext ec)
4146 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4147 return Block.Resolve (ec);
4150 protected override void DoEmit (EmitContext ec)
4152 using (ec.With (EmitContext.Flags.AllCheckStateFlags, true))
4156 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4158 Block.MutateHoistedGenericType (storey);
4161 protected override void CloneTo (CloneContext clonectx, Statement t)
4163 Checked target = (Checked) t;
4165 target.Block = clonectx.LookupBlock (Block);
4169 public class Unsafe : Statement {
4172 public Unsafe (Block b)
4175 Block.Unsafe = true;
4178 public override bool Resolve (EmitContext ec)
4180 using (ec.With (EmitContext.Flags.InUnsafe, true))
4181 return Block.Resolve (ec);
4184 protected override void DoEmit (EmitContext ec)
4186 using (ec.With (EmitContext.Flags.InUnsafe, true))
4190 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4192 Block.MutateHoistedGenericType (storey);
4195 protected override void CloneTo (CloneContext clonectx, Statement t)
4197 Unsafe target = (Unsafe) t;
4199 target.Block = clonectx.LookupBlock (Block);
4206 public class Fixed : Statement {
4208 ArrayList declarators;
4209 Statement statement;
4214 abstract class Emitter
4216 protected LocalInfo vi;
4217 protected Expression converted;
4219 protected Emitter (Expression expr, LocalInfo li)
4225 public abstract void Emit (EmitContext ec);
4226 public abstract void EmitExit (EmitContext ec);
4229 class ExpressionEmitter : Emitter {
4230 public ExpressionEmitter (Expression converted, LocalInfo li) :
4231 base (converted, li)
4235 public override void Emit (EmitContext ec) {
4237 // Store pointer in pinned location
4239 converted.Emit (ec);
4243 public override void EmitExit (EmitContext ec)
4245 ec.ig.Emit (OpCodes.Ldc_I4_0);
4246 ec.ig.Emit (OpCodes.Conv_U);
4251 class StringEmitter : Emitter {
4252 class StringPtr : Expression
4256 public StringPtr (LocalBuilder b, Location l)
4259 eclass = ExprClass.Value;
4260 type = TypeManager.char_ptr_type;
4264 public override Expression CreateExpressionTree (EmitContext ec)
4266 throw new NotSupportedException ("ET");
4269 public override Expression DoResolve (EmitContext ec)
4271 // This should never be invoked, we are born in fully
4272 // initialized state.
4277 public override void Emit (EmitContext ec)
4279 if (TypeManager.int_get_offset_to_string_data == null) {
4280 // TODO: Move to resolve !!
4281 TypeManager.int_get_offset_to_string_data = TypeManager.GetPredefinedMethod (
4282 TypeManager.runtime_helpers_type, "get_OffsetToStringData", loc, Type.EmptyTypes);
4285 ILGenerator ig = ec.ig;
4287 ig.Emit (OpCodes.Ldloc, b);
4288 ig.Emit (OpCodes.Conv_I);
4289 ig.Emit (OpCodes.Call, TypeManager.int_get_offset_to_string_data);
4290 ig.Emit (OpCodes.Add);
4294 LocalBuilder pinned_string;
4297 public StringEmitter (Expression expr, LocalInfo li, Location loc):
4303 public override void Emit (EmitContext ec)
4305 ILGenerator ig = ec.ig;
4306 pinned_string = TypeManager.DeclareLocalPinned (ig, TypeManager.string_type);
4308 converted.Emit (ec);
4309 ig.Emit (OpCodes.Stloc, pinned_string);
4311 Expression sptr = new StringPtr (pinned_string, loc);
4312 converted = Convert.ImplicitConversionRequired (
4313 ec, sptr, vi.VariableType, loc);
4315 if (converted == null)
4318 converted.Emit (ec);
4322 public override void EmitExit (EmitContext ec)
4324 ec.ig.Emit (OpCodes.Ldnull);
4325 ec.ig.Emit (OpCodes.Stloc, pinned_string);
4329 public Fixed (Expression type, ArrayList decls, Statement stmt, Location l)
4332 declarators = decls;
4337 public Statement Statement {
4338 get { return statement; }
4341 public override bool Resolve (EmitContext ec)
4344 Expression.UnsafeError (loc);
4348 TypeExpr texpr = type.ResolveAsContextualType (ec, false);
4349 if (texpr == null) {
4350 if (type is VarExpr)
4351 Report.Error (821, type.Location, "A fixed statement cannot use an implicitly typed local variable");
4356 expr_type = texpr.Type;
4358 data = new Emitter [declarators.Count];
4360 if (!expr_type.IsPointer){
4361 Report.Error (209, loc, "The type of locals declared in a fixed statement must be a pointer type");
4366 foreach (Pair p in declarators){
4367 LocalInfo vi = (LocalInfo) p.First;
4368 Expression e = (Expression) p.Second;
4370 vi.VariableInfo.SetAssigned (ec);
4371 vi.SetReadOnlyContext (LocalInfo.ReadOnlyContext.Fixed);
4374 // The rules for the possible declarators are pretty wise,
4375 // but the production on the grammar is more concise.
4377 // So we have to enforce these rules here.
4379 // We do not resolve before doing the case 1 test,
4380 // because the grammar is explicit in that the token &
4381 // is present, so we need to test for this particular case.
4385 Report.Error (254, loc, "The right hand side of a fixed statement assignment may not be a cast expression");
4389 ec.InFixedInitializer = true;
4391 ec.InFixedInitializer = false;
4398 if (e.Type.IsArray){
4399 Type array_type = TypeManager.GetElementType (e.Type);
4402 // Provided that array_type is unmanaged,
4404 if (!TypeManager.VerifyUnManaged (array_type, loc))
4408 // and T* is implicitly convertible to the
4409 // pointer type given in the fixed statement.
4411 ArrayPtr array_ptr = new ArrayPtr (e, array_type, loc);
4413 Expression converted = Convert.ImplicitConversionRequired (
4414 ec, array_ptr, vi.VariableType, loc);
4415 if (converted == null)
4419 // fixed (T* e_ptr = (e == null || e.Length == 0) ? null : converted [0])
4421 converted = new Conditional (new Binary (Binary.Operator.LogicalOr,
4422 new Binary (Binary.Operator.Equality, e, new NullLiteral (loc)),
4423 new Binary (Binary.Operator.Equality, new MemberAccess (e, "Length"), new IntConstant (0, loc))),
4424 new NullPointer (loc),
4427 converted = converted.Resolve (ec);
4429 data [i] = new ExpressionEmitter (converted, vi);
4438 if (e.Type == TypeManager.string_type){
4439 data [i] = new StringEmitter (e, vi, loc);
4444 // Case 4: fixed buffer
4445 if (e is FixedBufferPtr) {
4446 data [i++] = new ExpressionEmitter (e, vi);
4451 // Case 1: & object.
4453 Unary u = e as Unary;
4454 if (u != null && u.Oper == Unary.Operator.AddressOf) {
4455 IVariableReference vr = u.Expr as IVariableReference;
4456 if (vr == null || !vr.IsFixed) {
4457 data [i] = new ExpressionEmitter (e, vi);
4461 if (data [i++] == null)
4462 Report.Error (213, vi.Location, "You cannot use the fixed statement to take the address of an already fixed expression");
4464 e = Convert.ImplicitConversionRequired (ec, e, expr_type, loc);
4467 ec.StartFlowBranching (FlowBranching.BranchingType.Conditional, loc);
4468 bool ok = statement.Resolve (ec);
4469 bool flow_unreachable = ec.EndFlowBranching ();
4470 has_ret = flow_unreachable;
4475 protected override void DoEmit (EmitContext ec)
4477 for (int i = 0; i < data.Length; i++) {
4481 statement.Emit (ec);
4487 // Clear the pinned variable
4489 for (int i = 0; i < data.Length; i++) {
4490 data [i].EmitExit (ec);
4494 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4496 // Fixed statement cannot be used inside anonymous methods or lambdas
4497 throw new NotSupportedException ();
4500 protected override void CloneTo (CloneContext clonectx, Statement t)
4502 Fixed target = (Fixed) t;
4504 target.type = type.Clone (clonectx);
4505 target.declarators = new ArrayList (declarators.Count);
4506 foreach (Pair p in declarators) {
4507 LocalInfo vi = (LocalInfo) p.First;
4508 Expression e = (Expression) p.Second;
4510 target.declarators.Add (
4511 new Pair (clonectx.LookupVariable (vi), e.Clone (clonectx)));
4514 target.statement = statement.Clone (clonectx);
4518 public class Catch : Statement {
4519 public readonly string Name;
4521 public Block VarBlock;
4523 Expression type_expr;
4526 public Catch (Expression type, string name, Block block, Block var_block, Location l)
4531 VarBlock = var_block;
4535 public Type CatchType {
4541 public bool IsGeneral {
4543 return type_expr == null;
4547 protected override void DoEmit (EmitContext ec)
4549 ILGenerator ig = ec.ig;
4551 if (CatchType != null)
4552 ig.BeginCatchBlock (CatchType);
4554 ig.BeginCatchBlock (TypeManager.object_type);
4556 if (VarBlock != null)
4560 // TODO: Move to resolve
4561 LocalVariableReference lvr = new LocalVariableReference (Block, Name, loc);
4565 if (lvr.IsHoisted) {
4566 LocalTemporary lt = new LocalTemporary (lvr.Type);
4570 // Variable is at the top of the stack
4571 source = EmptyExpression.Null;
4574 lvr.EmitAssign (ec, source, false, false);
4576 ig.Emit (OpCodes.Pop);
4581 public override bool Resolve (EmitContext ec)
4583 using (ec.With (EmitContext.Flags.InCatch, true)) {
4584 if (type_expr != null) {
4585 TypeExpr te = type_expr.ResolveAsTypeTerminal (ec, false);
4591 if (type != TypeManager.exception_type && !TypeManager.IsSubclassOf (type, TypeManager.exception_type)){
4592 Error (155, "The type caught or thrown must be derived from System.Exception");
4598 if (!Block.Resolve (ec))
4601 // Even though VarBlock surrounds 'Block' we resolve it later, so that we can correctly
4602 // emit the "unused variable" warnings.
4603 if (VarBlock != null)
4604 return VarBlock.Resolve (ec);
4610 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4613 type = storey.MutateType (type);
4614 if (VarBlock != null)
4615 VarBlock.MutateHoistedGenericType (storey);
4616 Block.MutateHoistedGenericType (storey);
4619 protected override void CloneTo (CloneContext clonectx, Statement t)
4621 Catch target = (Catch) t;
4623 if (type_expr != null)
4624 target.type_expr = type_expr.Clone (clonectx);
4625 if (VarBlock != null)
4626 target.VarBlock = clonectx.LookupBlock (VarBlock);
4627 target.Block = clonectx.LookupBlock (Block);
4631 public class TryFinally : ExceptionStatement {
4635 public TryFinally (Statement stmt, Block fini, Location l)
4642 public override bool Resolve (EmitContext ec)
4646 ec.StartFlowBranching (this);
4648 if (!stmt.Resolve (ec))
4652 ec.CurrentBranching.CreateSibling (fini, FlowBranching.SiblingType.Finally);
4653 using (ec.With (EmitContext.Flags.InFinally, true)) {
4654 if (!fini.Resolve (ec))
4658 ec.EndFlowBranching ();
4660 ResolveReachability (ec);
4665 protected override void EmitPreTryBody (EmitContext ec)
4669 protected override void EmitTryBody (EmitContext ec)
4674 protected override void EmitFinallyBody (EmitContext ec)
4679 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4681 stmt.MutateHoistedGenericType (storey);
4682 fini.MutateHoistedGenericType (storey);
4685 protected override void CloneTo (CloneContext clonectx, Statement t)
4687 TryFinally target = (TryFinally) t;
4689 target.stmt = (Statement) stmt.Clone (clonectx);
4691 target.fini = clonectx.LookupBlock (fini);
4695 public class TryCatch : Statement {
4697 public ArrayList Specific;
4698 public Catch General;
4699 bool inside_try_finally, code_follows;
4701 public TryCatch (Block block, ArrayList catch_clauses, Location l, bool inside_try_finally)
4704 this.Specific = catch_clauses;
4705 this.General = null;
4706 this.inside_try_finally = inside_try_finally;
4708 for (int i = 0; i < catch_clauses.Count; ++i) {
4709 Catch c = (Catch) catch_clauses [i];
4711 if (i != catch_clauses.Count - 1)
4712 Report.Error (1017, c.loc, "Try statement already has an empty catch block");
4714 catch_clauses.RemoveAt (i);
4722 public override bool Resolve (EmitContext ec)
4726 ec.StartFlowBranching (this);
4728 if (!Block.Resolve (ec))
4731 Type[] prev_catches = new Type [Specific.Count];
4733 foreach (Catch c in Specific){
4734 ec.CurrentBranching.CreateSibling (c.Block, FlowBranching.SiblingType.Catch);
4736 if (c.Name != null) {
4737 LocalInfo vi = c.Block.GetLocalInfo (c.Name);
4739 throw new Exception ();
4741 vi.VariableInfo = null;
4744 if (!c.Resolve (ec))
4747 Type resolved_type = c.CatchType;
4748 for (int ii = 0; ii < last_index; ++ii) {
4749 if (resolved_type == prev_catches [ii] || TypeManager.IsSubclassOf (resolved_type, prev_catches [ii])) {
4750 Report.Error (160, c.loc, "A previous catch clause already catches all exceptions of this or a super type `{0}'", prev_catches [ii].FullName);
4755 prev_catches [last_index++] = resolved_type;
4758 if (General != null) {
4759 if (CodeGen.Assembly.WrapNonExceptionThrows) {
4760 foreach (Catch c in Specific){
4761 if (c.CatchType == TypeManager.exception_type) {
4762 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'");
4767 ec.CurrentBranching.CreateSibling (General.Block, FlowBranching.SiblingType.Catch);
4769 if (!General.Resolve (ec))
4773 ec.EndFlowBranching ();
4775 // System.Reflection.Emit automatically emits a 'leave' at the end of a try/catch clause
4776 // So, ensure there's some IL code after this statement
4777 if (!inside_try_finally && !code_follows && ec.CurrentBranching.CurrentUsageVector.IsUnreachable)
4778 ec.NeedReturnLabel ();
4783 public void SomeCodeFollows ()
4785 code_follows = true;
4788 protected override void DoEmit (EmitContext ec)
4790 ILGenerator ig = ec.ig;
4792 if (!inside_try_finally)
4793 ig.BeginExceptionBlock ();
4797 foreach (Catch c in Specific)
4800 if (General != null)
4803 if (!inside_try_finally)
4804 ig.EndExceptionBlock ();
4807 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4809 Block.MutateHoistedGenericType (storey);
4811 if (General != null)
4812 General.MutateHoistedGenericType (storey);
4813 if (Specific != null) {
4814 foreach (Catch c in Specific)
4815 c.MutateHoistedGenericType (storey);
4819 protected override void CloneTo (CloneContext clonectx, Statement t)
4821 TryCatch target = (TryCatch) t;
4823 target.Block = clonectx.LookupBlock (Block);
4824 if (General != null)
4825 target.General = (Catch) General.Clone (clonectx);
4826 if (Specific != null){
4827 target.Specific = new ArrayList ();
4828 foreach (Catch c in Specific)
4829 target.Specific.Add (c.Clone (clonectx));
4834 public class UsingTemporary : ExceptionStatement {
4835 TemporaryVariable local_copy;
4836 public Statement Statement;
4840 public UsingTemporary (Expression expr, Statement stmt, Location l)
4847 public override bool Resolve (EmitContext ec)
4849 expr = expr.Resolve (ec);
4853 expr_type = expr.Type;
4855 if (!TypeManager.ImplementsInterface (expr_type, TypeManager.idisposable_type)) {
4856 if (Convert.ImplicitConversion (ec, expr, TypeManager.idisposable_type, loc) == null) {
4857 Using.Error_IsNotConvertibleToIDisposable (expr);
4862 local_copy = new TemporaryVariable (expr_type, loc);
4863 local_copy.Resolve (ec);
4865 ec.StartFlowBranching (this);
4867 bool ok = Statement.Resolve (ec);
4869 ec.EndFlowBranching ();
4871 ResolveReachability (ec);
4873 if (TypeManager.void_dispose_void == null) {
4874 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
4875 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
4881 protected override void EmitPreTryBody (EmitContext ec)
4883 local_copy.EmitAssign (ec, expr);
4886 protected override void EmitTryBody (EmitContext ec)
4888 Statement.Emit (ec);
4891 protected override void EmitFinallyBody (EmitContext ec)
4893 ILGenerator ig = ec.ig;
4894 if (!expr_type.IsValueType) {
4895 Label skip = ig.DefineLabel ();
4896 local_copy.Emit (ec);
4897 ig.Emit (OpCodes.Brfalse, skip);
4898 local_copy.Emit (ec);
4899 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4900 ig.MarkLabel (skip);
4904 Expression ml = Expression.MemberLookup (
4905 ec.ContainerType, TypeManager.idisposable_type, expr_type,
4906 "Dispose", Location.Null);
4908 if (!(ml is MethodGroupExpr)) {
4909 local_copy.Emit (ec);
4910 ig.Emit (OpCodes.Box, expr_type);
4911 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
4915 MethodInfo mi = null;
4917 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
4918 if (TypeManager.GetParameterData (mk).Count == 0) {
4925 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
4929 local_copy.AddressOf (ec, AddressOp.Load);
4930 ig.Emit (OpCodes.Call, mi);
4933 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
4935 expr_type = storey.MutateType (expr_type);
4936 local_copy.MutateHoistedGenericType (storey);
4937 Statement.MutateHoistedGenericType (storey);
4940 protected override void CloneTo (CloneContext clonectx, Statement t)
4942 UsingTemporary target = (UsingTemporary) t;
4944 target.expr = expr.Clone (clonectx);
4945 target.Statement = Statement.Clone (clonectx);
4949 public class Using : ExceptionStatement {
4951 public Statement EmbeddedStatement {
4952 get { return stmt is Using ? ((Using) stmt).EmbeddedStatement : stmt; }
4958 Expression converted_var;
4959 ExpressionStatement assign;
4961 public Using (Expression var, Expression init, Statement stmt, Location l)
4969 bool ResolveVariable (EmitContext ec)
4971 ExpressionStatement a = new SimpleAssign (var, init, loc);
4972 a = a.ResolveStatement (ec);
4978 if (TypeManager.ImplementsInterface (a.Type, TypeManager.idisposable_type)) {
4979 converted_var = var;
4983 Expression e = Convert.ImplicitConversionStandard (ec, a, TypeManager.idisposable_type, var.Location);
4985 Error_IsNotConvertibleToIDisposable (var);
4994 static public void Error_IsNotConvertibleToIDisposable (Expression expr)
4996 Report.SymbolRelatedToPreviousError (expr.Type);
4997 Report.Error (1674, expr.Location, "`{0}': type used in a using statement must be implicitly convertible to `System.IDisposable'",
4998 expr.GetSignatureForError ());
5001 protected override void EmitPreTryBody (EmitContext ec)
5003 assign.EmitStatement (ec);
5006 protected override void EmitTryBody (EmitContext ec)
5011 protected override void EmitFinallyBody (EmitContext ec)
5013 ILGenerator ig = ec.ig;
5015 if (!var.Type.IsValueType) {
5016 Label skip = ig.DefineLabel ();
5018 ig.Emit (OpCodes.Brfalse, skip);
5019 converted_var.Emit (ec);
5020 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5021 ig.MarkLabel (skip);
5023 Expression ml = Expression.MemberLookup(ec.ContainerType, TypeManager.idisposable_type, var.Type, "Dispose", Mono.CSharp.Location.Null);
5025 if (!(ml is MethodGroupExpr)) {
5027 ig.Emit (OpCodes.Box, var.Type);
5028 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5030 MethodInfo mi = null;
5032 foreach (MethodInfo mk in ((MethodGroupExpr) ml).Methods) {
5033 if (TypeManager.GetParameterData (mk).Count == 0) {
5040 Report.Error(-100, Mono.CSharp.Location.Null, "Internal error: No Dispose method which takes 0 parameters.");
5044 IMemoryLocation mloc = (IMemoryLocation) var;
5046 mloc.AddressOf (ec, AddressOp.Load);
5047 ig.Emit (OpCodes.Call, mi);
5052 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5054 assign.MutateHoistedGenericType (storey);
5055 var.MutateHoistedGenericType (storey);
5056 stmt.MutateHoistedGenericType (storey);
5059 public override bool Resolve (EmitContext ec)
5061 if (!ResolveVariable (ec))
5064 ec.StartFlowBranching (this);
5066 bool ok = stmt.Resolve (ec);
5068 ec.EndFlowBranching ();
5070 ResolveReachability (ec);
5072 if (TypeManager.void_dispose_void == null) {
5073 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5074 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5080 protected override void CloneTo (CloneContext clonectx, Statement t)
5082 Using target = (Using) t;
5084 target.var = var.Clone (clonectx);
5085 target.init = init.Clone (clonectx);
5086 target.stmt = stmt.Clone (clonectx);
5091 /// Implementation of the foreach C# statement
5093 public class Foreach : Statement {
5095 sealed class ArrayForeach : Statement
5097 class ArrayCounter : TemporaryVariable
5099 StatementExpression increment;
5101 public ArrayCounter (Location loc)
5102 : base (TypeManager.int32_type, loc)
5106 public void ResolveIncrement (EmitContext ec)
5108 increment = new StatementExpression (new UnaryMutator (UnaryMutator.Mode.PostIncrement, this));
5109 increment.Resolve (ec);
5112 public void EmitIncrement (EmitContext ec)
5114 increment.Emit (ec);
5118 readonly Foreach for_each;
5119 readonly Statement statement;
5122 TemporaryVariable[] lengths;
5123 Expression [] length_exprs;
5124 ArrayCounter[] counter;
5126 TemporaryVariable copy;
5129 public ArrayForeach (Foreach @foreach, int rank)
5131 for_each = @foreach;
5132 statement = for_each.statement;
5135 counter = new ArrayCounter [rank];
5136 length_exprs = new Expression [rank];
5139 // Only use temporary length variables when dealing with
5140 // multi-dimensional arrays
5143 lengths = new TemporaryVariable [rank];
5146 protected override void CloneTo (CloneContext clonectx, Statement target)
5148 throw new NotImplementedException ();
5151 public override bool Resolve (EmitContext ec)
5153 copy = new TemporaryVariable (for_each.expr.Type, loc);
5156 int rank = length_exprs.Length;
5157 ArrayList list = new ArrayList (rank);
5158 for (int i = 0; i < rank; i++) {
5159 counter [i] = new ArrayCounter (loc);
5160 counter [i].ResolveIncrement (ec);
5163 length_exprs [i] = new MemberAccess (copy, "Length").Resolve (ec);
5165 lengths [i] = new TemporaryVariable (TypeManager.int32_type, loc);
5166 lengths [i].Resolve (ec);
5168 ArrayList args = new ArrayList (1);
5169 args.Add (new Argument (new IntConstant (i, loc)));
5170 length_exprs [i] = new Invocation (new MemberAccess (copy, "GetLength"), args).Resolve (ec);
5173 list.Add (counter [i]);
5176 access = new ElementAccess (copy, list).Resolve (ec);
5180 Expression var_type = for_each.type;
5181 VarExpr ve = var_type as VarExpr;
5183 // Infer implicitly typed local variable from foreach array type
5184 var_type = new TypeExpression (access.Type, ve.Location);
5187 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5188 if (var_type == null)
5191 conv = Convert.ExplicitConversion (ec, access, var_type.Type, loc);
5197 ec.StartFlowBranching (FlowBranching.BranchingType.Loop, loc);
5198 ec.CurrentBranching.CreateSibling ();
5200 for_each.variable = for_each.variable.ResolveLValue (ec, conv, loc);
5201 if (for_each.variable == null)
5204 ec.StartFlowBranching (FlowBranching.BranchingType.Embedded, loc);
5205 if (!statement.Resolve (ec))
5207 ec.EndFlowBranching ();
5209 // There's no direct control flow from the end of the embedded statement to the end of the loop
5210 ec.CurrentBranching.CurrentUsageVector.Goto ();
5212 ec.EndFlowBranching ();
5217 protected override void DoEmit (EmitContext ec)
5219 ILGenerator ig = ec.ig;
5221 copy.EmitAssign (ec, for_each.expr);
5223 int rank = length_exprs.Length;
5224 Label[] test = new Label [rank];
5225 Label[] loop = new Label [rank];
5227 for (int i = 0; i < rank; i++) {
5228 test [i] = ig.DefineLabel ();
5229 loop [i] = ig.DefineLabel ();
5231 if (lengths != null)
5232 lengths [i].EmitAssign (ec, length_exprs [i]);
5235 IntConstant zero = new IntConstant (0, loc);
5236 for (int i = 0; i < rank; i++) {
5237 counter [i].EmitAssign (ec, zero);
5239 ig.Emit (OpCodes.Br, test [i]);
5240 ig.MarkLabel (loop [i]);
5243 ((IAssignMethod) for_each.variable).EmitAssign (ec, conv, false, false);
5245 statement.Emit (ec);
5247 ig.MarkLabel (ec.LoopBegin);
5249 for (int i = rank - 1; i >= 0; i--){
5250 counter [i].EmitIncrement (ec);
5252 ig.MarkLabel (test [i]);
5253 counter [i].Emit (ec);
5255 if (lengths != null)
5256 lengths [i].Emit (ec);
5258 length_exprs [i].Emit (ec);
5260 ig.Emit (OpCodes.Blt, loop [i]);
5263 ig.MarkLabel (ec.LoopEnd);
5266 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5268 for_each.expr.MutateHoistedGenericType (storey);
5270 copy.MutateHoistedGenericType (storey);
5271 conv.MutateHoistedGenericType (storey);
5272 statement.MutateHoistedGenericType (storey);
5274 for (int i = 0; i < counter.Length; i++) {
5275 counter [i].MutateHoistedGenericType (storey);
5276 if (lengths != null)
5277 lengths [i].MutateHoistedGenericType (storey);
5282 sealed class CollectionForeach : Statement
5284 class CollectionForeachStatement : Statement
5287 Expression variable, current, conv;
5288 Statement statement;
5291 public CollectionForeachStatement (Type type, Expression variable,
5292 Expression current, Statement statement,
5296 this.variable = variable;
5297 this.current = current;
5298 this.statement = statement;
5302 protected override void CloneTo (CloneContext clonectx, Statement target)
5304 throw new NotImplementedException ();
5307 public override bool Resolve (EmitContext ec)
5309 current = current.Resolve (ec);
5310 if (current == null)
5313 conv = Convert.ExplicitConversion (ec, current, type, loc);
5317 assign = new SimpleAssign (variable, conv, loc);
5318 if (assign.Resolve (ec) == null)
5321 if (!statement.Resolve (ec))
5327 protected override void DoEmit (EmitContext ec)
5329 assign.EmitStatement (ec);
5330 statement.Emit (ec);
5333 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5335 assign.MutateHoistedGenericType (storey);
5336 statement.MutateHoistedGenericType (storey);
5340 Expression variable, expr;
5341 Statement statement;
5343 TemporaryVariable enumerator;
5348 MethodGroupExpr get_enumerator;
5349 PropertyExpr get_current;
5350 MethodInfo move_next;
5351 Expression var_type;
5352 Type enumerator_type;
5353 bool enumerator_found;
5355 public CollectionForeach (Expression var_type, Expression var,
5356 Expression expr, Statement stmt, Location l)
5358 this.var_type = var_type;
5359 this.variable = var;
5365 protected override void CloneTo (CloneContext clonectx, Statement target)
5367 throw new NotImplementedException ();
5370 bool GetEnumeratorFilter (EmitContext ec, MethodInfo mi)
5372 Type return_type = mi.ReturnType;
5375 // Ok, we can access it, now make sure that we can do something
5376 // with this `GetEnumerator'
5379 if (return_type == TypeManager.ienumerator_type ||
5380 TypeManager.ienumerator_type.IsAssignableFrom (return_type) ||
5381 (!RootContext.StdLib && TypeManager.ImplementsInterface (return_type, TypeManager.ienumerator_type))) {
5383 // If it is not an interface, lets try to find the methods ourselves.
5384 // For example, if we have:
5385 // public class Foo : IEnumerator { public bool MoveNext () {} public int Current { get {}}}
5386 // We can avoid the iface call. This is a runtime perf boost.
5387 // even bigger if we have a ValueType, because we avoid the cost
5390 // We have to make sure that both methods exist for us to take
5391 // this path. If one of the methods does not exist, we will just
5392 // use the interface. Sadly, this complex if statement is the only
5393 // way I could do this without a goto
5396 if (TypeManager.bool_movenext_void == null) {
5397 TypeManager.bool_movenext_void = TypeManager.GetPredefinedMethod (
5398 TypeManager.ienumerator_type, "MoveNext", loc, Type.EmptyTypes);
5401 if (TypeManager.ienumerator_getcurrent == null) {
5402 TypeManager.ienumerator_getcurrent = TypeManager.GetPredefinedProperty (
5403 TypeManager.ienumerator_type, "Current", loc, TypeManager.object_type);
5408 // Prefer a generic enumerator over a non-generic one.
5410 if (return_type.IsInterface && return_type.IsGenericType) {
5411 enumerator_type = return_type;
5412 if (!FetchGetCurrent (ec, return_type))
5413 get_current = new PropertyExpr (
5414 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5415 if (!FetchMoveNext (return_type))
5416 move_next = TypeManager.bool_movenext_void;
5421 if (return_type.IsInterface ||
5422 !FetchMoveNext (return_type) ||
5423 !FetchGetCurrent (ec, return_type)) {
5424 enumerator_type = return_type;
5425 move_next = TypeManager.bool_movenext_void;
5426 get_current = new PropertyExpr (
5427 ec.ContainerType, TypeManager.ienumerator_getcurrent, loc);
5432 // Ok, so they dont return an IEnumerable, we will have to
5433 // find if they support the GetEnumerator pattern.
5436 if (TypeManager.HasElementType (return_type) || !FetchMoveNext (return_type) || !FetchGetCurrent (ec, return_type)) {
5437 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",
5438 TypeManager.CSharpName (return_type), TypeManager.CSharpSignature (mi));
5443 enumerator_type = return_type;
5449 // Retrieves a `public bool MoveNext ()' method from the Type `t'
5451 bool FetchMoveNext (Type t)
5453 MemberList move_next_list;
5455 move_next_list = TypeContainer.FindMembers (
5456 t, MemberTypes.Method,
5457 BindingFlags.Public | BindingFlags.Instance,
5458 Type.FilterName, "MoveNext");
5459 if (move_next_list.Count == 0)
5462 foreach (MemberInfo m in move_next_list){
5463 MethodInfo mi = (MethodInfo) m;
5465 if ((TypeManager.GetParameterData (mi).Count == 0) &&
5466 TypeManager.TypeToCoreType (mi.ReturnType) == TypeManager.bool_type) {
5476 // Retrieves a `public T get_Current ()' method from the Type `t'
5478 bool FetchGetCurrent (EmitContext ec, Type t)
5480 PropertyExpr pe = Expression.MemberLookup (
5481 ec.ContainerType, t, "Current", MemberTypes.Property,
5482 Expression.AllBindingFlags, loc) as PropertyExpr;
5491 // Retrieves a `public void Dispose ()' method from the Type `t'
5493 static MethodInfo FetchMethodDispose (Type t)
5495 MemberList dispose_list;
5497 dispose_list = TypeContainer.FindMembers (
5498 t, MemberTypes.Method,
5499 BindingFlags.Public | BindingFlags.Instance,
5500 Type.FilterName, "Dispose");
5501 if (dispose_list.Count == 0)
5504 foreach (MemberInfo m in dispose_list){
5505 MethodInfo mi = (MethodInfo) m;
5507 if (TypeManager.GetParameterData (mi).Count == 0){
5508 if (mi.ReturnType == TypeManager.void_type)
5515 void Error_Enumerator ()
5517 if (enumerator_found) {
5521 Report.Error (1579, loc,
5522 "foreach statement cannot operate on variables of type `{0}' because it does not contain a definition for `GetEnumerator' or is not accessible",
5523 TypeManager.CSharpName (expr.Type));
5526 bool IsOverride (MethodInfo m)
5528 m = (MethodInfo) TypeManager.DropGenericMethodArguments (m);
5530 if (!m.IsVirtual || ((m.Attributes & MethodAttributes.NewSlot) != 0))
5532 if (m is MethodBuilder)
5535 MethodInfo base_method = m.GetBaseDefinition ();
5536 return base_method != m;
5539 bool TryType (EmitContext ec, Type t)
5541 MethodGroupExpr mg = Expression.MemberLookup (
5542 ec.ContainerType, t, "GetEnumerator", MemberTypes.Method,
5543 Expression.AllBindingFlags, loc) as MethodGroupExpr;
5547 MethodInfo result = null;
5548 MethodInfo tmp_move_next = null;
5549 PropertyExpr tmp_get_cur = null;
5550 Type tmp_enumerator_type = enumerator_type;
5551 foreach (MethodInfo mi in mg.Methods) {
5552 if (TypeManager.GetParameterData (mi).Count != 0)
5555 // Check whether GetEnumerator is public
5556 if ((mi.Attributes & MethodAttributes.Public) != MethodAttributes.Public)
5559 if (IsOverride (mi))
5562 enumerator_found = true;
5564 if (!GetEnumeratorFilter (ec, mi))
5567 if (result != null) {
5568 if (TypeManager.IsGenericType (result.ReturnType)) {
5569 if (!TypeManager.IsGenericType (mi.ReturnType))
5572 MethodBase mb = TypeManager.DropGenericMethodArguments (mi);
5573 Report.SymbolRelatedToPreviousError (t);
5574 Report.Error(1640, loc, "foreach statement cannot operate on variables of type `{0}' " +
5575 "because it contains multiple implementation of `{1}'. Try casting to a specific implementation",
5576 TypeManager.CSharpName (t), TypeManager.CSharpSignature (mb));
5580 // Always prefer generics enumerators
5581 if (!TypeManager.IsGenericType (mi.ReturnType)) {
5582 if (TypeManager.ImplementsInterface (mi.DeclaringType, result.DeclaringType) ||
5583 TypeManager.ImplementsInterface (result.DeclaringType, mi.DeclaringType))
5586 Report.SymbolRelatedToPreviousError (result);
5587 Report.SymbolRelatedToPreviousError (mi);
5588 Report.Warning (278, 2, loc, "`{0}' contains ambiguous implementation of `{1}' pattern. Method `{2}' is ambiguous with method `{3}'",
5589 TypeManager.CSharpName (t), "enumerable", TypeManager.CSharpSignature (result), TypeManager.CSharpSignature (mi));
5594 tmp_move_next = move_next;
5595 tmp_get_cur = get_current;
5596 tmp_enumerator_type = enumerator_type;
5597 if (mi.DeclaringType == t)
5601 if (result != null) {
5602 move_next = tmp_move_next;
5603 get_current = tmp_get_cur;
5604 enumerator_type = tmp_enumerator_type;
5605 MethodInfo[] mi = new MethodInfo[] { (MethodInfo) result };
5606 get_enumerator = new MethodGroupExpr (mi, enumerator_type, loc);
5608 if (t != expr.Type) {
5609 expr = Convert.ExplicitConversion (
5612 throw new InternalErrorException ();
5615 get_enumerator.InstanceExpression = expr;
5616 get_enumerator.IsBase = t != expr.Type;
5624 bool ProbeCollectionType (EmitContext ec, Type t)
5626 int errors = Report.Errors;
5627 for (Type tt = t; tt != null && tt != TypeManager.object_type;){
5628 if (TryType (ec, tt))
5633 if (Report.Errors > errors)
5637 // Now try to find the method in the interfaces
5639 Type [] ifaces = TypeManager.GetInterfaces (t);
5640 foreach (Type i in ifaces){
5641 if (TryType (ec, i))
5648 public override bool Resolve (EmitContext ec)
5650 enumerator_type = TypeManager.ienumerator_type;
5652 if (!ProbeCollectionType (ec, expr.Type)) {
5653 Error_Enumerator ();
5657 bool is_disposable = !enumerator_type.IsSealed ||
5658 TypeManager.ImplementsInterface (enumerator_type, TypeManager.idisposable_type);
5660 VarExpr ve = var_type as VarExpr;
5662 // Infer implicitly typed local variable from foreach enumerable type
5663 var_type = new TypeExpression (get_current.PropertyInfo.PropertyType, var_type.Location);
5666 var_type = var_type.ResolveAsTypeTerminal (ec, false);
5667 if (var_type == null)
5670 enumerator = new TemporaryVariable (enumerator_type, loc);
5671 enumerator.Resolve (ec);
5673 init = new Invocation (get_enumerator, null);
5674 init = init.Resolve (ec);
5678 Expression move_next_expr;
5680 MemberInfo[] mi = new MemberInfo[] { move_next };
5681 MethodGroupExpr mg = new MethodGroupExpr (mi, var_type.Type, loc);
5682 mg.InstanceExpression = enumerator;
5684 move_next_expr = new Invocation (mg, null);
5687 get_current.InstanceExpression = enumerator;
5689 Statement block = new CollectionForeachStatement (
5690 var_type.Type, variable, get_current, statement, loc);
5692 loop = new While (move_next_expr, block, loc);
5694 wrapper = is_disposable ?
5695 (Statement) new DisposableWrapper (this) :
5696 (Statement) new NonDisposableWrapper (this);
5697 return wrapper.Resolve (ec);
5700 protected override void DoEmit (EmitContext ec)
5705 class NonDisposableWrapper : Statement {
5706 CollectionForeach parent;
5708 internal NonDisposableWrapper (CollectionForeach parent)
5710 this.parent = parent;
5713 protected override void CloneTo (CloneContext clonectx, Statement target)
5715 throw new NotSupportedException ();
5718 public override bool Resolve (EmitContext ec)
5720 return parent.ResolveLoop (ec);
5723 protected override void DoEmit (EmitContext ec)
5725 parent.EmitLoopInit (ec);
5726 parent.EmitLoopBody (ec);
5729 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5731 throw new NotSupportedException ();
5735 class DisposableWrapper : ExceptionStatement {
5736 CollectionForeach parent;
5738 internal DisposableWrapper (CollectionForeach parent)
5740 this.parent = parent;
5743 protected override void CloneTo (CloneContext clonectx, Statement target)
5745 throw new NotSupportedException ();
5748 public override bool Resolve (EmitContext ec)
5752 ec.StartFlowBranching (this);
5754 if (!parent.ResolveLoop (ec))
5757 ec.EndFlowBranching ();
5759 ResolveReachability (ec);
5761 if (TypeManager.void_dispose_void == null) {
5762 TypeManager.void_dispose_void = TypeManager.GetPredefinedMethod (
5763 TypeManager.idisposable_type, "Dispose", loc, Type.EmptyTypes);
5768 protected override void EmitPreTryBody (EmitContext ec)
5770 parent.EmitLoopInit (ec);
5773 protected override void EmitTryBody (EmitContext ec)
5775 parent.EmitLoopBody (ec);
5778 protected override void EmitFinallyBody (EmitContext ec)
5780 parent.EmitFinallyBody (ec);
5783 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5785 throw new NotSupportedException ();
5789 bool ResolveLoop (EmitContext ec)
5791 return loop.Resolve (ec);
5794 void EmitLoopInit (EmitContext ec)
5796 enumerator.EmitAssign (ec, init);
5799 void EmitLoopBody (EmitContext ec)
5804 void EmitFinallyBody (EmitContext ec)
5806 ILGenerator ig = ec.ig;
5808 if (enumerator_type.IsValueType) {
5809 MethodInfo mi = FetchMethodDispose (enumerator_type);
5811 enumerator.AddressOf (ec, AddressOp.Load);
5812 ig.Emit (OpCodes.Call, mi);
5814 enumerator.Emit (ec);
5815 ig.Emit (OpCodes.Box, enumerator_type);
5816 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5819 Label call_dispose = ig.DefineLabel ();
5821 enumerator.Emit (ec);
5822 ig.Emit (OpCodes.Isinst, TypeManager.idisposable_type);
5823 ig.Emit (OpCodes.Dup);
5824 ig.Emit (OpCodes.Brtrue_S, call_dispose);
5826 // 'endfinally' empties the evaluation stack, and can appear anywhere inside a finally block
5827 // (Partition III, Section 3.35)
5828 ig.Emit (OpCodes.Endfinally);
5830 ig.MarkLabel (call_dispose);
5831 ig.Emit (OpCodes.Callvirt, TypeManager.void_dispose_void);
5835 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5837 enumerator_type = storey.MutateType (enumerator_type);
5838 init.MutateHoistedGenericType (storey);
5839 loop.MutateHoistedGenericType (storey);
5844 Expression variable;
5846 Statement statement;
5848 public Foreach (Expression type, LocalVariableReference var, Expression expr,
5849 Statement stmt, Location l)
5852 this.variable = var;
5858 public Statement Statement {
5859 get { return statement; }
5862 public override bool Resolve (EmitContext ec)
5864 expr = expr.Resolve (ec);
5869 Report.Error (186, loc, "Use of null is not valid in this context");
5873 if (expr.Type == TypeManager.string_type) {
5874 statement = new ArrayForeach (this, 1);
5875 } else if (expr.Type.IsArray) {
5876 statement = new ArrayForeach (this, expr.Type.GetArrayRank ());
5878 if (expr.eclass == ExprClass.MethodGroup || expr is AnonymousMethodExpression) {
5879 Report.Error (446, expr.Location, "Foreach statement cannot operate on a `{0}'",
5880 expr.ExprClassName);
5884 statement = new CollectionForeach (type, variable, expr, statement, loc);
5887 return statement.Resolve (ec);
5890 protected override void DoEmit (EmitContext ec)
5892 ILGenerator ig = ec.ig;
5894 Label old_begin = ec.LoopBegin, old_end = ec.LoopEnd;
5895 ec.LoopBegin = ig.DefineLabel ();
5896 ec.LoopEnd = ig.DefineLabel ();
5898 statement.Emit (ec);
5900 ec.LoopBegin = old_begin;
5901 ec.LoopEnd = old_end;
5904 protected override void CloneTo (CloneContext clonectx, Statement t)
5906 Foreach target = (Foreach) t;
5908 target.type = type.Clone (clonectx);
5909 target.variable = variable.Clone (clonectx);
5910 target.expr = expr.Clone (clonectx);
5911 target.statement = statement.Clone (clonectx);
5914 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
5916 statement.MutateHoistedGenericType (storey);